Используйте лимонный парсер (LALR) для генерации калькулятора, как получить параметр из выражений

Я хочу получить параметр из входных данных. Например: 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);
}

1 ответ

Решение

Если вы хотите передать некоторые данные в lemonблоки через 4-й параметр, вы должны добавить в свой .y подайте следующую строку:

%extra_argument { const char* arg }

См. Лимонную документацию ( http://www.hwaci.com/sw/lemon/lemon.html):

%extra_argumentдиректива

%extra_argument Директива указывает Lemon добавить 4-й параметр в список параметров Parse() функция, которую он генерирует. Lemon ничего не делает сам с этим дополнительным аргументом, но он делает этот аргумент доступным для подпрограмм действия C-кода, деструкторов и так далее. Например, если файл грамматики содержит:

%extra_argument { MyStruct *pAbc }

Тогда Parse() сгенерированная функция будет иметь 4-й параметр типа MyStruct* и все подпрограммы действий будут иметь доступ к переменной с именем pAbc это значение 4-го параметра в последнем вызове Parse(),

Но обратите внимание, что "это значение 4-го параметра в последнем вызове 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)
Другие вопросы по тегам