2011-05-03 103 views
1

我不太清楚還有其他問題。我認爲我需要這裏的一般指導。我有這樣的事情:複雜的Parsec解析器

expr = buildExpressionParser table term 
    <?> "expression" 

term = choice [ 
    (float >>= return . EDouble) 
    , try (natural >>= return . EInteger) 
    , try (stringLiteral >>= return . EString) 
    , try (reserved "true" >> return (EBool True)) 
    , try (reserved "false" >> return (EBool False)) 
    , try assign 
    , try ifelse 
    , try lambda 
    , try array 
    , try eseq 
    , parens expr 
    ] 
    <?> "simple expression" 

當我測試解析器,我主要是讓問題...就像當我嘗試解析

(a,b) -> "b" 

它是由lambda解析器接受,但expr解析器討厭它。有時它甚至完全停留在永恆的規則中。

我已閱讀Write Yourself a Scheme,但它只解析Scheme的同質源。

也許我一般都在想錯方向。

編輯:這裏的內部解析器:

assign = do 
    i <- identifier 
    reservedOp "=" 
    e <- expr 
    return $ EAssign i e 

ifelse = do 
    reserved "if" 
    e <- expr 
    reserved "then" 
    a <- expr 
    reserved "else" 
    b <- expr 
    return $ EIfElse e a b 

lambda = do 
    ls <- parens $ commaSep identifier 
    reservedOp "->" 
    e <- expr 
    return $ ELambda ls e 

array = (squares $ commaSep expr) >>= return . EArray 

eseq = do 
    a <- expr 
    semi <|> (newline >>= (\x -> return [x])) 
    b <- expr 
    return $ ESequence a b 

table = [ 
     [binary "*" EMult AssocLeft, binary "/" EDiv AssocLeft, binary "%" EMod AssocLeft ], 
     [binary "+" EPlus AssocLeft, binary "-" EMinus AssocLeft ], 
     [binary "~" EConcat AssocLeft], 
     [prefixF "not" ENot], 
     [binaryF "and" EAnd AssocLeft, binaryF "or" EAnd AssocLeft] 
    ] 

並以「討厭」我的意思是,它告訴我,它期待一個整數或浮點數。

+2

我們需要內部語句的內部,因爲你可能有語法衝突。另外,你是什麼意思「恨它」? – 2011-05-03 22:05:56

回答

4

似乎有左遞歸,這將導致解析器掛起,如果在termchoice還未獲得eseq

expr - >term - >eseq - >expr

term(a,b)會不解析爲lambdaarray,因此它將落入eseq循環中。

我不明白爲什麼(a,b) -> "b"不解析爲expr,因爲在長期的choice應該打在了lambda,你說的作品,達到了eseq之前。解析錯誤中報告的位置是什麼?

+0

我找了30分鐘,看不到它。做得好。 +1 – 2011-05-04 17:37:47

+0

感謝您的意見。我把'eseq','array'和'lambda'換成了一個不同的combinator,比如現在更多的ebnf。它現在似乎工作。 – Lanbo 2011-05-05 19:02:38

6

愛德華在評論和我都試圖做的是精神上運行你的解析器,這是有點困難,沒有更多的解析器繼續。我要在這裏做一些猜測,也許他們會幫助你改進你的問題。

猜測1):您已嘗試GHCI> parse expr "(input)" "(a,b) -> \"b\",它已返回Left …。知道錯誤是什麼會有幫助。

猜測2):您也試過GHCI> parse lambda "(input)" "(a,b) -> \"b\",它返回Right …。基於這個愛德華和我都推斷,在你的term解析器或可能在生成的expr解析器中的某處存在衝突這是解析器的某些部分成功匹配字符串的開頭並返回一個值,但是什麼遺體不再有效。如果您想嘗試GHCI> parse term "(input)" "(a,b) -> \"b\"會很有幫助,因爲這會讓我們知道問題出在term還是expr

猜測3):字符串"(a,b)"本身就是一個語法中的有效表達式,因爲它已經編程了。 (雖然可能不像你意圖來編程它;-)。嘗試通過expr解析器發送,看看會發生什麼。

猜測4):您的文法是遞歸的。這是導致它卡住並永久循環的原因。 Parsec是一個LL(k)解析器。如果您習慣於LR(1)或LR(k)解析器的Yacc和family,則遞歸規則完全相反。如果你不明白這最後一句話,那就行,但請告訴我們。

猜測5):表達式構建器中的代碼看起來像來自函數的文檔。我想你也可能在某處找到了term表達式。如果是這種情況,你指出它來自哪裏。如果不是,你能用幾句話解釋你認爲term應該如何工作。


一般建議:經大量try陳述最終(又名現在)打算給你造成的悲傷。它們在某些情況下很有用,但也有點頑皮。如果下一個角色能夠確定成功的選擇,那麼他們就不需要了。如果你只是試圖讓一些東西運行,那麼回溯會減少中間形式的數量,但是它也隱藏了病態情況並使錯誤更加模糊。

+3

不錯的心靈調試。至少我會把我的錢放在這些猜測中的一個上。 – 2011-05-04 00:06:23

+0

術語'「(a,b)」'導致循環。而'term'應該是用來表達我想要允許的一切詞語。我實際上想要像'a = z +(如果是true,然後是1 else 2)' – Lanbo 2011-05-04 07:07:24