module Language.Sifflet.ExprTree
(
ExprTree, ExprNode(..), ExprNodeLabel(..)
, exprNodeIoletCounter
, exprToTree, treeToExpr, exprToReprTree
, evalTree, unevalTree
)
where
import Data.Number.Sifflet
import Data.Sifflet.Tree as T
import Data.Sifflet.TreeLayout (IoletCounter)
import Language.Sifflet.Expr
import Text.Sifflet.Repr ()
import Language.Sifflet.Util
type ExprTree = Tree ExprNode
data ExprNode = ENode ExprNodeLabel EvalResult
deriving (Eq, Show)
data ExprNodeLabel = NUndefined
| NSymbol Symbol
|
NBool Bool | NChar Char | NNumber Number
| NString String
| NList [Expr]
deriving (Eq, Show)
instance Repr ExprNode where
reprl (ENode label evalRes) =
case label of
NUndefined ->
case evalRes of
EvalUntried -> ["undefined"]
EvalError e -> ["undefined", "error: " ++ e]
EvalOk _ ->
errcats ["reprl of ExprNode: NUndefined with EvalOk",
"should not happen!"]
NSymbol s ->
case evalRes of
EvalOk v -> [repr s, repr v]
EvalError e -> [repr s, "error: " ++ e]
EvalUntried -> reprl s
NBool b -> reprl b
NChar c -> reprl c
NNumber n -> reprl n
NString s -> [show s]
NList es -> reprl (EList es)
exprNodeIoletCounter :: Env -> [ArgSpec] -> IoletCounter ExprNode
exprNodeIoletCounter env aspecs (ENode nodeLabel _nodeResult) =
case nodeLabel of
NUndefined -> (0, 1)
NSymbol (Symbol "if") -> (3, 1)
NSymbol (Symbol s) ->
case envLookup env s of
Nothing ->
case aspecsLookup s aspecs of
Nothing -> (0, 1)
Just i -> (i, 1)
Just value ->
case value of
VFun function -> (functionNArgs function, 1)
_ -> (0, 1)
_ -> (0, 1)
exprToTree :: Expr -> ExprTree
exprToTree expr =
let leafnode :: ExprNodeLabel -> T.Tree ExprNode
leafnode e = node e []
node :: ExprNodeLabel -> [T.Tree ExprNode] -> T.Tree ExprNode
node e ts = T.Node (ENode e EvalUntried) ts
errext = error ("exprToTree: extended expr: " ++ show expr)
in case expr of
EUndefined -> leafnode NUndefined
ESymbol s -> leafnode (NSymbol s)
EBool b -> leafnode (NBool b)
EChar c -> leafnode (NChar c)
ENumber n -> leafnode (NNumber n)
EString s -> leafnode (NString s)
EIf t a b -> node (NSymbol (Symbol "if")) (map exprToTree [t, a, b])
ELambda _x _body -> error "exprToTree: not implemented for lambda expr"
EApp f arg -> node (NSymbol (Symbol "@")) (map exprToTree [f, arg])
ECall f args -> node (NSymbol f) (map exprToTree args)
EList xs -> leafnode (NList xs)
EGroup _ -> errext
EOp _ _ _ -> errext
treeToExpr :: ExprTree -> SuccFail Expr
treeToExpr (T.Node (ENode label _) trees) =
let lit e = if null trees then Succ e
else Fail "literal node with non-empty subtrees"
in case label of
NUndefined -> Succ EUndefined
NBool b -> lit (EBool b)
NChar c -> lit (EChar c)
NNumber n -> lit (ENumber n)
NString s -> lit (EString s)
NList xs -> lit (EList xs)
NSymbol (Symbol "@") ->
case trees of
[f, arg] ->
do
{
f' <- treeToExpr f
; arg' <- treeToExpr arg
; Succ $ EApp f' arg'
}
_ -> Fail "'@' node with /= 2 subtrees"
NSymbol (Symbol "if") ->
case trees of
[q, a, b] ->
do
{
q' <- treeToExpr q
; a' <- treeToExpr a
; b' <- treeToExpr b
; Succ $ EIf q' a' b'
}
_ -> Fail ("An 'if' node has the wrong number of subtrees" ++
" (should be 3)")
NSymbol s ->
if null trees
then
Succ $ ESymbol s
else
do
{
trees' <- mapM treeToExpr trees
; Succ $ ECall s trees'
}
exprToReprTree :: Expr -> Tree String
exprToReprTree = fmap repr . exprToTree
evalTree :: ExprTree -> Env -> ExprTree
evalTree atree env = evalTreeWithLimit atree env stackSize
evalTreeWithLimit :: ExprTree -> Env -> Int -> ExprTree
evalTreeWithLimit atree env stacksize =
let T.Node root subtrees = atree
ss' = pred stacksize
in case root of
ENode (NSymbol (Symbol "if")) _ ->
case subtrees of
[tt, ta, tb] ->
let tt' = evalTreeWithLimit tt env ss'
ENode _ testResult = rootLabel tt'
subEval subtree =
let subtree' = evalTreeWithLimit subtree env ss'
ENode _ subresult = rootLabel subtree'
in (subresult, subtree')
ifNode result = ENode (NSymbol (Symbol "if")) result
in case testResult of
EvalOk (VBool True) ->
let (taValue, ta') = subEval ta in
T.Node (ifNode taValue) [tt', ta', tb]
EvalOk (VBool False) ->
let (tbValue, tb') = subEval tb in
T.Node (ifNode tbValue) [tt', ta, tb']
EvalOk weirdValue ->
let msg = "if: non-boolean condition value: " ++
repr weirdValue
in T.Node (ifNode (EvalError msg)) [tt', ta, tb]
EvalError msg ->
T.Node (ifNode (EvalError msg)) [tt', ta, tb]
_ -> errcats ["evalTreeWithLimit (if):",
"unexpected test result"]
_ -> error "evalTreeWithLimit: if: wrong number of subtrees"
ENode rootOper _ ->
let evalResult =
case treeToExpr atree of
Succ expr -> evalWithLimit expr env ss'
Fail msg -> EvalError msg
in T.Node (ENode rootOper evalResult)
[evalTreeWithLimit s env ss' | s <- subtrees]
unevalTree :: ExprTree -> ExprTree
unevalTree atree =
let unevalNode (ENode oper _) = ENode oper EvalUntried
in fmap unevalNode atree