#parsing #parser-combinators
#синтаксический анализ #анализатор-комбинаторы
Вопрос:
Привет, я пытаюсь создать анализатор combinator, и в настоящее время я пытаюсь заставить его читать заголовки и создавать анализаторы на основе того, какой заголовок анализируется. Т.е. Заголовок; int, float, string приведет к Parser<Parser<int>*Parser<float>*Parser<string>>
.
Однако мне интересно, как бы вы распаковали «внутренние» анализаторы, которые затем заканчиваются чем-то вроде; Parser<int*float*string>?
Тип анализатора равен: type Parser<'a> = Parser of (string -> Result<'a * string, string>)
Ответ №1:
Я не уверен, что ваша идея с вложенными анализаторами сработает — если вы будете динамически анализировать заголовок, то вам нужно будет создать список анализаторов одного и того же типа. То, как вы написали это, предполагает, что тип анализатора будет зависеть от входных данных, что невозможно в F #.
Итак, я ожидаю, что вам нужно будет определить значение, подобное этому:
type Value = Int of int | String of string | Float of float
И тогда ваш анализатор, который анализирует заголовок, выдаст что-то вроде:
let parseHeaders args : Parser<Parser<Value> list> = (...)
Следующий вопрос заключается в том, что вы хотите делать с вложенными анализаторами? Предположительно, вам нужно будет превратить их в единый анализатор, который анализирует всю строку данных (если это что-то вроде CSV-файла). Обычно вы определяете функцию sequence
:
val sequence : sep:Parser<unit> -> parsers:Parser<'a> list -> Parser<'a list>
Для этого требуется разделитель (скажем, анализатор для распознавания запятой) и список анализаторов и создается единый анализатор, который запускает все анализаторы в последовательности с разделителем между ними.
Затем вы можете сделать:
parseHeaders input |> map (fun parsers -> sequence (char ',') parsers)
И вы получаете единый анализатор Parser<Parser<string>>
. Теперь вы хотите запустить вложенный синтаксический анализатор на остальной части, оставшейся после запуска внешнего синтаксического анализатора, который распознает заголовки. Следующая функция выполняет трюк:
let unwrap (Parser f:Parser<Parser<'a>>) = Parser (fun s ->
match f s with
| Result.Ok(Parser nested, rest) -> nested rest
| Result.Error e -> Result.Error e )
Комментарии:
1. @Simplexity Я думаю, что это лучше задать как другой вопрос — я не уверен, что вы имеете в виду, и я не думаю, что мы можем осмысленно прояснить это в комментарии.