2010-11-20 98 views
4

我是Haskell和Parsec的新手。在努力瞭解更多的語言,特別是我想創建一個解析器,可以解析Lua中保存的變量文件庫。在這些文件中的變量可以採取以下形式:Haskell's Parsec的問題<|>運營商

VARNAME =價值

VARNAME = {值,值,...}

VARNAME = {{值,值},{價值,價值, ...}}

我創建的解析器每種類型的,但是當我把它們串與選擇<一起|>運營商,我得到一個類型錯誤。

Couldn't match expected type `[Char]' against inferred type `Char' 
    Expected type: GenParser Char st [[[Char]]] 
    Inferred type: GenParser Char st [[Char]] 
In the first argument of `try', namely `lList' 
In the first argument of `(<|>)', namely `try lList' 

我的假設是(雖然我不能在文檔中找到它)傳遞給選擇運營商每個解析器必須返還相同種類。 這裏是有問題的代碼:

data Variable = LuaString ([Char], [Char]) 
      | LuaList ([Char], [[Char]]) 
      | NestedLuaList ([Char], [[[Char]]]) 
      deriving (Show) 

main:: IO() 
main = do 
     case (parse varName "" "variable = {{1234,\"Josh\"},{123,222}}") of 
      Left err -> print err 
      Right xs -> print xs 

varName :: GenParser Char st Variable 
varName = do{ 
     vName <- (many letter); 
     eq <- string " = "; 
     vCon <- try nestList 
      <|> try lList 
      <|> varContent; 
     return (vName, vCon)} 

varContent :: GenParser Char st [Char] 
varContent = quotedString 
    <|> many1 letter 
    <|> many1 digit 

quotedString :: GenParser Char st [Char] 
quotedString = do{ 
     s1 <- string "\""; 
     s2 <- varContent; 
     s3 <- string "\""; 
     return (s1++s2++s3)} 

lList :: GenParser Char st [[Char]] 
lList = between (string "{") (string "}") (sepBy varContent (string ",")) 

nestList :: GenParser Char st [[[Char]]] 
nestList = between (string "{") (string "}") (sepBy lList (string ",")) 

回答

7

這是正確的。

(<|>) :: (Alternative f) => f a -> f a -> f a 

請注意這兩個參數如何是完全相同的類型。

我不完全理解您的Variable數據類型。這是我會這樣做的方式:

data LuaValue = LuaString String | LuaList [LuaValue] 
data Binding = Binding String LuaValue 

這允許值任意嵌套,而不是像您的那樣嵌套深兩級。然後寫:

luaValue :: GenParser Char st LuaValue 
luaValue = (LuaString <$> identifier) 
     <|> (LuaList <$> between (string "{") (string "}") (sepBy (string ",") luaValue)) 

這是解析器luaValue。那麼你只需要寫:

binding :: GenParser Char st Binding 
content :: GenParser Char st [Binding] 

你會擁有它。使用準確表示可能性的數據類型非常重要。

+0

變量數據類型旨在將變量名稱和變量內容封裝在元組中。我可以從你和Martijn的迴應中看出,這不是最好的行動方式,因爲我需要一個額外的抽象層。 我有一個關於你的迴應的問題,但: 運營商在你的代碼中的作用是什麼? (我以爲他們只是爲了錯誤信息)。 – GraemeFore 2010-11-20 11:42:01

+0

我沒有使用''。也許你的意思是'<$>'。這只是'fmap'的中綴速記,所以'LuaString <$> identifier'的意思是「解析一個標識符,然後用'LuaString'函數包裝結果。 – luqui 2010-11-20 12:07:08

+0

Yikes!爲什麼你不應該在通宵的時候發佈問題的完美例子。再次感謝。 – GraemeFore 2010-11-20 22:18:09

3

事實上,傳遞給操作者選擇解析器必須具有相同的類型。您可以通過選擇運營商的類型告訴:

(<|>) :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a 

這是說,它會很高興,只要他們的標記類型,狀態類型和結果類型是相同的組合兩個解析器。

那麼,我們如何確保您嘗試組合的解析器具有相同的結果類型?好吧,你已經有一個數據類型Variable捕捉不同形式的,可以出現在Lua變量,所以我們需要做的是不返回String[String][[String]]只是Variable秒。

但是,當我們嘗試我們遇到問題。我們不能讓nestList等返回Variable s因爲Variable的構造函數需要變量名稱,我們現在還不知道那些。這有變通方法(如返回一個函數String -> Variable仍然預計,變量名),但有一個更好的解決方案:從不同類型的一個變量可以具有的值分隔變量名。

data Variable = Variable String Value 
    deriving Show 

data Value = LuaString String 
      | LuaList [Value] 
      deriving (Show) 

請注意,我刪除了NestedLuaList構造函數。我已經改變了LuaList接受Value真是讓人不是String個列表,所以現在嵌套列表可以表示爲LuaListLuaList A S。這允許列表任意嵌套,而不僅僅是兩個級別,如你的例子。我不知道在Lua中是否允許這樣做,但是它使編寫解析器變得更容易。 :-)

現在我們可以讓lListnestList回報Value S:

lList :: GenParser Char st Value 
lList = do 
    ss <- between (string "{") (string "}") (sepBy varContent (string ",")) 
    return (LuaList (map LuaString ss)) 

nestList :: GenParser Char st Value 
nestList = do 
    vs <- between (string "{") (string "}") (sepBy lList (string ",")) 
    return (LuaList vs) 

而且varName,我在這裏已經改名爲variable,現在返回一個Variable

variable :: GenParser Char st Variable 
variable = do 
    vName <- (many letter) 
    eq <- string " = " 
    vCon <- try nestList 
     <|> try lList 
     <|> (do v <- varContent; return (LuaString v)) 
    return (Variable vName vCon) 

我想你會發現,當你在某些輸入上運行解析器時,仍然存在一些問題,但是現在比以前更接近解決方案。

我希望這有助於!

+0

這有助於很多謝謝你。我認爲你是對的,我的數據類型不足以滿足我想要做的事情。儘管我發現Haskell的類型系統很吸引人,但我不得不承認,我還沒有完全理解它。正確定義遞歸數據類型等事情仍然讓我興奮不已。 – GraemeFore 2010-11-20 11:23:36