Повторение циклов в ANTLR

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

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 в начало цикла, но каждый цикл либо повторялся один раз, либо выдавал исключения, такие как Unexpected token, например ':= ' (задание). Я понятия не имею, как заставить это работать правильно, и не могу найти ни одного рабочего примера. Может ли кто-нибудь предложить решение этой проблемы?


person Vercetti    schedule 02.03.2011    source источник
comment
Эээ, эти методы Mark() и Rewind(...) вызываются из вашей древовидной грамматики, верно? Я предполагаю, что они также генерируют исключения? Вы говорите, что знаете, как использовать input.Mark() и input.Rewind(), но кто вам это сказал? Простой интерпретатор на основе дерева см. по адресу: antlr.org/ wiki/display/ANTLR3/Simple+tree-based+interpeter   -  person Bart Kiers    schedule 02.03.2011
comment
Хорошо, этот человек пытается сделать это в комбинированной грамматике, а вы делаете это в древовидной грамматике (обратите внимание, что он/она пытается сделать это, но безуспешно!). Грамматика дерева представляет собой структурированную коллекцию объектов CommonTree, у которых нет доступа к методам mark() или rewind(...), насколько я знаю. Кроме того, интерпретация таких сложных структур внутри вашей (древовидной) грамматики - это (ИМХО) не лучший путь, и я действительно не вижу здесь быстрого решения для вас. При правильном выполнении получится статья приличного размера, состоящая из нескольких страниц.   -  person Bart Kiers    schedule 03.03.2011


Ответы (3)


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

person Aasmund Eldhuset    schedule 06.03.2011
comment
Это должно быть помечено как ответ, и это добавляет к тому, что @Bart Kiers имел в виду в комментарии к вопросу OP. - person Lorenzo; 29.06.2021

Я работаю с 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);}
                          | ...
person Farid Derradji    schedule 02.01.2013

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

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

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

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

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);
        }
person Community    schedule 29.10.2011