【问题标题】:Solving a left recursion problem in xText解决 xText 中的左递归问题
【发布时间】:2019-05-09 14:51:43
【问题描述】:

我目前正致力于在 xText 中创建 DSL,但我偶然发现了一个可能与歧义或左递归问题有关的问题。我不确定这两个问题中的哪一个适用于我的案例(我在网上找到的类似主题也提到这些问题通常看起来相关)但我想这与左递归有关。

在我的 DSL 代码的第 100 行,我声明了一个名为 Expression 的规则。如您所见,它聚合了多种其他类型(它们又聚合了多种其他类型,最终也可以聚合称为 Factor 的类型(第 130 行))。最终,整个“聚合树”归结为这种因子类型的问题。如您所见,这种类型可以再次聚合一个表达式。所以有一个循环; Expression 类型最终可以包含一个 Factor 类型,然后 Factor 类型可以再次包含一个 Expression 类型(之后这个循环理论上可以无限继续;我想这就是问题所在,因为 xText 使用的 ANTLR 解析器不是为此而设计的一种递归)。我试图通过在 Expression 类型中使用语法谓词(=> 符号)来解决这个问题(参见

(=> "endSimpleExpression")

但它仍然无法正常工作。我确定它与 Expressions 和 Factor 类型之间的关系有关(因为如果我不在 Factor 类型中添加 Expression 类型,DSL 就可以正常工作)。我假设我没有将句法谓词放在正确的位置。我考虑的另一个解决方案是使用左分解,但我不知道在这种情况下如何应用左分解。我很好奇你对这个问题的看法。

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='=='
;

【问题讨论】:

  • 一个表达式可以包含一个词,一个词可以包含一个表达式,这并没有错。 ANTLR 不仅完全能够处理这种递归,而且大多数语言都需要这种递归。 ANTLR 无法处理的是左递归(尽管如果您以某种方式构建语法,ANTLR 4 可以处理它,但这无关紧要,因为 xtext 使用 ANTLR 3),但是您的规则不是左递归的,因为有一个“(”在调用Expression 之前,这意味着递归不是定义的最左边部分。
  • 所以请更具体地说明您的问题。您是否收到一条错误消息,告诉您您的语法不明确或左递归?如果是这样,请准确发布该错误消息。还是语法根本不匹配您想要匹配的内容?如果是这样,请显示一个应该匹配但不匹配的简单输入(反之亦然)。
  • @sepp2k 感谢您的回复。问题不在于我的 xText 语法无法编译。上面的语法实际上并没有给出任何直接的错误,但是当它编译时我无法执行编译后的文件(我可以,但是当执行编译后的文件以在新的 Eclipse 环境中启动我的 DSL 时,出现插件错误意味着显然出了点问题)。但是,控制台在编译语法时确实会出现以下错误:
  • 错误(211):../org.xtext.example.finaldsl/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalFinalDsl.g:139:2: [致命] 规则 ruleFunctionElements 具有非 LL(*) 决策,因为可以从 alts 1,2 访问递归规则调用。通过左分解或使用句法谓词或使用 backtrack=true 选项来解决。
  • 所以“在幕后”这似乎是一个左递归问题(尽管它以“(”开头,正如你已经指出的那样)。问题是当我删除这些括号,受此左递归问题影响的规则立即用红线下划线。当我将鼠标悬停在它们上时,它指出“此规则调用是左递归调用图的一部分”。所以它似乎是左递归问题(是否添加“(”括号无关紧要)。

标签: eclipse xtext xbase


【解决方案1】:

InternalFinalDsl.g:139:2:[fatal] 规则 ruleFunctionElements 具有非 LL(*) 决策,因为可以从 alts 1,2 访问递归规则调用。

那么我们来看看规则FunctionElements

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

好的,所以FunctionElements 可以是IfElseStatementStatement。这听起来很可疑,而且很确定:Statement 可以是StructuredStatement,而IfElseStatement 又可以是IfElseStatement。所以上面是模棱两可的,因为IfElseStatement 可以直接通过FunctionElements -&gt; IfElseStatement 或间接通过FunctionElements -&gt; Statement -&gt; StructuredStatement -&gt; IfElseStatement 导出。

因此,您应该简单地删除 IfElseStatement 替代项,因为它是多余的。

【讨论】:

  • 再次感谢。既然您声明了它,这完全有道理(我可能在进一步阐述我的 DSL 时不小心把它留在那里)。我现在已经如下声明了 FunctionElements 规则(因此它没有多余的 IfElseStatements)。 FunctionElements: statements += Statement ; 但是,遗憾的是,表达式和因子类型最终导致左递归调用图问题的问题仍然存在。我发现另一个topic 讨论了一个与我相似的问题。
  • 如果您查看 Christian Dietrich 在该主题中的最后一条评论,他提出的语法似乎可以解决那里讨论的问题。该语法的最后三个规则似乎通过将表达式定义为包含左右部分来解决问题(查看他定义的名为 PARLIST 的规则)。我现在正在研究如何将此解决方案转换为我的 DSL。
  • @JosB。当您说“不幸的是,表达式和因子类型最终导致左递归调用图问题的问题仍然存在”时,您到底是什么意思?你说过当你删除“(”时你会得到一个关于左递归的错误,这是有道理的,因为这种变化会使你的语法左递归。你还说如果你把“( “返回。所以就我而言,这就是你遇到的任何左递归问题的结束。
  • 如果您在语法的其他地方遇到另一个关于左递归的错误,请发布该错误。如果没有,则您没有任何左递归,并且您遇到的任何问题都与左递归无关。
  • 请注意,您链接的线程中的语法实际上是递归的,似乎与您的语法没有任何关系。所以我认为该线程与您的问题无关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
相关资源
最近更新 更多