【问题标题】:Left associative grammar for parser解析器的左联想文法
【发布时间】:2018-11-05 22:52:10
【问题描述】:

我有一个关于我用 C 语言编写的解析器的问题。假设使用左递归(为运算符提供左关联性),然后以后缀表示法返回表达式。专门针对我的 Expr 和 Term 规则。目前我遇到了一个我认为这不会发生的问题。这是我的语法:

extern int yylex(); /* The next token function. */
extern char *yytext; /* The matched token text. */
extern int yyleng; /* The token text length. */
void yyerror(char *s);
#define YYSTYPE long /* 64 bit so can hold pointer and int */
%}

// These token definitions populate y.tab.h
// Single character tokens are their own token numbers (e.g. scanner returns
// the value ';' for the semicolon token)
%token INT_TOK 1
%token CHR_TOK 2
%token ASSIGN_TOK 3
%token INTLIT_TOK 4
%token IDENT_TOK 5

%%
Prog    : IDENT_TOK '{' StmtSeq '}'    { $$ = (long) strdup(yytext); }  ;
StmtSeq : Stmt ';' StmtSeq                                                        ;
StmtSeq :                                                                         ;

Assign  : LHS ASSIGN_TOK Expr         { printf("%s =\n",(char *)$1); } ;
LHS     : IDENT_TOK                   { $$ = (long) strdup(yytext);  } ;

Stmt    : Decl;
Stmt    : Assign;
Decl    : Type IDLst;
Type    : INT_TOK;
Type    : CHR_TOK;
IDLst   : IDENT_TOK MLst;
MLst    : ',' IDLst;
MLst    : ;
Expr    : Term MExpr;
MExpr   : AddOp Term MExpr            { printf("%s ",(char *)$1); } ;
MExpr   : ;
Term    : Factor MTerm;
MTerm   : MultOp Factor MTerm         { printf("%s ",(char *)$1); } ;
MTerm   : ;
Factor  :  '(' Expr ')';
Factor  :  '-' Factor;
Factor  : INTLIT_TOK                  { printf("%s ",yytext); }
Factor  : IDENT_TOK                   { printf("%s ",yytext); }
AddOp   : '-'                                               { $$ = (long) strdup(yytext); }  ;
AddOp   : '+'                                               { $$ = (long) strdup(yytext); }  ;
MultOp  : '*'                                               { $$ = (long) strdup(yytext); }  ;
MultOp  : '/'                                               { $$ = (long) strdup(yytext); }  ;
%%

我正在使用的测试文件是这样的:

test1 {
  int amt, box;
  int x, y, w;
  x := 4 - 2 - 1;             // 4 2 - 1 - x =
  amt := 5 * y - 2;           // 5 y * 2 - amt =
  x := 5 * (y - 2);           // 5 y 2 - * x =
  box := 5 * x / amt + 3 * 4; // 5 x * amt / 3 4 * + box =
  y := z; w:= 1;              // z y =    1 w =
  }

注释的表达式表示我应该得到的输出。因此我的语法应该返回,

  1. x := 4 - 2 - 1; should produce 4 2 - 1 - x =
  2. amt := 5 * y - 2; should produce 5 y * 2 - amt =
  3. x := 5 * (y - 2); should produce 5 y 2 - * x =
  4.box := 5 * x / amt + 3 * 4; should produce 5 x * amt / 3 4 * + box =
  5. y := z; w:= 1;   should produce z y =    1 w =

我的语法回来了,

1. 4 2 1 - - x =
2. I get the correct output
3. I get the correct output
4. I get the correct output
5. 5 x amt / * 3 4 * + box =

根据我的理解,我的运算符似乎没有关联。有人知道为什么会这样吗?

【问题讨论】:

  • 该语法不是左递归的。左递归将是expr: term | expr AddOp term
  • 此外,在 yacc 操作中引用 yytext 几乎总是错误的。如果您想允许两种可能的语义类型,请使用语义联合。
  • 除了 rici 所说的:绝对不能保证long 在 64 位平台上有 64 位(例如,sizeof(long) 在 32 位和 64 位上都是 4 Windows),因此将指针存储在 long 中是不安全的。
  • 如果你想要一个大到足以容纳指针的整数类型,你需要intptr_tuintptr_t。但在这种情况下,你真的想要一个 %union,就像 rici 所说的那样。
  • 顺便说一句,像 Yacc 中的 LALR(1) 解析器生成实际上非常适合左递归;无论递归的深度如何,左递归都以恒定的 Yacc 堆栈深度运行。这是因为生成的状态机散布了 shift 和 reduce 动作。右递归使 Yacc 解析器消化不良;部分推导堆积在 Yacc 堆栈上,然后在到达表达式末尾时进行级联归约。

标签: c regex parsing yacc lex


【解决方案1】:

左递归产生式是一种产生式,其中产生的非终结符是右侧的第一个(最左边)符号。例如,

MTerm: MTerm MultOp Factor

右递归产生式是其中产生的非终结符是右侧最后一个(最右边的)符号。例如,

MTerm: MultOp Factor MTerm

您的语法没有左递归规则,但有许多右递归规则。所以它不会产生左结合性也就不足为奇了。

语法似乎是为了生成 LL 语法而尝试删除左递归的结果,尽管您似乎正在使用 yacc/bison 并且作业假设左递归是可能的,这两者都表明你需要一个LR语法。 LR 文法既不需要左分解也不需要左递归消除。

【讨论】:

  • 感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
相关资源
最近更新 更多