Created
October 18, 2013 10:15
-
-
Save darkf/7039468 to your computer and use it in GitHub Desktop.
Interpreter for a simple dynamically typed pattern matching language
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Interpreter for a simple dynamically typed pattern matching language | |
-- Copyright (c) 2013 darkf | |
-- Written on 10/18/2013 | |
import Control.Monad.State (State, runState, evalState, get, put) | |
import qualified Data.Map as M | |
data AST = Add AST AST | |
| Def String AST | |
| Var String | |
| Lambda [(Pattern, [AST])] | |
| Call String [AST] | |
| StrConst String | |
| IntConst Int | |
deriving (Show, Eq) | |
data Pattern = VarP String | |
| IntP Int | |
deriving (Show, Eq) | |
data Value = IntV Int | |
| StrV String | |
| FnV [(Pattern, [AST])] -- pattern->body bindings | |
deriving (Show, Eq) | |
type Env = M.Map String Value -- an environment | |
type InterpState = State Env -- interpreter state (pass along the global environment) | |
(IntV l) +$ (IntV r) = IntV (l + r) | |
(StrV l) +$ (StrV r) = StrV (l ++ r) | |
l +$ r = error $ "cannot + " ++ show l ++ " and " ++ show r | |
initialState = M.empty | |
eval :: AST -> InterpState Value | |
eval (IntConst i) = return $ IntV i | |
eval (StrConst s) = return $ StrV s | |
eval (Var var) = get >>= \m -> | |
case M.lookup var m of | |
Just v -> return v | |
Nothing -> error $ "unbound variable " ++ var | |
eval (Def name v') = do | |
v <- eval v' | |
m <- get | |
put $ M.insert name v m | |
return v | |
eval (Lambda pats) = | |
return $ FnV pats | |
eval (Add l r) = do | |
l <- eval l | |
r <- eval r | |
return $ l +$ r | |
eval (Call name args) = get >>= \m -> | |
case M.lookup name m of | |
Just fn@(FnV _) -> | |
do | |
xargs <- mapM eval args | |
applyMany fn xargs | |
Nothing -> error $ "call: name " ++ name ++ " doesn't exist or is not a function" | |
patternBindings :: Pattern -> Value -> Maybe Env | |
patternBindings (VarP n) v = Just $ M.fromList [(n, v)] | |
patternBindings (IntP n) (IntV v) | |
| v == n = Just M.empty | |
| otherwise = Nothing | |
patternBindings (IntP n) _ = Nothing | |
-- applies many arguments to a function | |
applyMany :: Value -> [Value] -> InterpState Value | |
applyMany fn@(FnV _) (arg:xs) = | |
apply fn arg >>= \value -> | |
applyMany value xs | |
applyMany value [] = return value | |
applyMany _ xs = error "couldn't apply all arguments" | |
-- applies a function | |
apply :: Value -> Value -> InterpState Value | |
apply (FnV pats) arg = | |
apply' pats | |
where | |
apply' [] = error $ "argument " ++ show arg ++ " doesn't satisfy any patterns" | |
apply' ((pat, body):xs) = | |
case patternBindings pat arg of | |
Just env' -> -- satisfies | |
do | |
env <- get | |
put $ M.union env env' | |
foldr1 (>>) $ map eval body | |
Nothing -> -- doesn't satisfy this pattern | |
apply' xs | |
evalProgram :: [AST] -> Value -- fold the state from each node and return the result | |
evalProgram nodes = evalState (foldr1 (>>) $ map eval nodes) initialState | |
main = do | |
print $ evalProgram prg | |
where | |
prg = [ | |
Def "f" $ Lambda [ | |
(IntP 0, [IntConst 100]), | |
(IntP 1, [IntConst 200]), | |
(VarP "x", [IntConst 300]) | |
], | |
Call "f" [IntConst 2]] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment