使用柠檬解析器(LALR)生成一个计算器,如何从表达式中获取参数

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

我想从输入中获取参数。例如:Input:12+10。 在 运行 我的计算器之后。

我想得到 12 和 10。我知道,我必须使用 Parse(pParser, hTokenID, sTokenData, pArg); 中的第四个参数,但是如何呢?

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

如果你想通过第 4 个参数将一些数据传递到 lemon 的块中,你必须将以下行添加到你的 .y 文件中:

%extra_argument { const char* arg }

查看 lemon 的文档 (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().

但是请注意 "that is the value of the 4th parameter in the most recent call to Parse()"

所以,我相信你想传递准确的令牌值。在这种情况下,您必须将令牌值包装到结构中:

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

你的程序是这样修改的:

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

示例输出:

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$

为了以防万一,CMake 脚本编译这个示例:

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)