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