2013-05-27 2 views
1

Я пытаюсь познакомиться с генератором счастливых парсеров для Haskell. В настоящее время у меня есть пример из документации, но когда я скомпилирую программу, я получаю сообщение об ошибке. Это код:Haskell - Happy - «Нет экземпляра ...» error

{ 
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: Попробуйте заменить 'getContents'' getLine' и добавить рекурсивный вызов 'main' после' print'. Однако этот пример не будет работать, поскольку эта программа предназначена для одновременного анализа одного выражения. Не существует положения для определения переменных, которые могут использоваться в другом выражении, так что это потребует больших изменений в программе. – hammar

Смежные вопросы