首页 > 解决方案 > 否定其他有效匹配的 Xtext 规则

问题描述

我对 Xtext 很陌生,所以我可能问错了问题,或者使用了不正确的术语。请在您的回复中牢记这一点。

我正在尝试在 Xtext 中从头开始实施JBehave EBNF Spec作为学习练习。JBehave 是一种非常“冗长”的语法,类似于我需要能够维护的语法,因此我需要了解如何在不同的上下文中处理各种类型的“单词”。

我已经能够让这个测试用例作为起点通过。

@Test
def void loadModel() {

    // Multi-line
    var story = parseHelper.parse('''
        The quick brown fox
        Jumps over the lazy dog
    ''')

    assertThat(story, notNullValue())
    assertThat(
        story.description,
        equalTo('''
            The quick brown fox
            Jumps over the lazy dog
        ''')
    )

    // Single-line description
    story = parseHelper.parse('''
        The quick brown fox
    ''')
    assertThat(
        story.description,
        equalTo("The quick brown fox\n")
    )
}

使用这个语法定义......

grammar org.example.jbehave.JBehave hidden (WS)

import "http://www.eclipse.org/emf/2002/Ecore" as ecore
generate jbehave "http://www.example.org/jbehave"

// The story describes a feature via description, narrative and a set of scenarios
// Story := Description? Meta? Narrative? GivenStories? Lifecycle? Scenario+ ;
Story:
    description=Description?
;

// The Description is expressed by any sequence of words that must not contain any keywords at start of lines.
// Description := (Word Space?)* ;
Description:
    ((WORD) (WORD)* EOL+)+
//  ((NON_KEYWORD) (WORD)* EOL+)+
;

// Key Words
////

// TODO: parser fails when uncommented
//terminal NON_KEYWORD: 
//  !(IN_ORDER_TO
//      | AS_A
//      | I_WANT_TO
//      | SO_THAT
//      | SCENARIO
//      | GIVEN_STORIES
//      | GIVEN
//      | THEN
//      | WHEN
//      | AND
//  )
//; 

terminal fragment IN_ORDER_TO: "In order to";
terminal fragment AS_A: "As a";
terminal fragment I_WANT_TO: "I want to";
terminal fragment SO_THAT: "So that";
terminal fragment SCENARIO: "Scenario:";
terminal fragment GIVEN_STORIES: "GivenStories:";
terminal fragment GIVEN: "Given";
terminal fragment WHEN: "When";
terminal fragment THEN: "Then";
terminal fragment AND: "And";

// Common Terminals
////

terminal WORD: ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;

terminal WS: (' '|'\t')+;

terminal EOL: NL;
terminal fragment NL: ('\r'? '\n');

评论中概述了我遇到的问题。

  1. 当我取消注释终端NON_KEYWORD时,测试失败

    预期:“敏捷的棕色狐狸\n跳过懒狗\n”但:是“The”

  2. 如果我然后替换注释掉的行Description,则测试根本无法解析为

    预期:不为空,但:为空

我有点模糊地理解这里发生的事情。我在 WORD 之前定义的标记也是有效的单词,因此它会抛出解析器。因此,我的问题如下。

  1. 我在哪里可以找到 Xtext 文档(或其他来源),这些文档描述了此处受影响的基础原则。到目前为止,我已经多次阅读 Xtext 文档,但我能找到的只是关于终端语句的顺序依赖性的简要说明。

  2. 调试解析器如何解释我的语法的好方法是什么?是否有类似于转储IFormattableDocument到控制台的东西,但对于词法分析器/解析器/其他?

  3. 最后,从 Xtext 的角度来看,解决这个问题的最佳方法是什么。我应该研究自定义数据类型,还是可以在纯 Xtext 中表达?

我正在寻求了解基本原则。

更新

这当然很奇怪。我试图暂时跳过这一点并实施规范的下一部分。

; The narrative is identified by keyword "Narrative:" (or equivalent in I18n-ed locale),
; It is followed by the narrative elements
Narrative:= "Narrative:" ( InOrderTo AsA IWantTo | AsA IWantTo SoThat ) ;

我实际上无法独自完成这项工作。但是,当我取消注释原始代码并一起尝试时,它可以工作!

    @Test
    def void narrativeOnly() {
        var story = _th.parse('''
            Narrative:
            In order check reports
            As a Developer
            I want to workin with todos using examples
        ''')
        assertThat(story, notNullValue())
    }

    @Test
    def void descriptionOnly() {

        // Multi-line
        var story = _th.parse('''
            The quick brown fox
            Jumps over the lazy dog
        ''')

        assertThat(story, notNullValue())
        assertThat(
            story.description,
            equalTo('''
                The quick brown fox
                Jumps over the lazy dog
            ''')
        )

        // Single-line description
        story = _th.parse('''
            The quick brown fox
        ''')
        assertThat(
            story.description,
            equalTo("The quick brown fox\n")
        )
    }
grammar org.agileware.natural.jbehave.JBehave hidden (WS)

import "http://www.eclipse.org/emf/2002/Ecore" as ecore
generate jbehave "http://www.agileware.org/natural/jbehave"

// Story
////

// The story describes a feature via description, narrative and a set of scenarios
// Story := Description? Meta? Narrative? GivenStories? Lifecycle? Scenario+ ;
Story:
    description=Description?
    narrative=Narrative?
;

// Narrative
////

// The narrative is identified by keyword "Narrative:" (or equivalent in I18n-ed locale),
// It is followed by the narrative elements
// Narrative:= "Narrative:" ( InOrderTo AsA IWantTo | AsA IWantTo SoThat ) ;

// The narrative element content is any sequence of characters that do not match a narrative starting word
// NarrativeElementContent := ? Any sequence of NarrativeCharacter that does not match NarrativeStartingWord ? ;

Narrative:
    'Narrative:'
    inOrderTo=InOrderTo
    asA=AsA
    wantTo=IWantTo
;

// InOrderTo:= "In order to" NarrativeElementContent ;
InOrderTo:
    IN_ORDER_TO (WORD) (WORD)* EOL+;

// AsA:= "As a" NarrativeElementContent ;
AsA:
    AS_A (WORD) (WORD)* EOL+;

// IWantTo:= "I want to" NarrativeElementContent ;
IWantTo:
    I_WANT_TO (WORD) (WORD)* EOL+;

// SoThat:= "So that" NarrativeElementContent ;
SoThat:
    SO_THAT (WORD) (WORD)* EOL+;

// The Description is expressed by any sequence of words that must not contain any keywords at start of lines.
// Description := (Word Space?)* ;
Description:
    ((WORD) (WORD)* EOL+)+
;

// Key Words
////

//terminal NON_KEYWORD: 
//  !(IN_ORDER_TO
//      | AS_A
//      | I_WANT_TO
//      | SO_THAT
//      | SCENARIO
//      | GIVEN_STORIES
//      | GIVEN
//      | THEN
//      | WHEN
//      | AND
//  )
//; 

terminal IN_ORDER_TO: "In order to";
terminal AS_A: "As a";
terminal I_WANT_TO: "I want to";
terminal SO_THAT: "So that";
//terminal SCENARIO: "Scenario:";
//terminal GIVEN_STORIES: "GivenStories:";
//terminal GIVEN: "Given";
//terminal WHEN: "When";
//terminal THEN: "Then";
//terminal AND: "And";

// Common Terminals
////

terminal WORD: (LETTER)(LETTER|DIGIT)*;

terminal fragment LETTER: ('a'..'z'|'A'..'Z');

terminal fragment DIGIT: ('0'..'9');

terminal WS: (' '|'\t')+;

terminal EOL: NL;
terminal fragment NL: ('\r'? '\n');

我猜这可以解决#3,但是偶然到达那里有点违背了目的。我现在将接受任何可以指出或向我描述导致我所描述的行为的基本原则的答案。

为什么我不能只匹配一组随机的单词?定义narrative赋值以及description赋值Story如何改变解析器解释语法的方式?

标签: grammarxtext

解决方案


我已经能够使用ANTLRWorks回答我的所有 3 个问题,这是一个可执行 jar 形式的 gui 工具,其明确目的是调试、可视化和帮助人们理解解析器的行为。

要使用 Xtext 进行此操作,需要添加以下 mwe2 生成器:

language = StandardLanguage {
    // ...

    parserGenerator = {
        debugGrammar = true
    }
}

然后在 ANTLRWorks 工具中打开生成的调试文件并点击“Bug”(调试)图标。该文件应位于src-gen/*/parser/antlr/internal/DebugInternal*.g

来源:https ://blogs.itemis.com/en/debugging-xtext-grammars-what-to-do-when-your-language-is-ambiguous


推荐阅读