Two basic ANTLR questions

喜你入骨 提交于 2019-12-04 08:39:00
Bart Kiers



Returning values from one rule to another

Let's say you want to parse simple expressions and provide a map of variables at runtime that can be used in these expressions. A simple grammar including the custom Python code, returns statements from the rules, and the parameter vars to the entry point of your grammar could look like this:

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};

If you now generate a parser using ANTLR v3.1.3 (no later version!):

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

and run the script:

#!/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}))

you will see the following output being printed:

a + (1.0 + 2) * 3 = 51.0

Note that you can define more than a single "return" type:

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}
  ;



How to write out a target language code

The easiest to go about this is to simply put print statements inside the custom code blocks and pipe the output to a file:

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

and then run the script like this:

./run.py > out.txt

which will create a file 'out.txt' containing: OUT: 51.0. If your grammar isn't that big, you might get away with this. However, this might become a bit messy, in which case you could set the output of your parser to template:

options {
  output=template;
  language=Python;
}

and emit custom code through your own defined templates.

See:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!