hydra-0.15.0: src/main/haskell/Hydra/Sources/JavaScript/Serde.hs
-- | Serialization functions for JavaScript AST to abstract syntax expressions.
--
-- This module provides functions to convert JavaScript AST types to Hydra's
-- abstract Expr type, which can then be rendered to concrete syntax.
module Hydra.Sources.JavaScript.Serde where
-- Standard imports for term-level sources outside of the kernel
import Hydra.Kernel
import Hydra.Sources.Libraries
import Hydra.Dsl.Meta.Lib.Strings as Strings
import Hydra.Dsl.Meta.Phantoms as Phantoms
import qualified Hydra.Dsl.Annotations as Annotations
import qualified Hydra.Dsl.Bootstrap as Bootstrap
import qualified Hydra.Dsl.LiteralTypes as LiteralTypes
import qualified Hydra.Dsl.Literals as Literals
import qualified Hydra.Dsl.Paths as Paths
import qualified Hydra.Dsl.Ast as Ast
import qualified Hydra.Dsl.Meta.Base as MetaBase
import qualified Hydra.Dsl.Coders as Coders
import qualified Hydra.Dsl.Util as Util
import qualified Hydra.Dsl.Meta.Core as Core
import qualified Hydra.Dsl.Meta.Graph as Graph
import qualified Hydra.Dsl.Json.Model as Json
import qualified Hydra.Dsl.Meta.Lib.Chars as Chars
import qualified Hydra.Dsl.Meta.Lib.Eithers as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality as Equality
import qualified Hydra.Dsl.Meta.Lib.Lists as Lists
import qualified Hydra.Dsl.Meta.Lib.Literals as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps as Maps
import qualified Hydra.Dsl.Meta.Lib.Math as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs as Pairs
import qualified Hydra.Dsl.Meta.Lib.Sets as Sets
import qualified Hydra.Dsl.Packaging as Packaging
import qualified Hydra.Dsl.Meta.Terms as MetaTerms
import qualified Hydra.Dsl.Meta.Testing as Testing
import qualified Hydra.Dsl.Topology as Topology
import qualified Hydra.Dsl.Meta.Types as MetaTypes
import qualified Hydra.Dsl.Typing as Typing
import qualified Hydra.Dsl.Util as Util
import qualified Hydra.Dsl.Meta.Variants as Variants
import qualified Hydra.Dsl.Prims as Prims
import qualified Hydra.Dsl.Meta.Tabular as Tabular
import qualified Hydra.Dsl.Terms as Terms
import qualified Hydra.Dsl.Tests as Tests
import qualified Hydra.Dsl.Types as Types
import qualified Hydra.Sources.Decode.Core as DecodeCore
import qualified Hydra.Sources.Encode.Core as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Adapt as Adapt
import qualified Hydra.Sources.Kernel.Terms.All as KernelTerms
import qualified Hydra.Sources.Kernel.Terms.Annotations as Annotations
import qualified Hydra.Sources.Kernel.Terms.Arity as Arity
import qualified Hydra.Sources.Kernel.Terms.Checking as Checking
import qualified Hydra.Sources.Kernel.Terms.Constants as Constants
import qualified Hydra.Sources.Kernel.Terms.Extract.Core as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Util as ExtractUtil
import qualified Hydra.Sources.Kernel.Terms.Formatting as Formatting
import qualified Hydra.Sources.Kernel.Terms.Inference as Inference
import qualified Hydra.Sources.Kernel.Terms.Languages as Languages
import qualified Hydra.Sources.Kernel.Terms.Lexical as Lexical
import qualified Hydra.Sources.Kernel.Terms.Literals as Literals
import qualified Hydra.Sources.Kernel.Terms.Names as Names
import qualified Hydra.Sources.Kernel.Terms.Reduction as Reduction
import qualified Hydra.Sources.Kernel.Terms.Reflect as Reflect
import qualified Hydra.Sources.Kernel.Terms.Serialization as Serialization
import qualified Hydra.Sources.Kernel.Terms.Show.Paths as ShowPaths
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Show.Variants as ShowVariants
import qualified Hydra.Sources.Kernel.Terms.Show.Typing as ShowTyping
import qualified Hydra.Sources.Kernel.Terms.Sorting as Sorting
import qualified Hydra.Sources.Kernel.Terms.Substitution as Substitution
import qualified Hydra.Sources.Kernel.Terms.Templates as Templates
import qualified Hydra.Sources.Kernel.Terms.Unification as Unification
import qualified Hydra.Sources.Kernel.Types.All as KernelTypes
import Prelude hiding ((++))
import qualified Data.Int as I
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y
-- Additional imports
import Hydra.Ast
import qualified Hydra.JavaScript.Syntax as JS
import qualified Hydra.Sources.JavaScript.Syntax as JavaScriptSyntax
import qualified Hydra.Sources.JavaScript.Operators as JavaScriptOperators
define :: String -> TTerm a -> TTermDefinition a
define = definitionInModule module_
ns :: Namespace
ns = Namespace "hydra.javaScript.serde"
module_ :: Module
module_ = Module {
moduleNamespace = ns,
moduleDefinitions = definitions,
moduleTermDependencies = [Constants.ns, Serialization.ns, JavaScriptOperators.ns],
moduleTypeDependencies = (JavaScriptSyntax.ns:KernelTypes.kernelTypesNamespaces),
moduleDescription = Just "Serialization functions for converting JavaScript AST to abstract expressions"}
where
definitions = [
-- Core conversions
toDefinition identifierToExpr,
toDefinition literalToExpr,
toDefinition stringLiteralToExpr,
toDefinition escapeString,
toDefinition templateLiteralToExpr,
toDefinition numericLiteralToExpr,
-- Expression conversions
toDefinition expressionToExpr,
toDefinition arrayExpressionToExpr,
toDefinition arrayElementToExpr,
toDefinition objectExpressionToExpr,
toDefinition propertyToExpr,
toDefinition functionExpressionToExpr,
toDefinition arrowFunctionExpressionToExpr,
toDefinition callExpressionToExpr,
toDefinition memberExpressionToExpr,
toDefinition conditionalExpressionToExpr,
toDefinition binaryExpressionToExpr,
toDefinition unaryExpressionToExpr,
toDefinition assignmentExpressionToExpr,
-- Pattern conversions
toDefinition patternToExpr,
toDefinition objectPatternToExpr,
toDefinition objectPatternPropertyToExpr,
toDefinition arrayPatternToExpr,
toDefinition assignmentPatternToExpr,
-- Statement conversions
toDefinition statementToExpr,
toDefinition blockStatementToExpr,
toDefinition variableDeclarationToExpr,
toDefinition variableDeclaratorToExpr,
toDefinition variableKindToExpr,
toDefinition ifStatementToExpr,
toDefinition switchStatementToExpr,
toDefinition switchCaseToExpr,
toDefinition returnStatementToExpr,
toDefinition throwStatementToExpr,
toDefinition tryStatementToExpr,
toDefinition catchClauseToExpr,
toDefinition breakStatementToExpr,
toDefinition continueStatementToExpr,
toDefinition whileStatementToExpr,
toDefinition doWhileStatementToExpr,
toDefinition forStatementToExpr,
toDefinition forInStatementToExpr,
toDefinition forOfStatementToExpr,
toDefinition labeledStatementToExpr,
-- Declaration conversions
toDefinition functionDeclarationToExpr,
toDefinition classDeclarationToExpr,
toDefinition methodDefinitionToExpr,
-- Module conversions
toDefinition programToExpr,
toDefinition moduleItemToExpr,
toDefinition importDeclarationToExpr,
toDefinition importSpecifierToExpr,
toDefinition formatImportSpecifiers,
toDefinition exportDeclarationToExpr,
toDefinition namedExportToExpr,
toDefinition exportSpecifierToExpr,
toDefinition exportAllToExpr,
-- Operators
toDefinition binaryOperatorToExpr,
toDefinition unaryOperatorToString,
toDefinition assignmentOperatorToString,
-- Comments
toDefinition documentationCommentToExpr,
toDefinition toJavaScriptComments,
toDefinition documentationTagToLine,
toDefinition typeExpressionToString,
toDefinition toLineComment,
-- With comments variants
toDefinition moduleItemWithCommentsToExpr,
toDefinition functionDeclarationWithCommentsToExpr,
toDefinition classDeclarationWithCommentsToExpr]
-- ============================================================================
-- Core Conversions
-- ============================================================================
identifierToExpr :: TTermDefinition (JS.Identifier -> Expr)
identifierToExpr = define "identifierToExpr" $
doc "Convert an identifier to an AST expression" $
lambda "id" $ Serialization.cst @@ (unwrap JS._Identifier @@ var "id")
literalToExpr :: TTermDefinition (JS.Literal -> Expr)
literalToExpr = define "literalToExpr" $
doc "Convert a literal to an AST expression" $
lambda "lit" $
cases JS._Literal (var "lit") Nothing [
JS._Literal_string>>: lambda "s" $ stringLiteralToExpr @@ var "s",
JS._Literal_number>>: lambda "n" $ numericLiteralToExpr @@ var "n",
JS._Literal_boolean>>: lambda "b" $
Serialization.cst @@ (Logic.ifElse (var "b") (string "true") (string "false")),
JS._Literal_null>>: constant $ Serialization.cst @@ string "null",
JS._Literal_undefined>>: constant $ Serialization.cst @@ string "undefined",
JS._Literal_bigInt>>: lambda "n" $
Serialization.cst @@ (Strings.cat2 (Literals.showBigint $ var "n") (string "n")),
JS._Literal_template>>: lambda "t" $ templateLiteralToExpr @@ var "t"]
stringLiteralToExpr :: TTermDefinition (JS.StringLiteral -> Expr)
stringLiteralToExpr = define "stringLiteralToExpr" $
doc "Convert a string literal to an AST expression" $
lambda "s" $ lets [
"value">: project JS._StringLiteral JS._StringLiteral_value @@ var "s",
"singleQuote">: project JS._StringLiteral JS._StringLiteral_singleQuote @@ var "s",
"quote">: Logic.ifElse (var "singleQuote") (string "'") (string "\""),
"escaped">: escapeString @@ var "value" @@ var "singleQuote"] $
Serialization.cst @@ (Strings.cat $ list [var "quote", var "escaped", var "quote"])
escapeString :: TTermDefinition (String -> Bool -> String)
escapeString = define "escapeString" $
doc "Escape special characters in a string for JavaScript" $
lambda "s" $ lambda "singleQuote" $
-- Simple implementation: escape backslashes, quotes, and newlines
-- A full implementation would handle more escape sequences
var "s" -- TODO: implement proper escaping
templateLiteralToExpr :: TTermDefinition (JS.TemplateLiteral -> Expr)
templateLiteralToExpr = define "templateLiteralToExpr" $
doc "Convert a template literal to an AST expression" $
lambda "t" $ lets [
"quasis">: project JS._TemplateLiteral JS._TemplateLiteral_quasis @@ var "t",
"exprs">: project JS._TemplateLiteral JS._TemplateLiteral_expressions @@ var "t"] $
-- For now, just output the first quasi if no expressions
-- Full implementation would interleave quasis and expressions
Serialization.cst @@ (Strings.cat $ list [
string "`",
Strings.intercalate (string "") (Lists.map (lambda "q" $
project JS._TemplateElement JS._TemplateElement_value @@ var "q") (var "quasis")),
string "`"])
numericLiteralToExpr :: TTermDefinition (JS.NumericLiteral -> Expr)
numericLiteralToExpr = define "numericLiteralToExpr" $
doc "Convert a numeric literal to an AST expression" $
lambda "n" $
cases JS._NumericLiteral (var "n") Nothing [
JS._NumericLiteral_integer>>: lambda "i" $
Serialization.cst @@ (Literals.showInt64 $ var "i"),
JS._NumericLiteral_float>>: lambda "f" $
Serialization.cst @@ (Literals.showFloat64 $ var "f")]
-- ============================================================================
-- Expression Conversions
-- ============================================================================
expressionToExpr :: TTermDefinition (JS.Expression -> Expr)
expressionToExpr = define "expressionToExpr" $
doc "Convert a JavaScript expression to an AST expression" $
lambda "expr" $
cases JS._Expression (var "expr") Nothing [
JS._Expression_identifier>>: lambda "id" $ identifierToExpr @@ var "id",
JS._Expression_literal>>: lambda "lit" $ literalToExpr @@ var "lit",
JS._Expression_array>>: lambda "arr" $ arrayExpressionToExpr @@ var "arr",
JS._Expression_object>>: lambda "obj" $ objectExpressionToExpr @@ var "obj",
JS._Expression_function>>: lambda "fn" $ functionExpressionToExpr @@ var "fn",
JS._Expression_arrow>>: lambda "arrow" $ arrowFunctionExpressionToExpr @@ var "arrow",
JS._Expression_call>>: lambda "call" $ callExpressionToExpr @@ var "call",
JS._Expression_member>>: lambda "mem" $ memberExpressionToExpr @@ var "mem",
JS._Expression_conditional>>: lambda "cond" $ conditionalExpressionToExpr @@ var "cond",
JS._Expression_binary>>: lambda "bin" $ binaryExpressionToExpr @@ var "bin",
JS._Expression_unary>>: lambda "un" $ unaryExpressionToExpr @@ var "un",
JS._Expression_assignment>>: lambda "assign" $ assignmentExpressionToExpr @@ var "assign",
JS._Expression_sequence>>: lambda "exprs" $
Serialization.parenList @@ false @@ (Lists.map (expressionToExpr) (var "exprs")),
JS._Expression_this>>: constant $ Serialization.cst @@ string "this",
JS._Expression_new>>: lambda "call" $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "new",
callExpressionToExpr @@ var "call"],
JS._Expression_yield>>: lambda "maybeExpr" $
Maybes.maybe
(Serialization.cst @@ string "yield")
(lambda "e" $ Serialization.spaceSep @@ list [
Serialization.cst @@ string "yield",
expressionToExpr @@ var "e"])
(var "maybeExpr"),
JS._Expression_await>>: lambda "e" $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "await",
expressionToExpr @@ var "e"],
JS._Expression_spread>>: lambda "spread" $
Serialization.prefix @@ string "..." @@ (expressionToExpr @@ (unwrap JS._SpreadElement @@ var "spread")),
JS._Expression_parenthesized>>: lambda "e" $
Serialization.parenthesize @@ (expressionToExpr @@ var "e")]
arrayExpressionToExpr :: TTermDefinition (JS.ArrayExpression -> Expr)
arrayExpressionToExpr = define "arrayExpressionToExpr" $
doc "Convert an array expression to an AST expression" $
lambda "arr" $
Serialization.bracketList @@ Serialization.inlineStyle @@
(Lists.map (arrayElementToExpr) (var "arr"))
arrayElementToExpr :: TTermDefinition (JS.ArrayElement -> Expr)
arrayElementToExpr = define "arrayElementToExpr" $
doc "Convert an array element to an AST expression" $
lambda "elem" $
cases JS._ArrayElement (var "elem") Nothing [
JS._ArrayElement_expression>>: lambda "e" $ expressionToExpr @@ var "e",
JS._ArrayElement_spread>>: lambda "s" $
Serialization.prefix @@ string "..." @@ (expressionToExpr @@ (unwrap JS._SpreadElement @@ var "s")),
JS._ArrayElement_hole>>: constant $ Serialization.cst @@ string ""]
objectExpressionToExpr :: TTermDefinition (JS.ObjectExpression -> Expr)
objectExpressionToExpr = define "objectExpressionToExpr" $
doc "Convert an object expression to an AST expression" $
lambda "obj" $
Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
(Lists.map (propertyToExpr) (var "obj"))
propertyToExpr :: TTermDefinition (JS.Property -> Expr)
propertyToExpr = define "propertyToExpr" $
doc "Convert an object property to an AST expression" $
lambda "prop" $ lets [
"key">: project JS._Property JS._Property_key @@ var "prop",
"value">: project JS._Property JS._Property_value @@ var "prop",
"shorthand">: project JS._Property JS._Property_shorthand @@ var "prop",
"computed">: project JS._Property JS._Property_computed @@ var "prop",
"keyExpr">: Logic.ifElse (var "computed")
(Serialization.brackets @@ Serialization.squareBrackets @@ Serialization.inlineStyle @@ (expressionToExpr @@ var "key"))
(expressionToExpr @@ var "key")] $
Logic.ifElse (var "shorthand")
(var "keyExpr")
(Serialization.ifx @@ JavaScriptOperators.colonOp @@ var "keyExpr" @@ (expressionToExpr @@ var "value"))
functionExpressionToExpr :: TTermDefinition (JS.FunctionExpression -> Expr)
functionExpressionToExpr = define "functionExpressionToExpr" $
doc "Convert a function expression to an AST expression" $
lambda "fn" $ lets [
"mid">: project JS._FunctionExpression JS._FunctionExpression_id @@ var "fn",
"params">: project JS._FunctionExpression JS._FunctionExpression_params @@ var "fn",
"body">: project JS._FunctionExpression JS._FunctionExpression_body @@ var "fn",
"async">: project JS._FunctionExpression JS._FunctionExpression_async @@ var "fn",
"generator">: project JS._FunctionExpression JS._FunctionExpression_generator @@ var "fn",
"asyncKw">: Logic.ifElse (var "async") (list [Serialization.cst @@ string "async"]) (list ([] :: [TTerm Expr])),
"funcKw">: Logic.ifElse (var "generator")
(Serialization.cst @@ string "function*")
(Serialization.cst @@ string "function"),
"nameExpr">: Maybes.maybe
(list ([] :: [TTerm Expr]))
(lambda "id" $ list [identifierToExpr @@ var "id"])
(var "mid"),
"paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "params"))] $
Serialization.spaceSep @@ (Lists.concat $ list [
var "asyncKw",
list [var "funcKw"],
var "nameExpr",
list [var "paramsExpr", blockStatementToExpr @@ var "body"]])
arrowFunctionExpressionToExpr :: TTermDefinition (JS.ArrowFunctionExpression -> Expr)
arrowFunctionExpressionToExpr = define "arrowFunctionExpressionToExpr" $
doc "Convert an arrow function expression to an AST expression" $
lambda "arrow" $ lets [
"params">: project JS._ArrowFunctionExpression JS._ArrowFunctionExpression_params @@ var "arrow",
"body">: project JS._ArrowFunctionExpression JS._ArrowFunctionExpression_body @@ var "arrow",
"async">: project JS._ArrowFunctionExpression JS._ArrowFunctionExpression_async @@ var "arrow",
"asyncKw">: Logic.ifElse (var "async")
(list [Serialization.cst @@ string "async"])
(list ([] :: [TTerm Expr])),
"paramsExpr">: Logic.ifElse (Equality.equal (Lists.length $ var "params") (int32 1))
(Maybes.fromMaybe (Serialization.cst @@ string "") (Maybes.map patternToExpr (Lists.maybeHead $ var "params")))
(Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "params"))),
"bodyExpr">: cases JS._ArrowFunctionBody (var "body") Nothing [
JS._ArrowFunctionBody_expression>>: lambda "e" $ expressionToExpr @@ var "e",
JS._ArrowFunctionBody_block>>: lambda "b" $ blockStatementToExpr @@ var "b"]] $
Serialization.spaceSep @@ (Lists.concat $ list [
var "asyncKw",
list [Serialization.ifx @@ JavaScriptOperators.arrowOp @@ var "paramsExpr" @@ var "bodyExpr"]])
callExpressionToExpr :: TTermDefinition (JS.CallExpression -> Expr)
callExpressionToExpr = define "callExpressionToExpr" $
doc "Convert a call expression to an AST expression" $
lambda "call" $ lets [
"callee">: project JS._CallExpression JS._CallExpression_callee @@ var "call",
"args">: project JS._CallExpression JS._CallExpression_arguments @@ var "call",
"optional">: project JS._CallExpression JS._CallExpression_optional @@ var "call",
"calleeExpr">: expressionToExpr @@ var "callee",
"argsExpr">: Serialization.parenList @@ false @@ (Lists.map (expressionToExpr) (var "args")),
"optionalDot">: Logic.ifElse (var "optional") (string "?.") (string "")] $
Serialization.spaceSep @@ list [
var "calleeExpr",
Serialization.cst @@ var "optionalDot",
var "argsExpr"]
memberExpressionToExpr :: TTermDefinition (JS.MemberExpression -> Expr)
memberExpressionToExpr = define "memberExpressionToExpr" $
doc "Convert a member expression to an AST expression" $
lambda "mem" $ lets [
"obj">: project JS._MemberExpression JS._MemberExpression_object @@ var "mem",
"prop">: project JS._MemberExpression JS._MemberExpression_property @@ var "mem",
"computed">: project JS._MemberExpression JS._MemberExpression_computed @@ var "mem",
"optional">: project JS._MemberExpression JS._MemberExpression_optional @@ var "mem",
"objExpr">: expressionToExpr @@ var "obj",
"propExpr">: expressionToExpr @@ var "prop"] $
Logic.ifElse (var "computed")
-- obj[prop] or obj?.[prop]
(Serialization.spaceSep @@ list [
var "objExpr",
Logic.ifElse (var "optional") (Serialization.cst @@ string "?.") (Serialization.cst @@ string ""),
Serialization.brackets @@ Serialization.squareBrackets @@ Serialization.inlineStyle @@ var "propExpr"])
-- obj.prop or obj?.prop
(Serialization.ifx @@
(Logic.ifElse (var "optional") JavaScriptOperators.optionalChainOp JavaScriptOperators.memberOp) @@
var "objExpr" @@ var "propExpr")
conditionalExpressionToExpr :: TTermDefinition (JS.ConditionalExpression -> Expr)
conditionalExpressionToExpr = define "conditionalExpressionToExpr" $
doc "Convert a conditional expression to an AST expression" $
lambda "cond" $ lets [
"test">: project JS._ConditionalExpression JS._ConditionalExpression_test @@ var "cond",
"consequent">: project JS._ConditionalExpression JS._ConditionalExpression_consequent @@ var "cond",
"alternate">: project JS._ConditionalExpression JS._ConditionalExpression_alternate @@ var "cond"] $
Serialization.spaceSep @@ list [
expressionToExpr @@ var "test",
Serialization.cst @@ string "?",
expressionToExpr @@ var "consequent",
Serialization.cst @@ string ":",
expressionToExpr @@ var "alternate"]
binaryExpressionToExpr :: TTermDefinition (JS.BinaryExpression -> Expr)
binaryExpressionToExpr = define "binaryExpressionToExpr" $
doc "Convert a binary expression to an AST expression" $
lambda "bin" $ lets [
"op">: project JS._BinaryExpression JS._BinaryExpression_operator @@ var "bin",
"left">: project JS._BinaryExpression JS._BinaryExpression_left @@ var "bin",
"right">: project JS._BinaryExpression JS._BinaryExpression_right @@ var "bin"] $
Serialization.ifx @@
(binaryOperatorToExpr @@ var "op") @@
(expressionToExpr @@ var "left") @@
(expressionToExpr @@ var "right")
unaryExpressionToExpr :: TTermDefinition (JS.UnaryExpression -> Expr)
unaryExpressionToExpr = define "unaryExpressionToExpr" $
doc "Convert a unary expression to an AST expression" $
lambda "un" $ lets [
"op">: project JS._UnaryExpression JS._UnaryExpression_operator @@ var "un",
"arg">: project JS._UnaryExpression JS._UnaryExpression_argument @@ var "un",
"prefix">: project JS._UnaryExpression JS._UnaryExpression_prefix @@ var "un",
"opStr">: unaryOperatorToString @@ var "op",
"argExpr">: expressionToExpr @@ var "arg"] $
Logic.ifElse (var "prefix")
(Serialization.prefix @@ var "opStr" @@ var "argExpr")
(Serialization.suffix @@ var "opStr" @@ var "argExpr")
assignmentExpressionToExpr :: TTermDefinition (JS.AssignmentExpression -> Expr)
assignmentExpressionToExpr = define "assignmentExpressionToExpr" $
doc "Convert an assignment expression to an AST expression" $
lambda "assign" $ lets [
"op">: project JS._AssignmentExpression JS._AssignmentExpression_operator @@ var "assign",
"left">: project JS._AssignmentExpression JS._AssignmentExpression_left @@ var "assign",
"right">: project JS._AssignmentExpression JS._AssignmentExpression_right @@ var "assign",
"opStr">: assignmentOperatorToString @@ var "op"] $
Serialization.spaceSep @@ list [
patternToExpr @@ var "left",
Serialization.cst @@ var "opStr",
expressionToExpr @@ var "right"]
-- ============================================================================
-- Pattern Conversions
-- ============================================================================
patternToExpr :: TTermDefinition (JS.Pattern -> Expr)
patternToExpr = define "patternToExpr" $
doc "Convert a pattern to an AST expression" $
lambda "pat" $
cases JS._Pattern (var "pat") Nothing [
JS._Pattern_identifier>>: lambda "id" $ identifierToExpr @@ var "id",
JS._Pattern_object>>: lambda "obj" $ objectPatternToExpr @@ var "obj",
JS._Pattern_array>>: lambda "arr" $ arrayPatternToExpr @@ var "arr",
JS._Pattern_assignment>>: lambda "assign" $ assignmentPatternToExpr @@ var "assign",
JS._Pattern_rest>>: lambda "rest" $
Serialization.prefix @@ string "..." @@ (patternToExpr @@ (unwrap JS._RestElement @@ var "rest"))]
objectPatternToExpr :: TTermDefinition (JS.ObjectPattern -> Expr)
objectPatternToExpr = define "objectPatternToExpr" $
doc "Convert an object pattern to an AST expression" $
lambda "obj" $ lets [
"props">: project JS._ObjectPattern JS._ObjectPattern_properties @@ var "obj"] $
Serialization.curlyBracesList @@ nothing @@ Serialization.inlineStyle @@
(Lists.map (objectPatternPropertyToExpr) (var "props"))
objectPatternPropertyToExpr :: TTermDefinition (JS.ObjectPatternProperty -> Expr)
objectPatternPropertyToExpr = define "objectPatternPropertyToExpr" $
doc "Convert an object pattern property to an AST expression" $
lambda "prop" $
cases JS._ObjectPatternProperty (var "prop") Nothing [
JS._ObjectPatternProperty_property>>: lambda "p" $ propertyToExpr @@ var "p",
JS._ObjectPatternProperty_rest>>: lambda "r" $
Serialization.prefix @@ string "..." @@ (patternToExpr @@ (unwrap JS._RestElement @@ var "r"))]
arrayPatternToExpr :: TTermDefinition (JS.ArrayPattern -> Expr)
arrayPatternToExpr = define "arrayPatternToExpr" $
doc "Convert an array pattern to an AST expression" $
lambda "arr" $
Serialization.bracketList @@ Serialization.inlineStyle @@
(Lists.map
(lambda "maybeP" $ Maybes.maybe (Serialization.cst @@ string "") (patternToExpr) (var "maybeP"))
(var "arr"))
assignmentPatternToExpr :: TTermDefinition (JS.AssignmentPattern -> Expr)
assignmentPatternToExpr = define "assignmentPatternToExpr" $
doc "Convert an assignment pattern to an AST expression" $
lambda "assign" $ lets [
"left">: project JS._AssignmentPattern JS._AssignmentPattern_left @@ var "assign",
"right">: project JS._AssignmentPattern JS._AssignmentPattern_right @@ var "assign"] $
Serialization.ifx @@ JavaScriptOperators.defineOp @@
(patternToExpr @@ var "left") @@
(expressionToExpr @@ var "right")
-- ============================================================================
-- Statement Conversions
-- ============================================================================
statementToExpr :: TTermDefinition (JS.Statement -> Expr)
statementToExpr = define "statementToExpr" $
doc "Convert a statement to an AST expression" $
lambda "stmt" $
cases JS._Statement (var "stmt") Nothing [
JS._Statement_expression>>: lambda "e" $
Serialization.suffix @@ string ";" @@ (expressionToExpr @@ var "e"),
JS._Statement_block>>: lambda "b" $ blockStatementToExpr @@ var "b",
JS._Statement_empty>>: constant $ Serialization.cst @@ string ";",
JS._Statement_debugger>>: constant $ Serialization.cst @@ string "debugger;",
JS._Statement_return>>: lambda "r" $ returnStatementToExpr @@ var "r",
JS._Statement_break>>: lambda "b" $ breakStatementToExpr @@ var "b",
JS._Statement_continue>>: lambda "c" $ continueStatementToExpr @@ var "c",
JS._Statement_if>>: lambda "i" $ ifStatementToExpr @@ var "i",
JS._Statement_switch>>: lambda "s" $ switchStatementToExpr @@ var "s",
JS._Statement_throw>>: lambda "t" $ throwStatementToExpr @@ var "t",
JS._Statement_try>>: lambda "t" $ tryStatementToExpr @@ var "t",
JS._Statement_while>>: lambda "w" $ whileStatementToExpr @@ var "w",
JS._Statement_doWhile>>: lambda "d" $ doWhileStatementToExpr @@ var "d",
JS._Statement_for>>: lambda "f" $ forStatementToExpr @@ var "f",
JS._Statement_forIn>>: lambda "f" $ forInStatementToExpr @@ var "f",
JS._Statement_forOf>>: lambda "f" $ forOfStatementToExpr @@ var "f",
JS._Statement_variableDeclaration>>: lambda "v" $ variableDeclarationToExpr @@ var "v",
JS._Statement_functionDeclaration>>: lambda "f" $ functionDeclarationToExpr @@ var "f",
JS._Statement_classDeclaration>>: lambda "c" $ classDeclarationToExpr @@ var "c",
JS._Statement_labeled>>: lambda "l" $ labeledStatementToExpr @@ var "l"]
blockStatementToExpr :: TTermDefinition (JS.BlockStatement -> Expr)
blockStatementToExpr = define "blockStatementToExpr" $
doc "Convert a block statement to an AST expression" $
lambda "block" $
Serialization.curlyBracesList @@ nothing @@ Serialization.fullBlockStyle @@
(Lists.map (statementToExpr) (var "block"))
variableDeclarationToExpr :: TTermDefinition (JS.VariableDeclaration -> Expr)
variableDeclarationToExpr = define "variableDeclarationToExpr" $
doc "Convert a variable declaration to an AST expression" $
lambda "decl" $ lets [
"kind">: project JS._VariableDeclaration JS._VariableDeclaration_kind @@ var "decl",
"declarations">: project JS._VariableDeclaration JS._VariableDeclaration_declarations @@ var "decl"] $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
variableKindToExpr @@ var "kind",
Serialization.commaSep @@ Serialization.inlineStyle @@ (Lists.map (variableDeclaratorToExpr) (var "declarations"))])
variableDeclaratorToExpr :: TTermDefinition (JS.VariableDeclarator -> Expr)
variableDeclaratorToExpr = define "variableDeclaratorToExpr" $
doc "Convert a variable declarator to an AST expression" $
lambda "decl" $ lets [
"id">: project JS._VariableDeclarator JS._VariableDeclarator_id @@ var "decl",
"init">: project JS._VariableDeclarator JS._VariableDeclarator_init @@ var "decl"] $
Maybes.maybe
(patternToExpr @@ var "id")
(lambda "e" $ Serialization.ifx @@ JavaScriptOperators.defineOp @@
(patternToExpr @@ var "id") @@
(expressionToExpr @@ var "e"))
(var "init")
variableKindToExpr :: TTermDefinition (JS.VariableKind -> Expr)
variableKindToExpr = define "variableKindToExpr" $
doc "Convert a variable kind to an AST expression" $
lambda "kind" $
cases JS._VariableKind (var "kind") Nothing [
JS._VariableKind_var>>: constant $ Serialization.cst @@ string "var",
JS._VariableKind_let>>: constant $ Serialization.cst @@ string "let",
JS._VariableKind_const>>: constant $ Serialization.cst @@ string "const"]
ifStatementToExpr :: TTermDefinition (JS.IfStatement -> Expr)
ifStatementToExpr = define "ifStatementToExpr" $
doc "Convert an if statement to an AST expression" $
lambda "ifStmt" $ lets [
"test">: project JS._IfStatement JS._IfStatement_test @@ var "ifStmt",
"consequent">: project JS._IfStatement JS._IfStatement_consequent @@ var "ifStmt",
"alternate">: project JS._IfStatement JS._IfStatement_alternate @@ var "ifStmt",
"ifPart">: Serialization.spaceSep @@ list [
Serialization.cst @@ string "if",
Serialization.parenthesize @@ (expressionToExpr @@ var "test"),
statementToExpr @@ var "consequent"]] $
Maybes.maybe
(var "ifPart")
(lambda "alt" $ Serialization.spaceSep @@ list [
var "ifPart",
Serialization.cst @@ string "else",
statementToExpr @@ var "alt"])
(var "alternate")
switchStatementToExpr :: TTermDefinition (JS.SwitchStatement -> Expr)
switchStatementToExpr = define "switchStatementToExpr" $
doc "Convert a switch statement to an AST expression" $
lambda "switchStmt" $ lets [
"discriminant">: project JS._SwitchStatement JS._SwitchStatement_discriminant @@ var "switchStmt",
"cases">: project JS._SwitchStatement JS._SwitchStatement_cases @@ var "switchStmt"] $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "switch",
Serialization.parenthesize @@ (expressionToExpr @@ var "discriminant"),
Serialization.curlyBracesList @@ nothing @@ Serialization.fullBlockStyle @@
(Lists.map (switchCaseToExpr) (var "cases"))]
switchCaseToExpr :: TTermDefinition (JS.SwitchCase -> Expr)
switchCaseToExpr = define "switchCaseToExpr" $
doc "Convert a switch case to an AST expression" $
lambda "c" $ lets [
"test">: project JS._SwitchCase JS._SwitchCase_test @@ var "c",
"consequent">: project JS._SwitchCase JS._SwitchCase_consequent @@ var "c",
"caseLabel">: Maybes.maybe
(Serialization.cst @@ string "default:")
(lambda "t" $ Serialization.spaceSep @@ list [
Serialization.cst @@ string "case",
expressionToExpr @@ var "t",
Serialization.cst @@ string ":"])
(var "test")] $
Serialization.newlineSep @@ (Lists.cons (var "caseLabel") (Lists.map (statementToExpr) (var "consequent")))
returnStatementToExpr :: TTermDefinition (JS.ReturnStatement -> Expr)
returnStatementToExpr = define "returnStatementToExpr" $
doc "Convert a return statement to an AST expression" $
lambda "r" $
Maybes.maybe
(Serialization.cst @@ string "return;")
(lambda "e" $ Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "return",
expressionToExpr @@ var "e"]))
(var "r")
throwStatementToExpr :: TTermDefinition (JS.ThrowStatement -> Expr)
throwStatementToExpr = define "throwStatementToExpr" $
doc "Convert a throw statement to an AST expression" $
lambda "t" $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "throw",
expressionToExpr @@ (unwrap JS._ThrowStatement @@ var "t")])
tryStatementToExpr :: TTermDefinition (JS.TryStatement -> Expr)
tryStatementToExpr = define "tryStatementToExpr" $
doc "Convert a try statement to an AST expression" $
lambda "t" $ lets [
"block">: project JS._TryStatement JS._TryStatement_block @@ var "t",
"handler">: project JS._TryStatement JS._TryStatement_handler @@ var "t",
"finalizer">: project JS._TryStatement JS._TryStatement_finalizer @@ var "t",
"tryPart">: Serialization.spaceSep @@ list [
Serialization.cst @@ string "try",
blockStatementToExpr @@ var "block"],
"catchPart">: Maybes.maybe
(list ([] :: [TTerm Expr]))
(lambda "c" $ list [catchClauseToExpr @@ var "c"])
(var "handler"),
"finallyPart">: Maybes.maybe
(list ([] :: [TTerm Expr]))
(lambda "f" $ list [Serialization.spaceSep @@ list [
Serialization.cst @@ string "finally",
blockStatementToExpr @@ var "f"]])
(var "finalizer")] $
Serialization.spaceSep @@ (Lists.concat $ list [list [var "tryPart"], var "catchPart", var "finallyPart"])
catchClauseToExpr :: TTermDefinition (JS.CatchClause -> Expr)
catchClauseToExpr = define "catchClauseToExpr" $
doc "Convert a catch clause to an AST expression" $
lambda "c" $ lets [
"param">: project JS._CatchClause JS._CatchClause_param @@ var "c",
"body">: project JS._CatchClause JS._CatchClause_body @@ var "c",
"catchKw">: Maybes.maybe
(Serialization.cst @@ string "catch")
(lambda "p" $ Serialization.spaceSep @@ list [
Serialization.cst @@ string "catch",
Serialization.parenthesize @@ (patternToExpr @@ var "p")])
(var "param")] $
Serialization.spaceSep @@ list [var "catchKw", blockStatementToExpr @@ var "body"]
breakStatementToExpr :: TTermDefinition (JS.BreakStatement -> Expr)
breakStatementToExpr = define "breakStatementToExpr" $
doc "Convert a break statement to an AST expression" $
lambda "b" $
Maybes.maybe
(Serialization.cst @@ string "break;")
(lambda "label" $ Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "break",
identifierToExpr @@ var "label"]))
(var "b")
continueStatementToExpr :: TTermDefinition (JS.ContinueStatement -> Expr)
continueStatementToExpr = define "continueStatementToExpr" $
doc "Convert a continue statement to an AST expression" $
lambda "c" $
Maybes.maybe
(Serialization.cst @@ string "continue;")
(lambda "label" $ Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "continue",
identifierToExpr @@ var "label"]))
(var "c")
whileStatementToExpr :: TTermDefinition (JS.WhileStatement -> Expr)
whileStatementToExpr = define "whileStatementToExpr" $
doc "Convert a while statement to an AST expression" $
lambda "w" $ lets [
"test">: project JS._WhileStatement JS._WhileStatement_test @@ var "w",
"body">: project JS._WhileStatement JS._WhileStatement_body @@ var "w"] $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "while",
Serialization.parenthesize @@ (expressionToExpr @@ var "test"),
statementToExpr @@ var "body"]
doWhileStatementToExpr :: TTermDefinition (JS.DoWhileStatement -> Expr)
doWhileStatementToExpr = define "doWhileStatementToExpr" $
doc "Convert a do-while statement to an AST expression" $
lambda "d" $ lets [
"body">: project JS._DoWhileStatement JS._DoWhileStatement_body @@ var "d",
"test">: project JS._DoWhileStatement JS._DoWhileStatement_test @@ var "d"] $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "do",
statementToExpr @@ var "body",
Serialization.cst @@ string "while",
Serialization.parenthesize @@ (expressionToExpr @@ var "test")])
forStatementToExpr :: TTermDefinition (JS.ForStatement -> Expr)
forStatementToExpr = define "forStatementToExpr" $
doc "Convert a for statement to an AST expression" $
lambda "f" $ lets [
"init">: project JS._ForStatement JS._ForStatement_init @@ var "f",
"test">: project JS._ForStatement JS._ForStatement_test @@ var "f",
"update">: project JS._ForStatement JS._ForStatement_update @@ var "f",
"body">: project JS._ForStatement JS._ForStatement_body @@ var "f",
"initExpr">: Maybes.maybe (Serialization.cst @@ string "")
(lambda "i" $ cases JS._ForInit (var "i") Nothing [
JS._ForInit_variable>>: lambda "v" $ variableDeclarationToExpr @@ var "v",
JS._ForInit_expression>>: lambda "e" $ expressionToExpr @@ var "e"])
(var "init"),
"testExpr">: Maybes.maybe (Serialization.cst @@ string "") (expressionToExpr) (var "test"),
"updateExpr">: Maybes.maybe (Serialization.cst @@ string "") (expressionToExpr) (var "update")] $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "for",
Serialization.parenList @@ false @@ list [var "initExpr", var "testExpr", var "updateExpr"],
statementToExpr @@ var "body"]
forInStatementToExpr :: TTermDefinition (JS.ForInStatement -> Expr)
forInStatementToExpr = define "forInStatementToExpr" $
doc "Convert a for-in statement to an AST expression" $
lambda "f" $ lets [
"left">: project JS._ForInStatement JS._ForInStatement_left @@ var "f",
"right">: project JS._ForInStatement JS._ForInStatement_right @@ var "f",
"body">: project JS._ForInStatement JS._ForInStatement_body @@ var "f",
"leftExpr">: cases JS._ForInLeft (var "left") Nothing [
JS._ForInLeft_variable>>: lambda "v" $ variableDeclarationToExpr @@ var "v",
JS._ForInLeft_pattern>>: lambda "p" $ patternToExpr @@ var "p"]] $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "for",
Serialization.parenthesize @@ (Serialization.spaceSep @@ list [
var "leftExpr",
Serialization.cst @@ string "in",
expressionToExpr @@ var "right"]),
statementToExpr @@ var "body"]
forOfStatementToExpr :: TTermDefinition (JS.ForOfStatement -> Expr)
forOfStatementToExpr = define "forOfStatementToExpr" $
doc "Convert a for-of statement to an AST expression" $
lambda "f" $ lets [
"await">: project JS._ForOfStatement JS._ForOfStatement_await @@ var "f",
"left">: project JS._ForOfStatement JS._ForOfStatement_left @@ var "f",
"right">: project JS._ForOfStatement JS._ForOfStatement_right @@ var "f",
"body">: project JS._ForOfStatement JS._ForOfStatement_body @@ var "f",
"forKw">: Logic.ifElse (var "await")
(Serialization.cst @@ string "for await")
(Serialization.cst @@ string "for"),
"leftExpr">: cases JS._ForInLeft (var "left") Nothing [
JS._ForInLeft_variable>>: lambda "v" $ variableDeclarationToExpr @@ var "v",
JS._ForInLeft_pattern>>: lambda "p" $ patternToExpr @@ var "p"]] $
Serialization.spaceSep @@ list [
var "forKw",
Serialization.parenthesize @@ (Serialization.spaceSep @@ list [
var "leftExpr",
Serialization.cst @@ string "of",
expressionToExpr @@ var "right"]),
statementToExpr @@ var "body"]
labeledStatementToExpr :: TTermDefinition (JS.LabeledStatement -> Expr)
labeledStatementToExpr = define "labeledStatementToExpr" $
doc "Convert a labeled statement to an AST expression" $
lambda "l" $ lets [
"label">: project JS._LabeledStatement JS._LabeledStatement_label @@ var "l",
"body">: project JS._LabeledStatement JS._LabeledStatement_body @@ var "l"] $
Serialization.spaceSep @@ list [
Serialization.suffix @@ string ":" @@ (identifierToExpr @@ var "label"),
statementToExpr @@ var "body"]
-- ============================================================================
-- Declaration Conversions
-- ============================================================================
functionDeclarationToExpr :: TTermDefinition (JS.FunctionDeclaration -> Expr)
functionDeclarationToExpr = define "functionDeclarationToExpr" $
doc "Convert a function declaration to an AST expression" $
lambda "fn" $ lets [
"id">: project JS._FunctionDeclaration JS._FunctionDeclaration_id @@ var "fn",
"params">: project JS._FunctionDeclaration JS._FunctionDeclaration_params @@ var "fn",
"body">: project JS._FunctionDeclaration JS._FunctionDeclaration_body @@ var "fn",
"async">: project JS._FunctionDeclaration JS._FunctionDeclaration_async @@ var "fn",
"generator">: project JS._FunctionDeclaration JS._FunctionDeclaration_generator @@ var "fn",
"asyncKw">: Logic.ifElse (var "async") (list [Serialization.cst @@ string "async"]) (list ([] :: [TTerm Expr])),
"funcKw">: Logic.ifElse (var "generator")
(Serialization.cst @@ string "function*")
(Serialization.cst @@ string "function"),
"paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "params"))] $
Serialization.spaceSep @@ (Lists.concat $ list [
var "asyncKw",
list [var "funcKw", identifierToExpr @@ var "id", var "paramsExpr", blockStatementToExpr @@ var "body"]])
classDeclarationToExpr :: TTermDefinition (JS.ClassDeclaration -> Expr)
classDeclarationToExpr = define "classDeclarationToExpr" $
doc "Convert a class declaration to an AST expression" $
lambda "cls" $ lets [
"id">: project JS._ClassDeclaration JS._ClassDeclaration_id @@ var "cls",
"superClass">: project JS._ClassDeclaration JS._ClassDeclaration_superClass @@ var "cls",
"body">: project JS._ClassDeclaration JS._ClassDeclaration_body @@ var "cls",
"extendsClause">: Maybes.maybe
(list ([] :: [TTerm Expr]))
(lambda "s" $ list [Serialization.cst @@ string "extends", expressionToExpr @@ var "s"])
(var "superClass"),
"bodyExpr">: Serialization.curlyBracesList @@ nothing @@ Serialization.fullBlockStyle @@
(Lists.map (methodDefinitionToExpr) (var "body"))] $
Serialization.spaceSep @@ (Lists.concat $ list [
list [Serialization.cst @@ string "class", identifierToExpr @@ var "id"],
var "extendsClause",
list [var "bodyExpr"]])
methodDefinitionToExpr :: TTermDefinition (JS.MethodDefinition -> Expr)
methodDefinitionToExpr = define "methodDefinitionToExpr" $
doc "Convert a method definition to an AST expression" $
lambda "method" $ lets [
"key">: project JS._MethodDefinition JS._MethodDefinition_key @@ var "method",
"value">: project JS._MethodDefinition JS._MethodDefinition_value @@ var "method",
"kind">: project JS._MethodDefinition JS._MethodDefinition_kind @@ var "method",
"computed">: project JS._MethodDefinition JS._MethodDefinition_computed @@ var "method",
"static">: project JS._MethodDefinition JS._MethodDefinition_static @@ var "method",
"staticKw">: Logic.ifElse (var "static") (list [Serialization.cst @@ string "static"]) (list ([] :: [TTerm Expr])),
"kindKw">: cases JS._MethodKind (var "kind") Nothing [
JS._MethodKind_constructor>>: constant $ list ([] :: [TTerm Expr]),
JS._MethodKind_method>>: constant $ list ([] :: [TTerm Expr]),
JS._MethodKind_get>>: constant $ list [Serialization.cst @@ string "get"],
JS._MethodKind_set>>: constant $ list [Serialization.cst @@ string "set"]],
"keyExpr">: Logic.ifElse (var "computed")
(Serialization.brackets @@ Serialization.squareBrackets @@ Serialization.inlineStyle @@ (expressionToExpr @@ var "key"))
(expressionToExpr @@ var "key"),
"params">: project JS._FunctionExpression JS._FunctionExpression_params @@ var "value",
"body">: project JS._FunctionExpression JS._FunctionExpression_body @@ var "value",
"paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "params"))] $
Serialization.spaceSep @@ (Lists.concat $ list [
var "staticKw",
var "kindKw",
list [var "keyExpr", var "paramsExpr", blockStatementToExpr @@ var "body"]])
-- ============================================================================
-- Module Conversions
-- ============================================================================
programToExpr :: TTermDefinition (JS.Program -> Expr)
programToExpr = define "programToExpr" $
doc "Convert a JavaScript program to an AST expression" $
lambda "prog" $ lets [
"body">: project JS._Program JS._Program_body @@ var "prog",
"warning">: list [Serialization.cst @@ (toLineComment @@ Constants.warningAutoGeneratedFile)],
"items">: Lists.map (moduleItemToExpr) (var "body")] $
Serialization.doubleNewlineSep @@ (Lists.concat $ list [var "warning", var "items"])
moduleItemToExpr :: TTermDefinition (JS.ModuleItem -> Expr)
moduleItemToExpr = define "moduleItemToExpr" $
doc "Convert a module item to an AST expression" $
lambda "item" $
cases JS._ModuleItem (var "item") Nothing [
JS._ModuleItem_statement>>: lambda "s" $ statementToExpr @@ var "s",
JS._ModuleItem_import>>: lambda "i" $ importDeclarationToExpr @@ var "i",
JS._ModuleItem_export>>: lambda "e" $ exportDeclarationToExpr @@ var "e"]
importDeclarationToExpr :: TTermDefinition (JS.ImportDeclaration -> Expr)
importDeclarationToExpr = define "importDeclarationToExpr" $
doc "Convert an import declaration to an AST expression" $
lambda "imp" $ lets [
"specifiers">: project JS._ImportDeclaration JS._ImportDeclaration_specifiers @@ var "imp",
"source">: project JS._ImportDeclaration JS._ImportDeclaration_source @@ var "imp",
"sourceExpr">: stringLiteralToExpr @@ var "source",
"specExprs">: Lists.map (importSpecifierToExpr) (var "specifiers")] $
Logic.ifElse (Lists.null $ var "specifiers")
-- import "module"
(Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [Serialization.cst @@ string "import", var "sourceExpr"]))
-- import { x, y } from "module"
(Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "import",
formatImportSpecifiers @@ var "specExprs",
Serialization.cst @@ string "from",
var "sourceExpr"]))
importSpecifierToExpr :: TTermDefinition (JS.ImportClause -> Expr)
importSpecifierToExpr = define "importSpecifierToExpr" $
doc "Convert an import specifier to an AST expression" $
lambda "spec" $
cases JS._ImportClause (var "spec") Nothing [
JS._ImportClause_named>>: lambda "n" $ lets [
"imported">: project JS._ImportSpecifier JS._ImportSpecifier_imported @@ var "n",
"local">: project JS._ImportSpecifier JS._ImportSpecifier_local @@ var "n"] $
Logic.ifElse (Equality.equal (unwrap JS._Identifier @@ var "imported") (unwrap JS._Identifier @@ var "local"))
(identifierToExpr @@ var "local")
(Serialization.spaceSep @@ list [
identifierToExpr @@ var "imported",
Serialization.cst @@ string "as",
identifierToExpr @@ var "local"]),
JS._ImportClause_default>>: lambda "d" $ identifierToExpr @@ (unwrap JS._ImportDefaultSpecifier @@ var "d"),
JS._ImportClause_namespace>>: lambda "n" $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "*",
Serialization.cst @@ string "as",
identifierToExpr @@ (unwrap JS._ImportNamespaceSpecifier @@ var "n")]]
formatImportSpecifiers :: TTermDefinition ([Expr] -> Expr)
formatImportSpecifiers = define "formatImportSpecifiers" $
doc "Format import specifiers, handling default vs named imports" $
lambda "specs" $
-- Simplified: just wrap named imports in braces
Serialization.curlyBracesList @@ nothing @@ Serialization.inlineStyle @@ var "specs"
exportDeclarationToExpr :: TTermDefinition (JS.ExportDeclaration -> Expr)
exportDeclarationToExpr = define "exportDeclarationToExpr" $
doc "Convert an export declaration to an AST expression" $
lambda "exp" $
cases JS._ExportDeclaration (var "exp") Nothing [
JS._ExportDeclaration_named>>: lambda "n" $ namedExportToExpr @@ var "n",
JS._ExportDeclaration_default>>: lambda "e" $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "export",
Serialization.cst @@ string "default",
expressionToExpr @@ var "e"]),
JS._ExportDeclaration_declaration>>: lambda "d" $
Serialization.spaceSep @@ list [
Serialization.cst @@ string "export",
statementToExpr @@ var "d"],
JS._ExportDeclaration_all>>: lambda "a" $ exportAllToExpr @@ var "a"]
namedExportToExpr :: TTermDefinition (JS.NamedExport -> Expr)
namedExportToExpr = define "namedExportToExpr" $
doc "Convert a named export to an AST expression" $
lambda "n" $ lets [
"specifiers">: project JS._NamedExport JS._NamedExport_specifiers @@ var "n",
"source">: project JS._NamedExport JS._NamedExport_source @@ var "n",
"specExprs">: Lists.map (exportSpecifierToExpr) (var "specifiers"),
"fromClause">: Maybes.maybe
(list ([] :: [TTerm Expr]))
(lambda "s" $ list [Serialization.cst @@ string "from", stringLiteralToExpr @@ var "s"])
(var "source")] $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ (Lists.concat $ list [
list [Serialization.cst @@ string "export"],
list [Serialization.curlyBracesList @@ nothing @@ Serialization.inlineStyle @@ var "specExprs"],
var "fromClause"]))
exportSpecifierToExpr :: TTermDefinition (JS.ExportSpecifier -> Expr)
exportSpecifierToExpr = define "exportSpecifierToExpr" $
doc "Convert an export specifier to an AST expression" $
lambda "spec" $ lets [
"local">: project JS._ExportSpecifier JS._ExportSpecifier_local @@ var "spec",
"exported">: project JS._ExportSpecifier JS._ExportSpecifier_exported @@ var "spec"] $
Logic.ifElse (Equality.equal (unwrap JS._Identifier @@ var "local") (unwrap JS._Identifier @@ var "exported"))
(identifierToExpr @@ var "local")
(Serialization.spaceSep @@ list [
identifierToExpr @@ var "local",
Serialization.cst @@ string "as",
identifierToExpr @@ var "exported"])
exportAllToExpr :: TTermDefinition (JS.ExportAllDeclaration -> Expr)
exportAllToExpr = define "exportAllToExpr" $
doc "Convert an export all declaration to an AST expression" $
lambda "a" $ lets [
"exported">: project JS._ExportAllDeclaration JS._ExportAllDeclaration_exported @@ var "a",
"source">: project JS._ExportAllDeclaration JS._ExportAllDeclaration_source @@ var "a",
"exportedClause">: Maybes.maybe
(Serialization.cst @@ string "*")
(lambda "e" $ Serialization.spaceSep @@ list [
Serialization.cst @@ string "*",
Serialization.cst @@ string "as",
identifierToExpr @@ var "e"])
(var "exported")] $
Serialization.suffix @@ string ";" @@
(Serialization.spaceSep @@ list [
Serialization.cst @@ string "export",
var "exportedClause",
Serialization.cst @@ string "from",
stringLiteralToExpr @@ var "source"])
-- ============================================================================
-- Operators
-- ============================================================================
binaryOperatorToExpr :: TTermDefinition (JS.BinaryOperator -> Op)
binaryOperatorToExpr = define "binaryOperatorToExpr" $
doc "Convert a binary operator to an Op" $
lambda "op" $
cases JS._BinaryOperator (var "op") Nothing [
JS._BinaryOperator_add>>: constant JavaScriptOperators.addOp,
JS._BinaryOperator_subtract>>: constant JavaScriptOperators.subtractOp,
JS._BinaryOperator_multiply>>: constant JavaScriptOperators.multiplyOp,
JS._BinaryOperator_divide>>: constant JavaScriptOperators.divideOp,
JS._BinaryOperator_modulo>>: constant JavaScriptOperators.moduloOp,
JS._BinaryOperator_exponentiate>>: constant JavaScriptOperators.exponentiateOp,
JS._BinaryOperator_equal>>: constant JavaScriptOperators.equalOp,
JS._BinaryOperator_notEqual>>: constant JavaScriptOperators.notEqualOp,
JS._BinaryOperator_strictEqual>>: constant JavaScriptOperators.strictEqualOp,
JS._BinaryOperator_strictNotEqual>>: constant JavaScriptOperators.strictNotEqualOp,
JS._BinaryOperator_lessThan>>: constant JavaScriptOperators.lessThanOp,
JS._BinaryOperator_lessThanOrEqual>>: constant JavaScriptOperators.lessThanOrEqualOp,
JS._BinaryOperator_greaterThan>>: constant JavaScriptOperators.greaterThanOp,
JS._BinaryOperator_greaterThanOrEqual>>: constant JavaScriptOperators.greaterThanOrEqualOp,
JS._BinaryOperator_and>>: constant JavaScriptOperators.logicalAndOp,
JS._BinaryOperator_or>>: constant JavaScriptOperators.logicalOrOp,
JS._BinaryOperator_nullishCoalescing>>: constant JavaScriptOperators.nullishCoalescingOp,
JS._BinaryOperator_bitwiseAnd>>: constant JavaScriptOperators.bitwiseAndOp,
JS._BinaryOperator_bitwiseOr>>: constant JavaScriptOperators.bitwiseOrOp,
JS._BinaryOperator_bitwiseXor>>: constant JavaScriptOperators.bitwiseXorOp,
JS._BinaryOperator_leftShift>>: constant JavaScriptOperators.leftShiftOp,
JS._BinaryOperator_rightShift>>: constant JavaScriptOperators.rightShiftOp,
JS._BinaryOperator_unsignedRightShift>>: constant JavaScriptOperators.unsignedRightShiftOp,
JS._BinaryOperator_in>>: constant JavaScriptOperators.inOp,
JS._BinaryOperator_instanceof>>: constant JavaScriptOperators.instanceOfOp]
unaryOperatorToString :: TTermDefinition (JS.UnaryOperator -> String)
unaryOperatorToString = define "unaryOperatorToString" $
doc "Convert a unary operator to a string" $
lambda "op" $
cases JS._UnaryOperator (var "op") Nothing [
JS._UnaryOperator_negate>>: constant $ string "-",
JS._UnaryOperator_plus>>: constant $ string "+",
JS._UnaryOperator_not>>: constant $ string "!",
JS._UnaryOperator_bitwiseNot>>: constant $ string "~",
JS._UnaryOperator_typeof>>: constant $ string "typeof ",
JS._UnaryOperator_void>>: constant $ string "void ",
JS._UnaryOperator_delete>>: constant $ string "delete ",
JS._UnaryOperator_increment>>: constant $ string "++",
JS._UnaryOperator_decrement>>: constant $ string "--"]
assignmentOperatorToString :: TTermDefinition (JS.AssignmentOperator -> String)
assignmentOperatorToString = define "assignmentOperatorToString" $
doc "Convert an assignment operator to a string" $
lambda "op" $
cases JS._AssignmentOperator (var "op") Nothing [
JS._AssignmentOperator_assign>>: constant $ string "=",
JS._AssignmentOperator_addAssign>>: constant $ string "+=",
JS._AssignmentOperator_subtractAssign>>: constant $ string "-=",
JS._AssignmentOperator_multiplyAssign>>: constant $ string "*=",
JS._AssignmentOperator_divideAssign>>: constant $ string "/=",
JS._AssignmentOperator_moduloAssign>>: constant $ string "%=",
JS._AssignmentOperator_exponentiateAssign>>: constant $ string "**=",
JS._AssignmentOperator_leftShiftAssign>>: constant $ string "<<=",
JS._AssignmentOperator_rightShiftAssign>>: constant $ string ">>=",
JS._AssignmentOperator_unsignedRightShiftAssign>>: constant $ string ">>>=",
JS._AssignmentOperator_bitwiseAndAssign>>: constant $ string "&=",
JS._AssignmentOperator_bitwiseOrAssign>>: constant $ string "|=",
JS._AssignmentOperator_bitwiseXorAssign>>: constant $ string "^=",
JS._AssignmentOperator_andAssign>>: constant $ string "&&=",
JS._AssignmentOperator_orAssign>>: constant $ string "||=",
JS._AssignmentOperator_nullishAssign>>: constant $ string "??="]
-- ============================================================================
-- Comments
-- ============================================================================
documentationCommentToExpr :: TTermDefinition (JS.DocumentationComment -> Expr)
documentationCommentToExpr = define "documentationCommentToExpr" $
doc "Convert a documentation comment to an AST expression" $
lambda "doc" $ lets [
"description">: project JS._DocumentationComment JS._DocumentationComment_description @@ var "doc",
"tags">: project JS._DocumentationComment JS._DocumentationComment_tags @@ var "doc"] $
Serialization.cst @@ (toJavaScriptComments @@ var "description" @@ var "tags")
toJavaScriptComments :: TTermDefinition (String -> [JS.DocumentationTag] -> String)
toJavaScriptComments = define "toJavaScriptComments" $
doc ("Format a description and tags as a JSDoc comment. Empty doc lines"
<> " emit ` *` (no trailing space) so blank lines don't carry trailing whitespace.") $
lambda "desc" $ lambda "tags" $ lets [
"descLines">: Logic.ifElse (Equality.equal (var "desc") (string ""))
(list ([] :: [TTerm String]))
(Lists.map (lambda "line" $ Logic.ifElse (Equality.equal (var "line") (string ""))
(string " *")
(Strings.cat2 (string " * ") (var "line")))
(Strings.lines $ var "desc")),
"tagLines">: Lists.map (documentationTagToLine) (var "tags"),
"allLines">: Lists.concat $ list [var "descLines", var "tagLines"]] $
Logic.ifElse (Lists.null $ var "allLines")
(string "")
(Strings.intercalate (string "\n") $
Lists.concat $ list [
list [string "/**"],
var "allLines",
list [string " */"]])
documentationTagToLine :: TTermDefinition (JS.DocumentationTag -> String)
documentationTagToLine = define "documentationTagToLine" $
doc ("Convert a documentation tag to a JSDoc line. Built by joining"
<> " non-empty parts with spaces so that absent type/param/description"
<> " components don't introduce trailing whitespace.") $
lambda "tag" $ lets [
"name">: project JS._DocumentationTag JS._DocumentationTag_name @@ var "tag",
"mtype">: project JS._DocumentationTag JS._DocumentationTag_type @@ var "tag",
"mparamName">: project JS._DocumentationTag JS._DocumentationTag_paramName @@ var "tag",
"description">: project JS._DocumentationTag JS._DocumentationTag_description @@ var "tag",
"typePart">: Maybes.maybe (string "") (lambda "t" $ Strings.cat $ list [string "{", typeExpressionToString @@ var "t", string "}"]) (var "mtype"),
"paramPart">: Maybes.maybe (string "") (lambda "p" $ unwrap JS._Identifier @@ var "p") (var "mparamName"),
"parts">: list [
Strings.cat2 (string "@") (var "name"),
var "typePart",
var "paramPart",
var "description"],
"nonEmpty">: Lists.filter (lambda "p" $ Logic.not $ Equality.equal (var "p") (string "")) (var "parts")] $
Strings.cat2 (string " * ") (Strings.intercalate (string " ") (var "nonEmpty"))
typeExpressionToString :: TTermDefinition (JS.TypeExpression -> String)
typeExpressionToString = define "typeExpressionToString" $
doc "Convert a type expression to a string for JSDoc" $
lambda "typ" $
cases JS._TypeExpression (var "typ") Nothing [
JS._TypeExpression_identifier>>: lambda "id" $ unwrap JS._Identifier @@ var "id",
JS._TypeExpression_any>>: constant $ string "*",
JS._TypeExpression_void>>: constant $ string "void",
JS._TypeExpression_never>>: constant $ string "never",
-- Simplified handling for other cases
JS._TypeExpression_literal>>: lambda "l" $ string "literal",
JS._TypeExpression_array>>: lambda "a" $ Strings.cat2 (typeExpressionToString @@ (unwrap JS._ArrayTypeExpression @@ var "a")) (string "[]"),
JS._TypeExpression_function>>: constant $ string "Function",
JS._TypeExpression_object>>: constant $ string "Object",
JS._TypeExpression_union>>: lambda "u" $ Strings.intercalate (string "|") (Lists.map (typeExpressionToString) (var "u")),
JS._TypeExpression_parameterized>>: lambda "p" $ lets [
"base">: project JS._ParameterizedTypeExpression JS._ParameterizedTypeExpression_base @@ var "p",
"args">: project JS._ParameterizedTypeExpression JS._ParameterizedTypeExpression_arguments @@ var "p"] $
Strings.cat $ list [
typeExpressionToString @@ var "base",
string "<",
Strings.intercalate (string ", ") (Lists.map (typeExpressionToString) (var "args")),
string ">"],
JS._TypeExpression_optional>>: lambda "o" $ Strings.cat2 (string "?") (typeExpressionToString @@ var "o")]
toLineComment :: TTermDefinition (String -> String)
toLineComment = define "toLineComment" $
doc ("Convert a string to a JavaScript line comment. Empty source lines"
<> " emit `//` (no trailing space).") $
lambda "s" $ Strings.intercalate (string "\n") $ Lists.map
(lambda "line" $ Logic.ifElse (Equality.equal (var "line") (string ""))
(string "//")
(Strings.cat2 (string "// ") (var "line")))
(Strings.lines $ var "s")
-- ============================================================================
-- With Comments Variants
-- ============================================================================
moduleItemWithCommentsToExpr :: TTermDefinition (JS.ModuleItemWithComments -> Expr)
moduleItemWithCommentsToExpr = define "moduleItemWithCommentsToExpr" $
doc "Convert a module item with comments to an AST expression" $
lambda "miwc" $ lets [
"body">: project JS._ModuleItemWithComments JS._ModuleItemWithComments_body @@ var "miwc",
"mc">: project JS._ModuleItemWithComments JS._ModuleItemWithComments_comments @@ var "miwc"] $
Maybes.maybe
(moduleItemToExpr @@ var "body")
(lambda "c" $ Serialization.newlineSep @@ list [
documentationCommentToExpr @@ var "c",
moduleItemToExpr @@ var "body"])
(var "mc")
functionDeclarationWithCommentsToExpr :: TTermDefinition (JS.FunctionDeclarationWithComments -> Expr)
functionDeclarationWithCommentsToExpr = define "functionDeclarationWithCommentsToExpr" $
doc "Convert a function declaration with comments to an AST expression" $
lambda "fdwc" $ lets [
"body">: project JS._FunctionDeclarationWithComments JS._FunctionDeclarationWithComments_body @@ var "fdwc",
"mc">: project JS._FunctionDeclarationWithComments JS._FunctionDeclarationWithComments_comments @@ var "fdwc"] $
Maybes.maybe
(functionDeclarationToExpr @@ var "body")
(lambda "c" $ Serialization.newlineSep @@ list [
documentationCommentToExpr @@ var "c",
functionDeclarationToExpr @@ var "body"])
(var "mc")
classDeclarationWithCommentsToExpr :: TTermDefinition (JS.ClassDeclarationWithComments -> Expr)
classDeclarationWithCommentsToExpr = define "classDeclarationWithCommentsToExpr" $
doc "Convert a class declaration with comments to an AST expression" $
lambda "cdwc" $ lets [
"body">: project JS._ClassDeclarationWithComments JS._ClassDeclarationWithComments_body @@ var "cdwc",
"mc">: project JS._ClassDeclarationWithComments JS._ClassDeclarationWithComments_comments @@ var "cdwc"] $
Maybes.maybe
(classDeclarationToExpr @@ var "body")
(lambda "c" $ Serialization.newlineSep @@ list [
documentationCommentToExpr @@ var "c",
classDeclarationToExpr @@ var "body"])
(var "mc")