两个基础的ANTLR问题

5 投票
1 回答
2405 浏览
提问于 2025-04-17 03:10

我正在尝试使用ANTLR来处理一个简单的语法,并生成汇编输出。我选择的语言是Python。

很多教程看起来都很复杂,或者讲了一些对我来说不太相关的内容;其实我只需要一些非常简单的功能。所以我有两个问题:

如何在一个规则中“返回”值到另一个规则。

假设我有一个这样的规则:

assignment: name=IDENTIFIER ASSIGNMENT expression;

当这个规则被识别时,我可以在{}里运行Python代码,并且可以通过像这样传递参数给expression:

assignment: name=IDENTIFIER ASSIGNMENT expression[variablesList];

然后

expression[variablesList]: blah blah

但是我怎么把一个值“返回”到我最初的规则呢?比如说,我怎么计算出expression的值,然后把它传回到assignment规则中,在那儿用Python使用呢?

我怎么把目标语言的代码写出来?

我有一些Python代码在规则被识别时运行,然后我计算出我想要的汇编指令。但是我怎么说“把这串汇编指令写到我的目标文件中”呢?

如果有好的教程能帮助我理解这些内容(比如属性语法、编译成其他东西而不是抽象语法树等),那就太好了。如果我的问题不太清楚,请让我进一步解释;我在理解ANTLR时遇到了一些困难。

1 个回答

10



从一个规则返回值到另一个规则

假设你想解析简单的表达式,并在运行时提供一个变量的映射,这些变量可以在这些表达式中使用。一个简单的语法,包括自定义的Python代码、规则中的returns语句,以及语法入口点的参数vars,可能看起来像这样:

grammar T;

options {
  language=Python;
}

@members {
  variables = {}
}

parse_with [vars] returns [value]
@init{self.variables = vars}
  :  expression EOF                            {value = $expression.value}
  ;

expression returns [value]
  :  addition                                  {value = $addition.value}
  ;

addition returns [value]
  :  e1=multiplication                         {value = $e1.value}
                       ( '+' e2=multiplication {value = value + $e2.value}
                       | '-' e2=multiplication {value = value - $e2.value}
                       )*
  ;

multiplication returns [value]
  :  e1=unary                                  {value = $e1.value}
              ( '*' e2=unary                   {value = value * $e2.value}
              | '/' e2=unary                   {value = value / $e2.value}
              )*
  ;

unary returns [value]
  :  '-' atom                                  {value = -1 * $atom.value}
  |  atom                                      {value = $atom.value}
  ;

atom returns [value]
  :  Number                                    {value = float($Number.text)}
  |  ID                                        {value = self.variables[$ID.text]}
  |  '(' expression ')'                        {value = $expression.value}
  ;

Number : '0'..'9'+ ('.' '0'..'9'+)?;
ID     : ('a'..'z' | 'A'..'Z')+;
Space  : ' ' {$channel=HIDDEN};

如果你现在使用ANTLR v3.1.3(不要使用更高版本)生成一个解析器:

java -cp antlr-3.1.3.jar org.antlr.Tool T.g

然后运行这个脚本:

#!/usr/bin/env python
import antlr3
from antlr3 import *
from TLexer import *
from TParser import *

input = 'a + (1.0 + 2) * 3'
lexer = TLexer(antlr3.ANTLRStringStream(input))
parser = TParser(antlr3.CommonTokenStream(lexer))
print '{0} = {1}'.format(input, parser.parse_with({'a':42}))

你会看到以下输出被打印出来:

a + (1.0 + 2) * 3 = 51.0

注意,你可以定义不止一种“返回”类型:

parse
  :  foo              {print 'a={0} b={1} c={2}'.format($foo.a, $foo.b, $foo.c)}
  ;

foo returns [a, b, c]
  :  A B C            {a=$A.text; b=$B.text; b=$C.text}
  ;



如何输出目标语言代码

最简单的方法就是在自定义代码块中放入print语句,然后把输出重定向到一个文件:

parse_with [vars]
@init{self.variables = vars}
  :  expression EOF                            {print 'OUT:', $expression.value}
  ;

然后像这样运行脚本:

./run.py > out.txt

这将创建一个名为'out.txt'的文件,内容为:OUT: 51.0。如果你的语法不太复杂,这样做是可以的。不过,这样可能会变得有点混乱,在这种情况下,你可以将解析器的输出设置为template

options {
  output=template;
  language=Python;
}

然后通过你自己定义的模板输出自定义代码。

参考链接:

撰写回答