Oria Gruber
Oria Gruber

Reputation: 1533

C language - turning input into code

Most of the times, the questions I ask have to do with a specific part of a code that i did incorrectly, or some bug that i overlooked, but this time, I don't know where to start. I don't even know if what I am trying to do is possible.

I was given an assignment to write a code that gets a string that resembles a variable declaration, for example int x,y; is a valid input. char c,*cptr,carray[80]; is another example of valid input.

The code will create what the user inputs, and will print how much memory it took. For instance, in the first example (int x,y;) the code will create 2 integers, and print "x requires 4 bytes, y requires 4 bytes".

In the second example, the code will create a character, a pointer to a character, and a string with 80 characters, and will print "c requires 1 byte, cptr requires 4 bytes, carray requires 80 bytes"

Is this even possible? It is not valid code to declare variables after the beginning of the code. They must be declared before anything else in C. So I don't see a way to do this...

Upvotes: 0

Views: 236

Answers (5)

Oria Gruber
Oria Gruber

Reputation: 1533

Figured I would post my solution just incase anyone is interested

void* q5(char* str_in)
{
    char runner;
    int i=0,memory,counter=0,arr_size;
    runner=str_in[i];
    while(1)
    {
        if(runner=='i') //the input is integer
        {
            memory=sizeof(int);
            break;
        }
        if(runner=='c') //input is char
        {
            memory=sizeof(char);
            break;
        }
        if(runner=='d') //input is double
        {
            memory=sizeof(double);
            break;
        }
        if(runner=='s') //input is short
        {
            memory=sizeof(short);
            break;
        }
        if(runner=='l') //input is long
        {
            memory=sizeof(long);
            break;
        }
        if(runner=='f') //input is float
        {
            memory=sizeof(float);
            break;
        }
    } //we know the type of data, skip in the string until first variable
    while(runner!=' ') //advance until you see empty space, signaling next variable
    {
        i++;
        runner=str_in[i];
    }
    while(runner==' ') //advance until you encounter first letter of new variable
    {
        i++;
        runner=str_in[i];
    } //runner is now first letter of first variable
    while(runner!=';') //run on the string until its over
    {
        if(runner==',') //if its ',', then spaces will occur, skip all of them to first char that isnt space
        {
            i++;
            runner=str_in[i];
            while(runner==' ')
            {
                i++;
                runner=str_in[i];
            } //runner now points to first letter of variable
            continue;
        }
        if(runner=='*') //current variable is a pointer
        {
            counter=counter+4; //pointers are always 4 bytes regardless of type!
            i++;
            runner=str_in[i];
            while((runner!=',')&&(runner!=';')) //while runner is still on this variable
            {
                printf("%c",runner);
                i++;
                runner=str_in[i];
            }
            printf(" requires 4 bytes\n"); //now runner is the first character after the variable we just finished
            continue;
        }
        while((runner!=',')&&(runner!=';')) //now is the case that runner is the first letter of a non pointer variable
        {
            printf("%c",runner);
            i++;
            runner=str_in[i];
            if((runner==',')||(runner==';')) //we are done
            {
                printf(" requires %d bytes\n",memory);
                counter+=memory;
                continue;
            }
            if(runner=='[') //this variable is an array
            {
                printf("[");
                i++;
                runner=str_in[i]; //runner is now MSB of size of array
                arr_size=0;
                while(runner!=']')
                {
                    printf("%c",runner);
                    arr_size*=10;
                    arr_size=arr_size+runner-48; //48 is ascii of 0
                    i++;
                    runner=str_in[i];
                } //arr_size is now whats written in the [ ]
                printf("] requires %d bytes\n",arr_size*memory);
                counter+=arr_size*memory;
                i++;
                runner=str_in[i]; // should be ',' since we just finished a variable
                continue;
            }
        }
    }
    printf("Overall %d bytes needed to allocate\n",counter);
    return (malloc(counter));
}

Upvotes: 0

Chris Dodd
Chris Dodd

Reputation: 126408

This is a parsing problem -- you need to parse the input string and figure out what it means. You don't need to actually "create" anything, you just need to figure out the sizes of the variables that the compiler would create for that code.

Parsing actually a very large subject, with lots of books written about it and tools written to make it easier. While you could use a tool like antlr or bison to complete this task, they're probably overkill -- a simple recursive descent hand-written parser is probably the best approach.

Something like:

const char *parse_declaration(const char *p) {
    /* parse a declaration, printing out the names and sizes of the variables
     * 'p' points at the beginning of the string containing the declaration, and the
     * function returns the pointer immediately after the end or NULL on failure */
    int size;
    if (!(p = parse_declspecs(p, &size))) return 0;
    do {
        const char *name;
        int namelen, declsize;
        if (!(p = parse_declarator(p, size, &name, &namelen, &declsize))) return 0;
        printf("%.*s requires %d bytes\n", namelen, name, declsize);
        p += strspn(p, " \t\r\n");  /* skip whitespace */
    } while (*p++ == ',');
    if (p[-1] != ';') return 0;
    return p;
}

const char *parse_declspecs(const char *p, int *size) {
    /* parse declaration specifiers (a type), and output the size of that type
     * p points at the string to be parsed, and we return the point after the declspec */
    p += strspn(p, " \t\r\n");
    if (!isalpha(*p)) return 0;
    int len = 0;
    while (isalnum(p[len])) len++;
    if (!strncmp(p, "char", len)) {
        *size = sizeof(char);
          return p+len; }
    if (!strncmp(p, "int", len)) {
        *size = sizeof(int);
        return p+len; }
    ... more type tests here ...
    if (!strncmp(p, "unsigned", len)) {
        p += len;
        p += strspn(p, " \t\r\n");
        if (!isalpha(*p)) {
            *size = sizeof(unsigned);
            return p; }
        while (isalnum(p[len])) len++;
        if (!strncmp(p, "int", len)) {
            *size = sizeof(unsigned int);
            return p+len; }
        ... more type tests here ...
    }
    return 0;
}

const char *parse_declarator(const char *p, int typesize, const char **name, int *namelen, int *declsize) {
    /* parse a declarator */
    p += strspn(p, " \t\r\n");
    while (*p == '*') {
        typesize = sizeof(void *); /* assuming all pointers are the same size...*/
        p++;
        p += strspn(p, " \t\r\n"); }
    declsize = typesize;
    if (isalpha(*p)) {
        *name = p;
         while (isalnum(*p) | *p == '_') p++;
        *namelen = p - *name;
    } else if (*p == '(') {
        if (!(p = parse_declarator(p+1, typesize, name, namelen, declsize))) return 0;
        p += strspn(p, " \t\r\n");
        if (*p++ != ')') return 0;
    } else
        return 0;
    p += strspn(p, " \t\r\n");
    while (*p == '[') {
        int arraysize, len;
        if (sscanf(++p, "%d %n", &arraysize, &len) < 1) return 0;
        p += len;
        declsize *= arraysize;
        if (*p++ != ']') return 0;
        p += strspn(p, " \t\r\n"); }
    return p;
}

should get you started...

Upvotes: 1

SevenBits
SevenBits

Reputation: 2874

Sure, you could do this with a type of parser. Assuming that you do not want to actually execute the code that you are given, you could read the string and then count how many times a variable of each specific type is declared, and calculate the amount of memory thusly. But, depending on the requirements of the professor, you may run into a view different issues.

In particular, the sizes of different types will likely be different on each processor. With the exception of char, you need to account for this. This is easy if you are analyzing the memory requirements for the computer that your program is executing on, as you could just have const variables whose values are assigned via sizeof to get the sizes, but if not, your program is more difficult, especially since you cannot presume to know the size of any variable.

Secondly, structs will be a problem do to some of the more interesting rules of C. Do you need to account for them?

So, this is entirely possible, because contrary to what you stated in your question, your code doesn't have to "create" a variable at all - it can just create an in-memory total for each type and print them out when done.

Upvotes: 0

John Bode
John Bode

Reputation: 123558

Sure, it's possible; it's just a bit of work. You're going to have to study C declaration syntax, and then write the code to recognize it (basically a small compiler front end).

For example, in the declaration

char c, *cptr, carray[80];

you have a sequence of tokens:

char c , * cptr , carray [ 80 ] ;

which will be recognized as a type specifier (char) followed by three declarators; a direct declarator, a pointer declarator, and an array declarator.

You can create the space for the objects dynamically using malloc or calloc. Then you'll need to create some kind of table to map the identifier (the variable name) to the dynamically-created object. You won't be able to treat these things as regular variables in regular C code; you're going to be doing a lot of table lookups and dereferencing.

Upvotes: 0

AlanZ2223
AlanZ2223

Reputation: 144

If you are trying to execute input code dynamically, to my knowledge that would not be possible without storing the code and then compiling again. This however seems like a very nasty and lengthy approach. If all you are trying to do however is calculate the size of declarations from input, what I would do is take the string received, call a function that analyzes/decomposes the string. So for example if the string has "int", "char", etc.. I know would know what kind of declaration I am dealing with, and after I know what declaration I am dealing with I could just count the number of variables declared and keep a counter in your example it was x,y. I would a loop on the counter and calculate the sizeof the type of declaration and how many were declared.

Upvotes: 0

Related Questions