packages feed

hydra-typescript-0.17.3: src/main/haskell/Hydra/TypeScript/Serde.hs

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

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

module Hydra.TypeScript.Serde where

import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Constants as Constants
import qualified Hydra.Core as Core
import qualified Hydra.Docs as Docs
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.File as ErrorFile
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Error.System as ErrorSystem
import qualified Hydra.Errors as Errors
import qualified Hydra.File as File
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
import qualified Hydra.Overlay.Haskell.Lib.Literals as Literals
import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
import qualified Hydra.Overlay.Haskell.Lib.Math as Math
import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Overlay.Haskell.Lib.Ordering as Ordering
import qualified Hydra.Overlay.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Overlay.Haskell.Lib.Strings as Strings
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Regex as Regex
import qualified Hydra.Relational as Relational
import qualified Hydra.Serialization as Serialization
import qualified Hydra.System as System
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Time as Time
import qualified Hydra.Topology as Topology
import qualified Hydra.TypeScript.Operators as Operators
import qualified Hydra.TypeScript.Syntax as Syntax
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variants as Variants
import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci

-- | True iff every codepoint is an ASCII digit 0-9, and the list is non-empty
allDigits :: [Int] -> Bool
allDigits cps =
    Logic.and (Ordering.gt (Lists.length cps) 0) (Lists.foldl (\acc -> \c -> Logic.and acc (Logic.and (Ordering.gte c 48) (Ordering.lte c 57))) True cps)

-- | 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 -> Optionals.cases maybeP (Serialization.cst "") patternToExpr) 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 = Serialization.parenListAdaptive (Lists.map patternToExpr params)
          bodyExpr =
                  case body of
                    Syntax.ArrowFunctionBodyExpression v0 -> case v0 of
                      Syntax.ExpressionObject _ -> Serialization.parens (expressionToExpr v0)
                      Syntax.ExpressionSequence _ -> Serialization.parens (expressionToExpr 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. Renders as `{ stmt1\n stmt2\n ... }` using curlyBlock + newlineSep: statements are separated by newlines, NOT by commas (which curlyBracesList's default would insert and which TypeScript rejects between block statements).
blockStatementToExpr :: [Syntax.Statement] -> Ast.Expr
blockStatementToExpr block =
    Serialization.curlyBlock Serialization.fullBlockStyle (Serialization.newlineSep (Lists.map statementToExpr block))

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

-- | 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 =
                  case callee of
                    Syntax.ExpressionArrow _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionConditional _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionBinary _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionUnary _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionAssignment _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionSequence _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionObject _ -> Serialization.parens (expressionToExpr callee)
                    Syntax.ExpressionFunction _ -> Serialization.parens (expressionToExpr callee)
                    _ -> expressionToExpr callee
          argsExpr = Serialization.parenListAdaptive (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 =
                  Optionals.cases param (Serialization.cst "catch") (\p -> Serialization.spaceSep [
                    Serialization.cst "catch",
                    (Serialization.parens (patternToExpr p))])
      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 =
                  Optionals.cases superClass [] (\s -> [
                    Serialization.cst "extends",
                    (expressionToExpr s)])
          bodyExpr =
                  Serialization.curlyBlock Serialization.fullBlockStyle (Serialization.newlineSep (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 (Optionals.cases mc (classDeclarationToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (classDeclarationToExpr body)]))

-- | 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
          consExpr =
                  case consequent of
                    Syntax.ExpressionObject _ -> Serialization.parens (expressionToExpr consequent)
                    Syntax.ExpressionSequence _ -> Serialization.parens (expressionToExpr consequent)
                    _ -> expressionToExpr consequent
          altExpr =
                  case alternate of
                    Syntax.ExpressionObject _ -> Serialization.parens (expressionToExpr alternate)
                    Syntax.ExpressionSequence _ -> Serialization.parens (expressionToExpr alternate)
                    _ -> expressionToExpr alternate
      in (Serialization.spaceSep [
        expressionToExpr test,
        (Serialization.cst "?"),
        consExpr,
        (Serialization.cst ":"),
        altExpr])

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

-- | 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.parens (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 (toTypeScriptComments description tags))

-- | 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.
documentationTagToLine :: Syntax.DocumentationTag -> String
documentationTagToLine tag =

      let name = Syntax.documentationTagName tag
          mtype = Syntax.documentationTagType tag
          mparamName = Syntax.documentationTagParamName tag
          description = Syntax.documentationTagDescription tag
          typePart =
                  Optionals.cases mtype "" (\t -> Strings.concat [
                    "{",
                    (typeExpressionToString t),
                    "}"])
          paramPart = Optionals.cases mparamName "" (\p -> Syntax.unIdentifier p)
          parts =
                  [
                    Strings.concat2 "@" name,
                    typePart,
                    paramPart,
                    description]
          nonEmpty = Lists.filter (\p -> Logic.not (Equality.equal p "")) parts
      in (Strings.concat2 " * " (Strings.join " " nonEmpty))

-- | Escape special characters in a string for TypeScript
escapeString :: String -> Bool -> String
escapeString s singleQuote =

      let replace = \old -> \new_ -> \str -> Strings.join new_ (Strings.splitOn old str)
          s1 = replace "\\" "\\\\" s
          s2 = replace "\n" "\\n" s1
          s3 = replace "\r" "\\r" s2
          s4 = replace "\t" "\\t" s3
      in (Logic.ifElse singleQuote (replace "'" "\\'" s4) (replace "\"" "\\\"" s4))

-- | 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 =
                  Optionals.cases exported (Serialization.cst "*") (\e -> Serialization.spaceSep [
                    Serialization.cst "*",
                    (Serialization.cst "as"),
                    (identifierToExpr e)])
      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 TypeScript 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.parenListAdaptive (Lists.map expressionToExpr v0)
      Syntax.ExpressionThis -> Serialization.cst "this"
      Syntax.ExpressionNew v0 -> Serialization.spaceSep [
        Serialization.cst "new",
        (callExpressionToExpr v0)]
      Syntax.ExpressionYield v0 -> Optionals.cases v0 (Serialization.cst "yield") (\e -> Serialization.spaceSep [
        Serialization.cst "yield",
        (expressionToExpr e)])
      Syntax.ExpressionAwait v0 -> Serialization.spaceSep [
        Serialization.cst "await",
        (expressionToExpr v0)]
      Syntax.ExpressionSpread v0 -> Serialization.prefix "..." (expressionToExpr (Syntax.unSpreadElement v0))
      Syntax.ExpressionParenthesized v0 -> Serialization.parens (expressionToExpr v0)
      Syntax.ExpressionAsExpression v0 ->
        let innerE = Syntax.asExpressionExpression v0
            typ = Syntax.asExpressionType v0
        in (Serialization.parens (Serialization.spaceSep [
          expressionToExpr innerE,
          (Serialization.cst "as"),
          (Serialization.cst (tsTypeExpressionToString typ))]))

-- | 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.parens (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.parens (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 =
                  Optionals.cases init (Serialization.cst "") (\i -> case i of
                    Syntax.ForInitVariable v0 -> variableDeclarationToExpr v0
                    Syntax.ForInitExpression v0 -> expressionToExpr v0)
          testExpr = Optionals.cases test (Serialization.cst "") expressionToExpr
          updateExpr = Optionals.cases update (Serialization.cst "") expressionToExpr
      in (Serialization.spaceSep [
        Serialization.cst "for",
        (Serialization.parenListAdaptive [
          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.parenListAdaptive (Lists.map patternToExpr params)
          retAnnot = Serialization.cst ": any"
      in (Serialization.spaceSep (Lists.concat [
        asyncKw,
        [
          funcKw,
          (identifierToExpr id),
          paramsExpr,
          retAnnot,
          (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 (Optionals.cases mc (functionDeclarationToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (functionDeclarationToExpr body)]))

-- | 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 = Optionals.cases mid [] (\id -> [
                identifierToExpr id])
          paramsExpr = Serialization.parenListAdaptive (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.parens (expressionToExpr test)),
                    (statementToExpr consequent)]
      in (Optionals.cases alternate ifPart (\alt -> Serialization.spaceSep [
        ifPart,
        (Serialization.cst "else"),
        (statementToExpr alt)]))

-- | 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))]

-- | Detect a kernel-synthesized type-variable name like T0 or T12, which is unbound in inline type annotations
isKernelTypeVarName :: String -> Bool
isKernelTypeVarName s =

      let cps = Strings.toList s
          len = Lists.length cps
          first = Optionals.withDefault 0 (Lists.head cps)
          rest = Logic.ifElse (Ordering.gt len 0) (Lists.drop 1 cps) []
      in (Logic.and (Ordering.gt len 1) (Logic.and (Equality.equal first 84) (allDigits rest)))

-- | 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.concat2 (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 =
                  case obj of
                    Syntax.ExpressionArrow _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionConditional _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionBinary _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionUnary _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionAssignment _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionSequence _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionObject _ -> Serialization.parens (expressionToExpr obj)
                    Syntax.ExpressionFunction _ -> Serialization.parens (expressionToExpr obj)
                    _ -> 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.parenListAdaptive (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 (Optionals.cases mc (moduleItemToExpr body) (\c -> Serialization.newlineSep [
        documentationCommentToExpr c,
        (moduleItemToExpr body)]))

-- | 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 =
                  Optionals.cases source [] (\s -> [
                    Serialization.cst "from",
                    (stringLiteralToExpr s)])
      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))
      Syntax.PatternTyped v0 -> typedPatternToExpr v0

-- | Render a TS.Pattern as a plain string
patternToString :: Syntax.Pattern -> String
patternToString pat =
    case pat of
      Syntax.PatternIdentifier v0 -> Syntax.unIdentifier v0
      Syntax.PatternRest v0 -> Strings.concat2 "..." (patternToString (Syntax.unRestElement v0))
      Syntax.PatternTyped v0 -> Strings.concat [
        patternToString (Syntax.typedPatternPattern v0),
        ": ",
        (tsTypeExpressionToString (Syntax.typedPatternType v0))]
      _ -> "_"

-- | Convert a TypeScript 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 =
    Optionals.cases r (Serialization.cst "return;") (\e -> Serialization.suffix ";" (Serialization.spaceSep [
      Serialization.cst "return",
      (expressionToExpr e)]))

-- | 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.concat [
        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 =
                  Optionals.cases test (Serialization.cst "default:") (\t -> Serialization.spaceSep [
                    Serialization.cst "case",
                    (expressionToExpr t),
                    (Serialization.cst ":")])
      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.parens (expressionToExpr discriminant)),
        (Serialization.curlyBlock Serialization.fullBlockStyle (Serialization.newlineSep (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.concat [
        "`",
        (Strings.join "" (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))])

-- | Convert a string to a TypeScript line comment. Empty source lines emit `//` (no trailing space).
toLineComment :: String -> String
toLineComment s =
    Strings.join "\n" (Lists.map (\line -> Logic.ifElse (Equality.equal line "") "//" (Strings.concat2 "// " line)) (Strings.lines s))

-- | Format a description and tags as a JSDoc comment. Empty doc lines emit ` *` (no trailing space) so blank lines don't carry trailing whitespace.
toTypeScriptComments :: String -> [Syntax.DocumentationTag] -> String
toTypeScriptComments desc tags =

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

-- | 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 = Optionals.cases handler [] (\c -> [
                catchClauseToExpr c])
          finallyPart =
                  Optionals.cases finalizer [] (\f -> [
                    Serialization.spaceSep [
                      Serialization.cst "finally",
                      (blockStatementToExpr f)]])
      in (Serialization.spaceSep (Lists.concat [
        [
          tryPart],
        catchPart,
        finallyPart]))

-- | Render a TypeScript type expression as a string in TS syntax
tsTypeExpressionToString :: Syntax.TypeExpression -> String
tsTypeExpressionToString t =
    case t of
      Syntax.TypeExpressionIdentifier v0 ->
        let raw = Syntax.unIdentifier v0
        in (Logic.ifElse (isKernelTypeVarName raw) "any" raw)
      Syntax.TypeExpressionAny -> "any"
      Syntax.TypeExpressionVoid -> "void"
      Syntax.TypeExpressionNever -> "never"
      Syntax.TypeExpressionArray v0 -> Strings.concat [
        "ReadonlyArray<",
        (tsTypeExpressionToString (Syntax.unArrayTypeExpression v0)),
        ">"]
      Syntax.TypeExpressionTuple v0 -> Strings.concat [
        "readonly [",
        (Strings.join ", " (Lists.map tsTypeExpressionToString v0)),
        "]"]
      Syntax.TypeExpressionUnion v0 -> Strings.join " | " (Lists.map tsTypeExpressionToString v0)
      Syntax.TypeExpressionIntersection v0 -> Strings.join " & " (Lists.map tsTypeExpressionToString v0)
      Syntax.TypeExpressionParameterized v0 -> Strings.concat [
        tsTypeExpressionToString (Syntax.parameterizedTypeExpressionBase v0),
        "<",
        (Strings.join ", " (Lists.map tsTypeExpressionToString (Syntax.parameterizedTypeExpressionArguments v0))),
        ">"]
      Syntax.TypeExpressionOptional v0 -> Strings.concat [
        tsTypeExpressionToString v0,
        " | undefined"]
      Syntax.TypeExpressionReadonly v0 -> Strings.concat2 "readonly " (tsTypeExpressionToString v0)
      Syntax.TypeExpressionUnknown -> "unknown"
      Syntax.TypeExpressionFunction _ -> "((...args: any[]) => any)"
      _ -> "unknown"

-- | 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.concat2 (typeExpressionToString (Syntax.unArrayTypeExpression v0)) "[]"
      Syntax.TypeExpressionFunction v0 ->
        let params = Syntax.functionTypeExpressionParameters v0
            rt = Syntax.functionTypeExpressionReturnType v0
            rendered =
                    Pairs.second (Lists.foldl (\acc -> \p ->
                      let i = Pairs.first acc
                          soFar = Pairs.second acc
                          this =
                                  Strings.concat [
                                    "_a",
                                    (Literals.showInt32 i),
                                    ": ",
                                    (typeExpressionToString p)]
                      in (Math.add i 1, (Lists.concat2 soFar (Lists.pure this)))) (0, []) params)
        in (Strings.concat [
          "(",
          (Strings.join ", " rendered),
          ") => ",
          (typeExpressionToString rt)])
      Syntax.TypeExpressionObject _ -> "Object"
      Syntax.TypeExpressionUnion v0 -> Strings.join "|" (Lists.map typeExpressionToString v0)
      Syntax.TypeExpressionParameterized v0 ->
        let base = Syntax.parameterizedTypeExpressionBase v0
            args = Syntax.parameterizedTypeExpressionArguments v0
        in (Strings.concat [
          typeExpressionToString base,
          "<",
          (Strings.join ", " (Lists.map typeExpressionToString args)),
          ">"])
      Syntax.TypeExpressionOptional v0 -> Strings.concat2 "?" (typeExpressionToString v0)

-- | Render `<pattern>: <type>` (TypeScript parameter / variable type annotation)
typedPatternToExpr :: Syntax.TypedPattern -> Ast.Expr
typedPatternToExpr tp =

      let innerPat = Syntax.typedPatternPattern tp
          typ = Syntax.typedPatternType tp
          innerStr = patternToString innerPat
          typeStr = tsTypeExpressionToString typ
      in (Serialization.cst (Strings.concat [
        innerStr,
        ": ",
        typeStr]))

-- | 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 (Optionals.cases init (patternToExpr id) (\e -> Serialization.ifx Operators.defineOp (patternToExpr id) (expressionToExpr e)))

-- | 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.parens (expressionToExpr test)),
        (statementToExpr body)])