1
This is a simple test for happy.
3
First thing to declare is the name of your parser,
4
and the type of the tokens the parser reads.
11
> %tokentype { Token }
13
The parser will be of type [Token] -> ?, where ? is determined by the
14
production rules. Now we declare all the possible tokens:
35
var ( {alpha}{alphanum}+ )
43
%whitespace ( {space}|{tab} )
44
%newline ( {newline} )
46
The left hand side are the names of the terminals or tokens,
47
and the right hand side is how to pattern match them.
49
Like yacc, we include %% here, for no real reason.
53
Now we have the production rules.
56
> Exp : let var '=' Exp in Exp { Let $2 $4 $6 }
60
> Exp1 : Exp1 '+' Term { Plus $1 $3 }
61
> | Exp1 '-' Term { Minus $1 $3 }
65
> Term : Term '*' Factor { Times $1 $3 }
66
> | Term '/' Factor { Div $1 $3 }
67
> | Factor { Factor $1 }
69
> Factor :: { Factor }
70
> Factor : int { Int $1 }
72
> | '(' Exp ')' { Brack $2 }
74
We are simply returning the parsed data structure !
75
Now we need some extra code, to support this parser,
80
All parsers must declair this function,
81
which is called when an error is detected.
82
Note that currently we do no error recovery.
84
> happyError tks = error "Parse error"
86
Now we declare the datastructure that we are parsing.
88
> data Exp = Let String Exp Exp | Exp1 Exp1
89
> data Exp1 = Plus Exp1 Term | Minus Exp1 Term | Term Term
90
> data Term = Times Term Factor | Div Term Factor | Factor Factor
91
> data Factor = Int Int | Var String | Brack Exp
93
The datastructure for the tokens...
108
.. and a simple lexer that returns this datastructure.
110
> lexer :: String -> [Token]
113
> | isSpace c = lexer cs
114
> | isAlpha c = lexVar (c:cs)
115
> | isDigit c = lexNum (c:cs)
116
> lexer ('=':cs) = TokenEq : lexer cs
117
> lexer ('+':cs) = TokenPlus : lexer cs
118
> lexer ('-':cs) = TokenMinus : lexer cs
119
> lexer ('*':cs) = TokenTimes : lexer cs
120
> lexer ('/':cs) = TokenDiv : lexer cs
121
> lexer ('(':cs) = TokenOB : lexer cs
122
> lexer (')':cs) = TokenCB : lexer cs
124
> lexNum cs = TokenInt (read num) : lexer rest
125
> where (num,rest) = span isDigit cs
128
> case span isAlpha cs of
129
> ("let",rest) -> TokenLet : lexer rest
130
> ("in",rest) -> TokenIn : lexer rest
131
> (var,rest) -> TokenVar var : lexer rest
133
To run the program, call this in gofer, or use some code
136
> runCalc :: String -> Exp
137
> runCalc = calc . lexer
139
Here we test our parser.
141
> main = case runCalc "1 + 2 + 3" of {
142
> (Exp1 (Plus (Plus (Term (Factor (Int 1))) (Factor (Int 2))) (Factor (Int 3)))) ->
143
> case runCalc "1 * 2 + 3" of {
144
> (Exp1 (Plus (Term (Times (Factor (Int 1)) (Int 2))) (Factor (Int 3)))) ->
145
> case runCalc "1 + 2 * 3" of {
146
> (Exp1 (Plus (Term (Factor (Int 1))) (Times (Factor (Int 2)) (Int 3)))) ->
147
> case runCalc "let x = 2 in x * (x - 2)" of {
148
> (Let "x" (Exp1 (Term (Factor (Int 2)))) (Exp1 (Term (Times (Factor (Var "x")) (Brack (Exp1 (Minus (Term (Factor (Var "x"))) (Factor (Int 2))))))))) -> print "Test works\n";
149
> _ -> quit } ; _ -> quit } ; _ -> quit } ; _ -> quit }
150
> quit = print "Test failed\n"