Глава 9. Использование альтернативных синтаксических анализов

Аннотация

Публикация
Учебник библиотеки FParsec
Дата
Ссылки

В главе 7 “Синтаксический анализатор строковых данных” мы вкратце представили комбинатор выбора <|>en :

val (<|>): Parser<'a,'u> -> Parser<'a,'u> -> Parser<'a,u>

Этот комбинатор позволяет вам поддерживать несколько альтернативных вариантов.

Например, в главе 7 мы использовали <|> для объединения синтаксического анализатора для неэкранированных символов и анализатора для экранированных символов в новый анализатор, который поддерживает оба варианта: normalChar <|> escapedChar.

Другим примером, показывающим, как работает <|> является следующий синтаксический анализатор для разбора строкового представления булевых значений:

let boolean = 
  (stringReturn "true"  true) <|>
  (stringReturn "false" false)

Здесь мы использовали синтаксический анализатор stringReturnen, который берет в качестве первого аргумента строку, и, в случае успеха, возвращает значение, указанное в качестве второго аргумента.

Протестируем синтаксический анализатор boolean на примерах:

> test boolean "false";;
Success: false
> test boolean "true";;
Success: true
> test boolean "tru";;
Failure: Error in Ln: 1 Col: 1
tru
^
Expecting: 'false' or 'true'

Поведение комбинатора <|> имеет две важные характеристики: * <|> Правая часть синтаксического анализатора выполняется, если синтаксический анализатор левой части завершился с не фатальной ошибкой и без изменения состояния. Комбинатор не реализует правило самого длинного совпадения. * Однако, комбинатор пытается использовать правый синтаксический анализатор, если левый синтаксический анализатор при завершении вызывает нет данных для обработки.

Следствием второго пункта является то, что следующий тест завершился неудачно, потому что синтаксический анализатор в левой части <|> получает пробелы перед сбоем:

> test ((ws >>. str "a") <|> (ws >>. str "b")) " b";;
Failure: Error in Ln: 1 Col: 2
 b
 ^
Expecting: 'a'

К счастью, мы можем легко исправить этот синтаксический анализатор, выделив ws:

> test (ws >>. (str "a" <|> str "b")) " b";;
Success: "b"

Если вам интересно, почему <|> ведет себя таким образом и как вы можете обрабатывать ситуации, в которых вам нужно чтобы <|> пытался исполнять альтернативный синтаксический анализатор, даже если первый синтаксический анализатор вызвал ошибку после обработки входных данных смотрите главы 5.6 Parsing alternatives и 5.7 Looking ahead and backtracking в руководстве пользователя.

Если вы хотите использовать более двух альтернативных синтаксических анализаторов, вы можете комбинировать несколько операторов <|>, например, в p1 <|> p2 <|> p3 <|> ..., или вы можете использовать комбинатор choiceen, который принимает последовательность синтаксических анализаторов в качестве аргумента, например choice [ p1 ; p2 ; p3 ; ... ].