evaluate source matches with ordered clause semantics

This commit is contained in:
milner committed 2020-01-27 12:00:00 +00:00
1 parent 67f24dd845
commit 4fadf4bc01
1 file changed
+173
+173
View File
@@ -0,0 +1,173 @@
module Tessera.Eval where
import Data.List (foldl')
import qualified Data.Map.Strict as Map
import Tessera.Env
import Tessera.Matrix (Key (..))
import Tessera.Syntax
import Tessera.Tree
data Value
= VInt Integer
| VBool Bool
| VCon String [Value]
| VTuple [Value]
deriving (Eq, Show)
renderValue :: Value -> String
renderValue value = case value of
VInt n -> show n
VBool b -> if b then "True" else "False"
VCon name [] -> name
VCon name arguments -> name ++ " " ++ unwords (map renderAtom arguments)
VTuple elements -> "(" ++ joinComma (map renderValue elements) ++ ")"
where
renderAtom item = case item of
VCon _ (_ : _) -> "(" ++ renderValue item ++ ")"
VTuple _ -> "(" ++ renderValue item ++ ")"
_ -> renderValue item
joinComma :: [String] -> String
joinComma [] = ""
joinComma [x] = x
joinComma (x : rest) = x ++ ", " ++ joinComma rest
matchPattern :: Pattern -> Value -> Maybe (Map.Map String Value)
matchPattern pattern' value = case pattern' of
PWild -> Just Map.empty
PVar name -> Just (Map.singleton name value)
PInt n -> case value of
VInt m | n == m -> Just Map.empty
_ -> Nothing
PBool b -> case value of
VBool c | b == c -> Just Map.empty
_ -> Nothing
PTuple patterns -> case value of
VTuple values | length patterns == length values -> combine (zipWith matchPattern patterns values)
_ -> Nothing
PCon name patterns -> case value of
VCon name' values | name == name' && length patterns == length values -> combine (zipWith matchPattern patterns values)
_ -> Nothing
where
combine = foldl' step (Just Map.empty)
step Nothing _ = Nothing
step (Just acc) result = case result of
Nothing -> Nothing
Just extra -> Just (Map.union acc extra)
matchClause :: [Pattern] -> [Value] -> Maybe (Map.Map String Value)
matchClause patterns values
| length patterns /= length values = Nothing
| otherwise = foldl' step (Just Map.empty) (zipWith matchPattern patterns values)
where
step (Just acc) result = case result of
Nothing -> Nothing
Just extra -> Just (Map.union acc extra)
step Nothing _ = Nothing
sourceSelect :: [[Pattern]] -> [Value] -> Maybe Int
sourceSelect patternsList values =
case [i | (i, patterns) <- zip [0 ..] patternsList, matchClause patterns values /= Nothing] of
(i : _) -> Just i
[] -> Nothing
valueKey :: Value -> Key
valueKey value = case value of
VInt n -> KeyInt n
VBool b -> KeyBool b
VCon name _ -> KeyCon name
VTuple elements -> KeyTuple (length elements)
lookupPath :: [Int] -> [Value] -> Maybe Value
lookupPath [] _ = Nothing
lookupPath (index : rest) values = case drop index values of
(value : _) -> navigate rest value
[] -> Nothing
where
navigate [] value = Just value
navigate (j : js) (VCon _ values') = if j < length values' then navigate js (values' !! j) else Nothing
navigate (j : js) (VTuple values') = if j < length values' then navigate js (values' !! j) else Nothing
navigate _ _ = Nothing
runTree :: Tree -> [Value] -> Maybe Int
runTree tree values = case tree of
Success index -> Just index
Failure -> Nothing
Bind _ _ inner -> runTree inner values
Switch occurrence branches fallback ->
let chosen = case lookupPath (occurrencePath occurrence) values of
Just value -> lookup (valueKey value) branches
Nothing -> Nothing
in case chosen of
Just branch -> runTree branch values
Nothing -> case fallback of
Just branch -> runTree branch values
Nothing -> Nothing
evalExpr :: Env -> Map.Map String Value -> Expr -> Either String Value
evalExpr env bindings expression = case expression of
EVar name -> case Map.lookup name bindings of
Just value -> Right value
Nothing -> Left ("unbound variable " ++ name)
EInt n -> Right (VInt n)
EBool b -> Right (VBool b)
ECon name arguments -> VCon name <$> mapM (evalExpr env bindings) arguments
ETuple elements -> VTuple <$> mapM (evalExpr env bindings) elements
EBin operator left right -> do
leftValue <- evalExpr env bindings left
rightValue <- evalExpr env bindings right
applyOperator operator leftValue rightValue
EIf condition yes no -> do
conditionValue <- evalExpr env bindings condition
case conditionValue of
VBool True -> evalExpr env bindings yes
VBool False -> evalExpr env bindings no
_ -> Left "condition is not a boolean"
applyOperator :: Op -> Value -> Value -> Either String Value
applyOperator operator left right = case operator of
OpAdd -> arithmetic (+) left right
OpSub -> arithmetic (-) left right
OpMul -> arithmetic (*) left right
OpDiv -> case (left, right) of
(VInt a, VInt b) | b /= 0 -> Right (VInt (a `div` b))
(VInt _, VInt _) -> Left "division by zero"
_ -> Left "arithmetic expects integers"
OpMod -> case (left, right) of
(VInt a, VInt b) | b /= 0 -> Right (VInt (a `mod` b))
(VInt _, VInt _) -> Left "modulo by zero"
_ -> Left "arithmetic expects integers"
OpEq -> Right (VBool (left == right))
OpNe -> Right (VBool (left /= right))
OpLt -> comparison (<) left right
OpLe -> comparison (<=) left right
OpGt -> comparison (>) left right
OpGe -> comparison (>=) left right
OpAnd -> logical (&&) left right
OpOr -> logical (||) left right
where
arithmetic f (VInt a) (VInt b) = Right (VInt (f a b))
arithmetic _ _ _ = Left "arithmetic expects integers"
comparison f (VInt a) (VInt b) = Right (VBool (f a b))
comparison _ _ _ = Left "comparison expects integers"
logical f (VBool a) (VBool b) = Right (VBool (f a b))
logical _ _ _ = Left "logical operators expect booleans"
evaluateCompiled :: Env -> [Clause] -> Tree -> [Value] -> Either String Value
evaluateCompiled env clauses tree values = case runTree tree values of
Nothing -> Left "no clause matched"
Just index -> case drop index clauses of
(clause : _) -> case matchClause (clausePatterns clause) values of
Nothing -> Left "internal binding failure"
Just bindings -> evalExpr env bindings (clauseBody clause)
[] -> Left "clause index out of range"
evaluateSource :: Env -> [Clause] -> [Value] -> Either String Value
evaluateSource env clauses values = case sourceSelect (map clausePatterns clauses) values of
Nothing -> Left "no clause matched"
Just index -> case drop index clauses of
(clause : _) -> case matchClause (clausePatterns clause) values of
Nothing -> Left "internal binding failure"
Just bindings -> evalExpr env bindings (clauseBody clause)
[] -> Left "clause index out of range"