Как сделать парсер Haskell из списка слов? - PullRequest
1 голос
/ 06 февраля 2020

Я начинающий Haskell, использую Attoparse c, чтобы найти некоторые цветовые выражения в тексте. Я хочу иметь возможность сопоставлять, например, «светло-сине-зеленый» и «светло-сине-зеленый» в тексте. Но, конечно, мне нужно обобщенное решение для любой такой строки. Так что я думал, что это будет что-то вроде

"light" >> sep >> "blue" >> sep >> "green"
  where sep = inClass "\n\r- "

Другими словами, я думаю, что мне нужен способ вставить >> sep >> в список слов. Что-то вроде:

import qualified Data.Text as T
import Data.Attoparsec.Text

-- | Makes a parser from a list of words, accepting
-- spaces, newlines, and hyphens as separators.
wordListParser :: [T.Text] -> Parser
wordListParser wordList = -- Some magic here

Или, может быть, я думаю об этом совсем не так, и есть более простой способ?

Редактировать: этот минимальный нерабочий пример выглядит так, как будто его почти нет:

{-# LANGUAGE OverloadedStrings #-}

import Replace.Attoparsec.Text
import Data.Attoparsec.Text as AT
import qualified Data.Text as T
import Control.Applicative (empty)

wordListParser :: [T.Text] -> Parser T.Text
wordListParser (w:ws) = string w >> satisfy (inClass " -") >> wordListParser ws
wordListParser [w] = string w
wordListParser [] = empty  -- or whatever the empty parser is

main :: IO ()
main = parseTest (wordListParser (T.words "light green blue")) "light green-blue"

, который, я думаю, можно запустить с чем-то вроде

stack runhaskell ThisFile.hs --package attoparsec replace-attoparsec text

Ответы [ 2 ]

1 голос
/ 06 февраля 2020

Вот что я бы сделал, предполагая, что у вас есть тип данных для ваших цветов; если вы этого не сделаете, просто замените его на то, что вы используете. Функция 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

РЕДАКТИРОВАТЬ

После редактирования вашего вопроса, я думаю, я понимаю, что вы хотите немного лучше. Кстати, я бы все же предпочел решение, описанное выше, но вот как исправить ваш последний блок кода:

  1. Как должен сказать вам компилятор, шаблоны (w:ws) и [w] перекрываются, поэтому если вы хотите, чтобы среда выполнения ловила одноэлементный шаблон, вы должны поместить его сверху.
  2. 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 могут быть для вас, об этом следует помнить.

1 голос
/ 06 февраля 2020

Я не знаком с attoparse c, но вы могли бы использовать рекурсивное решение:

wordListParser :: [T.Text] -> Parser
wordListParser [] = empty
wordListParser [w] = text w
wordListParser (w:ws) = text w >> inClass "\n\r- " >> wordListParser ws
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...