evaluate source matches with ordered clause semantics
This commit is contained in:
1 file changed
+173
@@ -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"
|
||||
Reference in new issue
Block a user