1
\$\begingroup\$

This is my lex code to convert possibly nested for and do while loops into while loops. (Other control flows like if or switch are not supported.) The input program must be in a file called input.c and the output will be saved in a file called out.c. Please review the code and give suggestions regarding improving it.

%{ 
#include <iostream>
#include<vector>
#include<tuple>

using namespace std;

string foreword,declaration,condition1,condition2;
string loopBody = "";
vector<string> change;
vector<tuple<int,int>> stack;
int ind=0;
int changeNum = -1;
int braces=0;

void forEnd();
%} 
%x FOR DOWHILE dec condtn1 condtn2 chng text oneLineFor semicolon forString doWhileString normalString
%% 
<DOWHILE>\" {
    ECHO;
    loopBody += yytext;
    BEGIN doWhileString;
}
<text>\" {
    ECHO;
    loopBody += yytext;
    BEGIN forString;
}
<doWhileString>\" {
    ECHO;
    loopBody += yytext;
    BEGIN DOWHILE;
}
<forString>\" {
    ECHO;
    loopBody += yytext;
    BEGIN text;
}
<doWhileString>. {
    ECHO;
    loopBody += yytext;
}
<forString>. {
    ECHO;
    loopBody += yytext;
}
do[ ]*\{ {
    stack.push_back(make_tuple(0,ind));
    BEGIN DOWHILE;
}
<text>do[ ]*\{ {
    stack.push_back(make_tuple(0,ind));
    ind = loopBody.size();
    BEGIN DOWHILE;
}
<DOWHILE>do[ ]*\{ {
    stack.push_back(make_tuple(0,ind));
    ind = loopBody.size();
    BEGIN DOWHILE;
}
<DOWHILE>\} {
    if(braces){
        ECHO;
        braces--;
    }
}
<DOWHILE>for {
    stack.push_back(make_tuple(2,ind));
    ind = loopBody.size();
    BEGIN FOR;
    changeNum++;
}
<DOWHILE>while[ ]*\( {
    ECHO;
    BEGIN condtn2;
}
<condtn2>[^\)]*\) {
    condition2 = yytext;
    condition2 = condition2.substr(0,condition2.size()-1);
    fprintf(yyout,"%s{\n",yytext);
    BEGIN semicolon;
}
<semicolon>; {

    fprintf(yyout,"%s}",loopBody.substr(ind).c_str());
    if(stack.size()==1){
        BEGIN 0;
        loopBody = "";
    } else{

        loopBody += "while("+condition2+"){" + loopBody.substr(ind) + "}";
        ind = get<1>(stack.back());
        stack.pop_back();
        int s = get<0>(stack.back());
        if(s==0) {
            BEGIN DOWHILE;
        } else if (s==1){
            BEGIN text;
        } else if(s==2){
            forEnd();
        }
    }

}
<text>for {
    stack.push_back(make_tuple(2,ind));
    ind = loopBody.size();
    BEGIN FOR;
    changeNum++;
}
for {   
        stack.push_back(make_tuple(2,ind));
        ind = loopBody.size();
        changeNum++;
        BEGIN FOR;
}
<FOR>[ ]*\( {
            foreword=yytext;
            BEGIN dec;
        }
<dec>[^\;]*\;   {
            declaration=yytext; 
            BEGIN condtn1;
        }
<condtn1>[^\;]*\;   {
            condition1=yytext; 
            condition1 = condition1.substr(0,condition1.size()-1);
            BEGIN chng;
            }
<chng>[^\)]*\)  {
            change.push_back(yytext);
            change[changeNum] = change[changeNum].substr(0, change[changeNum].size()-1);
            fprintf(yyout, "%s\n", declaration.c_str());
            fprintf(yyout, "while%s%s){", foreword.c_str(), condition1.c_str());
            BEGIN oneLineFor;
        }       
<oneLineFor>\{ {
    get<0>(stack.back()) = 1;
    BEGIN text;
}
<oneLineFor>for {
    stack.push_back(make_tuple(2,ind));
    ind = loopBody.size();
    BEGIN FOR;
    changeNum++;
}
<oneLineFor>do[ ]*\{ {
    stack.push_back(make_tuple(0,ind));
    ind = loopBody.size();
    BEGIN DOWHILE;
}
<oneLineFor>; {
    ECHO;
    forEnd();
}
<oneLineFor>\n {
    ECHO;
    loopBody+=yytext;
}
<oneLineFor>. {
    ECHO;
    loopBody+=yytext;
}
<text>\{ {
    ECHO; 
    braces++;
}
<text>\} {
    if(braces){
        ECHO;
        braces--;
    }else{
        forEnd();
    }
}

<text>\n {
    loopBody+=yytext;
    ECHO;
}
<DOWHILE>\n {
    loopBody+=yytext;
    ECHO;
}
<text>. {
    loopBody+=yytext;
    ECHO;
}
<DOWHILE>. {
    loopBody+=yytext;
    ECHO;
}
\" {
    ECHO;
    BEGIN normalString;
}
<normalString>\" {
    ECHO;
    BEGIN 0;
}
<normalString>. {
    ECHO;
}

. {ECHO;}
%% 
int yywrap(){return 1;} 
void forEnd(){
    string tempChange = change[changeNum];
    change.pop_back();
    changeNum--;
    fprintf(yyout,"%s;}",tempChange.c_str());

    if(stack.size()==1){
        BEGIN 0;
    } else{
        loopBody = loopBody.substr(0,ind) + declaration + "\nwhile(" + condition1 + "){" + loopBody.substr(ind) + tempChange + ";}";
        ind = get<1>(stack.back());
        stack.pop_back();
        int s = get<0>(stack.back());
        if(s==0) {
            BEGIN DOWHILE;
        } else if (s==1){
            BEGIN text;
        } else if (s==2){

            forEnd();
        }
    }
}
int main() 
{ 
    extern FILE *yyin, *yyout; 
    yyin = fopen("input.c", "r"); 
    yyout = fopen("out.c", "w"); 
    yylex(); 
    return 0; 
} 
\$\endgroup\$
6
  • 2
    \$\begingroup\$ why the downvote? at least comment what is wrong so I can improve the question \$\endgroup\$ Mar 4, 2020 at 9:59
  • 4
    \$\begingroup\$ I think people are falling over the following: "this currently doesn't work if the loop is inside...". It is not clear if you're looking for a fix or for a review. If the current behaviour is acceptable, the question is ok. Please clarify, after looking at our FAQ on asking questions. \$\endgroup\$
    – Mast
    Mar 4, 2020 at 10:07
  • 1
    \$\begingroup\$ @Mast i am not asking for fix. I want a review. I would have posted on Stackoverflow if I wanted a fix \$\endgroup\$ Mar 4, 2020 at 11:11
  • \$\begingroup\$ Welcome to Code Review! Asking for advice on code that fails to work properly is off-topic for this site (regardless of whether you are asking for a fix). See What topics can I ask about? for reference. Once you have written working code, you're welcome to ask a new question here and we can then help you improve it! \$\endgroup\$
    – L. F.
    Mar 4, 2020 at 11:11
  • 7
    \$\begingroup\$ OK ... That really wasn't apparent. I've reworded the question slightly for you. However, your description is very general - there are many ways to convert for loops to do while loops. Please add some test cases (input, output) to show the intended usage of the program as well as to manifest that your code is really working. After you do this, your question will be on-topic for the site. \$\endgroup\$
    – L. F.
    Mar 4, 2020 at 11:38

1 Answer 1

0
\$\begingroup\$

I see a number of things that may help you improve your program.

Don't abuse using namespace std

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid.

Use consistent formatting

The code as posted has inconsistent formatting. For example, some places use tabs for indentation and other use four or eight spaces and sometimes there is a space after #include and sometimes not (I prefer the space). Pick a style and apply it consistently.

Describe your program better

For code reviewers and for any reader of your code, there should be a clear explanation of the purpose of the code. As one comment noted, the description "convert do a for loops into while loops" is quite vague. What transformations are being done and why? What is the expected output? Is it supposed to handle macros, for example?

Test your code

There's not much evidence here that the code has been tested, and a test stub is often a good way to both make sure there are fewer errors in the code and also to convey what the program is intended to do. For example, I used this sample input:

#include <stdio.h>
/* This is a comment and shouldn't be touched...
for (int i=10; i; --i) {
*/

int main() {
    int x = 0;

    for (int i=10; i; --i) {
        for (int j = i; j; --j, x+=i*j) {
            for (int x=1; x; --x) 
                printf("%d\n", i*j);
        }
    }

    while (x > 10000) 
        x >>= 1;

    printf("%d\n", x);
}

This results in a program which compiles and runs without error. If we use this software to transform it, it still results in a program which compiles and runs without error, but the output is completely different and never terminates!

Rethink your approach

The current lexer works but relies on thirteen different possible states which is a heavy cognitive load for any programmer reading or modifying this code. As an example, there is not really much difference between the FOR state and the oneLineFor state and I strongly suspect they should be combined.

Eliminate redundant rules

The code contains these four rules:

<text>\n {
    loopBody+=yytext;
    ECHO;
}
<DOWHILE>\n {
    loopBody+=yytext;
    ECHO;
}
<text>. {
    loopBody+=yytext;
    ECHO;
}
<DOWHILE>. {
    loopBody+=yytext;
    ECHO;

They can all be reduced to this single rule:

<text,DOWHILE>.|\n {
    loopBody+=yytext;
    ECHO;
}
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.