本人现在作一项目,需要进行词法语法分析:
本人借鉴常见的多功能计算器例子作为参考:在LEX文件中进行词法分析,用YACC文件中实现语法分析,但是现在多功能计算存在一个缺陷:每一行只能对一个变量赋值,不能在一行中对多个变量赋值,现在请根据已有的例子进行修改实现:
1:在一行中实现对一个或多个变量赋值;比如:A=56 C=78
2:赋值表达式之间可有可无空格,但不能有其它字符;比如:A=56C=78(可),A=56,C=78(不可)。
1)先给出LEX文件:TEST.LEX%{
#include "ch3-05_tab.h"
#include "ch3hdr2.h"
#include <math.h>
%}%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}[ \t] ;  /* ignore white space */[A-Za-z][A-Za-z0-9]* { /* return symbol pointer */
struct symtab *sp = symlook(yytext); yylval.symp = sp;
return NAME;
}"$" { return 0; }\n |
. return yytext[0];
%%int yywrap()
{
    return 1;
}2)再给出YACC文件 TEST.Y
%{
#include "ch3hdr2.h"
#include <string.h>
#include <math.h>
void yyerror(const char *errMsg); %}%union {
double dval;
struct symtab *symp;
}
%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS%type <dval> expression
%%
statement_list: statement '\n'
| statement_list statement '\n'
;statement: NAME '=' expression { $1->value = $3;  printf("\t%g\n", $1);}
| expression { printf("= %g\n", $1); }
| error { ; }
| '\n' { ; }
;expression: expression '+' expression { $$ = $1 + $3; }
| expression '-' expression { $$ = $1 - $3; }
| expression '*' expression { $$ = $1 * $3; }
| expression '/' expression
{ if($3 == 0.0)
yyerror("divide by zero");
else
$$ = $1 / $3;
}
| '-' expression %prec UMINUS { $$ = -$2; }
| '(' expression ')' { $$ = $2; }
| NUMBER
| NAME { $$ = $1->value; }
| NAME '(' expression ')' {
if($1->funcptr)
$$ = ($1->funcptr)($3);
else {
printf("%s not a function\n", $1->name);
$$ = 0.0;
}
}
;
%%
/* look up a symbol table entry, add if not present */
struct symtab *
symlook(s)
char *s;
{
char *p;
struct symtab *sp;

for(sp = symtab; sp < &symtab[NSYMS]; sp++) {
/* is it already here? */
if(sp->name && !strcmp(sp->name, s))
return sp;

/* is it free */
if(!sp->name) {
sp->name = strdup(s);
return sp;
}
/* otherwise continue to next */
}
yyerror("Too many symbols");
exit(1); /* cannot continue */
} /* symlook */addfunc(name, func)
char *name;
double (*func)();
{
struct symtab *sp = symlook(name);
sp->funcptr = func;
}main()
{
extern double sqrt(), exp(), log(); addfunc("sqrt", sqrt);
addfunc("exp", exp);
addfunc("log", log);
addfunc("sin", sin);
addfunc("cos", cos);
addfunc("tan", tan);
yyparse();
}void yyerror(const char *s)  
{
  printf ("%s\n", s);
}3)头文件:ch3hdr2.h
/*
 * Header for calculator program
 */#define NSYMS 20 /* maximum number of symbols */struct symtab {
char *name;
double (*funcptr)();
double value;
} symtab[NSYMS];struct symtab *symlook();

解决方案 »

  1.   

    问题有点麻烦:
    试试将
    statement_list: statement '\n'
    | statement_list statement '\n'
    ;
    改成:
    statement_list: statement 
    | statement_list statement 
    ;
    但这样将会产生6个移近/规约冲突。但这可以实现连续赋值的方式,不妨一试!
      

  2.   

    GZCompiler(编译器)
    应该是个高手吧!我对此也很有兴趣,
    GZCompiler(编译器)怎么不说呢?
      

  3.   

    a=4 b=5 或a=4b=5产生错误的原因是
    在你的test.y中:
    statement_list: statement '\n'
    | statement_list statement '\n'
    ;
    要求每个statement的结束符是'\n',就象C语言用';'作为statement的结束符一样。
    把'\n'才有可能实现a=4 b=5在同一行这样的赋值。
    但我试了一下,这会引入4个shift/reduce conflict.这样的conflict通常不是致命的,可以根据规则出现的顺序自动解决,即总是让shift先。就像你这个例子虽然有4个conflict,但是,
    $a=4b=5a+b
    结果已经可以输出9了。
    尽管如此,你仍然需要确认,上述的4个conflict不会引起其它的错误。
    当然另一个方法就是不删除'\n',而是用';'之类的可见字符作为statement结束符。
      

  4.   

    shift/reduce conflict
    是什么意思?我用AnaGram的syn文件时候,有时候也会出现,但是不会解决~
      

  5.   

    statement_list: statement '\n'
    | statement_list statement '\n'
    ;
    这一句是绝对错误的。建议将这一句再分解成两句,分解成
    statement_with_space和statement_withno_space。然后再statement_with_space ' '和statement句。应该就可以解决了。如果是'\n'的话,则一直要到遇到了换行符才会匹配这一句。至于楼上有朋友说会产生6个移进和规约冲突,我不太明白是怎么出来的?