Распаковка вложенных аппликативных функторов f#

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