on c4522b0 help sign in
elk/src/Generate/JavaScript/Variable.hs
161 lines 4.1 kB history raw
1module Generate.JavaScript.Variable
2 ( fresh
3 , canonical
4 , define
5 , safe
6 )
7 where
9import qualified Control.Monad.State as State
10import qualified Data.List as List
11import qualified Data.Set as Set
12import qualified Language.ECMAScript3.Syntax as JS
14import qualified AST.Helpers as Help
15import qualified AST.Module.Name as ModuleName
16import qualified AST.Variable as Var
17import qualified Generate.JavaScript.Helpers as JS
21-- FRESH NAMES
24fresh :: State.State Int String
25fresh =
26 do n <- State.get
27 State.modify (+1)
28 return ("_v" ++ show n)
32-- DEF NAMES
35define :: Maybe ModuleName.Canonical -> String -> JS.Expression () -> [JS.Statement ()]
36define maybeHome name body =
37 if not (Help.isOp name) then
38 let
39 jsName =
40 maybe unqualified qualified maybeHome name
41 in
42 [ JS.VarDeclStmt () [ JS.varDecl jsName body ] ]
44 else
45 case maybeHome of
46 Nothing ->
47 error "can only define infix operators at the top level"
49 Just home ->
50 let
51 opsDictName =
52 getOpsDictName (Var.TopLevel home)
54 lvalue =
55 JS.LBracket () (JS.ref opsDictName) (JS.StringLit () name)
56 in
57 [ JS.VarDeclStmt () [ JS.varDecl opsDictName (JS.refOrObject opsDictName) ]
58 , JS.ExprStmt () (JS.AssignExpr () JS.OpAssign lvalue body)
59 ]
63-- INSTANTIATE VARIABLES
66canonical :: Var.Canonical -> JS.Expression ()
67canonical (Var.Canonical home name) =
68 if Help.isOp name then
69 JS.BracketRef () (JS.ref (getOpsDictName home)) (JS.StringLit () name)
71 else
72 case home of
73 Var.Local ->
74 JS.ref (unqualified name)
76 Var.BuiltIn ->
77 JS.ref (unqualified name)
79 Var.Module moduleName@(ModuleName.Canonical _ rawName) ->
80 if ModuleName.isNative rawName then
81 native moduleName name
83 else
84 JS.ref (qualified moduleName name)
86 Var.TopLevel moduleName ->
87 JS.ref (qualified moduleName name)
90unqualified :: String -> String
91unqualified =
92 safe
95qualified :: ModuleName.Canonical -> String -> String
96qualified moduleName name =
97 moduleToString moduleName ++ "$" ++ removePrimes name
100native :: ModuleName.Canonical -> String -> JS.Expression ()
101native moduleName name =
102 JS.obj [ moduleToString moduleName, name ]
105getOpsDictName :: Var.Home -> String
106getOpsDictName home =
107 let
108 moduleName =
109 case home of
110 Var.Local -> error "infix operators should only be defined in top-level declarations"
111 Var.BuiltIn -> error "there should be no built-in infix operators"
112 Var.Module name -> name
113 Var.TopLevel name -> name
114 in
115 moduleToString moduleName ++ "_ops"
118moduleToString :: ModuleName.Canonical -> String
119moduleToString (ModuleName.Canonical _ moduleName) =
120 '_' : List.intercalate "_" moduleName
123swap :: Char -> Char -> Char -> Char
124swap from to c =
125 if c == from then to else c
129-- SAFE NAMES
132safe :: String -> String
133safe name =
134 removePrimes $
135 if Set.member name jsReserveds then '$' : name else name
138removePrimes :: String -> String
139removePrimes name =
140 map (swap '\'' '$') name
143jsReserveds :: Set.Set String
144jsReserveds =
145 Set.fromList
146 -- JS reserved words
147 [ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
148 , "arguments", "int", "byte", "char", "goto", "long", "final", "float"
149 , "short", "double", "native", "throws", "boolean", "abstract", "volatile"
150 , "transient", "synchronized", "function", "break", "case", "catch"
151 , "continue", "debugger", "default", "delete", "do", "else", "finally"
152 , "for", "function", "if", "in", "instanceof", "new", "return", "switch"
153 , "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
154 , "const", "enum", "export", "extends", "import", "super", "implements"
155 , "interface", "let", "package", "private", "protected", "public"
156 , "static", "yield"
157 -- reserved by the Elm runtime system
158 , "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"
159 , "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9"
160 , JS.localRuntime
161 ]