csqlitelemon

Use lemon parser(LALR) generate a calulator, how to get param from expressions


I want to get param from a input. For example: Input:12+10. After running my calculator.

I want to get 12 and 10. I know, I have to use the fourth param in Parse(pParser, hTokenID, sTokenData, pArg);, but how?

parser.y:

%syntax_error{fprintf(stderr, "Syntax error\n");}
%left PLUS MINUS.
%left TIMES DIVIDE.
program ::= expr(A).{printf("Result = %d\n", A);}
expr(A) ::= expr(B) PLUS expr(C).{A = B + C; }
expr(A) ::= expr(B) MINUS expr(C). {A = B - C; }
expr(A) ::= expr(B) TIMES expr(C). {A = B * C; }
expr(A) ::= expr(B) DIVIDE expr(C). {if (C != 0)A = B / C;else fprintf(stderr,"divide by 0");}
expr(A) ::= LPAR expr(B) RPAR. {A = (B);}
expr(A) ::= INTEGER(B).{A = B;}

calc.c:

int main(int argc, char ** argv){
pParser = (void *)ParseAlloc(malloc);
for (c = argv[1]; *c; c++){
switch (*c){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (value = 0; *c && *c >= '0' && *c <= '9'; c++)
    value = value * 10 + (*c - '0');
    c--;
    Parse(pParser, INTEGER, value);
    break;
case '+':
    Parse(pParser, PLUS, 0);
    break;
case '-':
    Parse(pParser, MINUS, 0);
    break;
case '*':
    Parse(pParser, TIMES, 0);
    break;
    ...(the rest case I dont write anymore,the same as before)
}
}
Parse(pParser, 0, 0);
ParseFree(pParser, free);
}

Solution

  • If you want to pass some data into lemon's blocks through 4-th parameter, you have to add into your .y file the following line:

    %extra_argument { const char* arg }
    

    See lemon's documentation (http://www.hwaci.com/sw/lemon/lemon.html):

    The %extra_argument directive

    The %extra_argument directive instructs Lemon to add a 4th parameter to the parameter list of the Parse() function it generates. Lemon doesn't do anything itself with this extra argument, but it does make the argument available to C-code action routines, destructors, and so forth. For example, if the grammar file contains:

    %extra_argument { MyStruct *pAbc }
    

    Then the Parse() function generated will have an 4th parameter of type MyStruct* and all action routines will have access to a variable named pAbc that is the value of the 4th parameter in the most recent call to Parse().

    But notice that "that is the value of the 4th parameter in the most recent call to Parse()"

    So, I believe that you want to pass exactly token value. In this case you have to wrap token value into structure:

    struct SToken
    {
        int value;
        const char* token;
    };
    

    Your program modified in this way:

    parse.y:

    %include
    {
    #include "types.h"
    
    #include "assert.h"
    }
    %syntax_error { fprintf(stderr, "Syntax error\n"); }
    %token_type { struct SToken* }
    %type expr { int }
    %left PLUS MINUS.
    %left TIMES DIVIDE.
    program ::= expr(A). { printf("Result = %d\n", A); }
    expr(A) ::= expr(B) PLUS expr(C). {A = B + C; }
    expr(A) ::= expr(B) MINUS expr(C). {A = B - C; }
    expr(A) ::= expr(B) TIMES expr(C). {A = B * C; }
    expr(A) ::= expr(B) DIVIDE expr(C).
    {
        if (C != 0)
        {
            A = B / C;
        }
        else
        {
            fprintf(stderr, "divide by 0");
        }
    }
    expr(A) ::= LPAR expr(B) RPAR. { A = B; }
    expr(A) ::= INTEGER(B).
    {
        A = B->value;
        printf("Passed argument: %s\n", B->token);
    }
    

    main.c:

    #include "types.h"
    #include "parse.h"
    
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char ** argv)
    {
        int value;
        void* pParser;
        const char *c;
        size_t i = 0;
        struct SToken v[argc];
    
        if (2 > argc)
        {
            printf("Usage: %s <expression>\n", argv[0]);
            return 1;
        }
    
        pParser = (void *) ParseAlloc(malloc);
        for (i = 1; i < argc; ++i)
        {
            c = argv[i];
            v[i].token = c;
            switch (*c)
            {
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    for (value = 0; *c && *c >= '0' && *c <= '9'; c++)
                        value = value * 10 + (*c - '0');
                    v[i].value = value;
                    Parse(pParser, INTEGER, &v[i]);
                    break;
    
                case '+':
                    Parse(pParser, PLUS, NULL);
                    break;
    
                case '-':
                    Parse(pParser, MINUS, NULL);
                    break;
    
                case '*':
                    Parse(pParser, TIMES, NULL);
                    break;
    
                case '/':
                    Parse(pParser, DIVIDE, NULL);
                    break;
    
                case '(':
                    Parse(pParser, LPAR, NULL);
                    break;
    
                case ')':
                    Parse(pParser, RPAR, NULL);
                    break;
    
                default:
                    fprintf(stderr, "Unexpected token %s\n", c);
            }
        }
        Parse(pParser, 0, NULL);
        ParseFree(pParser, free);
    
        return 0;
    }
    

    types.h:

    #ifndef __TYPES_H__
    #define __TYPES_H__
    
    #include <stdlib.h>
    
    struct SToken
    {
        int value;
        const char* token;
    };
    
    extern void *ParseAlloc(void *(*)(size_t));
    extern void Parse(void *, int, struct SToken*);
    void ParseFree(void *, void (*)(void*));
    
    #endif
    

    Sample output:

    veei@sauron:~/tmp/build$ ./test.it
    Usage: ./test.it <expression>
    veei@sauron:~/tmp/build$ ./test.it 12
    Passed argument: 12
    Result = 12
    veei@sauron:~/tmp/build$ ./test.it 12 + 12
    Passed argument: 12
    Passed argument: 12
    Result = 24
    veei@sauron:~/tmp/build$ ./test.it 12 - 12
    Passed argument: 12
    Passed argument: 12
    Result = 0
    veei@sauron:~/tmp/build$ ./test.it 12 "*" 12
    Passed argument: 12
    Passed argument: 12
    Result = 144
    veei@sauron:~/tmp/build$ ./test.it "(" 12 + 12 ")" "*" 2
    Passed argument: 12
    Passed argument: 12
    Passed argument: 2
    Result = 48
    veei@sauron:~/tmp/build$ ./test.it "(" 12 "*" 12 ")" "+" 2
    Passed argument: 12
    Passed argument: 12
    Passed argument: 2
    Result = 146
    veei@sauron:~/tmp/build$ ./test.it 12 / 12
    Passed argument: 12
    Passed argument: 12
    Result = 1
    veei@sauron:~/tmp/build$
    

    And just in case, CMake script to compile this sample:

    CMakeLists.txt:

    cmake_minimum_required(VERSION 3.0)
    
    project(lemon.test)
    
    add_executable(test.it main.c parse.c)
    
    add_custom_target(parser DEPENDS ${CMAKE_SOURCE_DIR}/parse.c)
    add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/parse.c COMMAND lemon -s ${CMAKE_SOURCE_DIR}/parse.y DEPENDS ${CMAKE_SOURCE_DIR}/parse.y)
    add_dependencies(test.it parser)