2013-05-27 28 views
1

我想熟悉哈斯克爾的快樂解析器生成器。目前,我有一個來自文檔的例子,但是當我編譯該程序時,出現錯誤。 這是代碼:哈斯克爾 - 快樂 - 「沒有實例...」的錯誤

{ 
module Main where 
import Data.Char 
} 

%name calc 
%tokentype { Token } 
%error { parseError } 

%token 
     let    { TokenLet } 
     in    { TokenIn } 
     int    { TokenInt $$ } 
     var    { TokenVar $$ } 
     '='    { TokenEq } 
     '+'    { TokenPlus } 
     '-'    { TokenMinus } 
     '*'    { TokenTimes } 
     '/'    { TokenDiv } 
     '('    { TokenOB } 
     ')'    { TokenCB } 

%% 

Exp : let var '=' Exp in Exp { \p -> $6 (($2,$4 p):p) } 
     | Exp1     { $1 } 

Exp1 : Exp1 '+' Term   { \p -> $1 p + $3 p } 
     | Exp1 '-' Term   { \p -> $1 p - $3 p } 
     | Term     { $1 } 

Term : Term '*' Factor   { \p -> $1 p * $3 p } 
     | Term '/' Factor   { \p -> $1 p `div` $3 p } 
     | Factor     { $1 } 

Factor      
     : int      { \p -> $1 } 
     | var      { \p -> case lookup $1 p of 
            Nothing -> error "no var" 
            Just i -> i } 
     | '(' Exp ')'    { $2 } 

{ 
parseError :: [Token] -> a 
parseError _ = error "Parse error" 

data Token 
     = TokenLet 
     | TokenIn 
     | TokenInt Int 
     | TokenVar String 
     | TokenEq 
     | TokenPlus 
     | TokenMinus 
     | TokenTimes 
     | TokenDiv 
     | TokenOB 
     | TokenCB 
deriving Show 

lexer :: String -> [Token] 
lexer [] = [] 
lexer (c:cs) 
     | isSpace c = lexer cs 
     | isAlpha c = lexVar (c:cs) 
     | isDigit c = lexNum (c:cs) 
lexer ('=':cs) = TokenEq : lexer cs 
lexer ('+':cs) = TokenPlus : lexer cs 
lexer ('-':cs) = TokenMinus : lexer cs 
lexer ('*':cs) = TokenTimes : lexer cs 
lexer ('/':cs) = TokenDiv : lexer cs 
lexer ('(':cs) = TokenOB : lexer cs 
lexer (')':cs) = TokenCB : lexer cs 

lexNum cs = TokenInt (read num) : lexer rest 
     where (num,rest) = span isDigit cs 

lexVar cs = 
    case span isAlpha cs of 
     ("let",rest) -> TokenLet : lexer rest 
     ("in",rest) -> TokenIn : lexer rest 
     (var,rest) -> TokenVar var : lexer rest 

main = getContents >>= print . calc . lexer 
} 

我得到這個錯誤:

[1 of 1] Compiling Main    (gr.hs, gr.o) 

gr.hs:310:24: 
No instance for (Show ([(String, Int)] -> Int)) 
    arising from a use of `print' 
Possible fix: 
    add an instance declaration for (Show ([(String, Int)] -> Int)) 
In the first argument of `(.)', namely `print' 
In the second argument of `(>>=)', namely `print . calc . lexer' 
In the expression: getContents >>= print . calc . lexer 

你知不知道爲什麼,我該如何解決呢?

回答

6

如果您檢查錯誤信息

No instance for (Show ([(String, Int)] -> Int)) 
    arising from a use of `print' 

很明顯的問題是,你正試圖print功能。實際上,解析器函數calc產生的值應該是一個函數,它需要一個變量綁定查找表並返回一個結果。例如參見規則變量:

{ \p -> case lookup $1 p of 
    Nothing -> error "no var" 
    Just i -> i } 

所以在main,我們需要在列表中以通爲p參數,例如一個空列表。 (或者你可以添加一些預定義的全局變量,如果你想的話)。我已經擴大了無點代碼爲do塊,以便更容易看到發生了什麼事情:

main = do 
    input <- getContents 
    let fn = calc $ lexer input 
    print $ fn [] -- or e.g. [("foo", 42)] if you wanted it pre-defined 

現在,它的工作原理:

$ happy Calc.y 
$ runghc Calc.hs <<< "let x = 1337 in x * 2" 
2674 
+0

非常感謝你,它的工作原理。還有一個問題:如何重複這個操作,例如,如果我運行它,它將只評估一個表達式,如果我嘗試寫兩個由'\ n'分隔的表達式,它會給我「解析錯誤」。例如,我想要做這樣的事情:「let a = 10 - 3'\ n'a + 8」。 –

+0

@JohnSmith:嘗試用'getLine'替換'getContents'並在'print'之後向'main'添加遞歸調用。然而,這個例子不起作用,因爲這個程序被設計爲一次解析一個表達式。沒有規定定義可以在另一個表達式中使用的變量,因此需要對程序進行更大的更改。 – hammar