packages feed

hydra-0.15.0: src/main/haskell/Hydra/JavaScript/Serde.hs

-- Note: this is an automatically generated file. Do not edit.

-- | Serialization functions for converting JavaScript AST to abstract expressions

module Hydra.JavaScript.Serde where

import qualified Hydra.Ast as Ast
import qualified Hydra.Constants as Constants
import qualified Hydra.JavaScript.Operators as Operators
import qualified Hydra.JavaScript.Syntax as Syntax
import qualified Hydra.Lib.Equality as Equality
import qualified Hydra.Lib.Lists as Lists
import qualified Hydra.Lib.Literals as Literals
import qualified Hydra.Lib.Logic as Logic
import qualified Hydra.Lib.Maybes as Maybes
import qualified Hydra.Lib.Strings as Strings
import qualified Hydra.Serialization as Serialization
import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci

-- | Convert an array element to an AST expression
arrayElementToExpr :: Syntax.ArrayElement -> Ast.Expr
arrayElementToExpr elem =
    case elem of
      Syntax.ArrayElementExpression v0 -> expressionToExpr v0
      Syntax.ArrayElementSpread v0 -> Serialization.prefix "..." (expressionToExpr (Syntax.unSpreadElement v0))
      Syntax.ArrayElementHole -> Serialization.cst ""

-- | Convert an array expression to an AST expression
arrayExpressionToExpr :: [Syntax.ArrayElement] -> Ast.Expr
arrayExpressionToExpr arr = Serialization.bracketList Serialization.inlineStyle (Lists.map arrayElementToExpr arr)

-- | Convert an array pattern to an AST expression
arrayPatternToExpr :: [Maybe Syntax.Pattern] -> Ast.Expr
arrayPatternToExpr arr =
    Serialization.bracketList Serialization.inlineStyle (Lists.map (\maybeP -> Maybes.maybe (Serialization.cst "") patternToExpr maybeP) arr)

-- | Convert an arrow function expression to an AST expression
arrowFunctionExpressionToExpr :: Syntax.ArrowFunctionExpression -> Ast.Expr
arrowFunctionExpressionToExpr arrow =

      let params = Syntax.arrowFunctionExpressionParams arrow
          body = Syntax.arrowFunctionExpressionBody arrow
          async = Syntax.arrowFunctionExpressionAsync arrow
          asyncKw = Logic.ifElse async [
                Serialization.cst "async"] []
          paramsExpr =
                  Logic.ifElse (Equality.equal (Lists.length params) 1) (Maybes.fromMaybe (Serialization.cst "") (Maybes.map patternToExpr (Lists.maybeHead params))) (Serialization.parenList False (Lists.map patternToExpr params))
          bodyExpr =
                  case body of
                    Syntax.ArrowFunctionBodyExpression v0 -> expressionToExpr v0
                    Syntax.ArrowFunctionBodyBlock v0 -> blockStatementToExpr v0
      in (Serialization.spaceSep (Lists.concat [
        asyncKw,
        [
          Serialization.ifx Operators.arrowOp paramsExpr bodyExpr]]))

-- | Convert an assignment expression to an AST expression
assignmentExpressionToExpr :: Syntax.AssignmentExpression -> Ast.Expr
assignmentExpressionToExpr assign =

      let op = Syntax.assignmentExpressionOperator assign
          left = Syntax.assignmentExpressionLeft assign
          right = Syntax.assignmentExpressionRight assign
          opStr = assignmentOperatorToString op
      in (Serialization.spaceSep [
        patternToExpr left,
        (Serialization.cst opStr),
        (expressionToExpr right)])

-- | Convert an assignment operator to a string
assignmentOperatorToString :: Syntax.AssignmentOperator -> String
assignmentOperatorToString op =
    case op of
      Syntax.AssignmentOperatorAssign -> "="
      Syntax.AssignmentOperatorAddAssign -> "+="
      Syntax.AssignmentOperatorSubtractAssign -> "-="
      Syntax.AssignmentOperatorMultiplyAssign -> "*="
      Syntax.AssignmentOperatorDivideAssign -> "/="
      Syntax.AssignmentOperatorModuloAssign -> "%="
      Syntax.AssignmentOperatorExponentiateAssign -> "**="
      Syntax.AssignmentOperatorLeftShiftAssign -> "<<="
      Syntax.AssignmentOperatorRightShiftAssign -> ">>="
      Syntax.AssignmentOperatorUnsignedRightShiftAssign -> ">>>="
      Syntax.AssignmentOperatorBitwiseAndAssign -> "&="
      Syntax.AssignmentOperatorBitwiseOrAssign -> "|="
      Syntax.AssignmentOperatorBitwiseXorAssign -> "^="
      Syntax.AssignmentOperatorAndAssign -> "&&="
      Syntax.AssignmentOperatorOrAssign -> "||="
      Syntax.AssignmentOperatorNullishAssign -> "??="

-- | Convert an assignment pattern to an AST expression
assignmentPatternToExpr :: Syntax.AssignmentPattern -> Ast.Expr
assignmentPatternToExpr assign =

      let left = Syntax.assignmentPatternLeft assign
          right = Syntax.assignmentPatternRight assign
      in (Serialization.ifx Operators.defineOp (patternToExpr left) (expressionToExpr right))

-- | Convert a binary expression to an AST expression
binaryExpressionToExpr :: Syntax.BinaryExpression -> Ast.Expr
binaryExpressionToExpr bin =

      let op = Syntax.binaryExpressionOperator bin
          left = Syntax.binaryExpressionLeft bin
          right = Syntax.binaryExpressionRight bin
      in (Serialization.ifx (binaryOperatorToExpr op) (expressionToExpr left) (expressionToExpr right))

-- | Convert a binary operator to an Op
binaryOperatorToExpr :: Syntax.BinaryOperator -> Ast.Op
binaryOperatorToExpr op =
    case op of
      Syntax.BinaryOperatorAdd -> Operators.addOp
      Syntax.BinaryOperatorSubtract -> Operators.subtractOp
      Syntax.BinaryOperatorMultiply -> Operators.multiplyOp
      Syntax.BinaryOperatorDivide -> Operators.divideOp
      Syntax.BinaryOperatorModulo -> Operators.moduloOp
      Syntax.BinaryOperatorExponentiate -> Operators.exponentiateOp
      Syntax.BinaryOperatorEqual -> Operators.equalOp
      Syntax.BinaryOperatorNotEqual -> Operators.notEqualOp
      Syntax.BinaryOperatorStrictEqual -> Operators.strictEqualOp
      Syntax.BinaryOperatorStrictNotEqual -> Operators.strictNotEqualOp
      Syntax.BinaryOperatorLessThan -> Operators.lessThanOp
      Syntax.BinaryOperatorLessThanOrEqual -> Operators.lessThanOrEqualOp
      Syntax.BinaryOperatorGreaterThan -> Operators.greaterThanOp
      Syntax.BinaryOperatorGreaterThanOrEqual -> Operators.greaterThanOrEqualOp
      Syntax.BinaryOperatorAnd -> Operators.logicalAndOp
      Syntax.BinaryOperatorOr -> Operators.logicalOrOp
      Syntax.BinaryOperatorNullishCoalescing -> Operators.nullishCoalescingOp
      Syntax.BinaryOperatorBitwiseAnd -> Operators.bitwiseAndOp
      Syntax.BinaryOperatorBitwiseOr -> Operators.bitwiseOrOp
      Syntax.BinaryOperatorBitwiseXor -> Operators.bitwiseXorOp
      Syntax.BinaryOperatorLeftShift -> Operators.leftShiftOp
      Syntax.BinaryOperatorRightShift -> Operators.rightShiftOp
      Syntax.BinaryOperatorUnsignedRightShift -> Operators.unsignedRightShiftOp
      Syntax.BinaryOperatorIn -> Operators.inOp
      Syntax.BinaryOperatorInstanceof -> Operators.instanceOfOp

-- | Convert a block statement to an AST expression
blockStatementToExpr :: [Syntax.Statement] -> Ast.Expr
blockStatementToExpr block =
    Serialization.curlyBracesList Nothing Serialization.fullBlockStyle (Lists.map statementToExpr block)

-- | Convert a break statement to an AST expression
breakStatementToExpr :: Maybe Syntax.Identifier -> Ast.Expr
breakStatementToExpr b =
    Maybes.maybe (Serialization.cst "break;") (\label -> Serialization.suffix ";" (Serialization.spaceSep [
      Serialization.cst "break",
      (identifierToExpr label)])) b

-- | Convert a call expression to an AST expression
callExpressionToExpr :: Syntax.CallExpression -> Ast.Expr
callExpressionToExpr call =

      let callee = Syntax.callExpressionCallee call
          args = Syntax.callExpressionArguments call
          optional = Syntax.callExpressionOptional call
          calleeExpr = expressionToExpr callee
          argsExpr = Serialization.parenList False (Lists.map expressionToExpr args)
          optionalDot = Logic.ifElse optional "?." ""
      in (Serialization.spaceSep [
        calleeExpr,
        (Serialization.cst optionalDot),
        argsExpr])

-- | Convert a catch clause to an AST expression
catchClauseToExpr :: Syntax.CatchClause -> Ast.Expr
catchClauseToExpr c =

      let param = Syntax.catchClauseParam c
          body = Syntax.catchClauseBody c
          catchKw =
                  Maybes.maybe (Serialization.cst "catch") (\p -> Serialization.spaceSep [
                    Serialization.cst "catch",
                    (Serialization.parenthesize (patternToExpr p))]) param
      in (Serialization.spaceSep [
        catchKw,
        (blockStatementToExpr body)])

-- | Convert a class declaration to an AST expression
classDeclarationToExpr :: Syntax.ClassDeclaration -> Ast.Expr
classDeclarationToExpr cls =

      let id = Syntax.classDeclarationId cls
          superClass = Syntax.classDeclarationSuperClass cls
          body = Syntax.classDeclarationBody cls
          extendsClause =
                  Maybes.maybe [] (\s -> [
                    Serialization.cst "extends",
                    (expressionToExpr s)]) superClass
          bodyExpr = Serialization.curlyBracesList Nothing Serialization.fullBlockStyle (Lists.map methodDefinitionToExpr body)
      in (Serialization.spaceSep (Lists.concat [
        [
          Serialization.cst "class",
          (identifierToExpr id)],
        extendsClause,
        [
          bodyExpr]]))

-- | Convert a class declaration with comments to an AST expression
classDeclarationWithCommentsToExpr :: Syntax.ClassDeclarationWithComments -> Ast.Expr
classDeclarationWithCommentsToExpr cdwc =

      let body = Syntax.classDeclarationWithCommentsBody cdwc
          mc = Syntax.classDeclarationWithCommentsComments cdwc
      in (Maybes.maybe (classDeclarationToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (classDeclarationToExpr body)]) mc)

-- | Convert a conditional expression to an AST expression
conditionalExpressionToExpr :: Syntax.ConditionalExpression -> Ast.Expr
conditionalExpressionToExpr cond =

      let test = Syntax.conditionalExpressionTest cond
          consequent = Syntax.conditionalExpressionConsequent cond
          alternate = Syntax.conditionalExpressionAlternate cond
      in (Serialization.spaceSep [
        expressionToExpr test,
        (Serialization.cst "?"),
        (expressionToExpr consequent),
        (Serialization.cst ":"),
        (expressionToExpr alternate)])

-- | Convert a continue statement to an AST expression
continueStatementToExpr :: Maybe Syntax.Identifier -> Ast.Expr
continueStatementToExpr c =
    Maybes.maybe (Serialization.cst "continue;") (\label -> Serialization.suffix ";" (Serialization.spaceSep [
      Serialization.cst "continue",
      (identifierToExpr label)])) c

-- | Convert a do-while statement to an AST expression
doWhileStatementToExpr :: Syntax.DoWhileStatement -> Ast.Expr
doWhileStatementToExpr d =

      let body = Syntax.doWhileStatementBody d
          test = Syntax.doWhileStatementTest d
      in (Serialization.suffix ";" (Serialization.spaceSep [
        Serialization.cst "do",
        (statementToExpr body),
        (Serialization.cst "while"),
        (Serialization.parenthesize (expressionToExpr test))]))

-- | Convert a documentation comment to an AST expression
documentationCommentToExpr :: Syntax.DocumentationComment -> Ast.Expr
documentationCommentToExpr doc =

      let description = Syntax.documentationCommentDescription doc
          tags = Syntax.documentationCommentTags doc
      in (Serialization.cst (toJavaScriptComments description tags))

-- | Convert a documentation tag to a JSDoc line
documentationTagToLine :: Syntax.DocumentationTag -> String
documentationTagToLine tag =

      let name = Syntax.documentationTagName tag
          mtype = Syntax.documentationTagType tag
          mparamName = Syntax.documentationTagParamName tag
          description = Syntax.documentationTagDescription tag
          typePart =
                  Maybes.maybe "" (\t -> Strings.cat [
                    "{",
                    (typeExpressionToString t),
                    "} "]) mtype
          paramPart = Maybes.maybe "" (\p -> Strings.cat2 (Syntax.unIdentifier p) " ") mparamName
      in (Strings.cat [
        " * @",
        name,
        " ",
        typePart,
        paramPart,
        description])

-- | Escape special characters in a string for JavaScript
escapeString :: t0 -> t1 -> t0
escapeString s singleQuote = s

-- | Convert an export all declaration to an AST expression
exportAllToExpr :: Syntax.ExportAllDeclaration -> Ast.Expr
exportAllToExpr a =

      let exported = Syntax.exportAllDeclarationExported a
          source = Syntax.exportAllDeclarationSource a
          exportedClause =
                  Maybes.maybe (Serialization.cst "*") (\e -> Serialization.spaceSep [
                    Serialization.cst "*",
                    (Serialization.cst "as"),
                    (identifierToExpr e)]) exported
      in (Serialization.suffix ";" (Serialization.spaceSep [
        Serialization.cst "export",
        exportedClause,
        (Serialization.cst "from"),
        (stringLiteralToExpr source)]))

-- | Convert an export declaration to an AST expression
exportDeclarationToExpr :: Syntax.ExportDeclaration -> Ast.Expr
exportDeclarationToExpr exp =
    case exp of
      Syntax.ExportDeclarationNamed v0 -> namedExportToExpr v0
      Syntax.ExportDeclarationDefault v0 -> Serialization.suffix ";" (Serialization.spaceSep [
        Serialization.cst "export",
        (Serialization.cst "default"),
        (expressionToExpr v0)])
      Syntax.ExportDeclarationDeclaration v0 -> Serialization.spaceSep [
        Serialization.cst "export",
        (statementToExpr v0)]
      Syntax.ExportDeclarationAll v0 -> exportAllToExpr v0

-- | Convert an export specifier to an AST expression
exportSpecifierToExpr :: Syntax.ExportSpecifier -> Ast.Expr
exportSpecifierToExpr spec =

      let local = Syntax.exportSpecifierLocal spec
          exported = Syntax.exportSpecifierExported spec
      in (Logic.ifElse (Equality.equal (Syntax.unIdentifier local) (Syntax.unIdentifier exported)) (identifierToExpr local) (Serialization.spaceSep [
        identifierToExpr local,
        (Serialization.cst "as"),
        (identifierToExpr exported)]))

-- | Convert a JavaScript expression to an AST expression
expressionToExpr :: Syntax.Expression -> Ast.Expr
expressionToExpr expr =
    case expr of
      Syntax.ExpressionIdentifier v0 -> identifierToExpr v0
      Syntax.ExpressionLiteral v0 -> literalToExpr v0
      Syntax.ExpressionArray v0 -> arrayExpressionToExpr v0
      Syntax.ExpressionObject v0 -> objectExpressionToExpr v0
      Syntax.ExpressionFunction v0 -> functionExpressionToExpr v0
      Syntax.ExpressionArrow v0 -> arrowFunctionExpressionToExpr v0
      Syntax.ExpressionCall v0 -> callExpressionToExpr v0
      Syntax.ExpressionMember v0 -> memberExpressionToExpr v0
      Syntax.ExpressionConditional v0 -> conditionalExpressionToExpr v0
      Syntax.ExpressionBinary v0 -> binaryExpressionToExpr v0
      Syntax.ExpressionUnary v0 -> unaryExpressionToExpr v0
      Syntax.ExpressionAssignment v0 -> assignmentExpressionToExpr v0
      Syntax.ExpressionSequence v0 -> Serialization.parenList False (Lists.map expressionToExpr v0)
      Syntax.ExpressionThis -> Serialization.cst "this"
      Syntax.ExpressionNew v0 -> Serialization.spaceSep [
        Serialization.cst "new",
        (callExpressionToExpr v0)]
      Syntax.ExpressionYield v0 -> Maybes.maybe (Serialization.cst "yield") (\e -> Serialization.spaceSep [
        Serialization.cst "yield",
        (expressionToExpr e)]) v0
      Syntax.ExpressionAwait v0 -> Serialization.spaceSep [
        Serialization.cst "await",
        (expressionToExpr v0)]
      Syntax.ExpressionSpread v0 -> Serialization.prefix "..." (expressionToExpr (Syntax.unSpreadElement v0))
      Syntax.ExpressionParenthesized v0 -> Serialization.parenthesize (expressionToExpr v0)

-- | Convert a for-in statement to an AST expression
forInStatementToExpr :: Syntax.ForInStatement -> Ast.Expr
forInStatementToExpr f =

      let left = Syntax.forInStatementLeft f
          right = Syntax.forInStatementRight f
          body = Syntax.forInStatementBody f
          leftExpr =
                  case left of
                    Syntax.ForInLeftVariable v0 -> variableDeclarationToExpr v0
                    Syntax.ForInLeftPattern v0 -> patternToExpr v0
      in (Serialization.spaceSep [
        Serialization.cst "for",
        (Serialization.parenthesize (Serialization.spaceSep [
          leftExpr,
          (Serialization.cst "in"),
          (expressionToExpr right)])),
        (statementToExpr body)])

-- | Convert a for-of statement to an AST expression
forOfStatementToExpr :: Syntax.ForOfStatement -> Ast.Expr
forOfStatementToExpr f =

      let await = Syntax.forOfStatementAwait f
          left = Syntax.forOfStatementLeft f
          right = Syntax.forOfStatementRight f
          body = Syntax.forOfStatementBody f
          forKw = Logic.ifElse await (Serialization.cst "for await") (Serialization.cst "for")
          leftExpr =
                  case left of
                    Syntax.ForInLeftVariable v0 -> variableDeclarationToExpr v0
                    Syntax.ForInLeftPattern v0 -> patternToExpr v0
      in (Serialization.spaceSep [
        forKw,
        (Serialization.parenthesize (Serialization.spaceSep [
          leftExpr,
          (Serialization.cst "of"),
          (expressionToExpr right)])),
        (statementToExpr body)])

-- | Convert a for statement to an AST expression
forStatementToExpr :: Syntax.ForStatement -> Ast.Expr
forStatementToExpr f =

      let init = Syntax.forStatementInit f
          test = Syntax.forStatementTest f
          update = Syntax.forStatementUpdate f
          body = Syntax.forStatementBody f
          initExpr =
                  Maybes.maybe (Serialization.cst "") (\i -> case i of
                    Syntax.ForInitVariable v0 -> variableDeclarationToExpr v0
                    Syntax.ForInitExpression v0 -> expressionToExpr v0) init
          testExpr = Maybes.maybe (Serialization.cst "") expressionToExpr test
          updateExpr = Maybes.maybe (Serialization.cst "") expressionToExpr update
      in (Serialization.spaceSep [
        Serialization.cst "for",
        (Serialization.parenList False [
          initExpr,
          testExpr,
          updateExpr]),
        (statementToExpr body)])

-- | Format import specifiers, handling default vs named imports
formatImportSpecifiers :: [Ast.Expr] -> Ast.Expr
formatImportSpecifiers specs = Serialization.curlyBracesList Nothing Serialization.inlineStyle specs

-- | Convert a function declaration to an AST expression
functionDeclarationToExpr :: Syntax.FunctionDeclaration -> Ast.Expr
functionDeclarationToExpr fn =

      let id = Syntax.functionDeclarationId fn
          params = Syntax.functionDeclarationParams fn
          body = Syntax.functionDeclarationBody fn
          async = Syntax.functionDeclarationAsync fn
          generator = Syntax.functionDeclarationGenerator fn
          asyncKw = Logic.ifElse async [
                Serialization.cst "async"] []
          funcKw = Logic.ifElse generator (Serialization.cst "function*") (Serialization.cst "function")
          paramsExpr = Serialization.parenList False (Lists.map patternToExpr params)
      in (Serialization.spaceSep (Lists.concat [
        asyncKw,
        [
          funcKw,
          (identifierToExpr id),
          paramsExpr,
          (blockStatementToExpr body)]]))

-- | Convert a function declaration with comments to an AST expression
functionDeclarationWithCommentsToExpr :: Syntax.FunctionDeclarationWithComments -> Ast.Expr
functionDeclarationWithCommentsToExpr fdwc =

      let body = Syntax.functionDeclarationWithCommentsBody fdwc
          mc = Syntax.functionDeclarationWithCommentsComments fdwc
      in (Maybes.maybe (functionDeclarationToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (functionDeclarationToExpr body)]) mc)

-- | Convert a function expression to an AST expression
functionExpressionToExpr :: Syntax.FunctionExpression -> Ast.Expr
functionExpressionToExpr fn =

      let mid = Syntax.functionExpressionId fn
          params = Syntax.functionExpressionParams fn
          body = Syntax.functionExpressionBody fn
          async = Syntax.functionExpressionAsync fn
          generator = Syntax.functionExpressionGenerator fn
          asyncKw = Logic.ifElse async [
                Serialization.cst "async"] []
          funcKw = Logic.ifElse generator (Serialization.cst "function*") (Serialization.cst "function")
          nameExpr = Maybes.maybe [] (\id -> [
                identifierToExpr id]) mid
          paramsExpr = Serialization.parenList False (Lists.map patternToExpr params)
      in (Serialization.spaceSep (Lists.concat [
        asyncKw,
        [
          funcKw],
        nameExpr,
        [
          paramsExpr,
          (blockStatementToExpr body)]]))

-- | Convert an identifier to an AST expression
identifierToExpr :: Syntax.Identifier -> Ast.Expr
identifierToExpr id = Serialization.cst (Syntax.unIdentifier id)

-- | Convert an if statement to an AST expression
ifStatementToExpr :: Syntax.IfStatement -> Ast.Expr
ifStatementToExpr ifStmt =

      let test = Syntax.ifStatementTest ifStmt
          consequent = Syntax.ifStatementConsequent ifStmt
          alternate = Syntax.ifStatementAlternate ifStmt
          ifPart =
                  Serialization.spaceSep [
                    Serialization.cst "if",
                    (Serialization.parenthesize (expressionToExpr test)),
                    (statementToExpr consequent)]
      in (Maybes.maybe ifPart (\alt -> Serialization.spaceSep [
        ifPart,
        (Serialization.cst "else"),
        (statementToExpr alt)]) alternate)

-- | Convert an import declaration to an AST expression
importDeclarationToExpr :: Syntax.ImportDeclaration -> Ast.Expr
importDeclarationToExpr imp =

      let specifiers = Syntax.importDeclarationSpecifiers imp
          source = Syntax.importDeclarationSource imp
          sourceExpr = stringLiteralToExpr source
          specExprs = Lists.map importSpecifierToExpr specifiers
      in (Logic.ifElse (Lists.null specifiers) (Serialization.suffix ";" (Serialization.spaceSep [
        Serialization.cst "import",
        sourceExpr])) (Serialization.suffix ";" (Serialization.spaceSep [
        Serialization.cst "import",
        (formatImportSpecifiers specExprs),
        (Serialization.cst "from"),
        sourceExpr])))

-- | Convert an import specifier to an AST expression
importSpecifierToExpr :: Syntax.ImportClause -> Ast.Expr
importSpecifierToExpr spec =
    case spec of
      Syntax.ImportClauseNamed v0 ->
        let imported = Syntax.importSpecifierImported v0
            local = Syntax.importSpecifierLocal v0
        in (Logic.ifElse (Equality.equal (Syntax.unIdentifier imported) (Syntax.unIdentifier local)) (identifierToExpr local) (Serialization.spaceSep [
          identifierToExpr imported,
          (Serialization.cst "as"),
          (identifierToExpr local)]))
      Syntax.ImportClauseDefault v0 -> identifierToExpr (Syntax.unImportDefaultSpecifier v0)
      Syntax.ImportClauseNamespace v0 -> Serialization.spaceSep [
        Serialization.cst "*",
        (Serialization.cst "as"),
        (identifierToExpr (Syntax.unImportNamespaceSpecifier v0))]

-- | Convert a labeled statement to an AST expression
labeledStatementToExpr :: Syntax.LabeledStatement -> Ast.Expr
labeledStatementToExpr l =

      let label = Syntax.labeledStatementLabel l
          body = Syntax.labeledStatementBody l
      in (Serialization.spaceSep [
        Serialization.suffix ":" (identifierToExpr label),
        (statementToExpr body)])

-- | Convert a literal to an AST expression
literalToExpr :: Syntax.Literal -> Ast.Expr
literalToExpr lit =
    case lit of
      Syntax.LiteralString v0 -> stringLiteralToExpr v0
      Syntax.LiteralNumber v0 -> numericLiteralToExpr v0
      Syntax.LiteralBoolean v0 -> Serialization.cst (Logic.ifElse v0 "true" "false")
      Syntax.LiteralNull -> Serialization.cst "null"
      Syntax.LiteralUndefined -> Serialization.cst "undefined"
      Syntax.LiteralBigInt v0 -> Serialization.cst (Strings.cat2 (Literals.showBigint v0) "n")
      Syntax.LiteralTemplate v0 -> templateLiteralToExpr v0

-- | Convert a member expression to an AST expression
memberExpressionToExpr :: Syntax.MemberExpression -> Ast.Expr
memberExpressionToExpr mem =

      let obj = Syntax.memberExpressionObject mem
          prop = Syntax.memberExpressionProperty mem
          computed = Syntax.memberExpressionComputed mem
          optional = Syntax.memberExpressionOptional mem
          objExpr = expressionToExpr obj
          propExpr = expressionToExpr prop
      in (Logic.ifElse computed (Serialization.spaceSep [
        objExpr,
        (Logic.ifElse optional (Serialization.cst "?.") (Serialization.cst "")),
        (Serialization.brackets Serialization.squareBrackets Serialization.inlineStyle propExpr)]) (Serialization.ifx (Logic.ifElse optional Operators.optionalChainOp Operators.memberOp) objExpr propExpr))

-- | Convert a method definition to an AST expression
methodDefinitionToExpr :: Syntax.MethodDefinition -> Ast.Expr
methodDefinitionToExpr method =

      let key = Syntax.methodDefinitionKey method
          value = Syntax.methodDefinitionValue method
          kind = Syntax.methodDefinitionKind method
          computed = Syntax.methodDefinitionComputed method
          static = Syntax.methodDefinitionStatic method
          staticKw = Logic.ifElse static [
                Serialization.cst "static"] []
          kindKw =
                  case kind of
                    Syntax.MethodKindConstructor -> []
                    Syntax.MethodKindMethod -> []
                    Syntax.MethodKindGet -> [
                      Serialization.cst "get"]
                    Syntax.MethodKindSet -> [
                      Serialization.cst "set"]
          keyExpr =
                  Logic.ifElse computed (Serialization.brackets Serialization.squareBrackets Serialization.inlineStyle (expressionToExpr key)) (expressionToExpr key)
          params = Syntax.functionExpressionParams value
          body = Syntax.functionExpressionBody value
          paramsExpr = Serialization.parenList False (Lists.map patternToExpr params)
      in (Serialization.spaceSep (Lists.concat [
        staticKw,
        kindKw,
        [
          keyExpr,
          paramsExpr,
          (blockStatementToExpr body)]]))

-- | Convert a module item to an AST expression
moduleItemToExpr :: Syntax.ModuleItem -> Ast.Expr
moduleItemToExpr item =
    case item of
      Syntax.ModuleItemStatement v0 -> statementToExpr v0
      Syntax.ModuleItemImport v0 -> importDeclarationToExpr v0
      Syntax.ModuleItemExport v0 -> exportDeclarationToExpr v0

-- | Convert a module item with comments to an AST expression
moduleItemWithCommentsToExpr :: Syntax.ModuleItemWithComments -> Ast.Expr
moduleItemWithCommentsToExpr miwc =

      let body = Syntax.moduleItemWithCommentsBody miwc
          mc = Syntax.moduleItemWithCommentsComments miwc
      in (Maybes.maybe (moduleItemToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (moduleItemToExpr body)]) mc)

-- | Convert a named export to an AST expression
namedExportToExpr :: Syntax.NamedExport -> Ast.Expr
namedExportToExpr n =

      let specifiers = Syntax.namedExportSpecifiers n
          source = Syntax.namedExportSource n
          specExprs = Lists.map exportSpecifierToExpr specifiers
          fromClause =
                  Maybes.maybe [] (\s -> [
                    Serialization.cst "from",
                    (stringLiteralToExpr s)]) source
      in (Serialization.suffix ";" (Serialization.spaceSep (Lists.concat [
        [
          Serialization.cst "export"],
        [
          Serialization.curlyBracesList Nothing Serialization.inlineStyle specExprs],
        fromClause])))

-- | Convert a numeric literal to an AST expression
numericLiteralToExpr :: Syntax.NumericLiteral -> Ast.Expr
numericLiteralToExpr n =
    case n of
      Syntax.NumericLiteralInteger v0 -> Serialization.cst (Literals.showInt64 v0)
      Syntax.NumericLiteralFloat v0 -> Serialization.cst (Literals.showFloat64 v0)

-- | Convert an object expression to an AST expression
objectExpressionToExpr :: [Syntax.Property] -> Ast.Expr
objectExpressionToExpr obj =
    Serialization.curlyBracesList Nothing Serialization.halfBlockStyle (Lists.map propertyToExpr obj)

-- | Convert an object pattern property to an AST expression
objectPatternPropertyToExpr :: Syntax.ObjectPatternProperty -> Ast.Expr
objectPatternPropertyToExpr prop =
    case prop of
      Syntax.ObjectPatternPropertyProperty v0 -> propertyToExpr v0
      Syntax.ObjectPatternPropertyRest v0 -> Serialization.prefix "..." (patternToExpr (Syntax.unRestElement v0))

-- | Convert an object pattern to an AST expression
objectPatternToExpr :: Syntax.ObjectPattern -> Ast.Expr
objectPatternToExpr obj =

      let props = Syntax.objectPatternProperties obj
      in (Serialization.curlyBracesList Nothing Serialization.inlineStyle (Lists.map objectPatternPropertyToExpr props))

-- | Convert a pattern to an AST expression
patternToExpr :: Syntax.Pattern -> Ast.Expr
patternToExpr pat =
    case pat of
      Syntax.PatternIdentifier v0 -> identifierToExpr v0
      Syntax.PatternObject v0 -> objectPatternToExpr v0
      Syntax.PatternArray v0 -> arrayPatternToExpr v0
      Syntax.PatternAssignment v0 -> assignmentPatternToExpr v0
      Syntax.PatternRest v0 -> Serialization.prefix "..." (patternToExpr (Syntax.unRestElement v0))

-- | Convert a JavaScript program to an AST expression
programToExpr :: Syntax.Program -> Ast.Expr
programToExpr prog =

      let body = Syntax.programBody prog
          warning = [
                Serialization.cst (toLineComment Constants.warningAutoGeneratedFile)]
          items = Lists.map moduleItemToExpr body
      in (Serialization.doubleNewlineSep (Lists.concat [
        warning,
        items]))

-- | Convert an object property to an AST expression
propertyToExpr :: Syntax.Property -> Ast.Expr
propertyToExpr prop =

      let key = Syntax.propertyKey prop
          value = Syntax.propertyValue prop
          shorthand = Syntax.propertyShorthand prop
          computed = Syntax.propertyComputed prop
          keyExpr =
                  Logic.ifElse computed (Serialization.brackets Serialization.squareBrackets Serialization.inlineStyle (expressionToExpr key)) (expressionToExpr key)
      in (Logic.ifElse shorthand keyExpr (Serialization.ifx Operators.colonOp keyExpr (expressionToExpr value)))

-- | Convert a return statement to an AST expression
returnStatementToExpr :: Maybe Syntax.Expression -> Ast.Expr
returnStatementToExpr r =
    Maybes.maybe (Serialization.cst "return;") (\e -> Serialization.suffix ";" (Serialization.spaceSep [
      Serialization.cst "return",
      (expressionToExpr e)])) r

-- | Convert a statement to an AST expression
statementToExpr :: Syntax.Statement -> Ast.Expr
statementToExpr stmt =
    case stmt of
      Syntax.StatementExpression v0 -> Serialization.suffix ";" (expressionToExpr v0)
      Syntax.StatementBlock v0 -> blockStatementToExpr v0
      Syntax.StatementEmpty -> Serialization.cst ";"
      Syntax.StatementDebugger -> Serialization.cst "debugger;"
      Syntax.StatementReturn v0 -> returnStatementToExpr v0
      Syntax.StatementBreak v0 -> breakStatementToExpr v0
      Syntax.StatementContinue v0 -> continueStatementToExpr v0
      Syntax.StatementIf v0 -> ifStatementToExpr v0
      Syntax.StatementSwitch v0 -> switchStatementToExpr v0
      Syntax.StatementThrow v0 -> throwStatementToExpr v0
      Syntax.StatementTry v0 -> tryStatementToExpr v0
      Syntax.StatementWhile v0 -> whileStatementToExpr v0
      Syntax.StatementDoWhile v0 -> doWhileStatementToExpr v0
      Syntax.StatementFor v0 -> forStatementToExpr v0
      Syntax.StatementForIn v0 -> forInStatementToExpr v0
      Syntax.StatementForOf v0 -> forOfStatementToExpr v0
      Syntax.StatementVariableDeclaration v0 -> variableDeclarationToExpr v0
      Syntax.StatementFunctionDeclaration v0 -> functionDeclarationToExpr v0
      Syntax.StatementClassDeclaration v0 -> classDeclarationToExpr v0
      Syntax.StatementLabeled v0 -> labeledStatementToExpr v0

-- | Convert a string literal to an AST expression
stringLiteralToExpr :: Syntax.StringLiteral -> Ast.Expr
stringLiteralToExpr s =

      let value = Syntax.stringLiteralValue s
          singleQuote = Syntax.stringLiteralSingleQuote s
          quote = Logic.ifElse singleQuote "'" "\""
          escaped = escapeString value singleQuote
      in (Serialization.cst (Strings.cat [
        quote,
        escaped,
        quote]))

-- | Convert a switch case to an AST expression
switchCaseToExpr :: Syntax.SwitchCase -> Ast.Expr
switchCaseToExpr c =

      let test = Syntax.switchCaseTest c
          consequent = Syntax.switchCaseConsequent c
          caseLabel =
                  Maybes.maybe (Serialization.cst "default:") (\t -> Serialization.spaceSep [
                    Serialization.cst "case",
                    (expressionToExpr t),
                    (Serialization.cst ":")]) test
      in (Serialization.newlineSep (Lists.cons caseLabel (Lists.map statementToExpr consequent)))

-- | Convert a switch statement to an AST expression
switchStatementToExpr :: Syntax.SwitchStatement -> Ast.Expr
switchStatementToExpr switchStmt =

      let discriminant = Syntax.switchStatementDiscriminant switchStmt
          cases = Syntax.switchStatementCases switchStmt
      in (Serialization.spaceSep [
        Serialization.cst "switch",
        (Serialization.parenthesize (expressionToExpr discriminant)),
        (Serialization.curlyBracesList Nothing Serialization.fullBlockStyle (Lists.map switchCaseToExpr cases))])

-- | Convert a template literal to an AST expression
templateLiteralToExpr :: Syntax.TemplateLiteral -> Ast.Expr
templateLiteralToExpr t =

      let quasis = Syntax.templateLiteralQuasis t
          exprs = Syntax.templateLiteralExpressions t
      in (Serialization.cst (Strings.cat [
        "`",
        (Strings.intercalate "" (Lists.map (\q -> Syntax.templateElementValue q) quasis)),
        "`"]))

-- | Convert a throw statement to an AST expression
throwStatementToExpr :: Syntax.ThrowStatement -> Ast.Expr
throwStatementToExpr t =
    Serialization.suffix ";" (Serialization.spaceSep [
      Serialization.cst "throw",
      (expressionToExpr (Syntax.unThrowStatement t))])

-- | Format a description and tags as a JSDoc comment
toJavaScriptComments :: String -> [Syntax.DocumentationTag] -> String
toJavaScriptComments desc tags =

      let descLines = Logic.ifElse (Equality.equal desc "") [] (Lists.map (\line -> Strings.cat2 " * " line) (Strings.lines desc))
          tagLines = Lists.map documentationTagToLine tags
          allLines =
                  Lists.concat [
                    descLines,
                    tagLines]
      in (Logic.ifElse (Lists.null allLines) "" (Strings.intercalate "\n" (Lists.concat [
        [
          "/**"],
        allLines,
        [
          " */"]])))

-- | Convert a string to a JavaScript line comment
toLineComment :: String -> String
toLineComment s = Strings.intercalate "\n" (Lists.map (\line -> Strings.cat2 "// " line) (Strings.lines s))

-- | Convert a try statement to an AST expression
tryStatementToExpr :: Syntax.TryStatement -> Ast.Expr
tryStatementToExpr t =

      let block = Syntax.tryStatementBlock t
          handler = Syntax.tryStatementHandler t
          finalizer = Syntax.tryStatementFinalizer t
          tryPart =
                  Serialization.spaceSep [
                    Serialization.cst "try",
                    (blockStatementToExpr block)]
          catchPart = Maybes.maybe [] (\c -> [
                catchClauseToExpr c]) handler
          finallyPart =
                  Maybes.maybe [] (\f -> [
                    Serialization.spaceSep [
                      Serialization.cst "finally",
                      (blockStatementToExpr f)]]) finalizer
      in (Serialization.spaceSep (Lists.concat [
        [
          tryPart],
        catchPart,
        finallyPart]))

-- | Convert a type expression to a string for JSDoc
typeExpressionToString :: Syntax.TypeExpression -> String
typeExpressionToString typ =
    case typ of
      Syntax.TypeExpressionIdentifier v0 -> Syntax.unIdentifier v0
      Syntax.TypeExpressionAny -> "*"
      Syntax.TypeExpressionVoid -> "void"
      Syntax.TypeExpressionNever -> "never"
      Syntax.TypeExpressionLiteral _ -> "literal"
      Syntax.TypeExpressionArray v0 -> Strings.cat2 (typeExpressionToString (Syntax.unArrayTypeExpression v0)) "[]"
      Syntax.TypeExpressionFunction _ -> "Function"
      Syntax.TypeExpressionObject _ -> "Object"
      Syntax.TypeExpressionUnion v0 -> Strings.intercalate "|" (Lists.map typeExpressionToString v0)
      Syntax.TypeExpressionParameterized v0 ->
        let base = Syntax.parameterizedTypeExpressionBase v0
            args = Syntax.parameterizedTypeExpressionArguments v0
        in (Strings.cat [
          typeExpressionToString base,
          "<",
          (Strings.intercalate ", " (Lists.map typeExpressionToString args)),
          ">"])
      Syntax.TypeExpressionOptional v0 -> Strings.cat2 "?" (typeExpressionToString v0)

-- | Convert a unary expression to an AST expression
unaryExpressionToExpr :: Syntax.UnaryExpression -> Ast.Expr
unaryExpressionToExpr un =

      let op = Syntax.unaryExpressionOperator un
          arg = Syntax.unaryExpressionArgument un
          prefix = Syntax.unaryExpressionPrefix un
          opStr = unaryOperatorToString op
          argExpr = expressionToExpr arg
      in (Logic.ifElse prefix (Serialization.prefix opStr argExpr) (Serialization.suffix opStr argExpr))

-- | Convert a unary operator to a string
unaryOperatorToString :: Syntax.UnaryOperator -> String
unaryOperatorToString op =
    case op of
      Syntax.UnaryOperatorNegate -> "-"
      Syntax.UnaryOperatorPlus -> "+"
      Syntax.UnaryOperatorNot -> "!"
      Syntax.UnaryOperatorBitwiseNot -> "~"
      Syntax.UnaryOperatorTypeof -> "typeof "
      Syntax.UnaryOperatorVoid -> "void "
      Syntax.UnaryOperatorDelete -> "delete "
      Syntax.UnaryOperatorIncrement -> "++"
      Syntax.UnaryOperatorDecrement -> "--"

-- | Convert a variable declaration to an AST expression
variableDeclarationToExpr :: Syntax.VariableDeclaration -> Ast.Expr
variableDeclarationToExpr decl =

      let kind = Syntax.variableDeclarationKind decl
          declarations = Syntax.variableDeclarationDeclarations decl
      in (Serialization.suffix ";" (Serialization.spaceSep [
        variableKindToExpr kind,
        (Serialization.commaSep Serialization.inlineStyle (Lists.map variableDeclaratorToExpr declarations))]))

-- | Convert a variable declarator to an AST expression
variableDeclaratorToExpr :: Syntax.VariableDeclarator -> Ast.Expr
variableDeclaratorToExpr decl =

      let id = Syntax.variableDeclaratorId decl
          init = Syntax.variableDeclaratorInit decl
      in (Maybes.maybe (patternToExpr id) (\e -> Serialization.ifx Operators.defineOp (patternToExpr id) (expressionToExpr e)) init)

-- | Convert a variable kind to an AST expression
variableKindToExpr :: Syntax.VariableKind -> Ast.Expr
variableKindToExpr kind =
    case kind of
      Syntax.VariableKindVar -> Serialization.cst "var"
      Syntax.VariableKindLet -> Serialization.cst "let"
      Syntax.VariableKindConst -> Serialization.cst "const"

-- | Convert a while statement to an AST expression
whileStatementToExpr :: Syntax.WhileStatement -> Ast.Expr
whileStatementToExpr w =

      let test = Syntax.whileStatementTest w
          body = Syntax.whileStatementBody w
      in (Serialization.spaceSep [
        Serialization.cst "while",
        (Serialization.parenthesize (expressionToExpr test)),
        (statementToExpr body)])