1

Parsec の使い方を学ぶために昔の宿題をやり直しているのですが、Exit (および含まれているデータ型) のパーサーを構築するのに苦労しています。まず、部屋のリストを含むファイルを取得します。各部屋には、部屋の名前 (以下 -- 部屋 --)、いくつかの説明またはストーリー、そして の形式の出口のリストが含まれています(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.

ご覧のとおり、一部の部屋には出口がありません(私が保管している方法はありません)。したがって、Maybe Exits が存在する可能性があります。

exits 部分まで到達し、そのステップの前にすべてのパーサーが機能しているようです。問題は、出口がないか、複数の出口がある可能性があるという事実を処理することです。また、Exit の処理方法は、Exit タイプの処理方法に影響すると思います (別名タイプ Exit は、たぶん [ 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 -- Exits -- と「多数の parseExit」を作成します。
  2. --Exit-- が見つからない場合、パーサーは失敗します (その場合は Nothing を返すと思います)
  3. oneRoom パーサーで、0 個または多数の parseExits または eof を探します (\n\n で事前に分割しているため)

質問:

  1. parsec docs optionMaybe または optional に従って none または many をどのように行うのですか? 出口または1つの部屋で?簡単にアクセスできるドキュメント
  2. ミニパーサーを扱う私のアプローチは、この問題を haskell と parsec で扱う正しい方法ですか?
  3. 最後に、oneRoom は現在 \n\n で分割されたファイル文字列を受け取りますが、それを oneRoom パーサーの正しい最後の行としてパーサーに含めることができると思いますか?
  4. 私の oneRoom パーサーでは、現在 Story を終了する要素として解析しています -- Exit -- しかし、ストーリーが次の -- Exits -- または eof を消費しないとは確信していませんか? Story パーサーを最初の -- 終了 -- 検出または eof (またはファイル全体を解析する場合は \n\n) で終了させるにはどうすればよいですか?

私の質問とコードが十分に明確であることを願っています。そうでない場合は、明確にする方法を教えてください。前もって感謝します。

4

2 に答える 2

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 に答える