on ceb9175 help sign in
elk/src/Generate/JavaScript/Variable.hs
206 lines 4.7 kB history raw
1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript.Variable
4 ( Generator
5 , run
6 , Table
7 , init
8 , fresh
9 , local
10 , safe
11 , global
12 , getGlobalName
13 )
14 where
17import Prelude hiding (init)
18import qualified Control.Monad.State as State
19import qualified Data.Char as Char
20import Data.Monoid ((<>))
21import qualified Data.Map as Map
22import qualified Data.Set as Set
23import qualified Data.Text as Text
24import Data.Text (Text)
25import Data.Word (Word16, Word32)
27import qualified AST.Helpers as Help
28import qualified AST.Module.Name as ModuleName
29import qualified AST.Variable as Var
30import qualified Elm.Package as Pkg
31import qualified Elm.Compiler.Objects.Internal as Obj
32import qualified Generate.JavaScript.Builder as JS
33import qualified Generate.JavaScript.Helpers as JS
37-- GENERATOR
40type Generator = State.State Table
43run :: Table -> Generator a -> ( a, Table )
44run table generator =
45 State.runState generator table
49-- STATE
52data Table =
53 Table
54 { _uid :: !Int
55 , _names :: Map.Map Var.Global Text
56 , _symbols :: Obj.SymbolTable
57 }
60init :: Obj.SymbolTable -> Table
61init symbols =
62 Table 0 Map.empty symbols
66-- FRESH NAMES
69fresh :: Generator Text
70fresh =
71 do (Table uid names symbols) <- State.get
72 State.put (Table (uid + 1) names symbols)
73 return (Text.pack ("_v" ++ show uid))
77-- LOCAL NAMES
80local :: Text -> JS.Expr
81local name =
82 JS.ref (safe name)
85safe :: Text -> Text
86safe name =
87 if Set.member name reservedWords then
88 "_" <> name
89 else
90 name
93reservedWords :: Set.Set Text
94reservedWords =
95 Set.fromList
96 -- reserved by JavaScript
97 [ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
98 , "arguments", "int", "byte", "char", "goto", "long", "final", "float"
99 , "short", "double", "native", "throws", "boolean", "abstract", "volatile"
100 , "transient", "synchronized", "function", "break", "case", "catch"
101 , "continue", "debugger", "default", "delete", "do", "else", "finally"
102 , "for", "function", "if", "in", "instanceof", "new", "return", "switch"
103 , "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
104 , "const", "enum", "export", "extends", "import", "super", "implements"
105 , "interface", "let", "package", "private", "protected", "public"
106 , "static", "yield"
107 -- reserved by Elm
108 , "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"
109 , "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9"
110 , "ctor"
111 ]
115-- GLOBAL NAMES
118global :: Var.Global -> Generator JS.Expr
119global var =
120 do table@(Table _ _ symbols) <- State.get
121 case Obj.lookup var symbols of
122 Nothing ->
123 JS.ref <$> getGlobalNameHelp var table
125 Just (Obj.Symbol symHome symName) ->
126 return $
127 JS.DotRef
128 (JS.ref (symbolHomeToText symHome))
129 (JS.Id (symbolNameToText symName))
132getGlobalName :: Var.Global -> Generator Text
133getGlobalName var =
134 do table <- State.get
135 getGlobalNameHelp var table
138getGlobalNameHelp :: Var.Global -> Table -> Generator Text
139getGlobalNameHelp var@(Var.Global home name) (Table uid names symbols) =
140 case Map.lookup var names of
141 Just jsName ->
142 return jsName
144 Nothing ->
145 case Help.isOp name of
146 False ->
147 do let jsName = globalToName home name
148 State.put (Table uid (Map.insert var jsName names) symbols)
149 return jsName
151 True ->
152 do let jsName = globalToName home ("_op" <> Text.pack (show uid))
153 State.put (Table (uid + 1) (Map.insert var jsName names) symbols)
154 return jsName
157globalToName :: ModuleName.Canonical -> Text -> Text
158globalToName (ModuleName.Canonical (Pkg.Name user project) moduleName) name =
159 Text.replace "-" "_" user
160 <> "$" <> Text.replace "-" "_" project
161 <> "$" <> Text.replace "." "_" moduleName
162 <> "$" <> name
166-- SYMBOLS
169symbolHomeToText :: Word16 -> Text
170symbolHomeToText word =
171 Text.pack ('$' : show word)
174symbolNameToText :: Word32 -> Text
175symbolNameToText word =
176 Text.pack (makeName word "")
179makeName :: Word32 -> String -> String
180makeName word str =
181 if word < 26 then
182 makeChar 97 word : str
184 else if word < 52 then
185 makeChar 65 (word - 26) : str
187 else
188 let
189 (next, leftover) =
190 quotRem word 52
191 in
192 makeName next (makeInnerChar leftover : str)
195makeInnerChar :: Word32 -> Char
196makeInnerChar word =
197 if word < 26 then
198 makeChar 97 word
199 else
200 makeChar 65 (word - 26)
203{-# INLINE makeChar #-}
204makeChar :: Word32 -> Word32 -> Char
205makeChar root offset =
206 Char.chr (fromIntegral (root + offset))