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