@@ -1,70 +1,171 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression
2module Generate.JavaScript.Expression
3− ( generateDecl
3+ ( generateJsExpr
7−import Prelude hiding (exp)
7+
8−import Control.Arrow (second)
8+import qualified Data.IntMap as IntMap
9−import Control.Monad (foldM)
10import qualified Data.List as List
9import qualified Data.List as List
11import qualified Data.Map as Map
10import qualified Data.Map as Map
12−import Data.Monoid ((<>))
13−import qualified Data.Text as Text
11+import qualified Data.Text.Encoding as Text
14−import Data.Text (Text)
16−import AST.Expression.Optimized as Opt
13+import qualified AST.Optimized as Opt
17−import qualified AST.Literal as L
18import qualified AST.Module.Name as ModuleName
14import qualified AST.Module.Name as ModuleName
19−import qualified AST.Variable as V
15+import qualified Data.Index as Index
20−import Generate.JavaScript.Helpers as Help
16+import qualified Elm.Name as N
17+import qualified Elm.Package as Pkg
21import qualified Generate.JavaScript.Builder as JS
18import qualified Generate.JavaScript.Builder as JS
22−import qualified Generate.JavaScript.BuiltIn as BuiltIn
19+import qualified Generate.JavaScript.Name as Name
23−import qualified Generate.JavaScript.Literal as Literal
24−import qualified Generate.JavaScript.Variable as Var
25−import Generate.JavaScript.Variable (Generator)
26import qualified Optimize.DecisionTree as DT
20import qualified Optimize.DecisionTree as DT
21+import qualified Reporting.Region as R
30−-- GENERATE DECLARATION
25+-- ENVIRONMENT
33−generateDecl :: ModuleName.Canonical -> Text -> Opt.Def -> Generator JS.Stmt
28+type Mode = Name.Mode
34−generateDecl home name def =
35− do jsName <- Var.globalName (V.Global home name)
36− jsBody <- generateDef name def
37− return $ JS.VarDeclStmt [ Help.varDecl jsName jsBody ]
41−-- CODE CHUNKS
32+-- EXPRESSIONS
44−data Code
35+generateJsExpr :: Mode -> Opt.Expr -> JS.Expr
45− = JsExpr JS.Expr
36+generateJsExpr mode expression =
46− | JsBlock [JS.Stmt]
37+ codeToJsExpr (generateCode mode expression)
49−jsExpr :: JS.Expr -> Generator Code
40+generateCode :: Mode -> Opt.Expr -> Code
50−jsExpr exp =
41+generateCode mode expression =
51− return (JsExpr exp)
42+ case expression of
47+ JS.Call toChar [ JS.String (Text.encodeUtf8Builder char) ]
50+ JS.String (Text.encodeUtf8Builder char)
54−jsBlock :: [JS.Stmt] -> Generator Code
52+ Opt.Str string ->
55−jsBlock stmts =
53+ JsExpr $ JS.String (Text.encodeUtf8Builder string)
56− return (JsBlock stmts)
59−isBlock :: Code -> Bool
58+ Opt.Float float ->
60−isBlock code =
59+ JsExpr $ JS.Float float
62+ JsExpr $ JS.Ref (Name.fromLocal name)
64+ Opt.VarGlobal (Opt.Global home name) ->
65+ JsExpr $ JS.Ref (Name.fromGlobal home name)
67+ Opt.VarCycle home name ->
68+ JsExpr $ JS.Ref (Name.fromCycle home name)
70+ Opt.VarDebug name home region unhandledValueName ->
71+ JsExpr $ generateDebug name home region unhandledValueName
73+ Opt.VarKernel home name ->
74+ JsExpr $ JS.Ref (Name.fromKernel home name)
79+ (JS.Ref (Name.fromKernel N.list "fromArray"))
80+ [ JS.Array $ map (generateJsExpr mode) entries
83+ Opt.Function args body ->
84+ generateFunction args (generateCode mode body)
86+ Opt.Call func args ->
87+ JsExpr $ generateCall mode func args
89+ Opt.TailCall name args ->
90+ JsBlock $ generateTailCall mode name args
92+ Opt.If branches final ->
93+ generateIf mode branches final
97+ generateDef mode def : codeToStatementList (generateCode mode body)
99+ Opt.Case label root decider jumps ->
100+ JsBlock $ generateCase mode label root decider jumps
102+ Opt.Index expr index ->
103+ JsExpr $ JS.Access (generateJsExpr mode expr) (Name.fromInt index)
105+ Opt.Accessor field ->
106+ JsExpr $ JS.Function Nothing [Name.dollar]
107+ [ JS.Return $ Just $
108+ JS.Access (JS.Ref Name.dollar) (Name.fromField mode field)
111+ Opt.Access record field ->
112+ JsExpr $ JS.Access (generateJsExpr mode record) (Name.fromField mode field)
114+ Opt.Update record fields ->
116+ JS.Call (JS.Ref (Name.fromKernel N.utils "update"))
117+ [ generateJsExpr mode record
118+ , generateRecord mode fields
121+ Opt.Record fields ->
122+ JsExpr $ generateRecord mode fields
125+ JsExpr $ JS.Ref (Name.fromKernel N.utils "Tuple0")
127+ Opt.Tuple a b maybeC ->
131+ JS.Call (JS.Ref (Name.fromKernel N.utils "Tuple2"))
132+ [ generateJsExpr mode a
133+ , generateJsExpr mode b
137+ JS.Call (JS.Ref (Name.fromKernel N.utils "Tuple3"))
138+ [ generateJsExpr mode a
139+ , generateJsExpr mode b
140+ , generateJsExpr mode c
144+ let string = JS.String (Text.encodeUtf8Builder src) in
145+ JsExpr $ JS.Object [ ( Name.fromLocal "src", string ) ]
157+codeToJsExpr :: Code -> JS.Expr
61 case code of
159 case code of
62− JsBlock _ -> True
163+ JsBlock stmts ->
63− JsExpr _ -> False
164+ JS.Call (JS.Function Nothing [] stmts) []
66−toStatementList :: Code -> [JS.Stmt]
167+codeToStatementList :: Code -> [JS.Stmt]
67−toStatementList code =
168+codeToStatementList code =
68 case code of
169 case code of
69 JsExpr expr ->
170 JsExpr expr ->
70 [ JS.Return (Just expr) ]
171 [ JS.Return (Just expr) ]
@@ -73,8 +174,8 @@ toStatementList code =
76−toStatement :: Code -> JS.Stmt
177+codeToStatement :: Code -> JS.Stmt
77−toStatement code =
178+codeToStatement code =
78 case code of
179 case code of
79 JsExpr expr ->
180 JsExpr expr ->
80 JS.Return (Just expr)
181 JS.Return (Just expr)
@@ -86,533 +187,603 @@ toStatement code =
86 JS.Block stmts
187 JS.Block stmts
89−toExpr :: Code -> JS.Expr
95− JsBlock stmts ->
191+-- CHARS
96− JS.Call (function [] stmts) []
99−
194+{-# NOINLINE toChar #-}
100−-- EXPRESSIONS
195+toChar :: JS.Expr
197+ JS.Ref (Name.fromKernel N.utils "chr")
103−generateJsExpr :: Opt.Expr -> Generator JS.Expr
104−generateJsExpr optExpr =
105− toExpr <$> generateCode optExpr
108−generateCode :: Opt.Expr -> Generator Code
112− jsExpr $ Var.local name
114− VarGlobal name ->
204+generateRecord :: Mode -> Map.Map N.Name Opt.Expr -> JS.Expr
115− JsExpr <$> Var.global name
205+generateRecord mode fields =
207+ toPair (field, value) =
208+ (Name.fromField mode field, generateJsExpr mode value)
210+ JS.Object (map toPair (Map.toList fields))
118− JsExpr <$> Literal.literal lit
120− Access record field ->
121− do jsRecord <- generateJsExpr record
122− jsExpr $ JS.DotRef jsRecord (JS.Id (Var.safe field))
124− Update record fields ->
214+-- DEBUG
126− fieldToJs (field, value) =
127− do jsValue <- generateJsExpr value
128− return (Var.safe field, jsValue)
130− do jsRecord <- generateJsExpr record
131− jsFields <- mapM fieldToJs fields
132− JsExpr <$> BuiltIn.recordUpdate jsRecord jsFields
136− toField (field, value) =
137− do jsValue <- generateJsExpr value
138− return (Var.safe field ==> jsValue)
140− do jsFields <- mapM toField fields
141− jsExpr $ JS.Object jsFields
143− Binop op leftExpr rightExpr ->
217+generateDebug :: N.Name -> ModuleName.Canonical -> R.Region -> Maybe N.Name -> JS.Expr
144− generateBinop op leftExpr rightExpr
218+generateDebug name (ModuleName.Canonical _ home) region unhandledValueName =
219+ if name /= "crash" then
220+ JS.Ref (Name.fromGlobal ModuleName.debug name)
222+ case unhandledValueName of
224+ JS.Call (JS.Ref (Name.fromKernel N.utils "crash")) $
225+ [ JS.String (N.toBuilder home)
226+ , regionToJsExpr region
146− Function args body ->
229+ Just valueName ->
147− generateFunction args body
230+ JS.Call (JS.Ref (Name.fromKernel N.utils "crashCase")) $
231+ [ JS.String (N.toBuilder home)
232+ , regionToJsExpr region
233+ , JS.Ref (Name.fromLocal valueName)
150− generateCall func args
152− TailCall name argNames args ->
237+regionToJsExpr :: R.Region -> JS.Expr
153− let
238+regionToJsExpr (R.Region start end) =
154− reassign argName tempName =
239+ JS.Object
155− JS.ExprStmt $
240+ [ ( Name.fromLocal "start", positionToJsExpr start )
156− JS.Assign (JS.LVar (Var.safe argName)) (ref tempName)
241+ , ( Name.fromLocal "end", positionToJsExpr end )
158− do args' <- mapM generateJsExpr args
159− tempNames <- mapM (\_ -> Var.fresh) args
161− JS.VarDeclStmt (zipWith varDecl tempNames args')
162− : zipWith reassign argNames tempNames
163− ++ [JS.Continue (Just (JS.Id (Var.safe name)))]
166− do stmts <- mapM generateLetDef defs
167− code <- generateCode body
168− jsBlock (stmts ++ toStatementList code)
170− If branches finally ->
245+positionToJsExpr :: R.Position -> JS.Expr
171− generateIf branches finally
246+positionToJsExpr (R.Position line column) =
248+ [ ( Name.fromLocal "line", JS.Int line )
249+ , ( Name.fromLocal "column", JS.Int column )
173− Case exprName decider jumps ->
174− JsBlock <$> generateCase exprName decider jumps
177− do jsElements <- mapM generateJsExpr elements
178− JsExpr <$> BuiltIn.list jsElements
180− Ctor tag members ->
254+-- FUNCTION
183− "$" ==> JS.String tag
186− (Help.toFieldName n) ==> entry
188− do jsMembers <- mapM generateJsExpr members
189− jsExpr $ JS.Object (tagField : zipWith toEntry jsMembers [ 0 :: Int .. ])
191− CtorAccess dataExpr index ->
257+generateFunction :: [N.Name] -> Code -> Code
192− do jsDataExpr <- generateJsExpr dataExpr
258+generateFunction args body =
193− jsExpr $ JS.DotRef jsDataExpr (JS.Id (Help.toFieldName index))
259+ case IntMap.lookup (length args) funcHelpers of
263+ [ JS.Function Nothing (map Name.fromLocal args) $
264+ codeToStatementList body
270+ JsExpr $ JS.Function Nothing [Name.fromLocal arg] $
271+ codeToStatementList code
273+ foldr addArg body args
276+{-# NOINLINE funcHelpers #-}
277+funcHelpers :: IntMap.IntMap JS.Expr
280+ [ (2, JS.Ref (Name.fromLocal "F2"))
281+ , (3, JS.Ref (Name.fromLocal "F3"))
282+ , (4, JS.Ref (Name.fromLocal "F4"))
283+ , (5, JS.Ref (Name.fromLocal "F5"))
284+ , (6, JS.Ref (Name.fromLocal "F6"))
285+ , (7, JS.Ref (Name.fromLocal "F7"))
286+ , (8, JS.Ref (Name.fromLocal "F8"))
287+ , (9, JS.Ref (Name.fromLocal "F9"))
196− JsExpr <$> BuiltIn.effect moduleName
199− JsExpr <$> BuiltIn.effect moduleName
201− OutgoingPort name encoder ->
292+-- CALLS
202− do jsEncoder <- generateJsExpr encoder
203− JsExpr <$> BuiltIn.outgoingPort name jsEncoder
205− IncomingPort name decoder ->
206− do jsDecoder <- generateJsExpr decoder
207− JsExpr <$> BuiltIn.incomingPort name jsDecoder
209− Program kind body ->
295+generateCall :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
210− generateProgram kind body
296+generateCall mode func args =
298+ Opt.VarGlobal global@(Opt.Global (ModuleName.Canonical pkg _) _) | pkg == Pkg.core ->
299+ generateCoreCall mode global args
212− GLShader src ->
301+ _ ->
213− do string <- Literal.literal (L.Str src)
302+ generateNormalCall
214− jsExpr $ JS.Object [(JS.StringProp "src", string)]
303+ (generateJsExpr mode func)
304+ (map (generateJsExpr mode) args)
307+generateNormalCall' :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
308+generateNormalCall' home name args =
309+ generateNormalCall (JS.Ref (Name.fromGlobal home name)) args
312+generateNormalCall :: JS.Expr -> [JS.Expr] -> JS.Expr
313+generateNormalCall func args =
314+ case IntMap.lookup (length args) callHelpers of
316+ JS.Call helper (func:args)
319+ List.foldl' (\f a -> JS.Call f [a]) func args
322+{-# NOINLINE callHelpers #-}
323+callHelpers :: IntMap.IntMap JS.Expr
326+ [ (2, JS.Ref (Name.fromLocal "A2"))
327+ , (3, JS.Ref (Name.fromLocal "A3"))
328+ , (4, JS.Ref (Name.fromLocal "A4"))
329+ , (5, JS.Ref (Name.fromLocal "A5"))
330+ , (6, JS.Ref (Name.fromLocal "A6"))
331+ , (7, JS.Ref (Name.fromLocal "A7"))
332+ , (8, JS.Ref (Name.fromLocal "A8"))
333+ , (9, JS.Ref (Name.fromLocal "A9"))
216− Crash home region maybeBranchProblem ->
217− do maybeOptBranchProblem <- traverse generateJsExpr maybeBranchProblem
218− JsExpr <$> BuiltIn.crash home region maybeOptBranchProblem
341+generateCoreCall :: Mode -> Opt.Global -> [Opt.Expr] -> JS.Expr
342+generateCoreCall mode (Opt.Global home@(ModuleName.Canonical _ moduleName) name) args =
343+ if moduleName == N.basics then
344+ generateBasicsCall mode home name args
225−generateProgram :: Opt.Main -> Opt.Expr -> Generator Code
346+ else if moduleName == N.bitwise then
226−generateProgram kind body =
347+ generateBitwiseCall home name (map (generateJsExpr mode) args)
229− do html <- generateJsExpr body
230− func <- BuiltIn.staticProgram
231− jsExpr (func <| html)
233− Opt.Dynamic decoder ->
349+ else if moduleName == N.tuple then
234− do almostProgram <- generateJsExpr body
350+ generateTupleCall home name (map (generateJsExpr mode) args)
235− flagDecoder <- generateJsExpr decoder
236− jsExpr (almostProgram <| flagDecoder)
353+ generateNormalCall' home name (map (generateJsExpr mode) args)
240−-- DEFINITIONS
356+generateTupleCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
357+generateTupleCall home name args =
361+ "first" -> JS.Access value (Name.fromLocal "a")
362+ "second" -> JS.Access value (Name.fromLocal "b")
363+ _ -> generateNormalCall' home name args
366+ generateNormalCall' home name args
243−generateLetDef :: (Text, Opt.Def) -> Generator JS.Stmt
244−generateLetDef (name, def) =
245− do jsBody <- generateDef name def
246− return $ JS.VarDeclStmt [ Help.varDecl (Var.safe name) jsBody ]
369+generateBitwiseCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
370+generateBitwiseCall home name args =
374+ "complement" -> JS.Prefix JS.PrefixBNot arg
375+ _ -> generateNormalCall' home name args
249−generateDef :: Text -> Opt.Def -> Generator JS.Expr
377+ [left,right] ->
250−generateDef name def =
251− case def of
378+ case name of
252− Opt.TailDef argNames body ->
379+ "and" -> JS.Infix JS.OpBAnd left right
253− generateTailFunction name argNames body
380+ "or" -> JS.Infix JS.OpBOr left right
381+ "xor" -> JS.Infix JS.OpBXor left right
382+ "shiftLeftBy" -> JS.Infix JS.OpLShift right left
383+ "shiftRightBy" -> JS.Infix JS.OpSpRShift right left
384+ "shiftRightZfBy" -> JS.Infix JS.OpZfRShift right left
385+ _ -> generateNormalCall' home name args
255− Opt.Def body ->
387+ _ ->
256− generateJsExpr body
388+ generateNormalCall' home name args
259−generateTailFunction :: Text -> [Text] -> Opt.Expr -> Generator JS.Expr
391+generateBasicsCall :: Mode -> ModuleName.Canonical -> N.Name -> [Opt.Expr] -> JS.Expr
260−generateTailFunction name args body =
392+generateBasicsCall mode home name args =
261− do code <- generateCode body
393+ case args of
262− return $ generateFunctionWithArity args $ JsBlock $ (:[]) $
394+ [elmArg] ->
263− JS.Labelled
395+ let arg = generateJsExpr mode elmArg in
264− (JS.Id (Var.safe name))
396+ case name of
265− (JS.While (JS.Bool True) (toStatement code))
397+ "not" -> JS.Prefix JS.PrefixLNot arg
399+ "truncate" -> JS.Infix JS.OpBOr arg (JS.Int 0)
400+ _ -> generateNormalCall' home name [arg]
402+ [elmLeft, elmRight] ->
404+ "composeL" -> decomposeL mode elmLeft [elmRight]
405+ "composeR" -> decomposeR mode [elmLeft] elmRight
406+ "apL" -> generateJsExpr mode $ apply elmLeft elmRight
407+ "apR" -> generateJsExpr mode $ apply elmRight elmLeft
410+ left = generateJsExpr mode elmLeft
411+ right = generateJsExpr mode elmRight
414+ "add" -> JS.Infix JS.OpAdd left right
415+ "sub" -> JS.Infix JS.OpSub left right
416+ "mul" -> JS.Infix JS.OpMul left right
417+ "fdiv" -> JS.Infix JS.OpDiv left right
418+ "idiv" -> JS.Infix JS.OpBOr (JS.Infix JS.OpDiv left right) (JS.Int 0)
419+ "eq" -> equal left right
420+ "neq" -> notEqual left right
421+ "lt" -> cmp JS.OpLT JS.OpLT 0 left right
422+ "gt" -> cmp JS.OpGT JS.OpGT 0 left right
423+ "le" -> cmp JS.OpLEq JS.OpLT 1 left right
424+ "ge" -> cmp JS.OpGEq JS.OpGT (-1) left right
425+ "or" -> JS.Infix JS.OpLOr left right
426+ "and" -> JS.Infix JS.OpLAnd left right
427+ "xor" -> JS.Infix JS.OpStrictNEq left right
428+ _ -> generateNormalCall' home name [left, right]
431+ generateNormalCall' home name (map (generateJsExpr mode) args)
434+equal :: JS.Expr -> JS.Expr -> JS.Expr
436+ if isLiteral left || isLiteral right then
439+ JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
272−generateFunction :: [Text] -> Opt.Expr -> Generator Code
273−generateFunction args body =
274− do code <- generateCode body
275− jsExpr (generateFunctionWithArity args code)
442+notEqual :: JS.Expr -> JS.Expr -> JS.Expr
443+notEqual left right =
444+ if isLiteral left || isLiteral right then
445+ strictNEq left right
447+ JS.Prefix JS.PrefixLNot $
448+ JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
278−generateFunctionWithArity :: [Text] -> Code -> JS.Expr
279−generateFunctionWithArity rawArgs code =
281− args = map Var.safe rawArgs
284− if 2 <= arity && arity <= 9 then
286− fN = "F" <> Text.pack (show arity)
288− ref fN <| function args (toStatementList code)
291− (lastArg:otherArgs) = reverse args
292− innerBody = function [lastArg] (toStatementList code)
294− foldl (\body arg -> function [arg] [JS.Return (Just body)]) innerBody otherArgs
451+cmp :: JS.InfixOp -> JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> JS.Expr
452+cmp idealOp backupOp backupInt left right =
453+ if isLiteral left || isLiteral right then
454+ JS.Infix idealOp left right
457+ (JS.Call (JS.Ref (Name.fromKernel N.utils "cmp")) [left, right])
298−-- GENERATE CALL
461+isLiteral :: JS.Expr -> Bool
301−generateCall :: Opt.Expr -> [Opt.Expr] -> Generator Code
470+ JS.Int _ ->
302−generateCall func args =
471+ True
303− case (func, args) of
304− (Opt.VarGlobal (V.Global home name), [arg]) ->
306− "complement" | home == bitwise -> genPrefix JS.PrefixBNot arg
307− "not" | home == basics -> genPrefix JS.PrefixLNot arg
308− _ -> generateCallHelp func args
310− (Opt.VarGlobal (V.Global home name), [ left, right ]) | home == bitwise ->
473+ JS.Bool _ ->
311− case name of
474+ True
312− "and" -> genInfix JS.OpBAnd left right
313− "or" -> genInfix JS.OpBOr left right
314− "xor" -> genInfix JS.OpBXor left right
315− "shiftLeftBy" -> genInfix JS.OpLShift right left
316− "shiftRightBy" -> genInfix JS.OpSpRShift right left
317− "shiftRightZfBy" -> genInfix JS.OpZfRShift right left
318− _ -> generateCallHelp func args
321− generateCallHelp func args
477+ False
324−genPrefix :: JS.PrefixOp -> Opt.Expr -> Generator Code
480+decomposeL :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
325−genPrefix op arg =
481+decomposeL mode expr funcs =
326− do jsArg <- generateJsExpr arg
482+ case expr of
327− jsExpr (JS.Prefix op jsArg)
483+ Opt.Call (Opt.VarGlobal (Opt.Global home "composeL")) [left, func]
484+ | home == ModuleName.basics ->
485+ decomposeL mode left (func:funcs)
488+ generateJsExpr mode $
489+ Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
330−genInfix :: JS.InfixOp -> Opt.Expr -> Opt.Expr -> Generator Code
331−genInfix op left right =
332− do jsLeft <- generateJsExpr left
333− jsRight <- generateJsExpr right
334− jsExpr $ JS.Infix op jsLeft jsRight
492+decomposeR :: Mode -> [Opt.Expr] -> Opt.Expr -> JS.Expr
493+decomposeR mode funcs expr =
495+ Opt.Call (Opt.VarGlobal (Opt.Global home "composeR")) [func, right]
496+ | home == ModuleName.basics ->
497+ decomposeR mode (func:funcs) right
337−basics :: ModuleName.Canonical
499+ _ ->
338−basics =
500+ generateJsExpr mode $
339− ModuleName.inCore "Basics"
501+ Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
342−bitwise :: ModuleName.Canonical
504+apply :: Opt.Expr -> Opt.Expr -> Opt.Expr
343−bitwise =
505+apply func value =
344− ModuleName.inCore "Bitwise"
506+ case func of
507+ Opt.Accessor field ->
508+ Opt.Access value field
511+ Opt.Call f (args ++ [value])
347−generateCallHelp :: Opt.Expr -> [Opt.Expr] -> Generator Code
513+ _ ->
348−generateCallHelp func args =
514+ Opt.Call func [value]
351− aN = "A" <> Text.pack (show arity)
353− do jsFunc <- generateJsExpr func
354− jsArgs <- mapM generateJsExpr args
356− if 2 <= arity && arity <= 9 then
357− JS.Call (ref aN) (jsFunc:jsArgs)
359− foldl1 (<|) (jsFunc:jsArgs)
363−-- GENERATE IFS
518+-- SIMPLIFY INFIX OPERATORS
366−generateIf :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Generator Code
521+strictEq :: JS.Expr -> JS.Expr -> JS.Expr
367−generateIf givenBranches givenFinally =
522+strictEq left right =
368− let
523+ case left of
369− (branches, finally) =
524+ JS.Int 0 ->
370− crushIfs givenBranches givenFinally
525+ JS.Prefix JS.PrefixLNot right
372− convertBranch (condition, expr) =
527+ JS.Bool bool ->
373− (,) <$> generateJsExpr condition <*> generateCode expr
528+ if bool then right else JS.Prefix JS.PrefixLNot right
375− ifExpression (condition, branch) final =
530+ _ ->
376− JS.If condition branch final
531+ case right of
533+ JS.Prefix JS.PrefixLNot left
378− ifStatement (condition, branch) final =
535+ JS.Bool bool ->
379− JS.IfStmt condition branch final
536+ if bool then left else JS.Prefix JS.PrefixLNot left
381− do jsBranches <- mapM convertBranch branches
382− jsFinally <- generateCode finally
384− if any isBlock (jsFinally : map snd jsBranches)
538+ _ ->
385− then
539+ JS.Infix JS.OpStrictEq left right
386− jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
388− jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
391−crushIfs
542+strictNEq :: JS.Expr -> JS.Expr -> JS.Expr
392− :: [(Opt.Expr, Opt.Expr)]
543+strictNEq left right =
393− -> Opt.Expr
544+ case left of
394− -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
545+ JS.Int 0 ->
395−crushIfs branches finally =
546+ right
396− crushIfsHelp [] branches finally
549+ if bool then JS.Prefix JS.PrefixLNot right else right
399−crushIfsHelp
551+ _ ->
400− :: [(Opt.Expr, Opt.Expr)]
552+ case right of
401− -> [(Opt.Expr, Opt.Expr)]
553+ JS.Int 0 ->
402− -> Opt.Expr
554+ left
403− -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
404−crushIfsHelp visitedBranches unvisitedBranches finally =
405− case unvisitedBranches of
408− If subBranches subFinally ->
409− crushIfsHelp visitedBranches subBranches subFinally
411− _ ->
556+ JS.Bool bool ->
412− (reverse visitedBranches, finally)
557+ if bool then JS.Prefix JS.PrefixLNot left else left
414− (Literal (L.Boolean True), branch) : _ ->
559+ _ ->
415− crushIfsHelp visitedBranches [] branch
560+ JS.Infix JS.OpStrictNEq left right
417− visiting : unvisited ->
418− crushIfsHelp (visiting : visitedBranches) unvisited finally
567+-- TODO check if JS minifiers collapse unnecessary temporary variables
569+generateTailCall :: Mode -> N.Name -> [(N.Name, Opt.Expr)] -> [JS.Stmt]
570+generateTailCall mode name args =
572+ toTempVars (argName, arg) =
573+ ( Name.makeTemp argName, Just (generateJsExpr mode arg) )
425−generateCase :: Text -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> Generator [JS.Stmt]
575+ toRealVars (argName, _) =
426−generateCase exprName decider jumps =
576+ JS.ExprStmt $
427− do labelRoot <- Var.fresh
577+ JS.Assign (JS.LVar (Name.fromLocal argName)) (JS.Ref (Name.makeTemp argName))
428− jsDecider <- generateDecider exprName labelRoot decider
578+ in
429− foldM (goto labelRoot) jsDecider jumps
579+ JS.Var (map toTempVars args)
580+ : map toRealVars args
581+ ++ [ JS.Continue (Just (Name.fromLocal name)) ]
433−-- handle any jumps
585+-- DEFINITIONS
436−goto :: Text -> [JS.Stmt] -> (Int, Opt.Expr) -> Generator [JS.Stmt]
588+generateDef :: Mode -> Opt.Def -> JS.Stmt
437−goto labelRoot deciderStmts (target, branch) =
589+generateDef mode def =
591+ Opt.Def name body ->
592+ JS.Var [ (Name.fromLocal name, Just (generateJsExpr mode body)) ]
594+ Opt.TailDef name argNames body ->
439− labeledDeciderStmt =
596+ function =
597+ JS.Function Nothing (map Name.fromLocal argNames)
440− JS.Labelled
598+ [ JS.Labelled (Name.fromLocal name) $
441− (toLabel labelRoot target)
599+ JS.While (JS.Bool True) $
442− (JS.DoWhile (JS.Block deciderStmts) (JS.Bool False))
444− do code <- generateCode branch
600+ codeToStatement $ generateCode mode body
445− return (labeledDeciderStmt : toStatementList code)
601+ ]
603+ JS.Var [ (Name.fromLocal name, Just function) ]
448−toLabel :: Text -> Int -> JS.Id
449−toLabel root target =
450− JS.Id (root <> "_" <> Text.pack (show target))
454−-- turn deciders into ifs and switches
610+generateIf :: Mode -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Code
611+generateIf mode givenBranches givenFinal =
614+ crushIfs givenBranches givenFinal
616+ convertBranch (condition, expr) =
617+ ( generateJsExpr mode condition
618+ , generateCode mode expr
457−generateDecider :: Text -> Text -> Opt.Decider Opt.Choice -> Generator [JS.Stmt]
621+ branchExprs = map convertBranch branches
458−generateDecider exprName labelRoot decisionTree =
622+ finalCode = generateCode mode final
459− case decisionTree of
623+ in
460− Opt.Leaf (Opt.Inline branch) ->
624+ if isBlock finalCode || any (isBlock . snd) branchExprs then
461− toStatementList <$> generateCode branch
625+ JsBlock [ foldr addStmtIf (codeToStatement finalCode) branchExprs ]
627+ JsExpr $ foldr addExprIf (codeToJsExpr finalCode) branchExprs
463− Opt.Leaf (Opt.Jump target) ->
464− return [ JS.Break (Just (toLabel labelRoot target)) ]
466− Opt.Chain testChain success failure ->
630+addExprIf :: (JS.Expr, Code) -> JS.Expr -> JS.Expr
467− let
631+addExprIf (condition, branch) final =
468− makeTest (path, test) =
632+ JS.If condition (codeToJsExpr branch) final
469− do testExpr <- pathToTestableExpr exprName path test
470− JS.Infix JS.OpStrictEq testExpr <$> testToExpr test
472− do testExprs <- mapM makeTest testChain
473− let cond = List.foldl1' (JS.Infix JS.OpLAnd) testExprs
474− thenBranch <- generateDecider exprName labelRoot success
475− elseBranch <- generateDecider exprName labelRoot failure
476− return [ JS.IfStmt cond (JS.Block thenBranch) (JS.Block elseBranch) ]
478− Opt.FanOut path edges fallback ->
479− do testExpr <- pathToTestableExpr exprName path (fst (head edges))
480− caseClauses <- mapM (edgeToCase exprName labelRoot) edges
481− caseDefault <- JS.Default <$> generateDecider exprName labelRoot fallback
482− return [ JS.Switch testExpr (caseClauses ++ [caseDefault]) ]
635+addStmtIf :: (JS.Expr, Code) -> JS.Stmt -> JS.Stmt
636+addStmtIf (condition, branch) final =
637+ JS.IfStmt condition (codeToStatement branch) final
485−edgeToCase :: Text -> Text -> (DT.Test, Opt.Decider Opt.Choice) -> Generator JS.Case
486−edgeToCase exprName labelRoot (test, subTree) =
489− <*> generateDecider exprName labelRoot subTree
640+isBlock :: Code -> Bool
492−testToExpr :: DT.Test -> Generator JS.Expr
495− DT.Constructor (V.Canonical _ tag) ->
496− return $ JS.String tag
498− DT.Literal (L.Chr char) ->
647+crushIfs :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
499− return $ JS.String char
648+crushIfs branches final =
649+ crushIfsHelp [] branches final
653+ :: [(Opt.Expr, Opt.Expr)]
654+ -> [(Opt.Expr, Opt.Expr)]
656+ -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
657+crushIfsHelp visitedBranches unvisitedBranches final =
658+ case unvisitedBranches of
661+ Opt.If subBranches subFinal ->
662+ crushIfsHelp visitedBranches subBranches subFinal
665+ (reverse visitedBranches, final)
506−-- work with paths
667+ visiting : unvisited ->
668+ crushIfsHelp (visiting : visitedBranches) unvisited final
509−pathToTestableExpr :: Text -> DT.Path -> DT.Test -> Generator JS.Expr
510−pathToTestableExpr root path exampleTest =
511− do accessExpr <- generateJsExpr (pathToExpr root path)
514− return $ JS.DotRef accessExpr (JS.Id "$")
516− DT.Literal (L.Chr _) ->
672+-- CASE EXPRESSIONS
517− return $ JS.Call (JS.DotRef accessExpr (JS.Id "valueOf")) []
675+generateCase :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> [JS.Stmt]
676+generateCase mode label root decider jumps =
677+ foldr (goto mode label) (generateDecider mode label root decider) jumps
523−pathToExpr :: Text -> DT.Path -> Opt.Expr
524−pathToExpr root fullPath =
525− go (Opt.VarLocal root) fullPath
529− DT.Position index subpath ->
530− go (Opt.CtorAccess expr index) subpath
532− DT.Field field subpath ->
680+goto :: Mode -> N.Name -> (Int, Opt.Expr) -> [JS.Stmt] -> [JS.Stmt]
533− go (Opt.Access expr field) subpath
681+goto mode label (index, branch) stmts =
683+ labeledDeciderStmt =
685+ (Name.makeLabel label index)
686+ (JS.While (JS.Bool True) (JS.Block stmts))
688+ labeledDeciderStmt : codeToStatementList (generateCode mode branch)
538− DT.Alias ->
691+generateDecider :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [JS.Stmt]
539− expr
692+generateDecider mode label root decisionTree =
693+ case decisionTree of
694+ Opt.Leaf (Opt.Inline branch) ->
695+ codeToStatementList (generateCode mode branch)
697+ Opt.Leaf (Opt.Jump index) ->
698+ [ JS.Break (Just (Name.makeLabel label index)) ]
700+ Opt.Chain testChain success failure ->
702+ makeTest (path, test) =
704+ (pathToTestableExpr mode root path test)
705+ (testToExpr mode test)
708+ (List.foldl1' (JS.Infix JS.OpLAnd) (map makeTest testChain))
709+ (JS.Block $ generateDecider mode label root success)
710+ (JS.Block $ generateDecider mode label root failure)
543−-- BINARY OPERATORS
713+ Opt.FanOut path edges fallback ->
715+ (pathToTestableExpr mode root path (fst (head edges)))
717+ (addCase mode label root)
718+ [ JS.Default (generateDecider mode label root fallback) ]
546−generateBinop :: V.Global -> Opt.Expr -> Opt.Expr -> Generator Code
724+addCase :: Mode -> N.Name -> N.Name -> (DT.Test, Opt.Decider Opt.Choice) -> [JS.Case] -> [JS.Case]
547−generateBinop op left right =
725+addCase mode label root (test, subTree) cases =
548− do jsLeft <- generateJsExpr left
726+ let stmts = generateDecider mode label root subTree in
549− jsRight <- generateJsExpr right
727+ JS.Case (testToExpr mode test) stmts : cases
550− JsExpr <$> generateBinopHelp op jsLeft jsRight
730+testToExpr :: Mode -> DT.Test -> JS.Expr
731+testToExpr mode test =
733+ DT.IsCtor _ tag index ->
736+ JS.String (N.toBuilder tag)
554−-- BINARY OPERATOR HELPERS
738+ Name.Prod _ ->
739+ JS.Int (Index.toZeroBased index)
557−generateBinopHelp :: V.Global -> JS.Expr -> JS.Expr -> Generator JS.Expr
744+ DT.IsChr char ->
558−generateBinopHelp var@(V.Global home op) jsLeft jsRight =
745+ JS.String (Text.encodeUtf8Builder char)
560− simpleMake left right =
561− do func <- Var.global var
562− return $ JS.Call (ref "A2") [ func, left, right ]
564− if home == basics then
565− (Map.findWithDefault simpleMake op basicsOps) jsLeft jsRight
567− else if op == "::" && home == list then
747+ DT.IsStr string ->
568− BuiltIn.cons jsLeft jsRight
748+ JS.String (Text.encodeUtf8Builder string)
571− simpleMake jsLeft jsRight
751+pathToTestableExpr :: Mode -> N.Name -> DT.Path -> DT.Test -> JS.Expr
752+pathToTestableExpr mode root path exampleTest =
755+ pathToJsExpr mode (JS.Ref (Name.fromLocal root)) path
759+ JS.Access expr Name.dollar
574−list :: ModuleName.Canonical
761+ DT.IsInt _ ->
576− ModuleName.inCore "List"
579−basicsOps :: Map.Map Text (JS.Expr -> JS.Expr -> Generator JS.Expr)
767+ DT.IsChr _ ->
580−basicsOps =
768+ case mode of
581− Map.fromList (basicsOpsNormal ++ basicsOpsSpecial)
769+ Name.Debug ->
770+ JS.Call (JS.Access expr (Name.fromLocal "valueOf")) []
584−basicsOpsNormal :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
588− ( str, \left right -> return (JS.Infix op left right) )
594− , "&&" =*> JS.OpLAnd
776+pathToJsExpr :: Mode -> JS.Expr -> DT.Path -> JS.Expr
777+pathToJsExpr mode expr path =
779+ DT.Index index subpath ->
780+ pathToJsExpr mode (JS.Access expr (Name.fromInt index)) subpath
599−basicsOpsSpecial :: [(Text, JS.Expr -> JS.Expr -> Generator JS.Expr)]
782+ DT.Field field subpath ->
600−basicsOpsSpecial =
783+ pathToJsExpr mode (JS.Access expr (Name.fromField mode field)) subpath
604− [ "^" =*> \a b -> return $ JS.Call (obj ["Math","pow"]) [a,b]
605− , "==" =*> BuiltIn.eq
606− , "/=" =*> \a b -> JS.Prefix JS.PrefixLNot <$> BuiltIn.eq a b
607− , "<" =*> cmp JS.OpLT 0
608− , ">" =*> cmp JS.OpGT 0
609− , "<=" =*> cmp JS.OpLT 1
610− , ">=" =*> cmp JS.OpGT (-1)
611− , "//" =*> \a b -> return $ JS.Infix JS.OpBOr (JS.Infix JS.OpDiv a b) (JS.Int 0)
615−cmp :: JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> Generator JS.Expr
788+ DT.Alias ->
616−cmp op n a b =
789+ expr
617− do ordering <- BuiltIn.cmp a b
618− return $ JS.Infix op ordering (JS.Int n)