1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript.Builder
8 , InfixOp(..), PrefixOp(..)
12-- Based on the language-ecmascript package.
13-- https://hackage.haskell.org/package/language-ecmascript
14-- They did the hard work of reading the spec to figure out
15-- how all the types should fit together.
17import Prelude hiding (lines)
18import qualified Data.List as List
19import qualified Data.ByteString as BS
20import Data.ByteString.Builder as B
21import Data.Monoid ((<>))
22import qualified Generate.JavaScript.Name as Name
23import Generate.JavaScript.Name (Name)
24import qualified Json.Encode as Json
31-- NOTE: I tried making this create a B.Builder directly.
33-- The hope was that it'd allocate less and speed things up, but it seemed
34-- to be neutral for perf.
36-- The downside is that Generate.JavaScript.Expression inspects the
37-- structure of Expr and Stmt on some occassions to try to strip out
38-- unnecessary closures. I think these closures are already avoided
39-- by other logic in code gen these days, but I am not 100% certain.
41-- For this to be worth it, I think it would be necessary to avoid
42-- returning tuples when generating expressions.
52 | Object [(Name, Expr)]
54 | Access Expr Name -- foo.bar
55 | Index Expr Expr -- foo[bar]
56 | Prefix PrefixOp Expr
57 | Infix InfixOp Expr Expr
61 | Function (Maybe Name) [Name] [Stmt]
78 | IfStmt Expr Stmt Stmt
82 | Continue (Maybe Name)
89 | FunctionStmt Name [Name] [Stmt]
126 | PrefixComplement -- ~
133stmtToBuilder :: Stmt -> Builder
135 fromStmt levelZero stmts
138exprToBuilder :: Expr -> Builder
140 snd $ fromExpr levelZero Whatever expr
153 Level mempty (makeLevel 1 (BS.replicate 16 0x09 {-\t-}))
156makeLevel :: Int -> BS.ByteString -> Level
157makeLevel level oldTabs =
160 if level <= BS.length oldTabs
162 else BS.replicate (BS.length oldTabs * 2) 0x09 {-\t-}
164 Level (B.byteString (BS.take level tabs)) (makeLevel (level + 1) tabs)
171commaSep :: [Builder] -> Builder
173 mconcat (List.intersperse ", " builders)
176commaNewlineSep :: Level -> [Builder] -> Builder
177commaNewlineSep (Level _ (Level deeperIndent _)) builders =
178 mconcat (List.intersperse (",\n" <> deeperIndent) builders)
185fromStmtBlock :: Level -> [Stmt] -> Builder
186fromStmtBlock level stmts =
187 mconcat (map (fromStmt level) stmts)
190fromStmt :: Level -> Stmt -> Builder
191fromStmt level@(Level indent nextLevel) statement =
194 fromStmtBlock level stmts
200 indent <> snd (fromExpr level Whatever expr) <> ";\n"
202 IfStmt condition thenStmt elseStmt ->
204 [ indent, "if (", snd (fromExpr level Whatever condition), ") {\n"
205 , fromStmt nextLevel thenStmt
206 , indent, "} else {\n"
207 , fromStmt nextLevel elseStmt
211 Switch expr clauses ->
213 [ indent, "switch (", snd (fromExpr level Whatever expr), ") {\n"
214 , mconcat (map (fromClause nextLevel) clauses)
220 [ indent, "while (", snd (fromExpr level Whatever expr), ") {\n"
221 , fromStmt nextLevel stmt
228 Break (Just label) ->
229 indent <> "break " <> Name.toBuilder label <> ";\n"
232 indent <> "continue;\n"
234 Continue (Just label) ->
235 indent <> "continue " <> Name.toBuilder label <> ";\n"
237 Labelled label stmt ->
239 [ indent, Name.toBuilder label, ":\n"
240 , fromStmt level stmt
243 Try tryStmt errorName catchStmt ->
246 , fromStmt nextLevel tryStmt
247 , indent, "} catch (", Name.toBuilder errorName, ") {\n"
248 , fromStmt nextLevel catchStmt
253 indent <> "throw " <> snd (fromExpr level Whatever expr) <> ";"
256 indent <> "return " <> snd (fromExpr level Whatever expr) <> ";\n"
259 indent <> "var " <> Name.toBuilder name <> " = " <> snd (fromExpr level Whatever expr) <> ";\n"
265 indent <> "var " <> commaNewlineSep level (map (varToBuilder level) vars) <> ";\n"
267 FunctionStmt name args stmts ->
268 indent <> "function " <> Name.toBuilder name <> "(" <> commaSep (map Name.toBuilder args) <> ") {\n"
270 fromStmtBlock nextLevel stmts
279fromClause :: Level -> Case -> Builder
280fromClause level@(Level indent nextLevel) clause =
283 indent <> "case " <> snd (fromExpr level Whatever expr) <> ":\n"
284 <> fromStmtBlock nextLevel stmts
287 indent <> "default:\n"
288 <> fromStmtBlock nextLevel stmts
295varToBuilder :: Level -> (Name, Expr) -> Builder
296varToBuilder level (name, expr) =
297 Name.toBuilder name <> " = " <> snd (fromExpr level Whatever expr)
304data Lines = One | Many deriving (Eq)
307merge :: Lines -> Lines -> Lines
309 if a == Many || b == Many then Many else One
312linesMap :: (a -> (Lines, b)) -> [a] -> (Bool, [b])
317 ( any ((==) Many . fst) pairs
322data Grouping = Atomic | Whatever
325parensFor :: Grouping -> Builder -> Builder
326parensFor grouping builder =
329 "(" <> builder <> ")"
335fromExpr :: Level -> Grouping -> Expr -> (Lines, Builder)
336fromExpr level@(Level indent nextLevel@(Level deeperIndent _)) grouping expression =
339 ( One, "'" <> string <> "'" )
348 ( One, if bool then "true" else "false" )
354 ( One, Json.encodeUgly json )
359 (anyMany, builders) = linesMap (fromExpr level Whatever) exprs
364 <> commaNewlineSep level builders
365 <> "\n" <> indent <> "]"
367 "[" <> commaSep builders <> "]"
372 (anyMany, builders) = linesMap (fromField nextLevel) fields
377 <> commaNewlineSep level builders
378 <> "\n" <> indent <> "}"
380 "{" <> commaSep builders <> "}"
383 ( One, Name.toBuilder name )
386 makeDot level expr field
388 Index expr bracketedExpr ->
389 makeBracketed level expr bracketedExpr
393 (lines, builder) = fromExpr level Atomic expr
396 , parensFor grouping (fromPrefix op <> builder)
399 Infix op leftExpr rightExpr ->
401 (leftLines , left ) = fromExpr level Atomic leftExpr
402 (rightLines, right) = fromExpr level Atomic rightExpr
404 ( merge leftLines rightLines
405 , parensFor grouping (left <> fromInfix op <> right)
408 If condExpr thenExpr elseExpr ->
410 condB = snd (fromExpr level Atomic condExpr)
411 thenB = snd (fromExpr level Atomic thenExpr)
412 elseB = snd (fromExpr level Atomic elseExpr)
415 , parensFor grouping (condB <> " ? " <> thenB <> " : " <> elseB)
418 Assign lValue expr ->
420 (leftLines , left ) = fromLValue level lValue
421 (rightLines, right) = fromExpr level Whatever expr
423 ( merge leftLines rightLines
424 , parensFor grouping (left <> " = " <> right)
427 Call function args ->
430 (_ , funcB) = fromExpr level Atomic function
431 (anyMany, argsB) = linesMap (fromExpr nextLevel Whatever) args
434 funcB <> "(\n" <> deeperIndent <> commaNewlineSep level argsB <> ")"
436 funcB <> "(" <> commaSep argsB <> ")"
438 Function maybeName args stmts ->
440 "function " <> maybe mempty Name.toBuilder maybeName <> "(" <> commaSep (map Name.toBuilder args) <> ") {\n"
442 fromStmtBlock nextLevel stmts
451fromField :: Level -> (Name, Expr) -> (Lines, Builder)
452fromField level (field, expr) =
454 (lines, builder) = fromExpr level Whatever expr
457 , Name.toBuilder field <> ": " <> builder
465fromLValue :: Level -> LValue -> (Lines, Builder)
466fromLValue level lValue =
469 (One, Name.toBuilder name)
472 makeDot level expr field
474 LBracket expr bracketedExpr ->
475 makeBracketed level expr bracketedExpr
478makeDot :: Level -> Expr -> Name -> (Lines, Builder)
479makeDot level expr field =
481 (lines, builder) = fromExpr level Atomic expr
483 (lines, builder <> "." <> Name.toBuilder field)
486makeBracketed :: Level -> Expr -> Expr -> (Lines, Builder)
487makeBracketed level expr bracketedExpr =
489 (lines , builder ) = fromExpr level Atomic expr
490 (bracketedLines, bracketedBuilder) = fromExpr level Whatever bracketedExpr
492 ( merge lines bracketedLines
493 , builder <> "[" <> bracketedBuilder <> "]"
501fromPrefix :: PrefixOp -> Builder
506 PrefixComplement -> "~"
509fromInfix :: InfixOp -> Builder
525 OpBitwiseAnd -> " & "
526 OpBitwiseXor -> " ^ "
530 OpZfRShift -> " >>> "
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