on df41ae8 help sign in
elk/src/Generate/JavaScript/Variable.hs
175 lines 4.4 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2module Generate.JavaScript.Variable
3 ( fresh
4 , canonical
5 , qualified
6 , native
7 , define
8 , safe
9 )
10 where
12import qualified Control.Monad.State as State
13import qualified Data.List as List
14import qualified Data.Set as Set
15import qualified Language.ECMAScript3.Syntax as JS
17import qualified AST.Helpers as Help
18import qualified AST.Module.Name as ModuleName
19import qualified AST.Variable as Var
20import qualified Elm.Package as Pkg
21import qualified Generate.JavaScript.Helpers as JS
25-- FRESH NAMES
28fresh :: State.State Int String
29fresh =
30 do n <- State.get
31 State.modify (+1)
32 return ("_v" ++ show n)
36-- DEF NAMES
39define :: Maybe ModuleName.Canonical -> String -> JS.Expression () -> [JS.Statement ()]
40define maybeHome name body =
41 if not (Help.isOp name) then
42 let
43 jsName =
44 maybe unqualified qualified maybeHome name
45 in
46 [ JS.VarDeclStmt () [ JS.varDecl jsName body ] ]
48 else
49 case maybeHome of
50 Nothing ->
51 error "can only define infix operators at the top level"
53 Just home ->
54 let
55 opsDictName =
56 getOpsDictName (Var.TopLevel home)
58 lvalue =
59 JS.LBracket () (JS.ref opsDictName) (JS.StringLit () name)
60 in
61 [ JS.VarDeclStmt () [ JS.varDecl opsDictName (JS.refOrObject opsDictName) ]
62 , JS.ExprStmt () (JS.AssignExpr () JS.OpAssign lvalue body)
63 ]
67-- INSTANTIATE VARIABLES
70canonical :: Var.Canonical -> JS.Expression ()
71canonical (Var.Canonical home name) =
72 if Help.isOp name then
73 JS.BracketRef () (JS.ref (getOpsDictName home)) (JS.StringLit () name)
75 else
76 case home of
77 Var.Local ->
78 JS.ref (unqualified name)
80 Var.BuiltIn ->
81 JS.ref (unqualified name)
83 Var.Module moduleName@(ModuleName.Canonical _ rawName) ->
84 if ModuleName.isNative rawName then
85 native moduleName name
87 else
88 JS.ref (qualified moduleName name)
90 Var.TopLevel moduleName ->
91 JS.ref (qualified moduleName name)
94unqualified :: String -> String
95unqualified =
96 safe
99qualified :: ModuleName.Canonical -> String -> String
100qualified moduleName name =
101 moduleToString moduleName ++ "$" ++ removePrimes name
104native :: ModuleName.Canonical -> String -> JS.Expression ()
105native moduleName name =
106 JS.obj [ moduleToString moduleName, name ]
109getOpsDictName :: Var.Home -> String
110getOpsDictName home =
111 let
112 moduleName =
113 case home of
114 Var.Local -> error "infix operators should only be defined in top-level declarations"
115 Var.BuiltIn -> error "there should be no built-in infix operators"
116 Var.Module name -> name
117 Var.TopLevel name -> name
118 in
119 moduleToString moduleName ++ "_ops"
122moduleToString :: ModuleName.Canonical -> String
123moduleToString (ModuleName.Canonical (Pkg.Name user project) moduleName) =
124 let
125 safeUser =
126 map (swap '-' '_') user
128 safeProject =
129 map (swap '-' '_') project
131 safeModuleName =
132 List.intercalate "_" moduleName
133 in
134 '_' : safeUser ++ "$" ++ safeProject ++ "$" ++ safeModuleName
137swap :: Char -> Char -> Char -> Char
138swap from to c =
139 if c == from then to else c
143-- SAFE NAMES
146safe :: String -> String
147safe name =
148 removePrimes $
149 if Set.member name jsReserveds then '$' : name else name
152removePrimes :: String -> String
153removePrimes name =
154 map (swap '\'' '$') name
157jsReserveds :: Set.Set String
158jsReserveds =
159 Set.fromList
160 -- JS reserved words
161 [ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
162 , "arguments", "int", "byte", "char", "goto", "long", "final", "float"
163 , "short", "double", "native", "throws", "boolean", "abstract", "volatile"
164 , "transient", "synchronized", "function", "break", "case", "catch"
165 , "continue", "debugger", "default", "delete", "do", "else", "finally"
166 , "for", "function", "if", "in", "instanceof", "new", "return", "switch"
167 , "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
168 , "const", "enum", "export", "extends", "import", "super", "implements"
169 , "interface", "let", "package", "private", "protected", "public"
170 , "static", "yield"
171 -- reserved by the Elm runtime system
172 , "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"
173 , "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9"
174 , JS.localRuntime
175 ]