Два основных вопроса ANTLR - PullRequest
       5

Два основных вопроса ANTLR

5 голосов
/ 27 сентября 2011

Я пытаюсь использовать ANTLR, чтобы взять простую грамматику и произвести вывод сборки.Мой язык выбора в ANTLR - Python.

Многие учебные пособия кажутся очень сложными или сложными для вещей, которые не имеют отношения к мне;Мне действительно нужны очень простые функции.Итак, у меня есть два вопроса:

«Возвращение» значений из одного правила в другое.

Итак, допустим, у меня есть правило типа:

назначение: name = IDENTIFIER ASSIGNMENT expression;

Я могу запустить код Python в {} s, когда это правило распознано, и передать аргументы в код Python для выражения, выполнив что-то вроде:

присваивание: имя = IDENTIFIER ASSIGNMENT выражение [variableList];

, а затем

выражение [variableList]: бла-бла

Но как мне «вернуть» значение в мой оригиналправить?Например, как мне вычислить значение выражения и затем отправить его обратно в мое правило назначения для использования в Python там?

Как выписать код моего целевого языка?

Итак, у меня есть какой-то Python, который запускается при распознавании правил, затем я вычисляю сборку, которую хочу создать для этого оператора.Но как мне сказать «записать эту строку инструкций по сборке в мой целевой файл»?

Любые хорошие учебники, которые имеют отношение к такого рода вещам (грамматика атрибутов, компиляция чего-то другого, кроме AST и т. Д.) тоже будет полезно.Если мои вопросы не имеют особого смысла, пожалуйста, попросите меня уточнить;Я с трудом оборачиваюсь вокруг ANTLR.

1 Ответ

10 голосов
/ 27 сентября 2011


Возвращение значений из одного правила в другое

Допустим, вы хотите проанализировать простые выражения и предоставить карту переменных во время выполнения, которые можно использовать в этих выражениях.Простая грамматика, включающая пользовательский код 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

Обратите внимание, что вы можете определить более одного типа "return":

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

и генерировать пользовательский код через ваши собственные определенные шаблоны.

См .:

...