Советы по FParsec для обработки пробелов

У меня есть следующее подвыражение для разбора "цитаты", которые имеют следующий формат

"5.75 @ 5.95"

Поэтому у меня есть это выражение парсек для его анализа

let pquote x = (sepBy (pfloat) ((spaces .>> (pchar '/' <|>  pchar '@' )>>. spaces))) x

Это работает нормально... за исключением случаев, когда в моем входе есть пробел, поскольку выражение-разделитель начинает потреблять контент. Итак, я обернул его вокруг попытки, которая работает и, насколько я понимаю, более или менее означает, что это означало быть.

let pquote x = (sepBy (pfloat) (attempt (spaces .>> (pchar '/' <|>  pchar '@' )>>. spaces))) x

Поскольку я не очень хорошо знаю fparsec, мне интересно, есть ли лучший способ написать это. это кажется немного тяжелым (хотя все еще очень управляемым, конечно)

3 ответа

Решение
let s1 = "5.75         @             5.95              "
let s2 = "5.75/5.95   "
let pquote: Parser<_> =
    pfloat
    .>> spaces .>> skipAnyOf ['@'; '/'] .>> spaces
    .>>. pfloat
    .>> spaces

Заметки:

  1. я сделал spaces необязательно везде spaces пропускает любую последовательность из нуля или более пробелов, поэтому нет необходимости использовать opt - спасибо @Daniel;
  2. type Parser<'t> = Parser<'t, UserState> - Я определяю это таким образом, чтобы избежать ошибки "ограничение значения"; Вы можете удалить это;
  3. Кроме того, не забывайте следующее, если ваша программа может работать в системе с настройками языка по умолчанию с десятичной запятой: System.Threading.Thread.CurrentThread.CurrentCulture <- Globalization.CultureInfo.GetCultureInfo "en-US" это не сработает, спасибо @Stephan
  4. Я бы не использовал sepBy если у меня нет списка значений неизвестного размера.
  5. Если вам не нужно возвращаемое значение (например, '@' символов), рекомендуется использовать skip* функции вместо p* по соображениям производительности.

UPD добавил косую черту в качестве разделителя

Я бы, наверное, сделал что-то вроде этого, который возвращает float * float:

let ws = spaces
let quantity = pfloat .>> ws
let price = pfloat .>> ws
let quoteSep = pstring "@" .>> ws
let quote = quantity .>> quoteSep .>>. price //`.>> eof` (if final parser)

Для каждого парсера характерно использовать конечные пробелы. Просто убедитесь, что ваш парсер верхнего уровня включает eof,

Предполагая, что вы можете иметь более двух float во входных данных и '/' и '@' являются разделителями:

let ws = spaces
let str_ws s = pstring s .>> ws
let float_ws = pfloat .>> ws
let pquote = sepBy float_ws (str_ws "/" <|> str_ws "@")

Говоря об обработке пробелов, этот раздел учебника FParsec действительно полезен.

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