on 8476fc9 help sign in
elk/src/Generate/JavaScript/Variable.hs
173 lines 4.4 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2module Generate.JavaScript.Variable
3 ( fresh
4 , canonical
5 , define
6 , safe
7 )
8 where
10import qualified Control.Monad.State as State
11import qualified Data.List as List
12import qualified Data.Set as Set
13import qualified Language.ECMAScript3.Syntax as JS
15import qualified AST.Helpers as Help
16import qualified AST.Module.Name as ModuleName
17import qualified AST.Variable as Var
18import qualified Elm.Package as Pkg
19import qualified Generate.JavaScript.Helpers as JS
23-- FRESH NAMES
26fresh :: State.State Int String
27fresh =
28 do n <- State.get
29 State.modify (+1)
30 return ("_v" ++ show n)
34-- DEF NAMES
37define :: Maybe ModuleName.Canonical -> String -> JS.Expression () -> [JS.Statement ()]
38define maybeHome name body =
39 if not (Help.isOp name) then
40 let
41 jsName =
42 maybe unqualified qualified maybeHome name
43 in
44 [ JS.VarDeclStmt () [ JS.varDecl jsName body ] ]
46 else
47 case maybeHome of
48 Nothing ->
49 error "can only define infix operators at the top level"
51 Just home ->
52 let
53 opsDictName =
54 getOpsDictName (Var.TopLevel home)
56 lvalue =
57 JS.LBracket () (JS.ref opsDictName) (JS.StringLit () name)
58 in
59 [ JS.VarDeclStmt () [ JS.varDecl opsDictName (JS.refOrObject opsDictName) ]
60 , JS.ExprStmt () (JS.AssignExpr () JS.OpAssign lvalue body)
61 ]
65-- INSTANTIATE VARIABLES
68canonical :: Var.Canonical -> JS.Expression ()
69canonical (Var.Canonical home name) =
70 if Help.isOp name then
71 JS.BracketRef () (JS.ref (getOpsDictName home)) (JS.StringLit () name)
73 else
74 case home of
75 Var.Local ->
76 JS.ref (unqualified name)
78 Var.BuiltIn ->
79 JS.ref (unqualified name)
81 Var.Module moduleName@(ModuleName.Canonical _ rawName) ->
82 if ModuleName.isNative rawName then
83 native moduleName name
85 else
86 JS.ref (qualified moduleName name)
88 Var.TopLevel moduleName ->
89 JS.ref (qualified moduleName name)
92unqualified :: String -> String
93unqualified =
94 safe
97qualified :: ModuleName.Canonical -> String -> String
98qualified moduleName name =
99 moduleToString moduleName ++ "$" ++ removePrimes name
102native :: ModuleName.Canonical -> String -> JS.Expression ()
103native moduleName name =
104 JS.obj [ moduleToString moduleName, name ]
107getOpsDictName :: Var.Home -> String
108getOpsDictName home =
109 let
110 moduleName =
111 case home of
112 Var.Local -> error "infix operators should only be defined in top-level declarations"
113 Var.BuiltIn -> error "there should be no built-in infix operators"
114 Var.Module name -> name
115 Var.TopLevel name -> name
116 in
117 moduleToString moduleName ++ "_ops"
120moduleToString :: ModuleName.Canonical -> String
121moduleToString (ModuleName.Canonical (Pkg.Name user project) moduleName) =
122 let
123 safeUser =
124 map (swap '-' '_') user
126 safeProject =
127 map (swap '-' '_') project
129 safeModuleName =
130 List.intercalate "_" moduleName
131 in
132 '_' : safeUser ++ "$" ++ safeProject ++ "$" ++ safeModuleName
135swap :: Char -> Char -> Char -> Char
136swap from to c =
137 if c == from then to else c
141-- SAFE NAMES
144safe :: String -> String
145safe name =
146 removePrimes $
147 if Set.member name jsReserveds then '$' : name else name
150removePrimes :: String -> String
151removePrimes name =
152 map (swap '\'' '$') name
155jsReserveds :: Set.Set String
156jsReserveds =
157 Set.fromList
158 -- JS reserved words
159 [ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
160 , "arguments", "int", "byte", "char", "goto", "long", "final", "float"
161 , "short", "double", "native", "throws", "boolean", "abstract", "volatile"
162 , "transient", "synchronized", "function", "break", "case", "catch"
163 , "continue", "debugger", "default", "delete", "do", "else", "finally"
164 , "for", "function", "if", "in", "instanceof", "new", "return", "switch"
165 , "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
166 , "const", "enum", "export", "extends", "import", "super", "implements"
167 , "interface", "let", "package", "private", "protected", "public"
168 , "static", "yield"
169 -- reserved by the Elm runtime system
170 , "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"
171 , "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9"
172 , JS.localRuntime
173 ]