Я знаю, что был старый вопрос Как правило синтаксического анализа PetitParser может сигнализировать об ошибке? . Лукас Ренггли написал:
вообще это нехороший стиль (смешивает синтаксическое и семантическое анализ)
Вот мой пример:
Я хочу проанализировать следующий термин:
(0.53,00)
^
Я хочу сообщить пользователю, что ошибка находится там, где указывает ^
. Что-то вроде: «пожалуйста, удалите лишнюю запятую из вашего ввода».
Грамматика:
^ openParenthesis, number, closeParenthesis
Токены:
openParenthesis
^ $( asPParser
number
^ wholeNumber plus, dot optional, wholeNumber optional
closeParenthesis
^ $) asPParser
wholeNumber
^#digit asPParser plus trim
Как бы вы написали код для обнаружения такой ошибки (дополнительной запятой)?
Вы можете использовать синтаксический анализатор с отрицательным прогнозом. Я не помню API в Smalltalk, но в Dart (прямой порт исходной реализации Smalltalk) вы бы написали:
final grammar = char('(')
& number
& char(',').not('please remove the extra comma from your input')
& char(')');
Чтобы реализовать это в оригинальном 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 ]
Чтобы просто протестировать 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 ]
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 ]
Это возвращает сообщение об ошибке, если за числом следует запятая, но в противном случае позволяет следующему синтаксическому анализатору обрабатывать остальную часть последовательности.
Хм, метод #not
определяется как ^ PP2NotNode on: self
как мне отправить сообщение? А #not:
нет.
В исходном PetitParser сообщение об ошибке PPNotParser
, к сожалению, не настраивается и жестко запрограммировано как пустая строка. Однако его можно легко изменить, добавив переменную экземпляра message
и создав ошибку с содержимым этой переменной: i.sstatic.net/6HnrePOB.png
Большое спасибо за ответ. Я настрою его на Smalltalk (я понятия не имею о Dart), отредактирую ваш ответ скорректированным кодом. Является ли синтаксический анализатор с отрицательным прогнозом единственным способом? Потому что, если пример станет еще сложнее, он может стать еще сложнее. Для этого я могу задать другой вопрос.