1
votes

I am currently working on creating a DSL in xText and I am stumbling upon a problem which is probably related to an ambiguity or left recursion problem. I am not sure which of these two problems applies to my case (similar topics I found online also mention that these problems often seem related) but I guess it has to do with left recursion.

On line 100 in my DSL code I declare a rule called Expression. As you can see it aggregates multiple other types (which on their part again aggregate multiple other types and eventually types called Factor (on line 130) can also be aggregated). Eventually this whole 'aggregation tree' boils down to a problem with this Factor type. As you can see, this type can aggregate an Expression again. So there is a loop; an Expression type can eventually contain a Factor type, and the Factor type can then again contain an Expression type (after which this loop can theoretically continue infinitely; I guess that's where the problem is because the ANTLR parser used by xText was not designed for this kind of recursion). I tried to solve this problem by using a syntactic predicate (=> symbol) in the Expression type (see

(=> "endSimpleExpression")

but it's still not working. I know for sure that it has to do with the relationship between the types Expressions and Factor (because if I don't add Expression types in the Factor type, the DSL works just fine). I assume that I am not placing the syntactic predicate on the right place. Another solution that I considered was the use of left Factoring, but I don't know how to apply left factoring in this case. I am curious to your thoughts on this problem.

grammar org.xtext.example.mydsl.FinalDsl with org.eclipse.xtext.common.Terminals

generate finalDsl "http://www.xtext.org/example/mydsl/FinalDsl"


Model:
    'functionName' name = STRING
    functions += FunctionElements*
;

// Function elements of which the model exists. The model can contain
// library functions, for loops, and if/else statements.
  FunctionElements:
    ifElseStatements += IfElseStatements |
    statements += Statement 
; 

// IfElse Statements requiring if statements and optionally followed by
// one else statement.
IfElseStatements: 
    ifStatements += IfStatements
    (elseStatement = ElseStatement)?
;

// If statements requiring conditions and optionally followed by
// library functions or for loops.
IfStatements:
    'if'
    expression = Expression
    (ifFunctions += libraryFunctionsEnum | forLoops += ForLoops)
;

// Else statement requiring one or multiple library functions.
ElseStatement:
    'else' elseFunctions += libraryFunctionsEnum
;

// For loops requiring one condition and followed by zero or more
// library functions
ForLoops:
    'for'
    expressions = Expression
    libraryFunctions += libraryFunctionsEnum*

;


Statement:
    //compoundStatement += CompoundStatement | //left out of Statement because 
    // otherwise a recursive call exists (statement += compoundstatement += statement
    simpleStatement += SimpleStatement |
    structuredStatement += StructuredStatement
;

SimpleStatement:
    classOperationStatement += ClassOperationStatement | 
    libraryInterFaceMethodStatement += LibraryInterFaceMethodStatement | 
    libraryPersistenceMethodStatement += LibraryPersistenceMethodStatement
;

StructuredStatement:
    forLoops += ForLoops | ifElseStatements += IfElseStatements
;



ClassOperationStatement:
    classOperationName += libraryFunctionsEnum
;

LibraryInterFaceMethodStatement:
    interfaceMethods += libraryInterFaceMethodStatementEnum
;


LibraryPersistenceMethodStatement:
    persistenceMethods += libraryPersistenceMethodStatementEnum
;




//*Eventually filled with details from class diagram, but for now we manually fill it for the sake of testing.
enum libraryFunctionsEnum:
    login='login'|
    hasCode= 'encrypt'|
    display='display'
;

enum libraryPersistenceMethodStatementEnum:
    createInstance = "createInstance" |
    log = "log"
;

enum libraryInterFaceMethodStatementEnum:
    mesasge = "message" |
    error = "error"
;

Expression:
simpleExpression = SimpleExpression 
(relationalOperator = RelationalOperator 
additionalSimpleExpression = SimpleExpression)?
(=> "endSimpleExpression")
;


SimpleExpression:

    term = Term
    additionalExpressions += AdditionalExpressions*
;

AdditionalExpressions:
    additionOperator = AdditionOperator
    term = Term
;

Term:
    factorTerm = Factor
    additionalTerm += AdditionalTerm*
;

AdditionalTerm:
    multiplicationOperator = MultiplicationOperator 
    factor = Factor
;

// We can optionally integrate Java types right here (int, boolean, string, etc.)
Factor: {Factor} (
    "("  expression = Expression ")" |
    //'not' factor += Factor |
     operationParameterName = OperationParameterName |
    classAttributeName += ClassAttributeName |
     INT //| STRING //| set = Set 
    )
;

OperationParameterName: // We can use identifiers right here, but for now I put in a string
    'operationParameter' STRING
;

ClassAttributeName: // We can use identifiers right here, but for now I put in a string
    STRING
;

RelationalOperator:
"=" | "<>" | "<" | "<=" | ">" | ">=" | "in"
;

AdditionOperator:
"+" | "-" | "or"
;

MultiplicationOperator:
"*" | "/" | "and"
;

enum logicalOperators:
    greaterThan='>'|
    smallerThan='<'|
    greaterOrEqualThan='=>'|
    smallerOrEqualThan='<='|
    equalTo='=='
;
1
There is nothing wrong with the fact that an expression can contain a term and a term can contain an expression. Not only is ANTLR perfectly capable of handling that kind of recursion, but you need that kind of recursion for most languages. What ANTLR can't handle is left recursion (though ANTLR 4 can handle it if you structure your grammar in a certain way, but that's irrelevant because xtext uses ANTLR 3), but your rule isn't left recursive because there's a "(" before the call to Expression, meaning the recursion isn't the left-most part of the definition. - sepp2k
So please be more specific about your problem. Do you get an error message that is telling you that your grammar is ambiguous or left recursive? If so please post that error message exactly. Or does the grammar simply not match what you want it to match? If so, please show a simple input that should match, but doesn't (or vice versa). - sepp2k
@sepp2k thanks for your reply. The problem is not that my xText grammar doesn't compile. The above grammar actually does not give any direct errors, but when it compiles I can't execute the compiled files (well I can, but when executing the compiled files to start my DSL in a new Eclipse Environment, a plugin error appears meaning that apparently something went wrong). The console does however state the following error when compiling the grammar: - Jos B.
error(211): ../org.xtext.example.finaldsl/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalFinalDsl.g:139:2: [fatal] rule ruleFunctionElements has non-LL(*) decision due to recursive rule invocations reachable from alts 1,2. Resolve by left-factoring or using syntactic predicates or using backtrack=true option. - Jos B.
So 'under the hood' this does seem to be a left-recursion problem (despite the fact that it starts with "(" as you have already noted). The thing is that when I remove these brackets, the rules which are impacted by this left-recursion problem are immediately underlined with a red line. When hovering my mouse over them, it states 'This rule call is part of a left recursive call graph'. So it does seem to be a left recursion problem (doesn't matter if you add "(" brackets or not). - Jos B.

1 Answers

0
votes

InternalFinalDsl.g:139:2: [fatal] rule ruleFunctionElements has non-LL(*) decision due to recursive rule invocations reachable from alts 1,2.

So let's look at the rule FunctionElements:

  FunctionElements:
    ifElseStatements += IfElseStatements |
    statements += Statement 
;

Okay, so FunctionElements can either be an IfElseStatement or a Statement. That sounds suspicious and sure enough: a Statement can be a StructuredStatement, which can in turn be an IfElseStatement. So the above is ambiguous because an IfElseStatement could either be derived directly via FunctionElements -> IfElseStatement or indirectly via FunctionElements -> Statement -> StructuredStatement -> IfElseStatement.

So you should simply remove the IfElseStatement alternative because it is redundant.