module MOO.AST (
Program(..)
, Statement(..)
, Then(..)
, ElseIf(..)
, Else(..)
, Except(..)
, Finally(..)
, Expr(..)
, Codes(..)
, Default(..)
, Argument(..)
, ScatterItem(..)
, isLValue
, precedence
) where
import Control.Applicative ((<$>), (<*>), pure)
import Data.Typeable (Typeable)
import Data.Word (Word8)
import Database.VCache (VCacheable(put, get), VPut, VGet, putWord8)
import MOO.Types
newtype Program = Program [Statement]
deriving (Show, Typeable)
instance VCacheable Program where
put (Program smts) = put smts
get = Program <$> get
instance Sizeable Program where
storageBytes (Program stmts) = storageBytes stmts
data Statement = Expression !LineNo Expr
| If !LineNo Expr Then [ElseIf] Else
| ForList !LineNo Id Expr [Statement]
| ForRange !LineNo Id (Expr, Expr) [Statement]
| While !LineNo (Maybe Id) Expr [Statement]
| Fork !LineNo (Maybe Id) Expr [Statement]
| Break (Maybe Id)
| Continue (Maybe Id)
| Return !LineNo (Maybe Expr)
| TryExcept [Statement] [Except]
| TryFinally [Statement] Finally
deriving (Show, Typeable)
newtype Then = Then [Statement] deriving (Show, Typeable)
data ElseIf = ElseIf !LineNo Expr [Statement] deriving (Show, Typeable)
newtype Else = Else [Statement] deriving (Show, Typeable)
data Except = Except !LineNo (Maybe Id) Codes [Statement]
deriving (Show, Typeable)
newtype Finally = Finally [Statement]
deriving (Show, Typeable)
instance VCacheable Statement where
put s = case s of
Expression line expr -> putTag2 00 line expr
If line expr thens elseIfs elses -> putTag5 01 line expr thens elseIfs elses
ForList line var expr smts -> putTag4 02 line var expr smts
ForRange line var range smts -> putTag4 03 line var range smts
While line var expr smts -> putTag4 04 line var expr smts
Fork line var expr smts -> putTag4 05 line var expr smts
Break var -> putTag1 06 var
Continue var -> putTag1 07 var
Return line expr -> putTag2 08 line expr
TryExcept smts excepts -> putTag2 09 smts excepts
TryFinally smts finally -> putTag2 10 smts finally
get = get >>= \tag -> case tag of
00 -> Expression <$> get <*> get
01 -> If <$> get <*> get <*> get <*> get <*> get
02 -> ForList <$> get <*> get <*> get <*> get
03 -> ForRange <$> get <*> get <*> get <*> get
04 -> While <$> get <*> get <*> get <*> get
05 -> Fork <$> get <*> get <*> get <*> get
06 -> Break <$> get
07 -> Continue <$> get
08 -> Return <$> get <*> get
09 -> TryExcept <$> get <*> get
10 -> TryFinally <$> get <*> get
_ -> unknownTag "Statement" tag
instance VCacheable Then where
put (Then smts) = put smts
get = Then <$> get
instance VCacheable ElseIf where
put (ElseIf line expr smts) = put line >> put expr >> put smts
get = ElseIf <$> get <*> get <*> get
instance VCacheable Else where
put (Else smts) = put smts
get = Else <$> get
instance VCacheable Except where
put (Except line var codes smts) =
put line >> put var >> put codes >> put smts
get = Except <$> get <*> get <*> get <*> get
instance VCacheable Finally where
put (Finally smts) = put smts
get = Finally <$> get
instance Sizeable Statement where
storageBytes (Expression line expr) =
storageBytes () + storageBytes line + storageBytes expr
storageBytes (If line expr (Then thens) elseIfs (Else elses)) =
storageBytes () + storageBytes line + storageBytes expr +
storageBytes thens + storageBytes elseIfs + storageBytes elses
storageBytes (ForList line var expr body) =
storageBytes () + storageBytes line + storageBytes var +
storageBytes expr + storageBytes body
storageBytes (ForRange line var range body) =
storageBytes () + storageBytes line + storageBytes var +
storageBytes range + storageBytes body
storageBytes (While line var expr body) =
storageBytes () + storageBytes line + storageBytes var +
storageBytes expr + storageBytes body
storageBytes (Fork line var expr body) =
storageBytes () + storageBytes line + storageBytes var +
storageBytes expr + storageBytes body
storageBytes (Break var) = storageBytes () + storageBytes var
storageBytes (Continue var) = storageBytes () + storageBytes var
storageBytes (Return line expr) =
storageBytes () + storageBytes line + storageBytes expr
storageBytes (TryExcept body excepts) =
storageBytes () + storageBytes body + storageBytes excepts
storageBytes (TryFinally body (Finally finally)) =
storageBytes () + storageBytes body + storageBytes finally
instance Sizeable ElseIf where
storageBytes (ElseIf line expr body) =
storageBytes () + storageBytes line + storageBytes expr + storageBytes body
instance Sizeable Except where
storageBytes (Except line var codes body) =
storageBytes () + storageBytes line + storageBytes var +
storageBytes codes + storageBytes body
data Expr = Literal Value
| List [Argument]
| Variable Id
| PropertyRef Expr Expr
| Assign Expr Expr
| Scatter [ScatterItem] Expr
| VerbCall Expr Expr [Argument]
| BuiltinFunc Id [Argument]
| Expr `Index` Expr
| Expr `Range` (Expr, Expr)
| Length
| Expr `In` Expr
| Expr `Plus` Expr
| Expr `Minus` Expr
| Expr `Times` Expr
| Expr `Divide` Expr
| Expr `Remain` Expr
| Expr `Power` Expr
| Negate Expr
| Conditional Expr Expr Expr
| Expr `And` Expr
| Expr `Or` Expr
| Not Expr
| Expr `CompareEQ` Expr
| Expr `CompareNE` Expr
| Expr `CompareLT` Expr
| Expr `CompareLE` Expr
| Expr `CompareGT` Expr
| Expr `CompareGE` Expr
| Catch Expr Codes Default
deriving (Show, Typeable)
instance VCacheable Expr where
put expr = case expr of
Literal val -> putTag1 00 val
List args -> putTag1 01 args
Variable var -> putTag1 02 var
PropertyRef obj name -> putTag2 03 obj name
Assign var expr -> putTag2 04 var expr
Scatter items expr -> putTag2 05 items expr
VerbCall obj name args -> putTag3 06 obj name args
BuiltinFunc name args -> putTag2 07 name args
expr `Index` index -> putTag2 08 expr index
expr `Range` range -> putTag2 09 expr range
Length -> putTag0 10
item `In` expr -> putTag2 11 item expr
x `Plus` y -> putTag2 12 x y
x `Minus` y -> putTag2 13 x y
x `Times` y -> putTag2 14 x y
x `Divide` y -> putTag2 15 x y
x `Remain` y -> putTag2 16 x y
x `Power` y -> putTag2 17 x y
Negate expr -> putTag1 18 expr
Conditional cond x y -> putTag3 19 cond x y
x `And` y -> putTag2 20 x y
x `Or` y -> putTag2 21 x y
Not expr -> putTag1 22 expr
x `CompareEQ` y -> putTag2 23 x y
x `CompareNE` y -> putTag2 24 x y
x `CompareLT` y -> putTag2 25 x y
x `CompareLE` y -> putTag2 26 x y
x `CompareGT` y -> putTag2 27 x y
x `CompareGE` y -> putTag2 28 x y
Catch expr codes def -> putTag3 29 expr codes def
get = get >>= \tag -> case tag of
00 -> Literal <$> get
01 -> List <$> get
02 -> Variable <$> get
03 -> PropertyRef <$> get <*> get
04 -> Assign <$> get <*> get
05 -> Scatter <$> get <*> get
06 -> VerbCall <$> get <*> get <*> get
07 -> BuiltinFunc <$> get <*> get
08 -> Index <$> get <*> get
09 -> Range <$> get <*> get
10 -> pure Length
11 -> In <$> get <*> get
12 -> Plus <$> get <*> get
13 -> Minus <$> get <*> get
14 -> Times <$> get <*> get
15 -> Divide <$> get <*> get
16 -> Remain <$> get <*> get
17 -> Power <$> get <*> get
18 -> Negate <$> get
19 -> Conditional <$> get <*> get <*> get
20 -> And <$> get <*> get
21 -> Or <$> get <*> get
22 -> Not <$> get
23 -> CompareEQ <$> get <*> get
24 -> CompareNE <$> get <*> get
25 -> CompareLT <$> get <*> get
26 -> CompareLE <$> get <*> get
27 -> CompareGT <$> get <*> get
28 -> CompareGE <$> get <*> get
29 -> Catch <$> get <*> get <*> get
_ -> unknownTag "Expr" tag
instance Sizeable Expr where
storageBytes (Literal value) = storageBytes () + storageBytes value
storageBytes (List args) = storageBytes () + storageBytes args
storageBytes (Variable var) = storageBytes () + storageBytes var
storageBytes (PropertyRef obj name) =
storageBytes () + storageBytes obj + storageBytes name
storageBytes (Assign lhs rhs) =
storageBytes () + storageBytes lhs + storageBytes rhs
storageBytes (Scatter scats expr) =
storageBytes () + storageBytes scats + storageBytes expr
storageBytes (VerbCall obj name args) =
storageBytes () + storageBytes obj + storageBytes name + storageBytes args
storageBytes (BuiltinFunc name args) =
storageBytes () + storageBytes name + storageBytes args
storageBytes (Index x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Range x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes Length = storageBytes ()
storageBytes (In x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Plus x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Minus x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Times x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Divide x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Remain x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Power x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Negate x) = storageBytes () + storageBytes x
storageBytes (Conditional x y z) =
storageBytes () + storageBytes x + storageBytes y + storageBytes z
storageBytes (And x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Or x y) = storageBytes () + storageBytes x + storageBytes y
storageBytes (Not x) = storageBytes () + storageBytes x
storageBytes (CompareEQ x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (CompareNE x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (CompareLT x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (CompareLE x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (CompareGT x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (CompareGE x y) =
storageBytes () + storageBytes x + storageBytes y
storageBytes (Catch expr codes (Default dv)) =
storageBytes () + storageBytes expr + storageBytes codes + storageBytes dv
data Codes = ANY | Codes [Argument] deriving (Show, Typeable)
newtype Default = Default (Maybe Expr) deriving (Show, Typeable)
instance VCacheable Codes where
put codes = put $ case codes of
ANY -> Nothing
Codes codes -> Just codes
get = maybe ANY Codes <$> get
instance VCacheable Default where
put (Default expr) = put expr
get = Default <$> get
instance Sizeable Codes where
storageBytes ANY = storageBytes ()
storageBytes (Codes args) = storageBytes () + storageBytes args
data Argument = ArgNormal Expr
| ArgSplice Expr
deriving (Show, Typeable)
instance VCacheable Argument where
put arg = put $ case arg of
ArgNormal expr -> Left expr
ArgSplice expr -> Right expr
get = either ArgNormal ArgSplice <$> get
instance Sizeable Argument where
storageBytes (ArgNormal expr) = storageBytes () + storageBytes expr
storageBytes (ArgSplice expr) = storageBytes () + storageBytes expr
data ScatterItem = ScatRequired Id
| ScatOptional Id (Maybe Expr)
| ScatRest Id
deriving (Show, Typeable)
instance VCacheable ScatterItem where
put item = case item of
ScatRequired var -> putTag1 0 var
ScatOptional var expr -> putTag2 1 var expr
ScatRest var -> putTag1 2 var
get = get >>= \tag -> case tag of
0 -> ScatRequired <$> get
1 -> ScatOptional <$> get <*> get
2 -> ScatRest <$> get
_ -> unknownTag "ScatterItem" tag
instance Sizeable ScatterItem where
storageBytes (ScatRequired var) = storageBytes () + storageBytes var
storageBytes (ScatOptional var expr) =
storageBytes () + storageBytes var + storageBytes expr
storageBytes (ScatRest var) = storageBytes () + storageBytes var
isLValue :: Expr -> Bool
isLValue (Range e _) = isLValue' e
isLValue e = isLValue' e
isLValue' :: Expr -> Bool
isLValue' Variable{} = True
isLValue' PropertyRef{} = True
isLValue' (Index e _) = isLValue' e
isLValue' _ = False
precedence :: Expr -> Int
precedence expr = case expr of
Assign{} -> 1
Scatter{} -> 1
Conditional{} -> 2
And{} -> 3
Or{} -> 3
CompareEQ{} -> 4
CompareNE{} -> 4
CompareLT{} -> 4
CompareLE{} -> 4
CompareGT{} -> 4
CompareGE{} -> 4
In{} -> 4
Plus{} -> 5
Minus{} -> 5
Times{} -> 6
Divide{} -> 6
Remain{} -> 6
Power{} -> 7
Not{} -> 8
Negate{} -> 8
PropertyRef{} -> 9
VerbCall{} -> 9
Index{} -> 9
Range{} -> 9
_ -> 10
putTag0 :: Word8 -> VPut ()
putTag0 = putWord8
putTag1 :: VCacheable a => Word8 -> a -> VPut ()
putTag1 tag a = putWord8 tag >> put a
putTag2 :: (VCacheable a, VCacheable b) => Word8 -> a -> b -> VPut ()
putTag2 tag a b = putWord8 tag >> put a >> put b
putTag3 :: (VCacheable a, VCacheable b, VCacheable c) =>
Word8 -> a -> b -> c -> VPut ()
putTag3 tag a b c = putWord8 tag >> put a >> put b >> put c
putTag4 :: (VCacheable a, VCacheable b, VCacheable c, VCacheable d) =>
Word8 -> a -> b -> c -> d -> VPut ()
putTag4 tag a b c d = putWord8 tag >> put a >> put b >> put c >> put d
putTag5 :: (VCacheable a, VCacheable b, VCacheable c, VCacheable d,
VCacheable e) => Word8 -> a -> b -> c -> d -> e -> VPut ()
putTag5 tag a b c d e =
putWord8 tag >> put a >> put b >> put c >> put d >> put e
unknownTag :: String -> Word8 -> VGet a
unknownTag what tag =
fail $ "get: unknown " ++ what ++ " tag (" ++ show tag ++ ")"