我正在重做一项旧的家庭作业,以获得有趣的学习如何使用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
我目前评论了房间,因为我正在测试较小的解析器。
我的方法:
问题:
我希望我的问题和代码足够清楚,如果没有,请告诉我如何澄清。 提前致谢。
答案 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
如果您想尝试几个房间,请确保在房间之间添加换行符