学习Haskell Parsec并处理可选的解析器

时间:2013-08-19 20:21:25

标签: haskell parsec

我正在重做一项旧的家庭作业,以获得有趣的学习如何使用Parsec,而我在为Exits(以及包含的数据类型)构建解析器时遇到了麻烦。首先,我们得到一个包含房间列表的文件。每个房间都包含一个房间名称(下方 - 房间 - ),一些描述或故事,然后是(direction, destination)格式的退出列表。最终一个人会选择一个方向,你会查找房间名称并将玩家带到下一个房间。

-- Room --
Center
You are in a square room. There are doors to the
north, south, east, and west.
-- Exits --
North: North Hall
South: South Room
East: East Room
West: West Room

-- Room --
North Hall
You are in a hallway. There are doors to the north and south.

正如你所看到的,有些房间没有出口(没有什么是我存储的方式)。所以可能会有出口。

我已经到了exits部分,并且在该步骤之前我的所有解析器似乎都能正常工作。问题在于处理可能没有退出或不止一个退出的事实。另外我如何处理退出我认为影响我如何处理我的退出类型(又名类型退出可能会变成可能[​​退出]

无论如何,这是我的代码:

--module Loader
-- Game parser will go here
--
import Text.ParserCombinators.Parsec
import Data.List.Split

astory = unlines [" -- Room --",
      "Cell",
      "You have been locked in a dungeon cell. The prisoner next",
      "to you whipsers, there's a tunnel behind the bed on the",
      "south wall. Good Luck!",
      "-- Exits --",
      "South: Tunnel"]

type Rooms = [Room]
type Story = String
type Destination = String
data Exit = Exit { direction :: Direction, destination :: Destination } deriving (Ord, Show, Eq)
type Exits = [ Exit ]

data Room = Room { name  :: String
                 , story :: String
                 , exits :: Exits
                 } deriving (Ord, Show, Eq)

data Direction = Nothing | North | South | East | West | Quit deriving (Ord, Show, Eq)

an_exit = "North: Tunnel \n"
a_full_exit = "-- Exits --\nSouth: Tunnel"

directionParser :: Parser Direction
directionParser =     (string "South:" >> return South)
 <|> (string "North:" >> return North)
 <|> (string "East:"  >> return East)
 <|> (string "West:"  >> return West)

parseExit :: Parser Exit
parseExit = do
    direction <- directionParser
    spaces
    destination <- many (noneOf "\n")
    return $ Exit direction destination

parseExits :: Parse Exits
parseExits = do
    string "-- Exits --\n"

--oneRoom :: Parser Room
--oneRoom = do
--  string "--Room--\n"
--  name <- many (noneOf "\n")
--  newline
--  story <- manyTill anyChar (string "-- Exits --")
--  optionMaybe (string "-- Exits --")
--  exits <- optionMaybe $ many1 anyExits
--  return $ Room name story exits


--main = do
--    file <- readFile "cave.adventure"
--    let stories = splitOn "\n\n" file
--    mapM putStrLn stories

我目前评论了房间,因为我正在测试较小的解析器。

我的方法:

  1. make parseExits,解析 - 退出 - 和“很多parseExit”。
  2. 如果找不到--Exit--解析器失败(我认为它返回Nothing)
  3. 在oneRoom解析器中,我查找0或多个parseExits或eof(因为我是 预分裂\ n \ n)
  4. 问题:

    1. 你如何根据parsec docs选项执行无或多个选项可能或者是可选的但是应用的位置是什么?在退出或一个房间? The docs for easy access
    2. 我处理迷你解析器的方法是在haskell和parsec中处理这个问题的正确方法吗?
    3. 最后oneRoom当前接收分割在\ n \ n上的文件字符串,但我想我可以在我的解析器中包含它作为oneRoom解析器的最后一行正确吗?
    4. 在我的oneRoom解析器中,我正在解析Story作为结束于 - Exit的元素 - 但我不相信故事不消耗下一个 - 退出 - 或者eof?如何让你的Story解析器以第一个结束 - 退出 - 它找到或者eof(或者如果我解析整个文件则为\ n \ n)
    5. 我希望我的问题和代码足够清楚,如果没有,请告诉我如何澄清。 提前致谢。

2 个答案:

答案 0 :(得分:2)

我不想为你编写整个解析器,所以我只想回答个别问题。

  
      
  • 你如何做无或多[...]
  •   

只需使用many代替many1

要使整个“ - 退出 - ”块可选,您可以尝试类似

的内容
exits <- parseExists <|> return []
  
      
  • 我处理迷你解析器的方法是在haskell和parsec中处理这个问题的正确方法吗?
  •   

是。根据经验,尝试为每种数据类型编写至少一个解析器,通常更多。

  
      
  • 最后oneRoom当前接收分割在\ n \ n上的文件字符串,但我想我可以在我的解析器中包含它作为oneRoom解析器的最后一行正确吗?
  •   

我认为您不应在split函数中使用main,而只需在解析器中使用newline,只要您想要换行符。

  
      
  • 在我的oneRoom解析器中,我正在解析Story作为结束于 - Exit的元素 - 但我不相信故事不消耗下一个 - 退出 - 或者eof?如何让你的Story解析器以第一个结束 - 退出 - 它找到或者eof(或者如果我解析整个文件则为\ n \ n)
  •   

也许您想要以下内容:

-- succeeds if we're at the end of story
-- never consumes any input
endOfStory :: Parser ()
endOfStory = lookAhead $
  try (string "-- Room --" >> newline) <|>
  try (string "-- Exits --" >> newline) <|>
  try (newline >> newline) <|>
  eof

使用这样的函数,您可以使用manyTill ... endOfStory

答案 1 :(得分:0)

我为迟到的回应道歉。我在业余时间这样做。非常感谢你的建议。 在推出GHCI并修改一些数据类型之后,以下解决方案起作用。我想在这里发布答案,希望它能帮助其他人。

    --module Loader
-- Game parser will go here
--
import Text.ParserCombinators.Parsec
import Data.List.Split

astory = unlines ["-- Room --",
      "Cell",
      "You have been locked in a dungeon cell. The prisoner next",
      "to you whipsers, there's a tunnel behind the bed on the",
      "south wall. Good Luck!",
      "-- Exits --",
      "South: Tunnel\n"]

a_new_story = unlines [
      "You have been locked in a dungeon cell. The prisoner next",
      "to you whipsers, there's a tunnel behind the bed on the",
      "south wall. Good Luck!",
      "-- Exits --"]

type Story = String
type Destination = String
data Exit = Exit { direction :: Direction, destination :: Destination } deriving (Ord, Show, Eq)
data Exits = Exits [ Exit ] deriving (Ord, Show, Eq)
data Name = Name String deriving (Ord, Show, Eq)

data Room = Room { name  :: Name
                 , story :: String
                 , exits :: Exits
                 } deriving (Ord, Show, Eq)

data Rooms = Rooms [ Room ] deriving (Ord, Show, Eq)

data Direction = Nothing | North | South | East | West | Quit deriving (Ord, Show, Eq)

an_exit = "North: Tunnel \n"
a_full_exit = "-- Exits --\nSouth: Tunnel\n"

directionParser :: Parser Direction
directionParser =     (string "South:" >> return South)
 <|> (string "North:" >> return North)
 <|> (string "East:"  >> return East)
 <|> (string "West:"  >> return West)

parseExit :: Parser Exit
parseExit = do
    direction <- directionParser
    spaces
    destination <- many (noneOf "\n")
    newline
    return $ Exit direction destination

parseEol :: Parser ()
parseEol = do
    newline
    return $ ()

parseExits :: Parser Exits
parseExits = do
    string "-- Exits --\n"
    exits <- many parseExit
    return $ Exits exits

endOfStory :: Parser ()
endOfStory = lookAhead $
    try (string "-- Room --" >> parseEol) <|>
    try (string "-- Exits --" >> parseEol) <|>
    try (parseEol >> parseEol) <|> eof

roomname = "-- Room --\nCell\n"

parseName :: Parser Name
parseName = do
    string "-- Room --\n"
    name <- many (noneOf "\n")
    newline
    return $ Name name

oneRoom :: Parser Room
oneRoom = do
    name <- parseName
    story <- manyTill anyChar endOfStory
    exits <- parseExits <|> return (Exits [])
    newline
    return $ Room name story exits

manyRooms :: Parser Rooms
manyRooms = do
    rooms <- many oneRoom
    return $ Rooms rooms

如果您想尝试几个房间,请确保在房间之间添加换行符