jwa
jwa

Reputation: 3281

Xtext: Recursive Rule Inovcations

I am attempting to build a language that can declare methods and fields, with intrinsic support for generics. I would like to be able to use primitive types like String, as well as declare my own classes.

This should be valid syntax:

String somePrimitive

class MyClass { }
MyClass someObject;

class List { }
List<String> stringList;
List<MyClass> objectList;

List<String> getNames() { }

I have a grammar that supports these operations:

Model:
    (members+=ModelMembers)*;

ModelMembers:
    Class | Field | MethodDeclaration
;

Class:
    'class' name=ID '{' '}'
;

Field:
    type=Type
    name=ID
;

enum PrimitiveType: STRING="String" | NUMBER="Number";

Type:
    (
        {TypeObject} clazz=[Class] ("<" a+=Type ("," a+=Type)* ">")? 
        |
        {TypePrimitive} type=PrimitiveType 
    )
;

MethodDeclaration:
    returnType=Type name=ID "(" ")" "{"

    "}"
;

But it contains an error:

[fatal] rule rule__ModelMembers__Alternatives has non-LL(*) decision due to recursive rule invocations reachable from alts 2,3.  Resolve by left-factoring or using syntactic predicates or using backtrack=true option.

The problem seems to stem from the fact that the Type rule is recursive, and can be matches as either the beginning of a MethodDeclaration or a Field.

However, it is possible to figure out what rule one is building, as the method will have () { } after the name.

What really confuses me, is if I replace the recursive rule with simply [Class], e.g. Field: type=[Class] name=ID (and the same for the MethodDeclaration) the grammar is valid.

I get that there is some ambiguity when one sees an instance of Type, as it could lead onto a method or field.... but that's exactly the same when I replace with [Class]. Instances of class can lead onto a method or field.

How can it be ambiguous using Type, but not ambiguous using [Class]?

Upvotes: 0

Views: 310

Answers (2)

Steve Jones
Steve Jones

Reputation: 171

This grammar parses the example code without throwing errors (the layout is the one favored by Terence Parr, the ANTLR man. I find it helps greatly):

Model
  : (members+=ModelMembers)*
  ;    

ModelMembers
  : Class 
  | MethodDeclaration
  | Field
  ;

Class
  : 'class' name=ID '{' '}'
  ;

Field
  : type=Type name=ID ';'
  ;

PrimitiveType
  : ("String" |"Number")
  ;

TypeReferenceOrPrimitive
  : {TypeClass} type=[Class]
  | {TypePrimitive} PrimitiveType
  ;

Type
  : {TypeObject} clazz=[Class] ("<" a+=TypeReferenceOrPrimitive ("," a+=TypeReferenceOrPrimitive)* ">")?
  | {TypePrimitive} type=PrimitiveType   
  ;

MethodDeclaration
  : returnType=Type name=ID "(" ")" "{"  "}"
  ;

I'm no Xtext expert so there may be better ways. My 'trick' is to define TypeReferenceOrPrimitive. You will probably need to play around with the grammar a bit more in order to get an AST that is easier to process.

Upvotes: 1

Boris Brodski
Boris Brodski

Reputation: 8695

This is not direct answer to your question, but did you considered using Xbase instead of the plain Xtext? With Xbase you can simple use predefined rules, that match everything you need:

  • Java types with generics
  • Expressions
  • Annotations

and many more.

Here are a couple of useful links:

If Xbase doesn't suite you, then you can learn from it's Xbase-Xtext-Grammar.

Upvotes: 1

Related Questions