我正在尝试使用lex / yacc构建计算器,您可以使用链接列表创建无限量的变量并在计算中使用它们。
当你输入“?”时,它应该打印出链表的内容,它会这样做,除了之后,它给我一个:syntax error
并结束我的程序。
计算器的其余部分按预期工作,我错过了什么?
示例输出
-bash-4.1$ ./calc
a = 42
b = 21
c = a / b
?
num-syms: 5
PHI => 1.61803
PI => 3.14159
a => 42
b => 21
c => 2
syntax error
-bash-4.1$
sym.h
#ifndef SYMTBL_H
#define SYMTBL_H
struct sym {
int length;
char * name;
double value;
struct sym *prev;
struct sym *next;
};
struct sym * sym_p;
struct sym * sym_lookup(char *);
void sym_inventory();
#endif /* SYMTBL_H */
calc.l
%{
/*#include <math.h> */
#include "y.tab.h"
#include "sym.h"
%}
%%
"?" { sym_inventory(); }
([0-9]+|([0-9]*\.[0-9]+)([eE][+-]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}
[ \t] ; /* ignore whitespace */
[A-Za-z][A-Za-z0-9]* {
/* return symbol pointer */
yylval.symptr = sym_lookup(yytext);
return NAME;
}
"$" { return 0; }
\n |
. { return yytext[0]; };
%%
int yywrap() { return 1; }
calc.y
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sym.h"
%}
%union {
double dval;
struct sym * symptr;
}
%token <symptr> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list
: statement '\n'
| statement_list statement '\n'
;
statement
: NAME '=' expression {
char *name = $1->name;
if (strcmp(name, "PI") != 0 && strcmp(name, "PHI") != 0) {
$1->value = $3;
} else {
yyerror("assign to const");
}
}
| expression { printf("= %g\n", $1); }
;
expression
: expression '+' expression { $$ = $1 + $3; }
| expression '-' expression { $$ = $1 - $3; }
| expression '*' expression { $$ = $1 * $3; }
| expression '/' expression {
if ($3 == 0) {
yyerror("divide by zero");
} else {
$$ = $1 / $3;
}
}
| '-' expression %prec UMINUS { $$ = -$2; }
| '(' expression ')' { $$ = $2; }
| NUMBER
| NAME { $$ = $1->value; }
;
%%
struct sym * sym_lookup(char * s)
{
if (sym_p == NULL) {
sym_p = (struct sym *)malloc(sizeof(struct sym));
}
struct sym *original = sym_p;
struct sym * sp = sym_p;
if (sp->name == NULL) {
struct sym *n = (struct sym *)malloc(sizeof(struct sym));
n->name = "PI";
n->value = 3.14159;
n->next = NULL;
sp->name = "PHI";
sp->value = 1.61803;
sp->next = n;
sp->length = 2;
}
while (1) {
if (sp->name == NULL) {
sp->name = strdup(s);
sp->next = NULL;
return sp;
} else if (strcmp(sp->name, s) == 0) {
return sp;
} else if (sp->next != NULL) {
sp = sp->next;
} else {
struct sym *n = (struct sym *)malloc(sizeof(struct sym));
n->name = strdup(s);
sp = original;
struct sym *old = NULL;
while (1) {
if (strcmp(sp->name, s) > 0) {
// new variable name comes before in ascii table
if (old == NULL) {
// new node insert at beginning of sym_p
n->next = original;
n->length = original->length;
sym_p = n;
original = sym_p;
sp = original;
} else {
// insert in middle and update links
old->next = n;
n->next = sp;
sp = original;
}
break;
} else {
if (sp->next != NULL) {
old = sp;
sp = sp->next;
} else {
sp->next = n;
break;
}
}
}
sp = original;
sp->length++;
return n;
}
}
}
void sym_inventory()
{
struct sym * sp = sym_p;
printf("num-syms: %d\n", sp->length);
int i;
int length = sp->length;
for (i = 0; i < length; i++) {
printf("\t%s => %g\n", sp->name, sp->value);
sp = sp->next;
}
}
答案 0 :(得分:1)
您的语法无法将空行识别为有效输入。当您键入?
后跟换行符时,会返回换行符,这在语法上无效,因此会出现“语法错误”报告。
例如,您可以通过键入?a + b
作为输入进行演示。
请注意,您的词法分析器会处理?
,而不会让解析器知道它发生了;解析器永远不会看到?
。
Lex代码的这种改编表明词法分析器将?
识别为输入。您的问题没有显示代码的使用方式,因此我们没有足够的信息来了解您的错误。您需要提供MCVE(Minimal, Complete, Verifiable Example),以便获得相关帮助。
%option noinput
%option nounput
%%
"?" { printf("Got a ?\n"); }
([0-9]+|([0-9]*\.[0-9]+)([eE][+-]?[0-9]+)?) {
printf("Number: %s\n", yytext);
}
[ \t] { printf("Space: [%s]\n", yytext); }
[A-Za-z][A-Za-z0-9]* {
printf("Name: %s\n", yytext);
}
"$" { printf("Dollar\n"); return 0; }
\n |
. { printf("Other: %c\n", yytext[0]); return yytext[0]; };
%%
int yywrap(void) { return 1; }
int main(void)
{
while (yylex() != 0)
;
return 0;
}
示例运行(shell提示符为JL:
而非$
,因为其中一个输入为$
):
JL: ./xy73
a b 23
Name: a
Space: [ ]
Name: b
Space: [ ]
Number: 23
Other:
?
Got a ?
Other:
=@%
Other: =
Other: @
Other: %
Other:
$
Dollar
JL:
答案 1 :(得分:1)
?后面跟一个换行符作为字符标记返回,但是你的语法只接受一个语句后的换行符,该语句总是以NAME开头。