开发者

ANTLR's AST tree grammar + lists

开发者 https://www.devze.com 2023-02-21 10:36 出处:网络
I\'ve have read a lot trying to find a way to cleanly consume lists in ANTLR\'s tree grammar. Here is what I have tried and their results (I really hope I\'m missing something trivial)...

I've have read a lot trying to find a way to cleanly consume lists in ANTLR's tree grammar. Here is what I have tried and their results (I really hope I'm missing something trivial)...

Using += Syntax

program returns [someInterface result]
  : m+=method* EOF {result = new SomeClass(m);};

method returns [SomeMethod result] : <definition here>

This fails with...

rule '+=' list labels are not allowed w/o output option

If I set the output to either "AST" or "template" (the only options) the method signatures of the generated class change. That is, m will not by a List of SomeMethod(s) but rather a List of Nodes or Templates respectively. I am open to suggestions if there is a way to make this method work.

Using Rule Scopes

program returns [CompilesToJav开发者_如何学编程aByteCode result]
    scope {
      List<SomeMethod> methods;
    }
    @init {
      $program::methods = new ArrayList<SomeMethod>();
    }
    : (m=method {$program::methods.add(m);})*
      EOF {result = new SomeClass($program::methods);};

This seems to work, though I'll admit that I haven't tested it with nested/recursive cases yet.

The End Goal

I want to build a set of classes that represent my language (Class, Method, Variable, Statement, ect) so that I can do some static analysis and optimization before I generate compiled code. To that end, I need to be able to consume lists. I expected += syntax to "just work" but I might be missing something. The second method works but seem overly verbose and inelegant.

The Question

What is the proper why to consume a list, in ANTLR's tree grammar, to pass to my concrete classes?


You can cut the scope from your example and do it all with local variables.

program returns [CompilesToJavaByteCode result]
    @init {
      List<SomeMethod> methods = new ArrayList<SomeMethod>();
    }
    : (m=method { methods.add($m.result); })* EOF 
      { $result = new SomeClass(methods); };

This is what we do for this case at work. Another option is to have your method rule handle it:

program returns [CompilesToJavaByteCode result]
    @init {
      List<SomeMethod> methods = new ArrayList<SomeMethod>();
    }
    : method[methods]* EOF { $result = new SomeClass(methods); };

method [List<SomeMethod> methods]
    : ...
      { methods.add(new SomeMethod(...); };

I don't really like the second option as a method rule probably shouldn't care what is being done with its results like that. But you can imagine a structure where the top rule creates a ClassBeingCompiled and the rest of the code incrementally fills it out with .addMethod().

0

精彩评论

暂无评论...
验证码 换一张
取 消