1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression
8import qualified Data.IntMap as IntMap
9import qualified Data.List as List
10import qualified Data.Map as Map
11import qualified Data.Text.Encoding as Text
13import qualified AST.Optimized as Opt
14import qualified AST.Module.Name as ModuleName
15import qualified Data.Index as Index
16import qualified Elm.Name as N
17import qualified Elm.Package as Pkg
18import qualified Generate.JavaScript.Builder as JS
19import qualified Generate.JavaScript.Name as Name
20import qualified Optimize.DecisionTree as DT
21import qualified Reporting.Region as R
35generateJsExpr :: Mode -> Opt.Expr -> JS.Expr
36generateJsExpr mode expression =
37 codeToJsExpr (generateCode mode expression)
40generateCode :: Mode -> Opt.Expr -> Code
41generateCode mode expression =
47 JS.Call toChar [ JS.String (Text.encodeUtf8Builder char) ]
50 JS.String (Text.encodeUtf8Builder char)
53 JsExpr $ JS.String (Text.encodeUtf8Builder string)
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)
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]
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
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 ) ]
157codeToJsExpr :: Code -> JS.Expr
164 JS.Call (JS.Function Nothing [] stmts) []
167codeToStatementList :: Code -> [JS.Stmt]
168codeToStatementList code =
171 [ JS.Return (Just expr) ]
177codeToStatement :: Code -> JS.Stmt
178codeToStatement code =
181 JS.Return (Just expr)
194{-# NOINLINE toChar #-}
197 JS.Ref (Name.fromKernel N.utils "chr")
204generateRecord :: Mode -> Map.Map N.Name Opt.Expr -> JS.Expr
205generateRecord mode fields =
207 toPair (field, value) =
208 (Name.fromField mode field, generateJsExpr mode value)
210 JS.Object (map toPair (Map.toList fields))
217generateDebug :: N.Name -> ModuleName.Canonical -> R.Region -> Maybe N.Name -> JS.Expr
218generateDebug 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
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)
237regionToJsExpr :: R.Region -> JS.Expr
238regionToJsExpr (R.Region start end) =
240 [ ( Name.fromLocal "start", positionToJsExpr start )
241 , ( Name.fromLocal "end", positionToJsExpr end )
245positionToJsExpr :: R.Position -> JS.Expr
246positionToJsExpr (R.Position line column) =
248 [ ( Name.fromLocal "line", JS.Int line )
249 , ( Name.fromLocal "column", JS.Int column )
257generateFunction :: [N.Name] -> Code -> Code
258generateFunction args body =
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 #-}
277funcHelpers :: 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"))
295generateCall :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
296generateCall mode func args =
298 Opt.VarGlobal global@(Opt.Global (ModuleName.Canonical pkg _) _) | pkg == Pkg.core ->
299 generateCoreCall mode global args
303 (generateJsExpr mode func)
304 (map (generateJsExpr mode) args)
307generateNormalCall' :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
308generateNormalCall' home name args =
309 generateNormalCall (JS.Ref (Name.fromGlobal home name)) args
312generateNormalCall :: JS.Expr -> [JS.Expr] -> JS.Expr
313generateNormalCall 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 #-}
323callHelpers :: 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"))
341generateCoreCall :: Mode -> Opt.Global -> [Opt.Expr] -> JS.Expr
342generateCoreCall mode (Opt.Global home@(ModuleName.Canonical _ moduleName) name) args =
343 if moduleName == N.basics then
344 generateBasicsCall mode home name args
346 else if moduleName == N.bitwise then
347 generateBitwiseCall home name (map (generateJsExpr mode) args)
349 else if moduleName == N.tuple then
350 generateTupleCall home name (map (generateJsExpr mode) args)
353 generateNormalCall' home name (map (generateJsExpr mode) args)
356generateTupleCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
357generateTupleCall 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
369generateBitwiseCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
370generateBitwiseCall home name args =
374 "complement" -> JS.Prefix JS.PrefixBNot arg
375 _ -> generateNormalCall' home name args
379 "and" -> JS.Infix JS.OpBAnd left right
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
388 generateNormalCall' home name args
391generateBasicsCall :: Mode -> ModuleName.Canonical -> N.Name -> [Opt.Expr] -> JS.Expr
392generateBasicsCall mode home name args =
395 let arg = generateJsExpr mode elmArg in
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 "append" -> append mode elmLeft elmRight
407 "apL" -> generateJsExpr mode $ apply elmLeft elmRight
408 "apR" -> generateJsExpr mode $ apply elmRight elmLeft
411 left = generateJsExpr mode elmLeft
412 right = generateJsExpr mode elmRight
415 "add" -> JS.Infix JS.OpAdd left right
416 "sub" -> JS.Infix JS.OpSub left right
417 "mul" -> JS.Infix JS.OpMul left right
418 "fdiv" -> JS.Infix JS.OpDiv left right
419 "idiv" -> JS.Infix JS.OpBOr (JS.Infix JS.OpDiv left right) (JS.Int 0)
420 "eq" -> equal left right
421 "neq" -> notEqual left right
422 "lt" -> cmp JS.OpLT JS.OpLT 0 left right
423 "gt" -> cmp JS.OpGT JS.OpGT 0 left right
424 "le" -> cmp JS.OpLEq JS.OpLT 1 left right
425 "ge" -> cmp JS.OpGEq JS.OpGT (-1) left right
426 "or" -> JS.Infix JS.OpLOr left right
427 "and" -> JS.Infix JS.OpLAnd left right
428 "xor" -> JS.Infix JS.OpStrictNEq left right
429 _ -> generateNormalCall' home name [left, right]
432 generateNormalCall' home name (map (generateJsExpr mode) args)
435equal :: JS.Expr -> JS.Expr -> JS.Expr
437 if isLiteral left || isLiteral right then
440 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
443notEqual :: JS.Expr -> JS.Expr -> JS.Expr
445 if isLiteral left || isLiteral right then
448 JS.Prefix JS.PrefixLNot $
449 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
452cmp :: JS.InfixOp -> JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> JS.Expr
453cmp idealOp backupOp backupInt left right =
454 if isLiteral left || isLiteral right then
455 JS.Infix idealOp left right
458 (JS.Call (JS.Ref (Name.fromKernel N.utils "cmp")) [left, right])
462isLiteral :: JS.Expr -> Bool
481decomposeL :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
482decomposeL mode expr funcs =
484 Opt.Call (Opt.VarGlobal (Opt.Global home "composeL")) [left, func]
485 | home == ModuleName.basics ->
486 decomposeL mode left (func:funcs)
489 generateJsExpr mode $
490 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
493decomposeR :: Mode -> [Opt.Expr] -> Opt.Expr -> JS.Expr
494decomposeR mode funcs expr =
496 Opt.Call (Opt.VarGlobal (Opt.Global home "composeR")) [func, right]
497 | home == ModuleName.basics ->
498 decomposeR mode (func:funcs) right
501 generateJsExpr mode $
502 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
505apply :: Opt.Expr -> Opt.Expr -> Opt.Expr
508 Opt.Accessor field ->
509 Opt.Access value field
512 Opt.Call f (args ++ [value])
515 Opt.Call func [value]
518append :: Mode -> Opt.Expr -> Opt.Expr -> JS.Expr
519append mode left right =
520 let seqs = generateJsExpr mode left : toSeqs mode right in
521 if any isStringLiteral seqs then
522 foldr1 (JS.Infix JS.OpAdd) seqs
527jsAppend :: JS.Expr -> JS.Expr -> JS.Expr
529 JS.Call (JS.Ref (Name.fromKernel N.utils "ap")) [a, b]
532toSeqs :: Mode -> Opt.Expr -> [JS.Expr]
535 Opt.Call (Opt.VarGlobal (Opt.Global home "append")) [left, right]
536 | home == ModuleName.basics ->
537 generateJsExpr mode left : toSeqs mode right
540 [generateJsExpr mode expr]
543isStringLiteral :: JS.Expr -> Bool
544isStringLiteral expr =
554-- SIMPLIFY INFIX OPERATORS
557strictEq :: JS.Expr -> JS.Expr -> JS.Expr
561 JS.Prefix JS.PrefixLNot right
564 if bool then right else JS.Prefix JS.PrefixLNot right
569 JS.Prefix JS.PrefixLNot left
572 if bool then left else JS.Prefix JS.PrefixLNot left
575 JS.Infix JS.OpStrictEq left right
578strictNEq :: JS.Expr -> JS.Expr -> JS.Expr
579strictNEq left right =
585 if bool then JS.Prefix JS.PrefixLNot right else right
593 if bool then JS.Prefix JS.PrefixLNot left else left
596 JS.Infix JS.OpStrictNEq left right
603-- TODO check if JS minifiers collapse unnecessary temporary variables
605generateTailCall :: Mode -> N.Name -> [(N.Name, Opt.Expr)] -> [JS.Stmt]
606generateTailCall mode name args =
608 toTempVars (argName, arg) =
609 ( Name.makeTemp argName, Just (generateJsExpr mode arg) )
611 toRealVars (argName, _) =
613 JS.Assign (JS.LVar (Name.fromLocal argName)) (JS.Ref (Name.makeTemp argName))
615 JS.Var (map toTempVars args)
616 : map toRealVars args
617 ++ [ JS.Continue (Just (Name.fromLocal name)) ]
624generateDef :: Mode -> Opt.Def -> JS.Stmt
625generateDef mode def =
628 JS.Var [ (Name.fromLocal name, Just (generateJsExpr mode body)) ]
630 Opt.TailDef name argNames body ->
633 JS.Function Nothing (map Name.fromLocal argNames)
634 [ JS.Labelled (Name.fromLocal name) $
635 JS.While (JS.Bool True) $
636 codeToStatement $ generateCode mode body
639 JS.Var [ (Name.fromLocal name, Just function) ]
646generateIf :: Mode -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Code
647generateIf mode givenBranches givenFinal =
650 crushIfs givenBranches givenFinal
652 convertBranch (condition, expr) =
653 ( generateJsExpr mode condition
654 , generateCode mode expr
657 branchExprs = map convertBranch branches
658 finalCode = generateCode mode final
660 if isBlock finalCode || any (isBlock . snd) branchExprs then
661 JsBlock [ foldr addStmtIf (codeToStatement finalCode) branchExprs ]
663 JsExpr $ foldr addExprIf (codeToJsExpr finalCode) branchExprs
666addExprIf :: (JS.Expr, Code) -> JS.Expr -> JS.Expr
667addExprIf (condition, branch) final =
668 JS.If condition (codeToJsExpr branch) final
671addStmtIf :: (JS.Expr, Code) -> JS.Stmt -> JS.Stmt
672addStmtIf (condition, branch) final =
673 JS.IfStmt condition (codeToStatement branch) final
676isBlock :: Code -> Bool
683crushIfs :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
684crushIfs branches final =
685 crushIfsHelp [] branches final
689 :: [(Opt.Expr, Opt.Expr)]
690 -> [(Opt.Expr, Opt.Expr)]
692 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
693crushIfsHelp visitedBranches unvisitedBranches final =
694 case unvisitedBranches of
697 Opt.If subBranches subFinal ->
698 crushIfsHelp visitedBranches subBranches subFinal
701 (reverse visitedBranches, final)
703 visiting : unvisited ->
704 crushIfsHelp (visiting : visitedBranches) unvisited final
711generateCase :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> [JS.Stmt]
712generateCase mode label root decider jumps =
713 foldr (goto mode label) (generateDecider mode label root decider) jumps
716goto :: Mode -> N.Name -> (Int, Opt.Expr) -> [JS.Stmt] -> [JS.Stmt]
717goto mode label (index, branch) stmts =
721 (Name.makeLabel label index)
722 (JS.While (JS.Bool True) (JS.Block stmts))
724 labeledDeciderStmt : codeToStatementList (generateCode mode branch)
727generateDecider :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [JS.Stmt]
728generateDecider mode label root decisionTree =
730 Opt.Leaf (Opt.Inline branch) ->
731 codeToStatementList (generateCode mode branch)
733 Opt.Leaf (Opt.Jump index) ->
734 [ JS.Break (Just (Name.makeLabel label index)) ]
736 Opt.Chain testChain success failure ->
738 makeTest (path, test) =
740 (pathToTestableExpr mode root path test)
741 (testToExpr mode test)
744 (List.foldl1' (JS.Infix JS.OpLAnd) (map makeTest testChain))
745 (JS.Block $ generateDecider mode label root success)
746 (JS.Block $ generateDecider mode label root failure)
749 Opt.FanOut path edges fallback ->
751 (pathToTestableExpr mode root path (fst (head edges)))
753 (addCase mode label root)
754 [ JS.Default (generateDecider mode label root fallback) ]
760addCase :: Mode -> N.Name -> N.Name -> (DT.Test, Opt.Decider Opt.Choice) -> [JS.Case] -> [JS.Case]
761addCase mode label root (test, subTree) cases =
762 let stmts = generateDecider mode label root subTree in
763 JS.Case (testToExpr mode test) stmts : cases
766testToExpr :: Mode -> DT.Test -> JS.Expr
767testToExpr mode test =
769 DT.IsCtor _ tag index ->
772 JS.String (N.toBuilder tag)
775 JS.Int (Index.toZeroBased index)
781 JS.String (Text.encodeUtf8Builder char)
784 JS.String (Text.encodeUtf8Builder string)
787pathToTestableExpr :: Mode -> N.Name -> DT.Path -> DT.Test -> JS.Expr
788pathToTestableExpr mode root path exampleTest =
791 pathToJsExpr mode (JS.Ref (Name.fromLocal root)) path
795 JS.Access expr Name.dollar
806 JS.Call (JS.Access expr (Name.fromLocal "valueOf")) []
812pathToJsExpr :: Mode -> JS.Expr -> DT.Path -> JS.Expr
813pathToJsExpr mode expr path =
815 DT.Index index subpath ->
816 pathToJsExpr mode (JS.Access expr (Name.fromInt index)) subpath
818 DT.Field field subpath ->
819 pathToJsExpr mode (JS.Access expr (Name.fromField mode field)) subpath
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list