Повторение циклов в ANTLR - PullRequest
0 голосов
/ 02 марта 2011

Я пытаюсь создать интерпретатор Pascal с использованием ANTLR, и в настоящее время у меня есть некоторые проблемы с обработкой циклов при обходе дерева AST.Например, для цикла анализируется как:

parametricLoop
    : FOR IDENTIFIER ASSIGN start = integerExpression TO end = integerExpression DO
    statement
    -> ^( PARAMETRIC_LOOP IDENTIFIER $start $end statement )
    ;

(вариант с DOWNTO игнорируется).Каким образом я могу заставить Уокера повторять выполнение цикла столько раз, сколько необходимо?Я знаю, что я должен использовать input.Mark () и input.Rewind () для этого.Но где именно они должны быть помещены?Мой текущий неправильный вариант выглядит так (целевой язык - C #):

parametricLoop
    :
        ^(
            PARAMETRIC_LOOP
            IDENTIFIER
            start = integerExpression
            {
                Variable parameter = Members.variable($IDENTIFIER.text);
                parameter.value = $start.result;
            }
            end = integerExpression
            {
                int end_value = $end.result;
                if ((int)parameter.value > end_value) goto EndLoop;
                parametric_loop_start = input.Mark();
            }
            statement
            {
                parameter.value = (int)parameter.value + 1;
                if ((int)parameter.value <= end_value)
                    input.Rewind(parametric_loop_start);
            )
            {
                EndLoop: ;
            }
        ;

(Надеюсь, все понятно).Условие повторения должно быть проверено перед первым выполнением оператора.Я попытался поиграть с размещением Mark и Rewind в разных блоках кода, включая @init и @after, и даже поместил конечный goto в заголовок цикла, но каждый временной цикл либо повторялся один раз, либо генерировал исключения, такие как встретился неожиданный токен, например ': ='(назначение).Я понятия не имею, как заставить это работать должным образом и не могу найти никакого рабочего примера.Кто-нибудь может предложить решение этой проблемы?

Ответы [ 3 ]

1 голос
/ 03 января 2013

Я работаю с 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);}
                          | ...
0 голосов
/ 29 октября 2011

Только что решил похожую проблему, несколько пунктов:

  1. Кажется, вам нужно использовать BufferedTreeNodeStream вместо CommonTreeNodeStream, CommonTreeNodeStream никогда не работает для меня (долго пытался выяснить)

  2. Использование поиска мне кажется более понятным

Вот мой код для команды списка, почти уверен, что ваш может быть легко изменен на этот стиль:

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);
        }
0 голосов
/ 06 марта 2011

Я не использовал ANTLR, но мне кажется, что вы пытаетесь выполнить программу во время ее синтаксического анализа, но это не совсем то, для чего предназначены парсеры (простые арифметические выражения могут выполняться во время синтаксического анализа, но как вы обнаружили, петли проблематичны). Я настоятельно рекомендую использовать синтаксический анализ only для построения AST. Таким образом, код синтаксического анализатора для parametricLoop должен создавать только узел дерева, представляющий цикл, с дочерними узлами, представляющими переменные, условия и тело. После этого в отдельном обычном классе C # (для которого вы предоставляете AST, сгенерированный синтаксическим анализатором), вы выполняете код, перебирая дерево каким-либо образом, и затем у вас есть полная свобода для перехода назад и вперед между узлами в порядке моделировать выполнение цикла.

...