Можно ли с помощью FParsec манипулировать позицией ошибки при сбое парсера?

В качестве примера я возьму этот простой синтаксический анализатор 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 показывать ошибку в начале проанализированного ввода? Использование >>?, .>>.? или любого из вариантов возврата, похоже, не имеет никакого эффекта.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
43
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Я думаю, вам нужен 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

Спасибо за ответ. К сожалению, это не сработало. Я все еще получаю сообщение об ошибке в той же позиции.

Stelios Adamantidis 30.03.2022 22:43

Я удивлен слышать это. У меня работает корректно. Я обновил свой ответ выводом, который вижу.

Brian Berns 30.03.2022 22:45

Виноват. Я не знаю, что я сделал не так. Спасибо за включение вывода, хотя. Возможно, отчет о возврате — это идентичный исходной ошибки, и когда прокрутка вывода поднялась, я его пропустил. Надеюсь, вы не возражаете, если я дам ему 24 часа, прежде чем приму ответ. :)

Stelios Adamantidis 30.03.2022 22:54

Я полностью пропустил оператор >>=?... Пожалуйста, смотрите мой ответ.

Stelios Adamantidis 30.03.2022 23:20

Да, я думаю, что >>=? эффективно >>= плюс attempt. Лично я всегда использую attempt, потому что он более общий и его легче запомнить. (FWIW, я также использую построитель вычислений parse вместо того, чтобы вручную писать привязки с помощью >>=.)

Brian Berns 30.03.2022 23:23

Я обновил свой ответ, включив в него упрощенную версию attempt, которая не включает информацию о возврате в выходное сообщение об ошибке.

Brian Berns 31.03.2022 00:30

Да, это все. Я наградил вас ответом - он все равно был вашим, ты дал пищу моему мозгу, чтобы начать думать :). Я думаю, что аналогичным образом я могу изменить >>=?, что менее многословно. PS В какой-то момент я удалю свои старые комментарии, так как они больше не служат цели.

Stelios Adamantidis 03.04.2022 18:36

К сожалению, я пропустил оператор >>=?, который, по-видимому, (по крайней мере, семантически) эквивалентен 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

Я пока не буду выбирать какой-либо ответ, на случай, если кто-то появится с более элегантным решением.

Stelios Adamantidis 30.03.2022 23:19

Да, информации о возврате может быть много в сообщении об ошибке. К сожалению, я не думаю, что есть простой способ удалить его.

Brian Berns 30.03.2022 23:25

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