我想编写一个混合了更多自由格式的“经典”BASIC语法,例如:
a=5:b=6:c=a+b // writing several instructrions into a single line
// using colon as separator
// but eliminating mandatory terminals (like ';' in c)
while (x < 3 ) { // condition has mandatory parentheses around, but
x=x+1:y=y+2 // body of 'while' may also have multi-statements line(s)
z=y+y // and may have several lines as well
}
要实现它,我设置了以下规则。我的flex会尽职尽责,正确地消除不必要的换行等等:
^[ \t\r\n]*\n /*ignore empty line */
\/\/.*\n ; /* skip comment */
[ \t\r\n]+ ; /* ignore whitespace */
\{[ \t\r\n]*\} ; return EMPTY;
\{[ \t\r\n]* ; return '{';
\}[ \t\r\n]* ; return '}';
不幸的是,stmt后跟冒号并不会强制编译器执行立即编译。 但是,在该行的后半部分通常会预期正在编译的代码。 这是我的野牛文件的结构。
%type <nPtr> stmt stmt_list expr
%%
line:
line stmt_list ':' { // <-- this is my problem
ex($2); freeNode($2);
}
| line stmt_list '\n' {
ex($2); freeNode($2);
}
| /* NULL */
;
stmt: VARIABLE '=' expr { $$ = opr('=', 2, $3, id($1)); }
| PORT '=' expr { $$ = opr('=', 2, $3, id($1)); }
| .... etc.
| WHILE '(' expr ')' EMPTY { $$ = opr(WHILE, 1, $3); }
| WHILE '(' expr ')' stmt_list { $$ = opr(WHILE, 2, $3, $5); }
;
stmt_list:
stmt { $$ = $1; }
| '{' stmt_list '}' { $$ = $2; }
| '{' error '}' { errorflag=1; }
| error '\n' { errorflag=1; }
;
expr:
INTEGER { $$ = con($1); }
| VARIABLE { $$ = id($1); }
| .... etc.
| '(' expr ')' { $$ = $2; }
;
如何修改它以达到预期的行为?
答案 0 :(得分:2)
关于野牛行动要记住的重要一点是,当右手边被完全解析时,它们会被执行。
考虑两个简单的递归制作:
a: N a | %empty ;
和
a: a N | %empty ;
让我们将它们应用于输入:
N1 N2 N3
使用右递归的情况(第一个),产品将如下(下标只是为了清晰):
a0 → N1 a1
a1 → N2 a2
a2 → N3 a3
a3 → ε
,解析树是:
a0
+-----+-----+
| |
| a1
| +---+---+
| | |
| | a2
| | +-+-+
| | | |
| | | a3
| | | |
N1 N2 N3 ε
而左递归的(第二个)产生:
a0 → a1 N3
a1 → a2 N2
a2 → a3 N1
a3 → ε
,解析树是:
a0
+-----+-----+
| |
a1 |
+---+---+ |
| | |
a2 | |
+-+-+ | |
| | | |
a3 | | |
| | | |
ε N1 N2 N3
要注意的重要一点是,在右递归的情况下,所有产品都包含整个输入字符串,因此解析器操作从从右到左进行。相反,在左递归的情况下,产生连续产生字符串的前缀,解析器操作发生在从左到右。
总之,如果您希望从左到右执行操作,请使用左递归。
答案 1 :(得分:2)
我看到你的例子存在许多严重问题
'\n'
(换行符)令牌,但您的词法分析器会忽略换行符并且永远不会返回它们,因此涉及换行符的规则永远不会减少WHILE
正文中,但您的语法不允许这样的因为这些,解析器甚至无法正确解析输入的第二行,更不用说进入循环了。