Как мне разобрать S-выражения в структуре данных в Haskell? - PullRequest
0 голосов
/ 11 октября 2018

Я новичок в Haskell и могу использовать некоторые рекомендации.

Задача: взять S-выражение и проанализировать его в записи.

Где я преуспел: я могу взятьфайл и читать его в разобранную строку.Тем не менее, использование синтаксического анализа Text для DFA st

 let 
        toDFA :: [L.Text] -> EntryDFA
        toDFA t =
           let [q,a,d,s,f] = t
           in EntryDFA { 
               state = read q
              ,alpha = read a
              ,delta = read d
              ,start = read s
              ,final = read f }

возвращает эту ошибку:

• Couldn't match type ‘L.Text’ with ‘[Char]’
  Expected type: String
    Actual type: L.Text

Должен быть более идиоматический подход.

1 Ответ

0 голосов
/ 11 октября 2018

read - это частичная функция с типом Read a => String -> a, которая выдает исключение при сбое синтаксического анализа.Обычно вы хотите избежать этого (используйте readMaybe вместо этого, если у вас есть строка).String и L.Text - это разные типы, поэтому вы получаете сообщение об ошибке.

В вашем примере кода отсутствует дополнительный ) после trans-func.

IЯ использую пакет Megaparsec, который обеспечивает простой способ работы с комбинаторами синтаксического анализа.Автор библиотеки написал более длинное руководство здесь .

Основная идея состоит в том, что Parser a - это тип значения, которое может анализировать что-то типа aText.Megaparsec есть несколько функций, которые вы можете использовать (parse, parseMaybe и т. Д.), Чтобы «запустить» синтаксический анализатор для «строкового» типа данных (например, String или строго / ленивый Text).

Когда вы используете do обозначение для IO, это означает «выполнять одно действие за другим».Точно так же вы можете использовать do нотацию с Parser, это означает «разобрать эту вещь, а затем проанализировать следующую вещь».

p1 *> p2 означает запустить анализатор p1, запустить p2и вернуть результат выполнения p2.p1 <* p2 означает запустить анализатор p1, запустить p2 и вернуть результат выполнения p1.Вы также можете посмотреть документацию по Hoogle , если у вас возникли проблемы с пониманием чего-либо.

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns    #-}

-- In practice, many of these imports would be unqualified, but I've
-- opted for explicitness for clarity.
import Control.Applicative (empty, many, some, (<*), (*>))
import Control.Exception (try, IOException)
import Data.Maybe (fromMaybe)
import Data.Set (Set)
import Data.Text (Text)

import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Text.Megaparsec as MP
import qualified Text.Megaparsec.Char as MPC
import qualified Text.Megaparsec.Char.Lexer as MPCL

type Q = Text
type E = Char

data EntryDFA = EntryDFA
  { state :: Set Q
  , alpha :: Set E
  , delta :: Set (Q,E,Q)
  , start :: Q
  , final :: Set Q
  } deriving Show

inputFile = "foo.sexp"

main :: IO ()
main = do
  -- read file and check for exception instead of checking if
  -- it exists and then trying to read it
  result <- try (TIO.readFile inputFile)
  case result of
    Left e -> print (e :: IOException)
    Right txt -> do
      case MP.parse dfaParser inputFile txt of
        Left e -> print e
        Right dfa -> print dfa

type Parser = MP.Parsec () Text

-- There are no comments in the S-exprs, so leave those empty
spaceConsumer :: Parser ()
spaceConsumer = MPCL.space MPC.space1 empty empty

symbol :: Text -> Parser Text
symbol txt = MPCL.symbol spaceConsumer txt

parens :: Parser a -> Parser a
parens p = MP.between (symbol "(") (symbol ")") p

setP :: Ord a => Parser a -> Parser (Set a)
setP p = do
  items <- parens (p `MP.sepBy1` (symbol ","))
  return (Set.fromList items)

pair :: Parser a -> Parser b -> Parser (a, b)
pair p1 p2 = parens $ do
  x1 <- p1
  x2 <- symbol "," *> p2
  return (x1, x2)

stateP :: Parser Text
stateP = do
  c <- MPC.letterChar
  cs <- many MPC.alphaNumChar
  return (T.pack (c:cs))

dfaParser :: Parser EntryDFA
dfaParser = do
  () <- spaceConsumer
  (_, state) <- pair (symbol "states") (setP stateP)
  (_, alpha) <- pair (symbol "alpha") (setP alphaP)
  (_, delta) <- pair (symbol "trans-func") (setP transFuncP)
  (_, start) <- pair (symbol "start") valP
  (_, final) <- pair (symbol "final") (setP valP)
  return (EntryDFA {state, alpha, delta, start, final})
  where
    alphaP :: Parser Char
    alphaP = MPC.letterChar <* spaceConsumer
    transFuncP :: Parser (Text, Char, Text)
    transFuncP = parens $ do
      s1 <- stateP
      a <- symbol "," *> alphaP
      s2 <- symbol "," *> stateP
      return (s1, a, s2)
    valP :: Parser Text
    valP = fmap T.pack (some MPC.digitChar)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...