/compiler/src/Generate/JavaScript/Builder.hs

https://github.com/elm-lang/elm-compiler · Haskell · 530 lines · 362 code · 136 blank · 32 comment · 25 complexity · f8d41646058c6d2d2756f175264c2c0f MD5 · raw file

  1. {-# OPTIONS_GHC -Wall #-}
  2. {-# LANGUAGE OverloadedStrings #-}
  3. module Generate.JavaScript.Builder
  4. ( stmtToBuilder
  5. , exprToBuilder
  6. , Expr(..), LValue(..)
  7. , Stmt(..), Case(..)
  8. , InfixOp(..), PrefixOp(..)
  9. )
  10. where
  11. -- Based on the language-ecmascript package.
  12. -- https://hackage.haskell.org/package/language-ecmascript
  13. -- They did the hard work of reading the spec to figure out
  14. -- how all the types should fit together.
  15. import Prelude hiding (lines)
  16. import qualified Data.List as List
  17. import qualified Data.ByteString as BS
  18. import Data.ByteString.Builder as B
  19. import Data.Monoid ((<>))
  20. import qualified Generate.JavaScript.Name as Name
  21. import Generate.JavaScript.Name (Name)
  22. import qualified Json.Encode as Json
  23. -- EXPRESSIONS
  24. -- NOTE: I tried making this create a B.Builder directly.
  25. --
  26. -- The hope was that it'd allocate less and speed things up, but it seemed
  27. -- to be neutral for perf.
  28. --
  29. -- The downside is that Generate.JavaScript.Expression inspects the
  30. -- structure of Expr and Stmt on some occassions to try to strip out
  31. -- unnecessary closures. I think these closures are already avoided
  32. -- by other logic in code gen these days, but I am not 100% certain.
  33. --
  34. -- For this to be worth it, I think it would be necessary to avoid
  35. -- returning tuples when generating expressions.
  36. --
  37. data Expr
  38. = String Builder
  39. | Float Builder
  40. | Int Int
  41. | Bool Bool
  42. | Null
  43. | Json Json.Value
  44. | Array [Expr]
  45. | Object [(Name, Expr)]
  46. | Ref Name
  47. | Access Expr Name -- foo.bar
  48. | Index Expr Expr -- foo[bar]
  49. | Prefix PrefixOp Expr
  50. | Infix InfixOp Expr Expr
  51. | If Expr Expr Expr
  52. | Assign LValue Expr
  53. | Call Expr [Expr]
  54. | Function (Maybe Name) [Name] [Stmt]
  55. data LValue
  56. = LRef Name
  57. | LDot Expr Name
  58. | LBracket Expr Expr
  59. -- STATEMENTS
  60. data Stmt
  61. = Block [Stmt]
  62. | EmptyStmt
  63. | ExprStmt Expr
  64. | IfStmt Expr Stmt Stmt
  65. | Switch Expr [Case]
  66. | While Expr Stmt
  67. | Break (Maybe Name)
  68. | Continue (Maybe Name)
  69. | Labelled Name Stmt
  70. | Try Stmt Name Stmt
  71. | Throw Expr
  72. | Return Expr
  73. | Var Name Expr
  74. | Vars [(Name, Expr)]
  75. | FunctionStmt Name [Name] [Stmt]
  76. data Case
  77. = Case Expr [Stmt]
  78. | Default [Stmt]
  79. -- OPERATORS
  80. data InfixOp
  81. = OpAdd -- +
  82. | OpSub -- -
  83. | OpMul -- *
  84. | OpDiv -- /
  85. | OpMod -- %
  86. | OpEq -- ===
  87. | OpNe -- !==
  88. | OpLt -- <
  89. | OpLe -- <=
  90. | OpGt -- >
  91. | OpGe -- >=
  92. | OpAnd -- &&
  93. | OpOr -- ||
  94. | OpBitwiseAnd -- &
  95. | OpBitwiseXor -- ^
  96. | OpBitwiseOr -- |
  97. | OpLShift -- <<
  98. | OpSpRShift -- >>
  99. | OpZfRShift -- >>>
  100. data PrefixOp
  101. = PrefixNot -- !
  102. | PrefixNegate -- -
  103. | PrefixComplement -- ~
  104. -- ENCODE
  105. stmtToBuilder :: Stmt -> Builder
  106. stmtToBuilder stmts =
  107. fromStmt levelZero stmts
  108. exprToBuilder :: Expr -> Builder
  109. exprToBuilder expr =
  110. snd $ fromExpr levelZero Whatever expr
  111. -- INDENT LEVEL
  112. data Level =
  113. Level Builder Level
  114. levelZero :: Level
  115. levelZero =
  116. Level mempty (makeLevel 1 (BS.replicate 16 0x09 {-\t-}))
  117. makeLevel :: Int -> BS.ByteString -> Level
  118. makeLevel level oldTabs =
  119. let
  120. tabs =
  121. if level <= BS.length oldTabs
  122. then oldTabs
  123. else BS.replicate (BS.length oldTabs * 2) 0x09 {-\t-}
  124. in
  125. Level (B.byteString (BS.take level tabs)) (makeLevel (level + 1) tabs)
  126. -- HELPERS
  127. commaSep :: [Builder] -> Builder
  128. commaSep builders =
  129. mconcat (List.intersperse ", " builders)
  130. commaNewlineSep :: Level -> [Builder] -> Builder
  131. commaNewlineSep (Level _ (Level deeperIndent _)) builders =
  132. mconcat (List.intersperse (",\n" <> deeperIndent) builders)
  133. -- STATEMENTS
  134. fromStmtBlock :: Level -> [Stmt] -> Builder
  135. fromStmtBlock level stmts =
  136. mconcat (map (fromStmt level) stmts)
  137. fromStmt :: Level -> Stmt -> Builder
  138. fromStmt level@(Level indent nextLevel) statement =
  139. case statement of
  140. Block stmts ->
  141. fromStmtBlock level stmts
  142. EmptyStmt ->
  143. mempty
  144. ExprStmt expr ->
  145. indent <> snd (fromExpr level Whatever expr) <> ";\n"
  146. IfStmt condition thenStmt elseStmt ->
  147. mconcat
  148. [ indent, "if (", snd (fromExpr level Whatever condition), ") {\n"
  149. , fromStmt nextLevel thenStmt
  150. , indent, "} else {\n"
  151. , fromStmt nextLevel elseStmt
  152. , indent, "}\n"
  153. ]
  154. Switch expr clauses ->
  155. mconcat
  156. [ indent, "switch (", snd (fromExpr level Whatever expr), ") {\n"
  157. , mconcat (map (fromClause nextLevel) clauses)
  158. , indent, "}\n"
  159. ]
  160. While expr stmt ->
  161. mconcat
  162. [ indent, "while (", snd (fromExpr level Whatever expr), ") {\n"
  163. , fromStmt nextLevel stmt
  164. , indent, "}\n"
  165. ]
  166. Break Nothing ->
  167. indent <> "break;\n"
  168. Break (Just label) ->
  169. indent <> "break " <> Name.toBuilder label <> ";\n"
  170. Continue Nothing ->
  171. indent <> "continue;\n"
  172. Continue (Just label) ->
  173. indent <> "continue " <> Name.toBuilder label <> ";\n"
  174. Labelled label stmt ->
  175. mconcat
  176. [ indent, Name.toBuilder label, ":\n"
  177. , fromStmt level stmt
  178. ]
  179. Try tryStmt errorName catchStmt ->
  180. mconcat
  181. [ indent, "try {\n"
  182. , fromStmt nextLevel tryStmt
  183. , indent, "} catch (", Name.toBuilder errorName, ") {\n"
  184. , fromStmt nextLevel catchStmt
  185. , indent, "}\n"
  186. ]
  187. Throw expr ->
  188. indent <> "throw " <> snd (fromExpr level Whatever expr) <> ";"
  189. Return expr ->
  190. indent <> "return " <> snd (fromExpr level Whatever expr) <> ";\n"
  191. Var name expr ->
  192. indent <> "var " <> Name.toBuilder name <> " = " <> snd (fromExpr level Whatever expr) <> ";\n"
  193. Vars [] ->
  194. mempty
  195. Vars vars ->
  196. indent <> "var " <> commaNewlineSep level (map (varToBuilder level) vars) <> ";\n"
  197. FunctionStmt name args stmts ->
  198. indent <> "function " <> Name.toBuilder name <> "(" <> commaSep (map Name.toBuilder args) <> ") {\n"
  199. <>
  200. fromStmtBlock nextLevel stmts
  201. <>
  202. indent <> "}\n"
  203. -- SWITCH CLAUSES
  204. fromClause :: Level -> Case -> Builder
  205. fromClause level@(Level indent nextLevel) clause =
  206. case clause of
  207. Case expr stmts ->
  208. indent <> "case " <> snd (fromExpr level Whatever expr) <> ":\n"
  209. <> fromStmtBlock nextLevel stmts
  210. Default stmts ->
  211. indent <> "default:\n"
  212. <> fromStmtBlock nextLevel stmts
  213. -- VAR DECLS
  214. varToBuilder :: Level -> (Name, Expr) -> Builder
  215. varToBuilder level (name, expr) =
  216. Name.toBuilder name <> " = " <> snd (fromExpr level Whatever expr)
  217. -- EXPRESSIONS
  218. data Lines = One | Many deriving (Eq)
  219. merge :: Lines -> Lines -> Lines
  220. merge a b =
  221. if a == Many || b == Many then Many else One
  222. linesMap :: (a -> (Lines, b)) -> [a] -> (Bool, [b])
  223. linesMap func xs =
  224. let
  225. pairs = map func xs
  226. in
  227. ( any ((==) Many . fst) pairs
  228. , map snd pairs
  229. )
  230. data Grouping = Atomic | Whatever
  231. parensFor :: Grouping -> Builder -> Builder
  232. parensFor grouping builder =
  233. case grouping of
  234. Atomic ->
  235. "(" <> builder <> ")"
  236. Whatever ->
  237. builder
  238. fromExpr :: Level -> Grouping -> Expr -> (Lines, Builder)
  239. fromExpr level@(Level indent nextLevel@(Level deeperIndent _)) grouping expression =
  240. case expression of
  241. String string ->
  242. ( One, "'" <> string <> "'" )
  243. Float float ->
  244. ( One, float )
  245. Int n ->
  246. ( One, B.intDec n )
  247. Bool bool ->
  248. ( One, if bool then "true" else "false" )
  249. Null ->
  250. ( One, "null" )
  251. Json json ->
  252. ( One, Json.encodeUgly json )
  253. Array exprs ->
  254. (,) Many $
  255. let
  256. (anyMany, builders) = linesMap (fromExpr level Whatever) exprs
  257. in
  258. if anyMany then
  259. "[\n"
  260. <> deeperIndent
  261. <> commaNewlineSep level builders
  262. <> "\n" <> indent <> "]"
  263. else
  264. "[" <> commaSep builders <> "]"
  265. Object fields ->
  266. (,) Many $
  267. let
  268. (anyMany, builders) = linesMap (fromField nextLevel) fields
  269. in
  270. if anyMany then
  271. "{\n"
  272. <> deeperIndent
  273. <> commaNewlineSep level builders
  274. <> "\n" <> indent <> "}"
  275. else
  276. "{" <> commaSep builders <> "}"
  277. Ref name ->
  278. ( One, Name.toBuilder name )
  279. Access expr field ->
  280. makeDot level expr field
  281. Index expr bracketedExpr ->
  282. makeBracketed level expr bracketedExpr
  283. Prefix op expr ->
  284. let
  285. (lines, builder) = fromExpr level Atomic expr
  286. in
  287. ( lines
  288. , parensFor grouping (fromPrefix op <> builder)
  289. )
  290. Infix op leftExpr rightExpr ->
  291. let
  292. (leftLines , left ) = fromExpr level Atomic leftExpr
  293. (rightLines, right) = fromExpr level Atomic rightExpr
  294. in
  295. ( merge leftLines rightLines
  296. , parensFor grouping (left <> fromInfix op <> right)
  297. )
  298. If condExpr thenExpr elseExpr ->
  299. let
  300. condB = snd (fromExpr level Atomic condExpr)
  301. thenB = snd (fromExpr level Atomic thenExpr)
  302. elseB = snd (fromExpr level Atomic elseExpr)
  303. in
  304. ( Many
  305. , parensFor grouping (condB <> " ? " <> thenB <> " : " <> elseB)
  306. )
  307. Assign lValue expr ->
  308. let
  309. (leftLines , left ) = fromLValue level lValue
  310. (rightLines, right) = fromExpr level Whatever expr
  311. in
  312. ( merge leftLines rightLines
  313. , parensFor grouping (left <> " = " <> right)
  314. )
  315. Call function args ->
  316. (,) Many $
  317. let
  318. (_ , funcB) = fromExpr level Atomic function
  319. (anyMany, argsB) = linesMap (fromExpr nextLevel Whatever) args
  320. in
  321. if anyMany then
  322. funcB <> "(\n" <> deeperIndent <> commaNewlineSep level argsB <> ")"
  323. else
  324. funcB <> "(" <> commaSep argsB <> ")"
  325. Function maybeName args stmts ->
  326. (,) Many $
  327. "function " <> maybe mempty Name.toBuilder maybeName <> "(" <> commaSep (map Name.toBuilder args) <> ") {\n"
  328. <>
  329. fromStmtBlock nextLevel stmts
  330. <>
  331. indent <> "}"
  332. -- FIELDS
  333. fromField :: Level -> (Name, Expr) -> (Lines, Builder)
  334. fromField level (field, expr) =
  335. let
  336. (lines, builder) = fromExpr level Whatever expr
  337. in
  338. ( lines
  339. , Name.toBuilder field <> ": " <> builder
  340. )
  341. -- VALUES
  342. fromLValue :: Level -> LValue -> (Lines, Builder)
  343. fromLValue level lValue =
  344. case lValue of
  345. LRef name ->
  346. (One, Name.toBuilder name)
  347. LDot expr field ->
  348. makeDot level expr field
  349. LBracket expr bracketedExpr ->
  350. makeBracketed level expr bracketedExpr
  351. makeDot :: Level -> Expr -> Name -> (Lines, Builder)
  352. makeDot level expr field =
  353. let
  354. (lines, builder) = fromExpr level Atomic expr
  355. in
  356. (lines, builder <> "." <> Name.toBuilder field)
  357. makeBracketed :: Level -> Expr -> Expr -> (Lines, Builder)
  358. makeBracketed level expr bracketedExpr =
  359. let
  360. (lines , builder ) = fromExpr level Atomic expr
  361. (bracketedLines, bracketedBuilder) = fromExpr level Whatever bracketedExpr
  362. in
  363. ( merge lines bracketedLines
  364. , builder <> "[" <> bracketedBuilder <> "]"
  365. )
  366. -- OPERATORS
  367. fromPrefix :: PrefixOp -> Builder
  368. fromPrefix op =
  369. case op of
  370. PrefixNot -> "!"
  371. PrefixNegate -> "-"
  372. PrefixComplement -> "~"
  373. fromInfix :: InfixOp -> Builder
  374. fromInfix op =
  375. case op of
  376. OpAdd -> " + "
  377. OpSub -> " - "
  378. OpMul -> " * "
  379. OpDiv -> " / "
  380. OpMod -> " % "
  381. OpEq -> " === "
  382. OpNe -> " !== "
  383. OpLt -> " < "
  384. OpLe -> " <= "
  385. OpGt -> " > "
  386. OpGe -> " >= "
  387. OpAnd -> " && "
  388. OpOr -> " || "
  389. OpBitwiseAnd -> " & "
  390. OpBitwiseXor -> " ^ "
  391. OpBitwiseOr -> " | "
  392. OpLShift -> " << "
  393. OpSpRShift -> " >> "
  394. OpZfRShift -> " >>> "