Правило Xtext, которое отменяет другие допустимые совпадения - PullRequest
0 голосов
/ 08 мая 2020

Я новичок в Xtext, поэтому, возможно, я задаю неправильный вопрос или использую неправильную терминологию. Помните об этом в своих ответах.

Я пытаюсь реализовать JBehave EBNF Spe c с нуля в Xtext в качестве обучающего упражнения. 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, тест вообще не сможет выполнить синтаксический анализ с помощью

    Ожидается: не null, но: было null

Я как бы понимаю, что здесь происходит, в некотором смысле. Токены, которые я определяю перед WORD, также являются допустимыми словами, поэтому анализатор сбрасывает их. Поэтому мои вопросы заключаются в следующем.

  1. Где я могу найти в документации Xtext (или других источниках), которые описывают основные принципы, которые здесь затрагиваются. К настоящему времени я прочитал документы Xtext много раз, но все, что я смог найти, это краткое замечание о зависимости от порядка терминальных операторов.

  2. Какой хороший способ отладить, как парсер интерпретирует мою грамматику? Есть ли что-то похожее на выгрузку IFormattableDocument в консоль, но для лексера / парсера / чего угодно?

  3. И, наконец, как лучше всего решить эту проблему с точки зрения Xtext . Следует ли мне изучать настраиваемые типы данных или это можно выразить в чистом Xtext?

Я пытаюсь понять основные принципы.

Обновление

Ну это конечно странно. Я попытался пока обойтись без этого и реализовать следующую часть spe c.

; 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 изменяет то, как синтаксический анализатор интерпретирует грамматику?

1 Ответ

0 голосов
/ 09 мая 2020

Я смог ответить на все 3 своих вопроса, используя ANTLRWorks , инструмент gui в форме исполняемого файла jar с целью express отладки, визуализации и помощи в понимании поведение парсера.

Чтобы это работало с Xtext, нужно добавить следующий генератор mwe2:

language = StandardLanguage {
    // ...

    parserGenerator = {
        debugGrammar = true
    }
}

Затем откройте сгенерированный файл отладки в инструменте ANTLRWorks и нажмите значок «Ошибка» (отладка). Этот файл должен находиться по адресу src-gen/*/parser/antlr/internal/DebugInternal*.g

Источник: https://blogs.itemis.com/en/debugging-xtext-grammars-what-to-do-when-your-language-is-ambiguous

...