1

我正在重做一个旧的家庭作业,以学习如何使用 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.

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

我已经到了出口部分,并且在那一步之前我的所有解析器似乎都可以工作。问题在于处理可能没有出口或多个出口的事实。另外,我认为如何处理出口会影响我处理出口类型的方式(也就是出口类型可能会变成 M​​aybe [ Exit ]

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

--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. 制作解析的 parseExits - 退出 - 和“许多 parseExit”。
  2. 如果 --Exit-- 没有发现解析器失败(我认为它返回 Nothing 然后)
  3. 在 oneRoom 解析器中,我查找 0 个或多个 parseExits 或 eof(因为我在 \n\n 上进行预拆分)

问题:

  1. 您如何根据 parsec docs optionMaybe 或 optional 做无或很多的技巧,但在哪里应用?在出口还是一个房间?易于访问的文档
  2. 我处理迷你解析器的方法是在 haskell 和 parsec 中处理这个问题的正确方法吗?
  3. 最后 oneRoom 当前接收到在 \n\n 上拆分的文件字符串,但我认为我可以将其包含在我的解析器中作为 oneRoom 解析器的最后一行,对吗?
  4. 在我的 oneRoom 解析器中,我目前正在将 Story 解析为以 - Exit 结尾的元素,但我不相信故事不会消耗下一个 - Exits - 或 eof?你如何让你的故事解析器在第一个 - 退出 - 它找到或 eof (或 \n\n 如果我解析完整文件)结束

我希望我的问题和代码足够清楚,如果不是,请让我知道如何澄清。提前致谢。

4

2 回答 2

2

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

  • 你怎么做没有或很多[...]

只需使用many而不是many1.

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

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

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

  • 最后 oneRoom 当前接收到在 \n\n 上拆分的文件字符串,但我认为我可以将其包含在我的解析器中作为 oneRoom 解析器的最后一行,对吗?

我认为你不应该splitmain函数中使用,而只是newline在你想要换行符的任何地方使用你的解析器。

  • 在我的 oneRoom 解析器中,我目前正在将 Story 解析为以 - Exit 结尾的元素,但我不相信故事不会消耗下一个 - Exits - 或 eof?你如何让你的故事解析器在第一个 - 退出 - 它找到或 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.

于 2013-08-20T12:50:59.110 回答
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

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

于 2013-08-28T17:39:52.593 回答