Вот что я бы сделал, предполагая, что у вас есть тип данных для ваших цветов; если вы этого не сделаете, просто замените его на то, что вы используете. Функция parseColourGen
принимает любые Text
, разделенные пробелами, и генерирует синтаксический анализатор, который принимает цвет, в котором каждое слово отделено одним или несколькими допустимыми разделителями.
import Prelude hiding (concat, words)
import Control.Applicative ((<|>))
import Data.Attoparsec.Text
import Data.List (intersperse)
import Data.Text (concat, pack, singleton, Text, words)
data Colour = LightBlue | DarkBlue | VibrantRed deriving Show
parseColourGen :: Text -> Parser [Text]
parseColourGen = sequence . intersperse (mempty <$ many1 legalSep) .
fmap string . words
parseColour :: [(Text, Colour)] -> Parser Colour
parseColour = foldl1 (<|>) . fmap (\(text, colour) ->
colour <$ parseColourGen text)
legalSep :: Parser Text
legalSep = singleton <$> satisfy (inClass "\n\r- ")
Затем вы можете передать свой wordList
парсеру; однако это должен быть список ассоциаций:
wordList :: [(Text, Colour)]
wordList = [("light blue", LightBlue), ("dark blue", DarkBlue), ("vibrant red", VibrantRed)]
Таким образом, вы можете настроить все свои цвета и соответствующие им названия цветов в одном месте, а затем запустить анализатор следующим образом:
> parse (parseColour wordList) $ pack "vibrant-red"
Done "" VibrantRed
РЕДАКТИРОВАТЬ
После редактирования вашего вопроса, я думаю, я понимаю, что вы хотите немного лучше. Кстати, я бы все же предпочел решение, описанное выше, но вот как исправить ваш последний блок кода:
- Как должен сказать вам компилятор, шаблоны
(w:ws)
и [w]
перекрываются, поэтому если вы хотите, чтобы среда выполнения ловила одноэлементный шаблон, вы должны поместить его сверху. a >> b
означает «выполнить действие a
, отменить его результат, затем выполнить действие b
и использовать его». результат". Вот почему ваш парсер (с исправлением выше) выведет Done "" "blue"
. Простой способ исправить это - использовать нотацию do
для привязки результата всех трех вычислений и возврата их конкатенации.
Вот как теперь выглядит ваш код:
wordListParser :: [Text] -> Parser Text
wordListParser [w] = string w
wordListParser (w:ws) = do
a <- string w
b <- satisfy (inClass " -")
c <- wordListParser ws
return (a `append` (singleton b) `append` c) -- singleton :: Char -> Text
wordListParser [] = empty
И последнее: ваша текущая реализация не будет анализировать Windows разрывы строк (\n\r
). Я не знаю, удалили ли вы \n
и \r
из символов-разделителей, но если у вас нет, и файлы Windows могут быть для вас, об этом следует помнить.