Loops 循环';在ANTLR中迭代
我正在尝试使用ANTLR制作一个Pascal解释器,目前在遍历AST树时处理循环时遇到了一些问题。 例如,For循环被解析为:Loops 循环';在ANTLR中迭代,loops,antlr,grammar,interpreter,pascal,Loops,Antlr,Grammar,Interpreter,Pascal,我正在尝试使用ANTLR制作一个Pascal解释器,目前在遍历AST树时处理循环时遇到了一些问题。 例如,For循环被解析为: parametricLoop : FOR IDENTIFIER ASSIGN start = integerExpression TO end = integerExpression DO statement -> ^( PARAMETRIC_LOOP IDENTIFIER $start $end statement ) ; (忽
parametricLoop
: FOR IDENTIFIER ASSIGN start = integerExpression TO end = integerExpression DO
statement
-> ^( PARAMETRIC_LOOP IDENTIFIER $start $end statement )
;
(忽略带有DOWNTO的变量)。
我怎样才能让walker根据需要重复循环执行如此多次?我知道我应该使用input.Mark()和input.Rewind()来实现这一点。但它们究竟应该放在哪里呢?我当前的错误变体看起来是这样的(目标语言是C#):
parametricLoop
:
^(
参数环
标识符
开始=整数表达式
{
变量参数=Members.Variable($IDENTIFIER.text);
parameter.value=$start.result;
}
end=integerExpression
{
int end_值=$end.result;
如果((int)parameter.value>end_value)转到EndLoop;
参数化_loop_start=input.Mark();
}
陈述
{
parameter.value=(int)parameter.value+1;
if((int)parameter.value我没有使用ANTLR,但在我看来,您在解析程序时试图执行该程序,但这并不是解析器的真正目的(解析过程中可以执行简单的算术表达式,但正如您所发现的,循环是有问题的)。我强烈建议您仅使用解析来构造AST。因此,parametricLoop
的解析器代码应该只构造一个表示循环的树节点,子节点表示变量、条件和主体。然后,在一个单独的常规C#类中(向其提供解析器生成的AST),通过以某种方式遍历树来执行代码,然后您可以完全自由地在节点之间来回跳跃,以模拟循环执行。刚刚解决了一个类似的问题,有几点:
似乎您需要使用BufferedTreeNodeStream而不是CommonTreeNodeStream,CommonTreeNodeStream对我来说从来都不起作用(花了很长时间才发现)
使用seek对我来说似乎更清楚
下面是我的列表命令代码,非常确定您的命令可以轻松更改为这种样式:
list returns [Object r]
: ^(LIST ID
{int e_index = input.Index;}
exp=.
{int s_index = input.Index;}
statements=.
)
{
int next = input.Index;
input.Seek(e_index);
object list = expression();
foreach(object o in (IEnumerable<object>)list)
{
model[$ID.Text] = o;
input.Seek(s_index);
$r += optional_block().ToString();
}
input.Seek(next);
}
list返回[objectr]
:^(列表ID)
{int e_index=input.index;}
exp=。
{int s_index=input.index;}
语句=。
)
{
int next=input.Index;
输入搜索(e_索引);
对象列表=表达式();
foreach(对象o在(IEnumerable)列表中)
{
模型[$ID.Text]=o;
输入搜索(s_索引);
$r+=可选的_块().ToString();
}
输入。查找(下一步);
}
我使用ANTLR 3.4,并找到了一个使用CommonTreeNodeStream类的解决方案
基本上,我分离了树解析器的新实例,树解析器又分析了所有子树。我的示例代码定义了一个while循环:
tree grammar Interpreter;
...
@members
{
...
private Interpreter (CommonTree node, Map<String, Integer> symbolTable)
{
this (new CommonTreeNodeStream (node));
...
}
...
}
...
stmt : ...
| ^(WHILE c=. s1=.) // ^(WHILE cond stmt)
{
for (;;)
{
Interpreter condition = new Interpreter (c, this.symbolTable);
boolean result = condition.cond ();
if (! result)
break;
Interpreter statement = new Interpreter (s1, this.symbolTable);
statement.stmt ();
}
}
...
cond returns [boolean result]
: ^(LT e1=expr e2=expr) {$result = ($e1.value < $e2.value);}
| ...
树语法解释器;
...
@成员
{
...
专用解释器(CommonTree节点,映射可符号化)
{
这个(新的CommonTreeNodeStream(节点));
...
}
...
}
...
stmt:。。。
|^(而c=.s1=)/^(而cond stmt)
{
对于(;;)
{
解释器条件=新解释器(c,this.symbolTable);
布尔结果=condition.cond();
如果(!结果)
打破
解释器语句=新解释器(s1,this.symbolTable);
statement.stmt();
}
}
...
cond返回[布尔结果]
:^(LT e1=expr e2=expr){$result=($e1.value<$e2.value);}
| ...
Err,这些Mark()
和revind(…)
方法是从树语法中调用的,对吗?我想它们也会引发异常?你说“知道使用input.Mark()和input.revind()”,但是谁告诉过你的呢?对于一个简单的基于树的解释器,请参阅:好的,那个人正试图用组合语法来实现这一点,而你正在用树语法来实现这一点(请注意,他/她正试图这样做,但没有成功!)。树语法是CommonTree对象的结构化集合,这些对象无权访问mark()
或倒带(…)
方法,好吧。此外,在你的(树)语法中解释如此复杂的结构不是(IMHO)的方法,我真的看不到“快速修复”的方法。用“正确”的方法来做这件事会让一整页的文章变得相当大。
tree grammar Interpreter;
...
@members
{
...
private Interpreter (CommonTree node, Map<String, Integer> symbolTable)
{
this (new CommonTreeNodeStream (node));
...
}
...
}
...
stmt : ...
| ^(WHILE c=. s1=.) // ^(WHILE cond stmt)
{
for (;;)
{
Interpreter condition = new Interpreter (c, this.symbolTable);
boolean result = condition.cond ();
if (! result)
break;
Interpreter statement = new Interpreter (s1, this.symbolTable);
statement.stmt ();
}
}
...
cond returns [boolean result]
: ^(LT e1=expr e2=expr) {$result = ($e1.value < $e2.value);}
| ...