Search code examples
eclipsemodelgrammarxtext

using same keyword for 2 differents syntactic rules in Xtext


I am using Xtext to specify a grammar and I have a problem when writing 2 rules that can possibly start with the same keywords and have neither the same syntax nor the same semantic. But at generation the generator says that the super rule of these 2 rules has non LLF decision. When I suppress the ambiguous keywords from one rule then it generates correctly. How can I make it deterministic?

Here are the details :

the keywords sets :

enum QuantifiedComplexExpressionQuantifier returns QuantifiedComplexExpressionQuantifier:
            all = 'all' | sum = 'sum' | no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';

enum QuantifiedTerminalExpressionQuantifier returns QuantifiedTerminalExpressionQuantifier:
            seq = 'seq' | set = 'set' | no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';

the 2 rules and their super rule :

QuantifiedExpression returns QuantifiedExpression:
QuantifiedComplexExpression |
QuantifiedTerminalExpression;   

QuantifiedComplexExpression returns QuantifiedComplexExpression :   
quantifier=QuantifiedComplexExpressionQuantifier =>varDeclaration+=VarDeclaration ( =>"," =>varDeclaration+=VarDeclaration)* =>blockOrBar=BlockOrBar;

QuantifiedTerminalExpression returns QuantifiedTerminalExpression:  
quantifier=QuantifiedTerminalExpressionQuantifier =>expr=TerminalExpression;

When I try to generate with the enums like this the rule QuantifiedExpression has non LLF decision. But when I suppress (as below) the shared keywords from one set or another, the grammar generates.

Normaly, it should be possible as the syntaxes are differents ; in one case it is an unary expression and on the other one a declaration of constraints on a set of elements.

enum QuantifiedComplexExpressionQuantifier returns QuantifiedComplexExpressionQuantifier:
            all = 'all' | sum = 'sum' ;

enum QuantifiedTerminalExpressionQuantifier returns QuantifiedTerminalExpressionQuantifier:
            seq = 'seq' | set = 'set' | no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';

the full grammar :

// automatically generated by Xtext
grammar org.xtext.alloy.Alloy with org.eclipse.xtext.common.Terminals

import "http://fr.cuauh.als/1.0" 
import "http://www.eclipse.org/emf/2002/Ecore" as ecore

//specification ::= [module] open* paragraph* 
//ok
Specification returns Specification:
{Specification}
(module=Module)?
(opens+=Library (opens+=Library)*)?
(paragraphs+=Paragraph (paragraphs+=Paragraph)*)?;

//module ::= "module" name  [ "["  ["exactly"] name  ("," ["exactly"] num)*    "]" ]
//module ::= "module" name?  [ "["  ["exactly"] name  ("," ExactlyNum )* "]" ]
//ok
Module returns Module:
{Module}
'module' (name=IDName)? ('['(exactly?='exactly')? extensionName=[Ref] (nums+=ExactlyNums ( "," nums+=ExactlyNums)*)?']')?
;


terminal ID         : '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'/')*;
//open ::= ["private"]  "open"  name  [ "[" ref,+ "]" ]  [ "as" name ]
//open ::= ["private"]  "open"  path  [ "[" ref,+ "]" ]  [ "as" name ]
//ok
Library returns Library:
{Library}
(private?='private')? 'open' path=EString ('['references+=Reference (',' references+=Reference)*']')? ('as' alias=Alias)? 
;
//a path 
//ok
//terminal PATH returns ecore::EString :
//  ('a'..'z'|'A'..'Z'|'_'|'.')+('/'('a'..'z'|'A'..'Z'|'_'|'.')+)*
//;

//paragraph ::= factDecl | assertDecl | funDecl | cmdDecl | enumDecl | sigDecl
//paragraph ::= factDecl | assertDecl | funDecl | predDecl | cmdDecl | enumDecl | sigDecl
//ok
Paragraph returns Paragraph:
FactDeclaration | AssertDeclaration | FunctionDeclaration | PredicatDeclaration | CmdDeclaration | EnumerationDeclaration | SignatureDeclaration;

//cmdDecl ::= [name ":"] ("run"|"check") (name|block) scope
//cmdDecl ::= [name ":"] command (ref|block) scope ["expect (0|1)"]
//ok
CmdDeclaration returns CmdDeclaration:
(name=IDName ':')? operation=cmdOp referenceOrBlock=ReferenceOrBlock (scope=Scope)? (expect?='expect' expectValue=EInt)?;

//ok
ReferenceOrBlock returns ReferenceOrBlock:
BlockExpr | ReferenceName;  

//sigDecl ::= sigQual* "sig" name,+ [sigExt] "{" decl,* "}" [block]
//sigDecl ::= ["private"] ["abstract"] [quant] "sig" name [sigExt] "{" relDecl,* "}" [block]
//ok
SignatureDeclaration returns SignatureDeclaration:
{SignatureDeclaration}
(isPrivate?='private')? (isAbstract?='abstract')? (quantifier=SignatureQuantifier)? 'sig' name=IDName (extension=SignatureExtension)? '{'
(relations+=RelationDeclaration ( ',' =>relations+=RelationDeclaration)* )? 
'}'
(block=Block)?;

//ok
SignatureExtension returns SignatureExtension:
SignatureinInheritance | SignatureInclusion;


TypeScopeTarget returns TypeScopeTarget:
Int0 | Seq | ReferenceName;

//EBoolean returns ecore::EBoolean:
//  'true' | 'false';

//["exactly"] num
//ok
ExactlyNums returns ExactlyNums:
exactly?='exactly' num=Number;

//ok do not need to be part of the concrete syntax
//  IDName | IDref;

Ref returns Ref : 
This | IDRef
;

IDRef returns IDRef:
namedElement=[IDName] ("/"refs=IDRef)?
;

IDName returns IDName:
name=ID
;   

This returns This:
{This}
'this' ("/"refs=IDRef)?
;

EString returns ecore::EString:
STRING | ID;

//ok
EInt returns ecore::EInt:
'-'? INT;

Alias returns Alias:
{Alias}
name=IDName;



//factDecl ::= "fact" [name] block
//ok
FactDeclaration returns FactDeclaration:
'fact' (name=IDName)? block=Block;

//assertDecl ::= "assert" [name] block
//ok
AssertDeclaration returns AssertDeclaration:
'assert' (name=IDName)? block=Block ;

//funDecl ::= ["private"] "fun" [ref "."] name "(" decl,* ")" ":" expr block
//funDecl ::= ["private"] "fun" [ref "."] name "[" decl,* "]" ":" expr block
//funDecl ::= ["private"] "fun" [ref "."] name                ":" expr block
//
//funDecl ::= ["private"] "fun" [ref "."] name ["(" paramDecl,* ")" | "[" paramDecl,* "]" ] ":" expr block
//ok
FunctionDeclaration returns FunctionDeclaration:
(private?='private')? 'fun' (reference=[Reference] ".")?    name=IDName
    ('(' (parameters+=ParameterDeclaration ( "," parameters+=ParameterDeclaration)*)? ')'|
     '[' (parameters+=ParameterDeclaration ( "," parameters+=ParameterDeclaration)*)? ']')?
    ':' ^returns=Expression
    block=Block;

//funDecl ::= ["private"] "pred" [ref "."] name "(" decl,* ")" block
//funDecl ::= ["private"] "pred" [ref "."] name "[" decl,* "]" block
//funDecl ::= ["private"] "pred" [ref "."] name                block
//
//predDecl ::= ["private"] "pred" [ref "."] name ["(" paramDecl,* ")" | "[" paramDecl,* "]" ] ":" block
//ok
PredicatDeclaration returns PredicatDeclaration:
(private?='private')? 'pred' (reference=[Reference|EString] ".")? name=IDName
    ('(' (parameters+=ParameterDeclaration ( "," parameters+=ParameterDeclaration)*)? ')'|
     '[' (parameters+=ParameterDeclaration ( "," parameters+=ParameterDeclaration)*)? ']')?
    block=Block;


//enumDecl ::= "enum" name "{" name  ("," name)*  "}"
//enumDecl ::= "enum" name "{" enumEl  ("," enumEl)*  "}"
//ok
EnumerationDeclaration returns EnumerationDeclaration:
'enum' name=IDName '{' enumeration+=EnumerationElement ( "," enumeration+=EnumerationElement)* '}';


//ok
EnumerationElement returns EnumerationElement:
{EnumerationElement}
name=IDName;

//"lone" | "one" | "some" 
//ok
enum SignatureQuantifier returns SignatureQuantifier:
lone = 'lone' | one = 'one' | some = 'some';


//decl ::= ["private"] ["disj"] name,+ ":" ["disj"] expr
//decl ::= ["private"] ["disj"] name,+ ":" ["disj"] expr
//ok
RelationDeclaration returns RelationDeclaration:
(isPrivate?='private')? (varsAreDisjoint?='disj')? names+=VarDecl (',' names+=VarDecl)* ':' (expressionIsDisjoint?='disj')? expression=Expression;

//sigExt ::= "extends" ref
//ok
SignatureinInheritance returns SignatureinInheritance:
'extends' extends=Reference;
//sigExt ::= "in" ref ["+" ref]*
//ok
SignatureInclusion returns SignatureInclusion:
'in' includes+=Reference ( "+" includes+=Reference)* ;

//ok
VarDecl returns VarDecl:
{VarDecl}
name=IDName;

//decl ::= ["private"] ["disj"] name,+ ":" ["disj"] expr
//ok
ParameterDeclaration returns ParameterDeclaration:
(isPrivate?='private')? (varsAreDisjoint?='disj')? names+=VarDecl ( "," names+=VarDecl)* ':' (expressionIsDisjoint?='disj')? expression=Expression;

//("run"|"check")
//ok
enum cmdOp returns cmdOp:
run = 'run' | check = 'check';


//expr ::= 
//1)          "let" letDecl,+ blockOrBar
//2)        | quant decl,+    blockOrBar
//3)        | unOp expr
//4)        | expr binOp   expr
//5)        | expr arrowOp expr
//6)        | expr ["!"|"not"] compareOp expr
//7)        | expr ("=>"|"implies") expr "else" expr
//8)        | expr "[" expr,* "]"
//9)        |     number
//10)       | "-" number
//11)       | "none"
//12)       | "iden"
//13)       | "univ"
//14)       | "Int"
//15)       | "seq/Int"
//16)       | "(" expr ")"
//17)       | ["@"] Name
//18)       | block
//19)       | "{" decl,+ blockOrBar "}" 

//  expr ::= leftPart [rightPart]
Expression returns Expression:
lhs=NonLeftRecursiveExpression (=>parts=NaryPart)?;     

//4)        | expr binOp   expr
//5)        | expr arrowOp expr
//6)        | expr ["!"|"not"] compareOp expr
//7)        | expr ("=>"|"implies") expr "else" expr
//8)        | expr "[" expr,* "]"
//ok
NaryPart returns NaryPart:
BinaryOrElsePart | CallPart;

//4)        | expr binOp   expr
//5)        | expr arrowOp expr
//6)        | expr ["!"|"not"] compareOp expr
//7)        | expr ("=>"|"implies") expr "else" expr
//
//7)        | expr ("=>"|"implies") expr "else" expr
//4)5)6)    | expr binaryOperator expr*
//ok
BinaryOrElsePart returns BinaryOrElsePart:
=>('=>'|'implies') rhs=Expression (=>'else' else=Expression)? |
operator=BinaryOperator rhs=Expression ;

//8)        | expr "[" expr,* "]"
//it is just the right part
//ok
CallPart returns CallPart:
{CallPart}
'['(params+=Expression ( "," params+=Expression)*)?']';


//1)          "let" letDecl,+ blockOrBar
//2)        | quant decl,+    blockOrBar
//19)       | "{" decl,+ blockOrBar "}" 
//18)       | block
//          | terminalExpression
NonLeftRecursiveExpression returns NonLeftRecursiveExpression:
 TerminalExpression | 
 LetExpression | CurlyBracketsExpression | BlockExpr  | 
 QuantifiedExpression  ;

//1)          "let" letDecl,+ blockOrBar
//ok
LetExpression returns LetExpression:
'let' letDeclarations+=LetDeclaration ( ","     letDeclarations+=LetDeclaration)* blockOrBar=BlockOrBar;


//
//binOp ::= "||" | "or" | "&&" | "and" | "&" | "<=>" | "iff" | "=>" |     "implies" | "+" | "-" | "++" | "<:" | ":>" | "." | "<<" | ">>" | ">>>"
//compareOp ::= "=" | "in" | "<" | ">" | "=<" | ">="
//arrowOp ::= ["some"|"one"|"lone"|"set"] "->" ["some"|"one"|"lone"|"set"]
//ok
BinaryOperator returns BinaryOperator:
RelationalOperator | CompareOperator | ArrowOperator;

//ok
RelationalOperator returns RelationalOperator:
operator=RelationalOp;

//binOp ::= "||" | "or" | "&&" | "and" | "&" | "<=>" | "iff" | "=>" | "implies" | "+" | "-" | "++" | "<:" | ":>" | "." | "<<" | ">>" | ">>>"
//ok
enum RelationalOp returns RelationalOp:
            or = '||' | and = '&&' | union = '+' | intersection = '&' |     difference = '-' | equivalence = '<=>' | override = '++' 
            | domain = '<:' | range = ':>' | join = '.' ; // | lshift = '<<' | rshift = '>>' | rrshift = '>>>'; 

//["!"|"not"] compareOp
//ok
CompareOperator returns CompareOperator:
(negated?='!' | negated?='not')? operator=CompareOp;

//compareOp ::= "=" | "in" | "<" | ">" | "=<" | ">="
enum CompareOp returns CompareOp:
            equal = '=' | inclusion = 'in' | lesser = '<' | greater = '>' | lesserOrEq = '<=' | greaterOrEq = '>=';

//arrowOp ::= ["some"|"one"|"lone"|"set"] "->" ["some"|"one"|"lone"|"set"]
//ok
ArrowOperator returns ArrowOperator:
{ArrowOperator}
(leftQuantifier=ArrowQuantifier)? '->' (=>rightQuantifier=ArrowQuantifier)?;

//"some"|"one"|"lone"|"set"
//ok
enum ArrowQuantifier returns ArrowQuantifier: 
            lone = 'lone' | one = 'one' | some = 'some' | set = 'set' ; 

//19)       | "{" decl,+ blockOrBar "}" 
//ok
CurlyBracketsExpression returns CurlyBracketsExpression:
'{' varDeclarations+=VarDeclaration ( ","     varDeclarations+=VarDeclaration)* blockOrBar=BlockOrBar '}';

//blockOrBar ::= block
//blockOrBar ::= "|" expr
//ok
BlockOrBar returns BlockOrBar:
BlockExpr | Bar;    

//blockOrBar ::= "|" expr
//ok
Bar returns Bar:
'|' expression=Expression;

//block ::= "{" expr* "}"
//ok
BlockExpr returns BlockExpr:
{BlockExpr}
'{' (expressions+=Expression ( "," expressions+=Expression)*)?'}';

//3)         unOp expr
//         | finalExpression    
TerminalExpression returns TerminalExpression:
finalExpression  | 
UnaryExpr   ;   

//2)        | quant decl,+    blockOrBar
//ok
QuantifiedExpression returns QuantifiedExpression:
QuantifiedComplexExpression |
QuantifiedTerminalExpression;


QuantifiedComplexExpression returns QuantifiedComplexExpression :   
quantifier=QuantifiedComplexExpressionQuantifier =>varDeclaration+=VarDeclaration ( =>"," =>varDeclaration+=VarDeclaration)* =>blockOrBar=BlockOrBar;

QuantifiedTerminalExpression returns QuantifiedTerminalExpression:  
quantifier=QuantifiedTerminalExpressionQuantifier =>expr=TerminalExpression;


//3)         unOp expr
//ok
UnaryExpr returns UnaryExpr:
unOp=UnaryOperator  =>expression=finalExpression;


//unOp ::= "!" | "not" | "no" | "some" | "lone" | "one" | "set" | "seq" | "#" | "~" | "*" | "^"
//unOp ::= "!" | "not" |"#" | "~" | "*" | "^"
//ok
enum UnaryOperator returns UnaryOperator:
            not = 'not' | card = '#' | transpose = '~' | reflexiveClosure = '*' | closure = '^' | not2 = '!' ;//|



//16)       | "(" expr ")"
//9)        |     number
//10)       | "-" number
//17)       | ["@"] Name
//11)       | "none"
//12)       | "iden"
//13)       | "univ"
//14)       | "Int"
//15)       | "seq/Int"

//16)       | "(" expr ")"
//10)       | ["-"] number
//17)       | "@" Name
//17)       | reference
//12)13)    | constante
finalExpression returns TerminalExpression: 
BracketExpression | NumberExpression | NotExpandedExpression |     ConstantExpression | ReferenceExpression ;   

//16)       | "(" expr ")"
//ok    
BracketExpression returns BracketExpression:
'('expression=Expression')';

//9)        |     number
//10)       | "-" number
//ok    
Number returns Number:
NumberExpression;

//ok
NumberExpression returns NumberExpression:
value=EInt;

//17)       | ["@"] Name
//17)       | "@" Name
//ok
NotExpandedExpression returns NotExpandedExpression:
'@' name=[IDRef];

//ok
ReferenceExpression returns ReferenceExpression:
reference=Reference;

//ref ::= name | "univ" | "Int" | "seq/Int"
//ok
Reference returns Reference:
ConstanteReference | ReferenceName ;

//ok
ConstanteReference returns ConstanteReference:
cst=constanteRef;

//13)       | "univ"
//14)       | "Int"
//15)       | "seq/Int"
//ok
enum constanteRef returns constanteRef:
            int = 'Int' | seqint = 'seq/Int' | univ = 'univ';

//ok
ReferenceName returns ReferenceName:
name=Ref;

//ok
ConstantExpression returns ConstantExpression:
constante=Constant;

//11)       | "none"
//12)       | "iden"
//ok    
enum Constant returns Constant:
            none = 'none' | iden = 'iden';


//ok    
Block returns Block:
    BlockExpr;

//letDecl ::= name "=" expr
//ok
LetDeclaration returns LetDeclaration:
varName=VarDecl '=' expression=Expression;

//quant ::= "all" | "no" | "some" | "lone" | "one" | "sum"
//quant ::= "all" | "no" | "some" | "lone" | "one" | "null"
//ok
enum QuantifiedComplexExpressionQuantifier returns QuantifiedComplexExpressionQuantifier:
            all = 'all' | sum = 'sum' | no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';

enum QuantifiedTerminalExpressionQuantifier returns QuantifiedTerminalExpressionQuantifier:
            seq = 'seq' | set = 'set' | no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';

//enum QuantifiedAmbigusExpressionQuantifier returns     QuantifiedAmbigusExpressionQuantifier:
//              no = 'no' | one = 'one' | lone = 'lone'  | some = 'some';               

//decl ::= ["private"] ["disj"] name,+ ":" ["disj"] expr
//ok
VarDeclaration returns VarDeclaration:
(=>isPrivate?='private')? (=>varsAreDisjoint?='disj')? =>names+=VarDecl ( =>"," =>names+=VarDecl)* ":" (=>expressionIsDisjoint?='disj') expression=Expression; 


//scope ::= "for" number                   ["expect" (0|1)]
//scope ::= "for" number "but" typescope,+ ["expect" (0|1)]
//scope ::= "for"              typescope,+ ["expect" (0|1)]
//scope ::=                                ["expect" (0|1)]
//
//scope ::= "for" [number] ["but"] typescope,* 
//ok
Scope returns Scope:
{Scope}
    'for' (number=Number)? (but?='but')? (typeScope+=TypeScope ( ","     typeScope+=TypeScope)*)?;

//typescope ::= ["exactly"] number [name|"int"|"seq"]
//typescope ::= ExactlyNumber target
TypeScope returns TypeScope:
num=ExactlyNums target=[TypeScopeTarget];


//[name|"int"|"seq"]
//[Validname|"int"|"seq"]
//ok
TypeScopeTarget_Impl returns TypeScopeTarget:
{TypeScopeTarget}
;

Int0 returns Int:
{Int}
'Int'
;

Seq returns Seq:
{Seq}
'Seq'
;

Thank you in advance.


Solution

  • You could possibly wrap another syntactic predicate around this

    =>(quantifier=QuantifiedComplexExpressionQuantifier varDeclaration+=VarDeclaration)
    

    But using long pedicates will make things like error recovery and content assist worse. You should revise and try to get rid of many the other predicates '=>', too. Also note that in many cases the alternative first-set predicate ('->') is good enough and preferable.

    My suggestion in this particular case, is to unify the two rules and have one enum, containing all the operators. Than use validation to give the user feedback if he/she uses them wrongly.

    E.g.:

    QuantifiedExpression returns QuantifiedExpression:
      quantifier=QuantifiedExpressionQuantifier (varDeclaration+=VarDeclaration ( "," varDeclaration+=VarDeclaration)* blockOrBar=BlockOrBar | expr=TerminalExpression);