Как сделать грамматику с операндами присваивания (=) и равенства (==)? - PullRequest
1 голос
/ 07 июня 2019

В качестве задания по курсу Теории компилятора я должен написать калькулятор в lex / yacc. Некоторые из требований:

  • Пользователь должен иметь возможность назначать числовые или логические значения переменной
  • Калькулятор должен уметь сравнивать логические выражения

Если я попытаюсь ввести это:

a=5;
a; // Output: 5
a==5; // Syntax error

Тем не менее, это работает, если я введу это:

5==a; // Output: 1

Я думаю, проблема в том, что yacc читает a==5 как новое назначение формы <a> <=> <=5> вместо <a> <==> <5>. Тем не менее, я не знаю, как решить проблему. Я попытался использовать аннотацию% prec, но это не решило проблему.

Вот код: calc.l

%{
#include "y.tab.h"
void yyerror(char* s);
%}

%%
"exit"      { return exit_command; }
[0-9]+      { yylval.num = atoi(yytext); return number; }
[a-zA-Z]    { yylval.id = yytext[0]; return identifier; }
[ \t\n]     ;
[-+*/()^%;] { return yytext[0]; }
[<>=!&|]    { return yytext[0]; }
.       { ECHO; yyerror("Unexpected character"); }
%%

int yywrap(void) { return 1; }

calc.y

%{
void yyerror(char *s);
int yylex();

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

int yydebug = 1;

void print(int num);
int symbols[52];
int symbolVal(char symbol);
void updateSymbolVal(char symbol, int val);
%}

%union { int num; char id; }
%start line
%token exit_command
%token <num> number
%token <id> identifier
%type <num> line bool_exp arit_exp term factor 
%type <id> assignment

%%

line:   line bool_exp ';'   { print($2); }
    | bool_exp ';'      { print($1); }
    | line assignment ';'   {;}
    | assignment ';'    {;}
    | line equality ';' {;}
    | equality ';'      {;}
    | line exit_command { printf("Goodbye\n"); exit(EXIT_SUCCESS); }
    | exit_command      { printf("Goodbye\n"); exit(EXIT_SUCCESS); }
    ;

bool_exp:   arit_exp            { $$ = $1; }
        | bool_exp '<' arit_exp     { $$ = $1 < $3; }
        | bool_exp '>' '=' arit_exp { $$ = $1 >= $4; }
        | bool_exp '>' arit_exp     { $$ = $1 > $3; }
        | bool_exp '<' '=' arit_exp { $$ = $1 <= $4; }
        | bool_exp '=' '=' arit_exp { $$ = $1 == $4; }
        | bool_exp '!' '=' arit_exp { $$ = $1 != $4; }
        | '!' bool_exp          { $$ = !$2; }
        | bool_exp '&' '&' arit_exp { $$ = $1 && $4; }
        | bool_exp '|' '|' arit_exp { $$ = $1 || $4; }
        | '+' '+' identifier        { updateSymbolVal($3, symbolVal($3) + 1); $$ = symbolVal($3); }
        | '-' '-' identifier        { updateSymbolVal($3, symbolVal($3) - 1); $$ = symbolVal($3); }
        ;

arit_exp:   term            { $$ = $1; }
        | arit_exp '+' term { $$ = $1 + $3; }
        | arit_exp '-' term { $$ = $1 - $3; }
        ;

term:   factor          { $$ = $1;  }
        | term '*' factor   { $$ = $1 * $3;  }
        | term '/' factor   { $$ = $1 / $3;  }
    | term '^' factor   { $$ = pow($1, $3); }
    | term '%' factor   { $$ = $1 % $3; }
    | '-' factor        { $$ = $2 * -1; }
    ;

factor: number          { $$ = $1; }
    | identifier        { $$ = symbolVal($1); }
        | '(' bool_exp ')'  { $$ = $2; }
    ;

assignment: identifier '=' bool_exp { updateSymbolVal($1, $3); printf("> %c <- %i\n> ", $1, $3); } ;

%%

void print(int num) {
    printf("> Result: %i\n< ", num);
}

int computeSymbolIndex(char token) {
    int idx = -1;
    if (islower(token)) {
        idx = token - 'a' + 26;
    }
    else if (isupper(token)) {
        idx = token - 'A';
    }
    return idx;
}

/* Returns the value of a given symbol */
int symbolVal(char symbol) {
    int bucket = computeSymbolIndex(symbol);
    return symbols[bucket];
}

void updateSymbolVal(char symbol, int val) {
    int bucket = computeSymbolIndex(symbol);
    symbols[bucket] = val;
}

int main(void) {
    printf("Calculator made with lex and yacc\n");
    printf("Usage: <Arithmetic or boolean expression> <semicolon>\n");
    printf("e.g: 2+2;\n\n< ");
    return yyparse();
}

void yyerror(char *s) { fprintf(stderr, "Error: %s\n", s); }

1 Ответ

5 голосов
/ 07 июня 2019

Операторы должны быть токенами.Таким образом, вы получите сканер Lex с:

"=="   { return EQUAL; }

и так далее.Неправильно формировать токены в парсере, как вы это делаете сейчас с '=' '='.

. В файле Yacc вы можете определить ассоциативность операторов, используя %left TOKEN и %right TOKEN.Порядок их размещения определяет их приоритет (см. этот пост или yacc руководство).

...