-->

Use lemon parser(LALR) generate a calulator, how t

2019-08-13 20:55发布

问题:

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);
}

回答1:

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)


标签: c sqlite lemon