1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Generate.JavaScript.Builder
5 , Expr(..), Id(..), Prop(..), LValue(..)
6 , Stmt(..), Case(..), VarDecl(..)
7 , InfixOp(..), PrefixOp(..)
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.
16import Prelude hiding (lines)
17import qualified Data.List as List
18import Data.ByteString.Builder
19import Data.Monoid ((<>))
20import Data.Text (Text)
21import qualified Data.Text.Encoding as Text
35 | Object [(Prop, Expr)]
37 | DotRef Expr Id -- ^ @foo.bar@, spec 11.2.1
38 | BracketRef Expr Expr -- ^ @foo[bar]@, spec 11.2.1
39 | Prefix PrefixOp Expr
40 | Infix InfixOp Expr Expr
43 | Call Expr [Expr] -- ^ @f(x,y,z)@, spec 11.2.3
44 | Function (Maybe Id) [Id] [Stmt]
67 = Block [Stmt] -- {stmts}
69 | ExprStmt Expr -- expr;
70 | IfStmt Expr Stmt Stmt -- if (e) stmt1 else stmt2
71 | IfSingleStmt Expr Stmt -- if (e) stmt
72 | Switch Expr [Case] -- switch (e) clauses
73 | While Expr Stmt -- while (e) do stmt
74 | DoWhile Stmt Expr -- do stmt while (e);
75 | Break (Maybe Id) -- break lab;
76 | Continue (Maybe Id) -- continue lab;
77 | Labelled Id Stmt -- lab: stmt
78 | Return (Maybe Expr) -- return expr;
79 | VarDeclStmt [VarDecl] -- var x, y=42;
80 | FunctionStmt Id [Id] [Stmt] -- function f(x, y, z) {...}
88data VarDecl = VarDecl Id (Maybe Expr)
101 | OpInstanceof -- instanceof
105 | OpStrictNEq -- !===
126 | PrefixTypeof -- typeof
133encodeUtf8 :: [Stmt] -> Builder
135 fromStmtBlock "" stmts
142deeper :: Builder -> Builder
147commaSep :: [Builder] -> Builder
149 mconcat (List.intersperse ", " builders)
152commaNewlineSep :: Builder -> [Builder] -> Builder
153commaNewlineSep indent builders =
154 mconcat (List.intersperse (",\n" <> deeper indent) builders)
161fromStmtBlock :: Builder -> [Stmt] -> Builder
162fromStmtBlock indent stmts =
163 mconcat (map (fromStmt indent) stmts)
166fromStmt :: Builder -> Stmt -> Builder
167fromStmt indent statement =
170 fromStmtBlock indent stmts
176 indent <> snd (fromExpr indent Whatever expr) <> ";\n"
178 IfStmt condition thenStmt elseStmt ->
180 [ indent, "if (", snd (fromExpr indent Whatever condition), ") {\n"
181 , fromStmt (deeper indent) thenStmt
182 , indent, "} else {\n"
183 , fromStmt (deeper indent) elseStmt
187 IfSingleStmt condition thenStmt ->
189 [ indent, "if (", snd (fromExpr indent Whatever condition), ") {\n"
190 , fromStmt (deeper indent) thenStmt
194 Switch expr clauses ->
196 [ indent, "switch (", snd (fromExpr indent Whatever expr), ") {\n"
197 , mconcat (map (fromClause (deeper indent)) clauses)
203 [ indent, "while (", snd (fromExpr indent Whatever expr), ") {\n"
204 , fromStmt (deeper indent) stmt
211 , fromStmt (deeper indent) stmt
212 , indent, "} while(", snd (fromExpr indent Whatever expr), ");\n"
218 Break (Just label) ->
219 indent <> "break " <> fromId label <> ";\n"
222 indent <> "continue;\n"
224 Continue (Just label) ->
225 indent <> "continue " <> fromId label <> ";\n"
227 Labelled label stmt ->
229 [ indent, fromId label, ":\n"
230 , fromStmt indent stmt
234 indent <> "return;\n"
236 Return (Just expr) ->
237 indent <> "return " <> snd (fromExpr indent Whatever expr) <> ";\n"
243 indent <> "var " <> commaNewlineSep indent (map (fromVarDecl indent) decls) <> ";\n"
245 FunctionStmt name args stmts ->
246 indent <> "function " <> fromId name <> "(" <> commaSep (map fromId args) <> ") {\n"
248 fromStmtBlock (deeper indent) stmts
257fromClause :: Builder -> Case -> Builder
258fromClause indent clause =
261 indent <> "case " <> snd (fromExpr indent Whatever expr) <> ":\n"
262 <> fromStmtBlock (deeper indent) stmts
265 indent <> "default:\n"
266 <> fromStmtBlock (deeper indent) stmts
273fromId :: Id -> Builder
275 Text.encodeUtf8Builder name
282fromVarDecl :: Builder -> VarDecl -> Builder
283fromVarDecl indent (VarDecl (Id name) maybeExpr) =
286 Text.encodeUtf8Builder name
289 Text.encodeUtf8Builder name <> " = " <> snd (fromExpr indent Whatever expr)
296data Lines = One | Many deriving (Eq)
299merge :: Lines -> Lines -> Lines
301 if a == Many || b == Many then Many else One
304linesMap :: (a -> (Lines, b)) -> [a] -> (Bool, [b])
310 ( any ((==) Many . fst) pairs
315data Grouping = Atomic | Whatever
318parensFor :: Grouping -> Builder -> Builder
319parensFor grouping builder =
322 "(" <> builder <> ")"
328fromExpr :: Builder -> Grouping -> Expr -> (Lines, Builder)
329fromExpr indent grouping expression =
341 (One, if bool then "true" else "false")
349 (anyMany, builders) =
350 linesMap (fromExpr indent Whatever) exprs
355 <> commaNewlineSep indent builders
356 <> "\n" <> indent <> "]"
359 "[" <> commaSep builders <> "]"
367 (anyMany, builders) =
368 linesMap (fromField deeperIndent) fields
373 <> commaNewlineSep indent builders
374 <> "\n" <> indent <> "}"
377 "{" <> commaSep builders <> "}"
382 DotRef expr (Id name) ->
383 makeDot indent expr name
385 BracketRef expr bracketedExpr ->
386 makeBracketed indent expr bracketedExpr
391 fromExpr indent Atomic expr
394 , parensFor grouping (fromPrefix op <> builder)
397 Infix op leftExpr rightExpr ->
400 fromExpr indent Atomic leftExpr
402 (rightLines, right) =
403 fromExpr indent Atomic rightExpr
405 ( merge leftLines rightLines
406 , parensFor grouping (left <> fromInfix op <> right)
409 If condExpr thenExpr elseExpr ->
411 condB = snd (fromExpr indent Atomic condExpr)
412 thenB = snd (fromExpr indent Atomic thenExpr)
413 elseB = snd (fromExpr indent Atomic elseExpr)
416 , parensFor grouping (condB <> " ? " <> thenB <> " : " <> elseB)
419 Assign lValue expr ->
422 fromLValue indent lValue
424 (rightLines, right) =
425 fromExpr indent Whatever expr
427 ( merge leftLines rightLines
428 , parensFor grouping (left <> " = " <> right)
431 Call function args ->
438 snd (fromExpr indent Atomic function)
441 linesMap (fromExpr deeperIndent Whatever) args
444 funcB <> "(\n" <> deeperIndent <> commaNewlineSep indent argsB <> ")"
447 funcB <> "(" <> commaSep argsB <> ")"
449 Function maybeName args stmts ->
451 "function " <> maybe mempty fromId maybeName <> "(" <> commaSep (map fromId args) <> ") {\n"
453 fromStmtBlock (deeper indent) stmts
462fromField :: Builder -> (Prop, Expr) -> (Lines, Builder)
463fromField indent (prop, expr) =
466 fromExpr indent Whatever expr
469 , fromProp prop <> ": " <> builder
473fromProp :: Prop -> Builder
490quoted :: Text -> Builder
492 "'" <> Text.encodeUtf8Builder str <> "'"
499fromLValue :: Builder -> LValue -> (Lines, Builder)
500fromLValue indent lValue =
503 (One, Text.encodeUtf8Builder name)
506 makeDot indent expr field
508 LBracket expr bracketedExpr ->
509 makeBracketed indent expr bracketedExpr
512makeDot :: Builder -> Expr -> Text -> (Lines, Builder)
513makeDot indent expr field =
516 fromExpr indent Atomic expr
518 (lines, builder <> "." <> Text.encodeUtf8Builder field)
521makeBracketed :: Builder -> Expr -> Expr -> (Lines, Builder)
522makeBracketed indent expr bracketedExpr =
525 fromExpr indent Atomic expr
527 (bracketedLines, bracketedBuilder) =
528 fromExpr indent Whatever bracketedExpr
530 ( merge lines bracketedLines
531 , builder <> "[" <> bracketedBuilder <> "]"
539fromPrefix :: PrefixOp -> Builder
546 PrefixTypeof -> "typeof "
549fromInfix :: InfixOp -> Builder
557 OpInstanceof -> " instanceof "
560 OpStrictEq -> " === "
561 OpStrictNEq -> " !=== "
570 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