Как правильно обрабатывать ошибки синтаксического анализа с помощью PetitParser или PetitParser2 в Pharo

Я знаю, что был старый вопрос Как правило синтаксического анализа PetitParser может сигнализировать об ошибке? . Лукас Ренггли написал:

вообще это нехороший стиль (смешивает синтаксическое и семантическое анализ)

Мой вопрос: как правильно это сделать на более сложном примере?

Вот мой пример:

Я хочу проанализировать следующий термин:

(0.53,00)
     ^

Я хочу сообщить пользователю, что ошибка находится там, где указывает ^. Что-то вроде: «пожалуйста, удалите лишнюю запятую из вашего ввода».

Грамматика:

^ openParenthesis, number, closeParenthesis

Токены:

openParenthesis
    ^ $( asPParser

number
    ^ wholeNumber plus, dot optional, wholeNumber optional

closeParenthesis
    ^ $) asPParser

wholeNumber 
   ^#digit asPParser plus trim

Как бы вы написали код для обнаружения такой ошибки (дополнительной запятой)?

Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
1
0
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете использовать синтаксический анализатор с отрицательным прогнозом. Я не помню API в Smalltalk, но в Dart (прямой порт исходной реализации Smalltalk) вы бы написали:

В Дарте для сравнения

final grammar = char('(') 
    & number 
    & char(',').not('please remove the extra comma from your input') 
    & char(')'); 

Оригинальный PetitParser в Smalltalk (как предложил Лукас Ренггли в разделе комментариев)

Чтобы реализовать это в оригинальном PetitParser (как предложено в комментариях):

Добавьте переменную экземпляра message в определение класса:

PPDelegateParser subclass:#PPNotParser
        instanceVariableNames:'message'
        classVariableNames:''
        poolDictionaries:''
        category:'PetitParser-Parsers'

В PPNotParser>>parseOn:

parseOn: aPPContext
        | element memento |
        memento := aPPContext remember.
        element := parser parseOn: aPPContext.
        aPPContext restore: memento.
        ^ element isPetitFailure
                ifFalse: [ PPFailure message: message context: aPPContext ]

Тестирование с помощью PetitParser2

Чтобы просто протестировать Pharo Smalltalk с помощью PetitParser2, вам нужно сделать следующее — реализовать это, как в PetitParser выше, вероятно, будет сложнее:

  • Добавьте #message к #PP2NotNode:
    PP2DelegateNode subclass: #PP2NotNode
       instanceVariableNames: 'message'
       classVariableNames: ''
       package: 'PetitParser2-Nodes'
  • Создайте сеттеры и геттеры в #PP2NotNode
   message: aString
       message := aString
   message
       ^ message
  • Отредактируйте PP2NotNode>>#parseOn: и примите решение, исходя из того, пусто сообщение или нет:
parseOn: aPP2Context
    ^ self message isEmptyOrNil
        ifTrue: [ strategy parseOn: aPP2Context ]
        ifFalse: [ strategy parseOn: aPP2Context message: self message ]
  • Затем создайте новый метод PP2Not>>parseOn:message:
parseOn: context message: aMessageString
    | memento retval |
    memento := self remember: context.
    retval := node child parseOn: context.
    self restore: context from: memento.
    ^ retval isPetit2Failure
        ifTrue: [ nil ]
        ifFalse: [ PP2Failure message: aMessageString context: context ]
  • Вам нужно улучшить PP2CompositeNodeTest>>parse: production:to:end:checkResult:
parse: aString production: production to: expectedResult end: end checkResult: aBoolean
    | ctx |
    ctx := self context.
    resultContext := self
        parse: aString
        withParser: production
        withContext: ctx.
    result := resultContext value.
    resultContext isPetit2Failure
        ifTrue: [ self unableToParse: resultContext withString: aString ].
    self assert: resultContext position equals: end.
    aBoolean
        ifTrue: [ self assert: expectedResult equals: result ].
    ^ result
  • И последнее, но не менее важное: создайте метод PP2CompositeNodeTest>>unableToParse:withString:
unableToParse: aResultContext withString: aString
    aResultContext message isEmptyOrNil
        ifTrue: [ self deny: aResultContext isPetit2Failure description: 'Unable to parse ' , aString printString ]
        ifFalse: [ self
                deny: aResultContext isPetit2Failure
                description:
                    aResultContext message , ' While parsing: ' , aString printString , '; ' , ' See postion: '
                        , aResultContext position asString ]

Это возвращает сообщение об ошибке, если за числом следует запятая, но в противном случае позволяет следующему синтаксическому анализатору обрабатывать остальную часть последовательности.

Большое спасибо за ответ. Я настрою его на Smalltalk (я понятия не имею о Dart), отредактирую ваш ответ скорректированным кодом. Является ли синтаксический анализатор с отрицательным прогнозом единственным способом? Потому что, если пример станет еще сложнее, он может стать еще сложнее. Для этого я могу задать другой вопрос.

tukan 04.07.2024 10:12

Хм, метод #not определяется как ^ PP2NotNode on: self как мне отправить сообщение? А #not: нет.

tukan 04.07.2024 10:31

В исходном PetitParser сообщение об ошибке PPNotParser, к сожалению, не настраивается и жестко запрограммировано как пустая строка. Однако его можно легко изменить, добавив переменную экземпляра message и создав ошибку с содержимым этой переменной: i.sstatic.net/6HnrePOB.png

Lukas Renggli 04.07.2024 13:10

Другие вопросы по теме

Похожие вопросы

Data.table использует df[, i] для получения столбца
Получение диапазона не найдено при использовании getRange()
Отправил каждую команду SAP другому субподрядчику и выполнил ее для обработки ошибок
Неожиданная «внутренняя ошибка» при доступе к моей авторизованной панели управления пользователем после входа в систему
Как получить ошибку SQLite в PHP/Laravel, когда цитируемый столбец не существует
Невозможно подключиться к базе данных Oracle 19c с помощью SSH-туннеля с использованием Python
Как я могу использовать TEST_PROTECT в Unity C Test Framework, чтобы добиться поведения примера
Запрос API Trefle с использованием Elm
Как избежать клонирования при передаче значения в функцию-потребитель, но при необходимости вернуть его в случае ошибки?
Как мне управлять возвращаемым нулевым значением при поиске в реестре в блоке Try-Catch? Получение «Вы не можете вызвать метод для выражения с нулевым значением»