module Language.ECMAScript3.Analysis.Environment
( env
, localVars
, EnvTree (..)
) where
import Data.List
import Data.Maybe
import qualified Data.Map as M
import Data.Map (Map)
import qualified Data.Set as S
import Data.Set (Set)
import Text.ParserCombinators.Parsec.Pos (SourcePos)
import Language.ECMAScript3.Syntax
data Partial = Partial {
partialLocals :: M.Map String SourcePos,
partialReferences :: M.Map String SourcePos,
partialNested :: [Partial]
}
empty :: Partial
empty = Partial M.empty M.empty []
ref :: Id SourcePos -> Partial
ref (Id p v) = Partial M.empty (M.singleton v p) []
decl :: Id SourcePos -> Partial
decl (Id p v) = Partial (M.singleton v p) M.empty []
nest :: Partial -> Partial
nest partial = Partial M.empty M.empty [partial]
unions :: [Partial] -> Partial
unions ps = Partial (M.unions (map partialLocals ps))
(M.unions (map partialReferences ps))
(concatMap partialNested ps)
javascript :: JavaScript SourcePos -> Partial
javascript (Script _ ss) = unions (map stmt ss)
lvalue :: LValue SourcePos -> Partial
lvalue lv = case lv of
LVar p x -> ref (Id p x)
LDot _ e _ -> expr e
LBracket _ e1 e2 -> unions [expr e1, expr e2]
expr :: Expression SourcePos -> Partial
expr e = case e of
StringLit _ _ -> empty
RegexpLit {} -> empty
NumLit _ _ -> empty
IntLit _ _ -> empty
BoolLit _ _ -> empty
NullLit _ -> empty
ArrayLit _ es -> unions (map expr es)
ObjectLit _ props -> unions (map (expr.snd) props)
ThisRef _ -> empty
VarRef _ id -> empty
DotRef _ e _ -> expr e
BracketRef _ e1 e2 -> unions [expr e1, expr e2]
NewExpr _ e1 es -> unions [expr e1, unions $ map expr es]
PrefixExpr _ _ e -> expr e
InfixExpr _ _ e1 e2 -> unions [expr e1, expr e2]
CondExpr _ e1 e2 e3 -> unions [expr e1, expr e2, expr e3]
AssignExpr _ _ lv e -> unions [lvalue lv, expr e]
UnaryAssignExpr _ _ lv -> lvalue lv
ListExpr _ es -> unions (map expr es)
CallExpr _ e es -> unions [expr e, unions $ map expr es]
FuncExpr _ _ args ss -> nest $ unions [unions $ map decl args
,unions $ map stmt ss]
caseClause :: CaseClause SourcePos -> Partial
caseClause cc = case cc of
CaseClause _ e ss -> unions [expr e, unions $ map stmt ss]
CaseDefault _ ss -> unions $ map stmt ss
catchClause :: CatchClause SourcePos -> Partial
catchClause (CatchClause _ id s) = unions [decl id, stmt s]
varDecl :: VarDecl SourcePos -> Partial
varDecl (VarDecl _ id Nothing) = decl id
varDecl (VarDecl _ id (Just e)) = unions [decl id, expr e]
forInit :: ForInit SourcePos -> Partial
forInit fi = case fi of
NoInit -> empty
VarInit ds -> unions $ map varDecl ds
ExprInit e -> expr e
forInInit :: ForInInit SourcePos -> Partial
forInInit (ForInVar id) = decl id
forInInit (ForInLVal lv) = lvalue lv
stmt :: Statement SourcePos -> Partial
stmt s = case s of
BlockStmt _ ss -> unions $ map stmt ss
EmptyStmt _ -> empty
ExprStmt _ e -> expr e
IfStmt _ e s1 s2 -> unions [expr e, stmt s1, stmt s2]
IfSingleStmt _ e s -> unions [expr e, stmt s]
SwitchStmt _ e cases -> unions [expr e, unions $ map caseClause cases]
WhileStmt _ e s -> unions [expr e, stmt s]
DoWhileStmt _ s e -> unions [stmt s, expr e]
BreakStmt _ _ -> empty
ContinueStmt _ _ -> empty
LabelledStmt _ _ s -> stmt s
ForInStmt _ fii e s -> unions [forInInit fii, expr e, stmt s]
ForStmt _ fi me1 me2 s ->
unions [forInit fi, maybe empty expr me1, maybe empty expr me2, stmt s]
TryStmt _ s mcatch ms ->
unions [stmt s, maybe empty catchClause mcatch, maybe empty stmt ms]
ThrowStmt _ e -> expr e
ReturnStmt _ me -> maybe empty expr me
WithStmt _ e s -> unions [expr e, stmt s]
VarDeclStmt _ decls -> unions $ map varDecl decls
FunctionStmt _ fnId args ss ->
unions [decl fnId, nest $ unions [unions $ map decl args,
unions $ map stmt ss]]
data EnvTree = EnvTree (M.Map String SourcePos) [EnvTree]
makeEnvTree :: Map String SourcePos
-> Partial
-> (EnvTree,Map String SourcePos)
makeEnvTree enclosing (Partial locals references nested) = (tree,globals) where
nestedResults = map (makeEnvTree (locals `M.union` enclosing)) nested
tree = EnvTree locals (map fst nestedResults)
globals' = (references `M.difference` locals) `M.difference` enclosing
globals = M.unions (globals':map snd nestedResults)
env :: Map String SourcePos
-> [Statement SourcePos]
-> (EnvTree,Map String SourcePos)
env globals program = makeEnvTree globals (unions $ map stmt program)
localVars :: [Statement SourcePos]
-> [(String, SourcePos)]
localVars body = M.toList locals where
Partial locals _ _ = unions $ map stmt body