В качестве примера я возьму этот простой синтаксический анализатор C# от Филиппа Трелфорда. Для разбора идентификатора он пишет это (немного изменено):
let reserved = ["for";"do"; "while";"if";"switch";"case";"default";"break" (*;...*)]
let pidentifierraw =
let isIdentifierFirstChar c = isLetter c || c = '_'
let isIdentifierChar c = isLetter c || isDigit c || c = '_'
many1Satisfy2L isIdentifierFirstChar isIdentifierChar "identifier"
let pidentifier =
pidentifierraw
>>= fun s ->
if reserved |> List.exists ((=) s) then fail "keyword instead of identifier"
else preturn s
Проблема с pidentifier заключается в том, что при сбое индикатор позиции оказывается в конце потока. Мой пример:
Error in Ln: 156 Col: 41 (UTF16-Col: 34)
Block "main" 116x60 font=default fg=textForeground
^
Note: The column count assumes a tab stop distance of 8 chars.
keyword instead of identifier
Очевидно, это не фрагмент C#, но для примера я использовал pidentifier
для разбора текста после font=
. Можно ли указать FParsec показывать ошибку в начале проанализированного ввода? Использование >>?
, .>>.?
или любого из вариантов возврата, похоже, не имеет никакого эффекта.
Я думаю, вам нужен attempt p
, который вернется к исходному состоянию синтаксического анализатора, если синтаксический анализатор p
выйдет из строя. Таким образом, вы можете просто определить pidentifier
как:
let pidentifier =
pidentifierraw
>>= fun s ->
if reserved |> List.exists ((=) s) then fail "keyword instead of identifier"
else preturn s
|> attempt // rollback on failure
Вывод тогда что-то вроде:
Failure:
Error in Ln: 1 Col: 1
default
^
The parser backtracked after:
Error in Ln: 1 Col: 8
default
^
Note: The error occurred at the end of the input stream.
keyword instead of identifier
Если вы не хотите видеть информацию о возврате в сообщении об ошибке, вы можете использовать упрощенную версию attempt
, например:
let attempt (parser : Parser<_, _>) : Parser<_, _> =
fun stream ->
let mutable state = CharStreamState(stream)
let reply = parser stream
if reply.Status <> Ok then
stream.BacktrackTo(&state)
reply
Вывод теперь просто:
Failure:
Error in Ln: 1 Col: 1
default
^
keyword instead of identifier
Я удивлен слышать это. У меня работает корректно. Я обновил свой ответ выводом, который вижу.
Виноват. Я не знаю, что я сделал не так. Спасибо за включение вывода, хотя. Возможно, отчет о возврате — это идентичный исходной ошибки, и когда прокрутка вывода поднялась, я его пропустил. Надеюсь, вы не возражаете, если я дам ему 24 часа, прежде чем приму ответ. :)
Я полностью пропустил оператор >>=?
... Пожалуйста, смотрите мой ответ.
Да, я думаю, что >>=?
эффективно >>=
плюс attempt
. Лично я всегда использую attempt
, потому что он более общий и его легче запомнить. (FWIW, я также использую построитель вычислений parse
вместо того, чтобы вручную писать привязки с помощью >>=
.)
Я обновил свой ответ, включив в него упрощенную версию attempt
, которая не включает информацию о возврате в выходное сообщение об ошибке.
Да, это все. Я наградил вас ответом - он все равно был вашим, ты дал пищу моему мозгу, чтобы начать думать :). Я думаю, что аналогичным образом я могу изменить >>=?
, что менее многословно. PS В какой-то момент я удалю свои старые комментарии, так как они больше не служат цели.
К сожалению, я пропустил оператор >>=?
, который, по-видимому, (по крайней мере, семантически) эквивалентен attempt
этому Брайан Бернс правильно предположил.
Проблема с обоими этими подходами заключается в том, что последующие сообщения The parser backtracked after:[…]
могут каскадироваться, если предыдущие парсеры также возвращаются:
Error in Ln: 156 Col: 29 (UTF16-Col: 22)
Block "main" 116x60 font=default fg=textForeground
^
Note: The column count assumes a tab stop distance of 8 chars.
Expecting: space/tab
The parser backtracked after:
Error in Ln: 156 Col: 42 (UTF16-Col: 35)
Block "main" 116x60 font=default fg=textForeground
^
Note: The column count assumes a tab stop distance of 8 chars.
Expecting: space/tab
Other error messages:
keyword instead of identifier
Я пока не буду выбирать какой-либо ответ, на случай, если кто-то появится с более элегантным решением.
Да, информации о возврате может быть много в сообщении об ошибке. К сожалению, я не думаю, что есть простой способ удалить его.
Спасибо за ответ. К сожалению, это не сработало. Я все еще получаю сообщение об ошибке в той же позиции.