简体   繁体   中英

Loops' iterating in ANTLR

I'm trying to make a Pascal interpreter using ANTLR and currently have some troubles with processing loops while walking the AST tree. For example for loop is parsed as:

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

(variant with DOWNTO is ignored). In what way can I make walker to repeat the loop's execution so much times as needed? I know that I should use input.Mark() and input.Rewind() for that. But exactly where should they be put? My current wrong variant looks so (target language is 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: ;
            }
        ;

(Hope everything is understandable). The condition of repeating should be checked before the statement's first execution. I tried to play with placing Mark and Rewind in different code blocks including @init and @after, and even put trailing goto to loops head, but each time loop either iterated one time or threw exceptions like Unexpected token met, for example ':=' (assignement). I have no idea, how to make that work properly and can't find any working example. Can anybody suggest a solution of this problem?

I work with ANTLR 3.4 and I found a solution which works with Class CommonTreeNodeStream.

Basically I splitted off new instances of my tree parser, which in turn analyzed all subtrees. My sample code defines a while-loop:

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

I haven't used ANTLR, but it seems to me that you are trying to execute the program while you're parsing it, but that's not really what parsers are designed for (simple arithmetic expressions can be executed during parsing, but as you have discovered, loops are problematic). I strongly suggest that you use the parsing only to construct the AST. So the parser code for parametricLoop should only construct a tree node that represents the loop, with child nodes representing the variables, conditions and body. Afterwards, in a separate, regular C# class (to which you provide the AST generated by the parser), you execute the code by traversing the tree in some manner, and then you have complete freedom to jump back and forth between the nodes in order to simulate the loop execution.

Just solved a similar problem, several points:

  1. Seems you need to use BufferedTreeNodeStream instead of CommonTreeNodeStream, CommonTreeNodeStream never works for me (struggled long time to find out)

  2. Use seek seems to be more clear to me

Here's my code for a list command, pretty sure yours can be easily changed to this style:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM