@@ -1,7 +1,6 @@
11module Generate.JavaScript.Variable
22 ( fresh
33 , canonical
4− , modulePrefix
54 , define
65 , safe
76 )
@@ -18,8 +17,10 @@ import qualified AST.Variable as Var
1817import qualified Generate.JavaScript.Helpers as JS
1918
2019
20+
2121-- FRESH NAMES
2222
23+
2324fresh :: State.State Int String
2425fresh =
2526 do n <- State.get
@@ -27,54 +28,96 @@ fresh =
2728 return ("_v" ++ show n)
2829
2930
31+
3032-- DEF NAMES
3133
32−define :: String -> JS.Expression () -> JS.Statement ()
33−define name body =
34− if Help.isOp name then
35− let
36− root =
37− JS.VarRef () (JS.Id () "_op")
3834
39− lvalue =
40− JS.LBracket () root (JS.StringLit () name)
35+define :: Maybe ModuleName.Canonical -> String -> JS.Expression () -> [JS.Statement ()]
36+define maybeHome name body =
37+ if not (Help.isOp name) then
38+ let
39+ jsName =
40+ maybe unqualified qualified maybeHome name
4141 in
42− JS.ExprStmt () (JS.AssignExpr () JS.OpAssign lvalue body)
42+ [ JS.VarDeclStmt () [ JS.varDecl jsName body ] ]
4343
4444 else
45− JS.VarDeclStmt () [ JS.VarDecl () (JS.Id () (safe name)) (Just body) ]
45+ case maybeHome of
46+ Nothing ->
47+ error "can only define infix operators at the top level"
48+
49+ Just home ->
50+ let
51+ opsDictName =
52+ getOpsDictName (Var.TopLevel home)
53+
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+ ]
60+
4661
4762
4863-- INSTANTIATE VARIABLES
4964
65+
5066canonical :: Var.Canonical -> JS.Expression ()
5167canonical (Var.Canonical home name) =
5268 if Help.isOp name then
53− JS.BracketRef () (addRoot home "_op") (JS.StringLit () name)
69+ JS.BracketRef () (JS.ref (getOpsDictName home)) (JS.StringLit () name)
5470
5571 else
56− addRoot home (safe name)
72+ case home of
73+ Var.Local ->
74+ JS.ref (unqualified name)
75+
76+ Var.BuiltIn ->
77+ JS.ref (unqualified name)
5778
79+ Var.Module moduleName@(ModuleName.Canonical _ rawName) ->
80+ if ModuleName.isNative rawName then
81+ native moduleName name
5882
59−addRoot :: Var.Home -> String -> JS.Expression ()
60−addRoot home name =
61− case home of
62− Var.Local ->
63− JS.ref name
83+ else
84+ JS.ref (qualified moduleName name)
6485
65− Var.TopLevel _moduleName ->
66− JS.ref name
86+ Var.TopLevel moduleName ->
87+ JS.ref (qualified moduleName name)
6788
68− Var.BuiltIn ->
69− JS.ref name
7089
71− Var.Module (ModuleName.Canonical _ moduleName) ->
72− JS.DotRef () (JS.ref (modulePrefix moduleName)) (JS.Id () name)
90+unqualified :: String -> String
91+unqualified =
92+ safe
7393
7494
75−modulePrefix :: ModuleName.Raw -> String
76−modulePrefix moduleName =
77− '$' : List.intercalate "$" moduleName
95+qualified :: ModuleName.Canonical -> String -> String
96+qualified moduleName name =
97+ moduleToString moduleName ++ "$" ++ removePrimes name
98+
99+
100+native :: ModuleName.Canonical -> String -> JS.Expression ()
101+native moduleName name =
102+ JS.obj [ moduleToString moduleName, name ]
103+
104+
105+getOpsDictName :: Var.Home -> String
106+getOpsDictName 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"
116+
117+
118+moduleToString :: ModuleName.Canonical -> String
119+moduleToString (ModuleName.Canonical _ moduleName) =
120+ '_' : List.intercalate "_" moduleName
78121
79122
80123swap :: Char -> Char -> Char -> Char
@@ -82,14 +125,19 @@ swap from to c =
82125 if c == from then to else c
83126
84127
128+
85129-- SAFE NAMES
86130
131+
87132safe :: String -> String
88133safe name =
89− let saferName =
90− if Set.member name jsReserveds then '$' : name else name
91− in
92− map (swap '\'' '$') saferName
134+ removePrimes $
135+ if Set.member name jsReserveds then '$' : name else name
136+
137+
138+removePrimes :: String -> String
139+removePrimes name =
140+ map (swap '\'' '$') name
93141
94142
95143jsReserveds :: Set.Set String