如何在 Flex/bison 中实现 If 语句
我没有收到错误消息,请您帮帮我,这是 .l 和 .y 文件。谢谢。
%{
#include "ifanw.tab.h"
extern int yylval;
%}
%%
"=" { return EQ; }
"!=" { return NE; }
"<" { return LT; }
"<=" { return LE; }
">" { return GT; }
">=" { return GE; }
"+" { return PLUS; }
"-" { return MINUS; }
"*" { return MULT; }
"/" { return DIVIDE; }
")" { return RPAREN; }
"(" { return LPAREN; }
":=" { return ASSIGN; }
";" { return SEMICOLON; }
"IF" { return IF; }
"THEN" { return THEN; }
"ELSE" { return ELSE; }
"FI" { return FI; }
"WHILE" { return WHILE; }
"DO" { return DO; }
"OD" { return OD; }
"PRINT" { return PRINT; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
[a-z] { yylval = yytext[0] - 'a'; return NAME; }
\ { ; }
\n { nextline(); }
\t { ; }
"//".*\n { nextline(); }
. { yyerror("illegal token"); }
%%
Yacc 文件
%start ROOT
%token EQ
%token NE
%token LT
%token LE
%token GT
%token GE
%token PLUS
%token MINUS
%token MULT
%token DIVIDE
%token RPAREN
%token LPAREN
%token ASSIGN
%token SEMICOLON
%token IF
%token THEN
%token ELSE
%token FI
%token WHILE
%token DO
%token OD
%token PRINT
%token NUMBER
%token NAME
%%
ROOT:
stmtseq { execute($1); }
;
statement:
designator ASSIGN expression { $$ = assignment($1, $3); }
| PRINT expression { $$ = print($2); }
| IF expression THEN stmtseq ELSE stmtseq FI
{ $$ = ifstmt($2, $4, $6); }
| IF expression THEN stmtseq FI
{ $$ = ifstmt($2, $4, empty()); }
| WHILE expression DO stmtseq OD { $$ = whilestmt($2, $4); }
;
stmtseq:
stmtseq SEMICOLON statement { $$ = seq($1, $3); }
| statement { $$ = $1; }
;
expression:
expr2 { $$ = $1; }
| expr2 EQ expr2 { $$ = eq($1, $3); }
| expr2 NE expr2 { $$ = ne($1, $3); }
| expr2 LT expr2 { $$ = le($1, $3); }
| expr2 LE expr2 { $$ = le($1, $3); }
| expr2 GT expr2 { $$ = gt($1, $3); }
| expr2 GE expr2 { $$ = gt($1, $3); }
;
expr2:
expr3 { $$ == $1; }
| expr2 PLUS expr3 { $$ = plus($1, $3); }
| expr2 MINUS expr3 { $$ = minus($1, $3); }
;
expr3:
expr4 { $$ = $1; }
| expr3 MULT expr4 { $$ = mult($1, $3); }
| expr3 DIVIDE expr4 { $$ = divide ($1, $3); }
;
expr4:
PLUS expr4 { $$ = $2; }
| MINUS expr4 { $$ = neg($2); }
| LPAREN expression RPAREN { $$ = $2; }
| NUMBER { $$ = number($1); }
| designator { $$ = $1; }
;
designator:
NAME { $$ = name($1); }
;
%%
我还有一个问题, 是否有可能像汇编器中那样使用 flex/bison 实现 JMP 指令以转到像我的示例一样的标签,感谢您的帮助。
:L1
IF FLAG AND X"0001"
EVT 23;
ELSE
WAIT 500 ms;
JMP L1;
END IF;
I dont get the error, please can you help me out, here is the .l and .y file.thanks.
%{
#include "ifanw.tab.h"
extern int yylval;
%}
%%
"=" { return EQ; }
"!=" { return NE; }
"<" { return LT; }
"<=" { return LE; }
">" { return GT; }
">=" { return GE; }
"+" { return PLUS; }
"-" { return MINUS; }
"*" { return MULT; }
"/" { return DIVIDE; }
")" { return RPAREN; }
"(" { return LPAREN; }
":=" { return ASSIGN; }
";" { return SEMICOLON; }
"IF" { return IF; }
"THEN" { return THEN; }
"ELSE" { return ELSE; }
"FI" { return FI; }
"WHILE" { return WHILE; }
"DO" { return DO; }
"OD" { return OD; }
"PRINT" { return PRINT; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
[a-z] { yylval = yytext[0] - 'a'; return NAME; }
\ { ; }
\n { nextline(); }
\t { ; }
"//".*\n { nextline(); }
. { yyerror("illegal token"); }
%%
Yacc-file
%start ROOT
%token EQ
%token NE
%token LT
%token LE
%token GT
%token GE
%token PLUS
%token MINUS
%token MULT
%token DIVIDE
%token RPAREN
%token LPAREN
%token ASSIGN
%token SEMICOLON
%token IF
%token THEN
%token ELSE
%token FI
%token WHILE
%token DO
%token OD
%token PRINT
%token NUMBER
%token NAME
%%
ROOT:
stmtseq { execute($1); }
;
statement:
designator ASSIGN expression { $ = assignment($1, $3); }
| PRINT expression { $ = print($2); }
| IF expression THEN stmtseq ELSE stmtseq FI
{ $ = ifstmt($2, $4, $6); }
| IF expression THEN stmtseq FI
{ $ = ifstmt($2, $4, empty()); }
| WHILE expression DO stmtseq OD { $ = whilestmt($2, $4); }
;
stmtseq:
stmtseq SEMICOLON statement { $ = seq($1, $3); }
| statement { $ = $1; }
;
expression:
expr2 { $ = $1; }
| expr2 EQ expr2 { $ = eq($1, $3); }
| expr2 NE expr2 { $ = ne($1, $3); }
| expr2 LT expr2 { $ = le($1, $3); }
| expr2 LE expr2 { $ = le($1, $3); }
| expr2 GT expr2 { $ = gt($1, $3); }
| expr2 GE expr2 { $ = gt($1, $3); }
;
expr2:
expr3 { $ == $1; }
| expr2 PLUS expr3 { $ = plus($1, $3); }
| expr2 MINUS expr3 { $ = minus($1, $3); }
;
expr3:
expr4 { $ = $1; }
| expr3 MULT expr4 { $ = mult($1, $3); }
| expr3 DIVIDE expr4 { $ = divide ($1, $3); }
;
expr4:
PLUS expr4 { $ = $2; }
| MINUS expr4 { $ = neg($2); }
| LPAREN expression RPAREN { $ = $2; }
| NUMBER { $ = number($1); }
| designator { $ = $1; }
;
designator:
NAME { $ = name($1); }
;
%%
I have another question,
is there a possibility to implement a JMP instruction with flex/bison like in Assembler to go to a label like my example, thanks for your help.
:L1
IF FLAG AND X"0001"
EVT 23;
ELSE
WAIT 500 ms;
JMP L1;
END IF;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
编辑:我把旧答案放在最后
这是承诺的更详细的示例:
通常我从所需语言的示例文件开始:
下一步是创建词法分析器+解析器组合,其中前一个文件
通过。
这里是词法分析器(使用
flex -o lexer.c lexer.l
生成源代码)。另请注意,词法分析器源依赖于解析器源(因为 TOKEN_* 常量),因此必须在编译词法分析器源和解析器之前运行 bison(使用
bison -d -o parser.c 解析器进行编译。 y
,-d
告诉 bison 创建 parser.h 头文件,其中包含词法分析器需要的一些内容)在
gcc parser.c lexer.c -o toylang-noop< /code> 调用
toylang-noop
example.toy
必须运行且没有任何错误。现在解析器本身可以工作并且可以解析示例脚本。下一步是创建一个所谓的语法抽象语法树。此时,我首先通过为标记和规则定义不同类型以及向每个解析步骤插入规则来增强解析器。
正如你所看到的,生成 AST 时的主要部分是创建以下节点:
当解析器的某个规则通过时的 AST。由于野牛保持着
当前解析过程本身的堆栈,只需要分配
当前解析堆栈元素的状态
(这些是
$$=foo(bar)
行)目标是内存中的以下结构:
要获得该图,需要生成代码,astgen.h:
astgen.c:
You可以看到这里 AST 元素的生成是相当单调的
工作。该步骤完成后,程序仍然不执行任何操作,但 AST 可以
在调试器中查看。
下一步是编写解释器。这是astexec.h:
嗯,这看起来很友好。解释器本身很简单,尽管它是
长度。大多数函数仅处理特定类型的 AstElement。这
通过dispatchExpression和dispatchStatement选择正确的函数
功能。调度函数在 valExecs 中查找目标函数
和 runExecs 数组。
astexec.c:
现在解释器已完成,并且可以在主函数更新后运行示例:
现在该语言的解释器可以工作。请注意,该解释器有一些限制:
您需要为您的语言定义语法。像这样的事情(词法分析器和解析器都不完整):
然后使用
bison -d foo.y -o foo.c
编译此文件。-d
开关指示 bison 生成包含解析器使用的所有标记的标头。现在您创建了词法分析器之后,您就完成了词法分析器和解析器,并且“仅”需要为您的语言编写语义操作。
EDIT: I put the old answer to the end
Here is the promised more detailed example:
usually I begin with an example file of the desired language:
The next step is to create a lexer+parser combination where the previous file
passes.
Here comes the lexer (generate the source with
flex -o lexer.c lexer.l
). Also note that the lexer source depends on the parser sources (because of the TOKEN_* constants), so bison must be run before compiling the lexer source:and the parser (compile with
bison -d -o parser.c parser.y
, the-d
tells bison to create the parser.h header file with some stuff the lexer needs)After
gcc parser.c lexer.c -o toylang-noop
the call oftoylang-noop < example.toy
must run without any error. So now the parser itself works and can parse the example script.The next step is to create a so called abstract syntax tree of the grammar. At this point I start with the augmenting of the parser by defining different types to the tokens and rules, as well as inserting rules to each parsing step.
As you can see, the main part when generating the AST is to create the nodes of
the AST when a certain rule of the parser was passed. Since bison maintains a
stack of the current parsing process itself, is is only needed to assign the
current parsing status to the elements of the stack
(these are the
$$=foo(bar)
lines)The target is the following structure in memory:
To get this graph, there is the generating code needed, astgen.h:
astgen.c:
You can see here that the generating of the AST elements is a rather monotone
job. After the step is done, the program still does nothing, but the AST can be
viewed in a debugger.
The next step is to write the interpreter. This is astexec.h:
Well, this looks friendly. The Interpreter itself is simple, despite it's
length. The most functions deals with only a particular kind of AstElement. The
correct function is selected by the dispatchExpression and dispatchStatement
functions. The dispatch functions looks for the target function in the valExecs
and runExecs arrays.
astexec.c:
Now the interpreter is complete, and the example can be run, after the main function is updated:
Now the interpreter for this language works. Note that there are some limitations within this interpreter:
execStmt
function, but to jump between different blocks or levels the execution machinery must be changed dramatically (this is because one can't jump between different stack frames in the interpreter). For example the AST can be transformed into byte code and this byte code is interpreted by a vm.You need to define the grammar for your language. Some thing like this (both lexer and parser are incomplete):
Then you compile this file with
bison -d foo.y -o foo.c
. The-d
switch instruct bison to generate a header with all the tokens the parser uses. Now you create your lexerAfter this you have your lexer and parser done, and "only" need to write the semantic actions for your language.