Разобрать идентификаторы, которые не заканчиваются определенными символами в attoparsec

Я застрял в написании синтаксического анализатора attoparsec для анализа Единого кода для единиц измерения. of Measure вызывает <ATOM-SYMBOL>. Он определяется как самая длинная последовательность символов определенного класса (этот класс включает все цифры от 0 до 9), которая не заканчивается цифрой.

Итак, учитывая ввод foo27, я хочу потреблять и возвращать foo, для 237bar26 я хочу потреблять и возвращать 237bar, для 19 я хочу потерпеть неудачу, ничего не потребляя.

Я не могу понять, как построить это из takeWhile1, takeTill или scan, но я, вероятно, упускаю что-то очевидное.

Обновление: моя лучшая попытка до сих пор состояла в том, что мне удалось исключить последовательности, которые полностью состоят из цифр.

atomSymbol :: Parser Text
atomSymbol = do
               r <- core
               if (P.all (inClass "0-9") . T.unpack $ r)
                 then fail "Expected an atom symbol but all characters were digits."
                 else return r
  where
    core = A.takeWhile1 $ inClass "!#-'*,0-<>-Z\\^-z|~"

Я попытался изменить это, чтобы проверить, был ли последний символ цифрой, а не все ли они были, но, похоже, он не отслеживает один символ за раз.

Обновление 2:

Весь файл находится по адресу https://github.com/dmcclean/Dimensional-attoparsec/blob/master/src/Numeric/Units/Dimensional/Parsing/Attoparsec.hs. Это строится только против ветки prefixes из https://github.com/dmcclean/Dimension.


person Doug McClean    schedule 04.12.2015    source источник
comment
Вот комбинатор, который может вам помочь: notFollowedBy p = p >> fail "not followed by"   -  person arrowd    schedule 04.12.2015
comment
Пожалуйста, сделайте свой вопрос автономным и добавьте свои заявления об импорте. Здесь возникает вопрос (даже если можно догадаться), чему соответствует квалифицированный импорт A, P и T.   -  person jub0bs    schedule 05.12.2015
comment
Не могли бы вы взять весь жетон, перевернуть его, отбросить цифры, перевернуть обратно и проверить, не осталось ли чего?   -  person VlatkoB    schedule 06.12.2015


Ответы (1)


Вам следует переформулировать задачу и обрабатывать диапазоны цифр (0-9) и диапазоны нецифровых символов (!#-'*,:-<>-Z\\^-z|~) отдельно. Затем интересующий синтаксический элемент может быть описан как

  • необязательный диапазон цифр, за которым следует
  • нецифровой диапазон, за которым следует
  • ноль или более {цифровой диапазон, за которым следует нецифровой диапазон}.
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Applicative ((<|>), many)
import Data.Char (isDigit)

import Data.Attoparsec.Combinator (option)
import Data.Attoparsec.Text (Parser)
import qualified Data.Attoparsec.Text as A
import Data.Text (Text)
import qualified Data.Text as T

atomSymbol :: Parser Text
atomSymbol = f <$> (option "" digitSpan)
               <*> (nonDigitSpan <|> fail errorMsg)
               <*> many (g <$> digitSpan <*> nonDigitSpan)
  where
    nonDigitSpan = A.takeWhile1 $ A.inClass "!#-'*,:-<>-Z\\^-z|~"
    digitSpan    = A.takeWhile1 isDigit
    f x y xss    = T.concat $ x : y : concat xss
    g x y        = [x,y]
    errorMsg     = "Expected an atom symbol but all characters (if any) were digits."

Тесты

[...] учитывая ввод foo27, я хочу потреблять и возвращать foo, для 237bar26 я хочу потреблять и возвращать 237bar, для 19 я хочу потерпеть неудачу, ничего не потребляя.

λ> A.parseOnly atomSymbol "foo26"
Right "foo"

λ> A.parseOnly atomSymbol "237bar26"
Right "237bar"

λ> A.parseOnly atomSymbol "19"
Left "Failed reading: Expected an atom symbol but all characters (if any) were digits."
person jub0bs    schedule 05.12.2015
comment
Это очень хороший ответ, большое спасибо. Я прошу прощения за задержку с отзывом об этом, я был совершенно завален в офисе. - person Doug McClean; 17.12.2015
comment
@DougMcClean Нет проблем. - person jub0bs; 18.12.2015