├── .gitignore ├── Makefile ├── README ├── Calc.hs ├── Tokens.x └── Grammar.y /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.hi 3 | Grammar.hs 4 | Tokens.hs 5 | Calc 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: Calc 2 | 3 | Tokens.hs : Tokens.x 4 | alex Tokens.x 5 | 6 | Grammar.hs : Grammar.y 7 | happy Grammar.y 8 | 9 | Calc : Tokens.hs Grammar.hs Calc.hs 10 | ghc --make Calc 11 | 12 | clean: 13 | rm -f Calc Grammar.hs Tokens.hs *.o *.hi 14 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A very simple example of how to use Alex and Happy to build a language 2 | processor in Haskell. 3 | 4 | To build this, you will need to install Alex and Happy. The easiest way to get 5 | them is via Hackage: 6 | 7 | $ cabal install alex 8 | $ cabal install happy 9 | -------------------------------------------------------------------------------- /Calc.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | import Grammar 3 | import Tokens 4 | 5 | type Env = String -> Exp 6 | emptyEnv = error "Not found" 7 | envLookup s env = env s 8 | envBind s v env = (\s' -> if s == s' then v else env s) 9 | 10 | eval :: Exp -> Env -> Int 11 | eval (Int v) _ = v 12 | eval (Plus e1 e2) env = (eval e1 env) + (eval e2 env) 13 | eval (Minus e1 e2) env = (eval e1 env) - (eval e2 env) 14 | eval (Times e1 e2) env = (eval e1 env) * (eval e2 env) 15 | eval (Div e1 e2) env = (eval e1 env) `div` (eval e2 env) 16 | eval (Negate e) env = -(eval e env) 17 | eval (Var s) env = eval (envLookup s env) env 18 | eval (Let s e1 e2) env = eval e2 env' 19 | where env' = envBind s e1 env 20 | 21 | run :: Exp -> Int 22 | run e = eval e emptyEnv 23 | 24 | main :: IO () 25 | main = do 26 | s <- getContents 27 | let ast = parseCalc (scanTokens s) 28 | print ast 29 | print (run ast) 30 | -------------------------------------------------------------------------------- /Tokens.x: -------------------------------------------------------------------------------- 1 | { 2 | module Tokens where 3 | } 4 | 5 | %wrapper "basic" 6 | 7 | $digit = 0-9 8 | $alpha = [a-zA-Z] 9 | 10 | tokens :- 11 | 12 | $white+ ; 13 | "--".* ; 14 | let { \s -> TokenLet } 15 | in { \s -> TokenIn } 16 | $digit+ { \s -> TokenInt (read s) } 17 | \= { \s -> TokenEq } 18 | \+ { \s -> TokenPlus } 19 | \- { \s -> TokenMinus } 20 | \* { \s -> TokenTimes } 21 | \/ { \s -> TokenDiv } 22 | \( { \s -> TokenLParen } 23 | \) { \s -> TokenRParen } 24 | $alpha [$alpha $digit \_ \']* { \s -> TokenSym s } 25 | 26 | { 27 | 28 | -- The token type: 29 | data Token = TokenLet 30 | | TokenIn 31 | | TokenInt Int 32 | | TokenSym String 33 | | TokenEq 34 | | TokenPlus 35 | | TokenMinus 36 | | TokenTimes 37 | | TokenDiv 38 | | TokenLParen 39 | | TokenRParen 40 | deriving (Eq,Show) 41 | 42 | scanTokens = alexScanTokens 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Grammar.y: -------------------------------------------------------------------------------- 1 | { 2 | module Grammar where 3 | import Tokens 4 | } 5 | 6 | %name parseCalc 7 | %tokentype { Token } 8 | %error { parseError } 9 | 10 | %token 11 | let { TokenLet } 12 | in { TokenIn } 13 | int { TokenInt $$ } 14 | var { TokenSym $$ } 15 | '=' { TokenEq } 16 | '+' { TokenPlus } 17 | '-' { TokenMinus } 18 | '*' { TokenTimes } 19 | '/' { TokenDiv } 20 | '(' { TokenLParen } 21 | ')' { TokenRParen } 22 | 23 | %right in 24 | %nonassoc '>' '<' 25 | %left '+' '-' 26 | %left '*' '/' 27 | %left NEG 28 | 29 | %% 30 | 31 | Exp : let var '=' Exp in Exp { Let $2 $4 $6 } 32 | | Exp '+' Exp { Plus $1 $3 } 33 | | Exp '-' Exp { Minus $1 $3 } 34 | | Exp '*' Exp { Times $1 $3 } 35 | | Exp '/' Exp { Div $1 $3 } 36 | | '(' Exp ')' { $2 } 37 | | '-' Exp %prec NEG { Negate $2 } 38 | | int { Int $1 } 39 | | var { Var $1 } 40 | 41 | { 42 | 43 | parseError :: [Token] -> a 44 | parseError _ = error "Parse error" 45 | 46 | data Exp = Let String Exp Exp 47 | | Plus Exp Exp 48 | | Minus Exp Exp 49 | | Times Exp Exp 50 | | Div Exp Exp 51 | | Negate Exp 52 | | Brack Exp 53 | | Int Int 54 | | Var String 55 | deriving Show 56 | } 57 | --------------------------------------------------------------------------------