rswier / c4

C in four functions
GNU General Public License v2.0
9.72k stars 1.43k forks source link

Attempt to add limited "for" support #80

Closed wololoa closed 3 months ago

wololoa commented 3 months ago

Hello,

This isn't really an issue with c4 itself, but rather with my poor implementation. I'm trying to add limited "for" loop support (just for a very simple case of the form for (i = X; i < Y; i++) ) and for some reason the loop iterates one time more than it should. I assume the issue is with my code (rather than expr(Cond)), but I have been looking at it for far too long without finding the culprit.

This is my attempt (right after While on void stmt()):

        char* pptp; // aux variable

        // ... 
        case For:
            next();
            if (tk == '(') next(); else { printf("%lld: open paren expected\n", line); exit(-1); }
            expr(Assign);               // parse first assignment (ie. i = 0)

            if (tk == ';') next(); else { printf("%lld: semicolon expected\n", line); exit(-1); }
            a = e + 1;                  // label A, before the conditional part
            expr(Cond);                 // condition (ie. i < 10)
            *++e = BZ;                  // jump if zero / end loop
            b = ++e;                    // label B

            if (tk == ';') next(); else { printf("%lld: semicolon expected\n", line); exit(-1); }
            pptp = p;                   // register current p BEFORE the ADD expression
            while(tk != ')') next();    // skip the Add section now, we will execute after statement
            next();
            stmt();                     // execute the statement first
            p = pptp - 1;               // go back to the place in the program where our Add was
            next();
            expr(Add);                  // execute Add (to respect the C order of execution) (ie. i++)

            if (tk == ')') next(); else { printf("%lld: close paren expected\n", line); exit(-1); }
            *++e = JMP;                 // jump back to A
            *++e = (int)a;
            *b = (int)(e + 1);
        return;

The issue I'm having is that the following example code:

    int i;
    for( i = 0; i < 5; i++)
    {
        printf("%d ", i);
    }

Prints 0 1 2 3 4 5 instead of 0 1 2 3 4. Any ideas are welcome!

rswier commented 3 months ago

My guess is that before p is set to pptp-1, you first need to save it and finally restore it at the very end before the return. If not, the body of the for loop will be parsed and processed again duplicating the printf().

wololoa commented 3 months ago

You were right, it seems to be working as expected now! Thank you so much. In case this helps someone:

        char* pptp, ppex; // aux variables

        // ... 
        case For:
            next();
            if (tk == '(') next(); else { printf("%lld: open paren expected\n", line); exit(-1); }
            expr(Assign);               // parse first assignment (i = 0)

            if (tk == ';') next(); else { printf("%lld: semicolon expected\n", line); exit(-1); }
            a = e + 1;                  // label A, before the conditional part
            expr(Cond);                 // condition of the type (i < 10)
            *++e = BZ;                  // jump if zero, skip back to label B
            b = ++e;                    // label B

            if (tk == ';') next(); else { printf("%lld: semicolon expected\n", line); exit(-1); }
            pptp = p;                   // register current p BEFORE the ADD expression
            while(tk != ')') next();    // skip the Add and everything to the statement section
            next();
            stmt();                     // execute the statement

            ppex = p;                   // save current program point after the statement, to not repeat it once we end the loop
            p = pptp - 1;               // go back to the place in the program where our Add was

            next();
            expr(Add);                  // execute Add (after statement, to respect the C order of execution), (i++)

            if (tk == ')') next(); else { printf("%lld: close paren expected\n", line); exit(-1); }

            *++e = JMP;
            *++e = (int)a;              // jump back to A
            *b = (int)(e + 1);

            next();
            p = ppex;                   // move back the program to the end of the loop (statement) and continue from there
        return;