两个基本的ANTLR问题

时间:2011-09-27 08:08:26

标签: python assembly antlr

我正在尝试使用ANTLR来获取简单的语法并生成程序集输出。我在ANTLR中选择的语言是Python。

许多教程似乎非常复杂或精心处理与我无关的事情;我只需要一些非常简单的功能。所以我有两个问题:

'将值从一个规则返回到另一个规则。

所以我想说我有一条规则:

赋值:name = IDENTIFIER ASSIGNMENT表达式;

当识别此规则时,我可以在{}中运行Python代码,并且我可以通过执行以下操作将args传递给Python代码以进行表达:

赋值:name = IDENTIFIER ASSIGNMENT表达式[variablesList];

然后

表达式[variablesList]:blah blah

但我如何'返回'我的原始规则的值?例如。如何计算表达式的值,然后将其发送回我的分配规则以在Python中使用?

如何写出我的目标语言代码?

所以我有一些Python在规则被识别时运行,然后我计算我希望该语句产生的程序集。但是我如何说“将这个汇编指令字符串写入我的目标文件”?

任何与这种东西相关的好教程(属性语法,编译到AST以外的东西等)也会有所帮助。如果我的问题没有太大意义,请让我澄清一下;我很难把头缠绕在ANTLR周围。

1 个答案:

答案 0 :(得分: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: 51.0的文件'out.txt'。如果你的语法不是那么大,你可能会侥幸逃脱。但是,这可能会有点混乱,在这种情况下,您可以将解析器的输出设置为template

options {
  output=template;
  language=Python;
}

并通过您自己定义的模板发出自定义代码。

见: