content: {-# LANGUAGE DeriveDataTypeable, FlexibleInstances #-}
  
  module Language.JavaScript.Parser.AST
      ( JSExpression (..)
      , JSAnnot (..)
      , JSBinOp (..)
      , JSUnaryOp (..)
      , JSSemi (..)
      , JSAssignOp (..)
      , JSTryCatch (..)
      , JSTryFinally (..)
      , JSStatement (..)
      , JSBlock (..)
      , JSSwitchParts (..)
      , JSAST (..)
      , JSObjectProperty (..)
      , JSPropertyName (..)
      , JSObjectPropertyList
      , JSAccessor (..)
      , JSMethodDefinition (..)
      , JSIdent (..)
      , JSVarInitializer (..)
      , JSArrayElement (..)
      , JSCommaList (..)
      , JSCommaTrailingList (..)
      , JSArrowParameterList (..)
      , JSTemplatePart (..)
      , JSClassHeritage (..)
      , JSClassElement (..)
  
      -- Modules
      , JSModuleItem (..)
      , JSImportDeclaration (..)
      , JSImportClause (..)
      , JSFromClause (..)
      , JSImportNameSpace (..)
      , JSImportsNamed (..)
      , JSImportSpecifier (..)
      , JSExportDeclaration (..)
      , JSExportClause (..)
      , JSExportSpecifier (..)
  
      , binOpEq
      , showStripped
      ) where
  
  import Data.Data
  import Data.List
  import Language.JavaScript.Parser.SrcLocation (TokenPosn (..))
  import Language.JavaScript.Parser.Token
  
  -- ---------------------------------------------------------------------
  
  data JSAnnot
      = JSAnnot !TokenPosn ![CommentAnnotation] -- ^Annotation: position and comment/whitespace information
      | JSAnnotSpace -- ^A single space character
      | JSNoAnnot -- ^No annotation
      deriving (Data, Eq, Show, Typeable)
  
  
  data JSAST
      = JSAstProgram ![JSStatement] !JSAnnot -- ^source elements, trailing whitespace
      | JSAstModule ![JSModuleItem] !JSAnnot
      | JSAstStatement !JSStatement !JSAnnot
      | JSAstExpression !JSExpression !JSAnnot
      | JSAstLiteral !JSExpression !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  -- Shift AST
  -- https://github.com/shapesecurity/shift-spec/blob/83498b92c436180cc0e2115b225a68c08f43c53e/spec.idl#L229-L234
  data JSModuleItem
      = JSModuleImportDeclaration !JSAnnot !JSImportDeclaration -- ^import,decl
      | JSModuleExportDeclaration !JSAnnot !JSExportDeclaration -- ^export,decl
      | JSModuleStatementListItem !JSStatement
      deriving (Data, Eq, Show, Typeable)
  
  data JSImportDeclaration
      = JSImportDeclaration !JSImportClause !JSFromClause !JSSemi -- ^imports, module, semi
      | JSImportDeclarationBare !JSAnnot !String !JSSemi -- ^module, module, semi
      deriving (Data, Eq, Show, Typeable)
  
  data JSImportClause
      = JSImportClauseDefault !JSIdent -- ^default
      | JSImportClauseNameSpace !JSImportNameSpace -- ^namespace
      | JSImportClauseNamed !JSImportsNamed -- ^named imports
      | JSImportClauseDefaultNameSpace !JSIdent !JSAnnot !JSImportNameSpace -- ^default, comma, namespace
      | JSImportClauseDefaultNamed !JSIdent !JSAnnot !JSImportsNamed -- ^default, comma, named imports
      deriving (Data, Eq, Show, Typeable)
  
  data JSFromClause
      = JSFromClause !JSAnnot !JSAnnot !String -- ^ from, string literal, string literal contents
      deriving (Data, Eq, Show, Typeable)
  
  -- | Import namespace, e.g. '* as whatever'
  data JSImportNameSpace
      = JSImportNameSpace !JSBinOp !JSAnnot !JSIdent -- ^ *, as, ident
      deriving (Data, Eq, Show, Typeable)
  
  -- | Named imports, e.g. '{ foo, bar, baz as quux }'
  data JSImportsNamed
      = JSImportsNamed !JSAnnot !(JSCommaList JSImportSpecifier) !JSAnnot -- ^lb, specifiers, rb
      deriving (Data, Eq, Show, Typeable)
  
  -- |
  -- Note that this data type is separate from ExportSpecifier because the
  -- grammar is slightly different (e.g. in handling of reserved words).
  data JSImportSpecifier
      = JSImportSpecifier !JSIdent -- ^ident
      | JSImportSpecifierAs !JSIdent !JSAnnot !JSIdent -- ^ident, as, ident
      deriving (Data, Eq, Show, Typeable)
  
  data JSExportDeclaration
      -- = JSExportAllFrom
      = JSExportFrom JSExportClause JSFromClause !JSSemi -- ^exports, module, semi
      | JSExportLocals JSExportClause !JSSemi -- ^exports, autosemi
      | JSExport !JSStatement !JSSemi -- ^body, autosemi
      -- | JSExportDefault
      deriving (Data, Eq, Show, Typeable)
  
  data JSExportClause
      = JSExportClause !JSAnnot !(JSCommaList JSExportSpecifier) !JSAnnot -- ^lb, specifiers, rb
      deriving (Data, Eq, Show, Typeable)
  
  data JSExportSpecifier
      = JSExportSpecifier !JSIdent -- ^ident
      | JSExportSpecifierAs !JSIdent !JSAnnot !JSIdent -- ^ident1, as, ident2
      deriving (Data, Eq, Show, Typeable)
  
  data JSStatement
      = JSStatementBlock !JSAnnot ![JSStatement] !JSAnnot !JSSemi     -- ^lbrace, stmts, rbrace, autosemi
      | JSBreak !JSAnnot !JSIdent !JSSemi        -- ^break,optional identifier, autosemi
      | JSLet   !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^const, decl, autosemi
      | JSClass !JSAnnot !JSIdent !JSClassHeritage !JSAnnot ![JSClassElement] !JSAnnot !JSSemi -- ^class, name, optional extends clause, lb, body, rb, autosemi
      | JSConstant !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^const, decl, autosemi
      | JSContinue !JSAnnot !JSIdent !JSSemi     -- ^continue, optional identifier,autosemi
      | JSDoWhile !JSAnnot !JSStatement !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSSemi -- ^do,stmt,while,lb,expr,rb,autosemi
      | JSFor !JSAnnot !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSStatement -- ^for,lb,expr,semi,expr,semi,expr,rb.stmt
      | JSForIn !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,expr,in,expr,rb,stmt
      | JSForVar !JSAnnot !JSAnnot !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSStatement -- ^for,lb,var,vardecl,semi,expr,semi,expr,rb,stmt
      | JSForVarIn !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSForLet !JSAnnot !JSAnnot !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSStatement -- ^for,lb,var,vardecl,semi,expr,semi,expr,rb,stmt
      | JSForLetIn !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSForLetOf !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSForConst !JSAnnot !JSAnnot !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSStatement -- ^for,lb,var,vardecl,semi,expr,semi,expr,rb,stmt
      | JSForConstIn !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSForConstOf !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSForOf !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,expr,in,expr,rb,stmt
      | JSForVarOf !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
      | JSAsyncFunction !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi  -- ^fn,name, lb,parameter list,rb,block,autosemi
      | JSFunction !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi  -- ^fn,name, lb,parameter list,rb,block,autosemi
      | JSGenerator !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi  -- ^fn,*,name, lb,parameter list,rb,block,autosemi
      | JSIf !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^if,(,expr,),stmt
      | JSIfElse !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement !JSAnnot !JSStatement -- ^if,(,expr,),stmt,else,rest
      | JSLabelled !JSIdent !JSAnnot !JSStatement -- ^identifier,colon,stmt
      | JSEmptyStatement !JSAnnot
      | JSExpressionStatement !JSExpression !JSSemi
      | JSAssignStatement !JSExpression !JSAssignOp !JSExpression !JSSemi -- ^lhs, assignop, rhs, autosemi
      | JSMethodCall !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSSemi
      | JSReturn !JSAnnot !(Maybe JSExpression) !JSSemi -- ^optional expression,autosemi
      | JSSwitch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSAnnot ![JSSwitchParts] !JSAnnot !JSSemi -- ^switch,lb,expr,rb,caseblock,autosemi
      | JSThrow !JSAnnot !JSExpression !JSSemi -- ^throw val autosemi
      | JSTry !JSAnnot !JSBlock ![JSTryCatch] !JSTryFinally -- ^try,block,catches,finally
      | JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var, decl, autosemi
      | JSWhile !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^while,lb,expr,rb,stmt
      | JSWith !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement !JSSemi -- ^with,lb,expr,rb,stmt list
      deriving (Data, Eq, Show, Typeable)
  
  data JSExpression
      -- | Terminals
      = JSIdentifier !JSAnnot !String
      | JSDecimal !JSAnnot !String
      | JSLiteral !JSAnnot !String
      | JSHexInteger !JSAnnot !String
      | JSOctal !JSAnnot !String
      | JSStringLiteral !JSAnnot !String
      | JSRegEx !JSAnnot !String
  
      -- | Non Terminals
      | JSArrayLiteral !JSAnnot ![JSArrayElement] !JSAnnot -- ^lb, contents, rb
      | JSAssignExpression !JSExpression !JSAssignOp !JSExpression -- ^lhs, assignop, rhs
      | JSAwaitExpression !JSAnnot !JSExpression -- ^await, expr
      | JSCallExpression !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot  -- ^expr, bl, args, rb
      | JSCallExpressionDot !JSExpression !JSAnnot !JSExpression  -- ^expr, dot, expr
      | JSCallExpressionSquare !JSExpression !JSAnnot !JSExpression !JSAnnot  -- ^expr, [, expr, ]
      | JSClassExpression !JSAnnot !JSIdent !JSClassHeritage !JSAnnot ![JSClassElement] !JSAnnot -- ^class, optional identifier, optional extends clause, lb, body, rb
      | JSCommaExpression !JSExpression !JSAnnot !JSExpression          -- ^expression components
      | JSExpressionBinary !JSExpression !JSBinOp !JSExpression -- ^lhs, op, rhs
      | JSExpressionParen !JSAnnot !JSExpression !JSAnnot -- ^lb,expression,rb
      | JSExpressionPostfix !JSExpression !JSUnaryOp -- ^expression, operator
      | JSExpressionTernary !JSExpression !JSAnnot !JSExpression !JSAnnot !JSExpression -- ^cond, ?, trueval, :, falseval
      | JSArrowExpression !JSArrowParameterList !JSAnnot !JSStatement -- ^parameter list,arrow,block`
      | JSFunctionExpression !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^fn,name,lb, parameter list,rb,block`
      | JSGeneratorExpression !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^fn,*,name,lb, parameter list,rb,block`
      | JSMemberDot !JSExpression !JSAnnot !JSExpression -- ^firstpart, dot, name
      | JSMemberExpression !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot -- expr, lb, args, rb
      | JSMemberNew !JSAnnot !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot -- ^new, name, lb, args, rb
      | JSMemberSquare !JSExpression !JSAnnot !JSExpression !JSAnnot -- ^firstpart, lb, expr, rb
      | JSNewExpression !JSAnnot !JSExpression -- ^new, expr
      | JSObjectLiteral !JSAnnot !JSObjectPropertyList !JSAnnot -- ^lbrace contents rbrace
      | JSSpreadExpression !JSAnnot !JSExpression
      | JSTemplateLiteral !(Maybe JSExpression) !JSAnnot !String ![JSTemplatePart] -- ^optional tag, lquot, head, parts
      | JSUnaryExpression !JSUnaryOp !JSExpression
      | JSVarInitExpression !JSExpression !JSVarInitializer -- ^identifier, initializer
      | JSYieldExpression !JSAnnot !(Maybe JSExpression) -- ^yield, optional expr
      | JSYieldFromExpression !JSAnnot !JSAnnot !JSExpression -- ^yield, *, expr
      deriving (Data, Eq, Show, Typeable)
  
  data JSArrowParameterList
      = JSUnparenthesizedArrowParameter !JSIdent
      | JSParenthesizedArrowParameterList !JSAnnot !(JSCommaList JSExpression) !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSBinOp
      = JSBinOpAnd !JSAnnot
      | JSBinOpBitAnd !JSAnnot
      | JSBinOpBitOr !JSAnnot
      | JSBinOpBitXor !JSAnnot
      | JSBinOpDivide !JSAnnot
      | JSBinOpEq !JSAnnot
      | JSBinOpGe !JSAnnot
      | JSBinOpGt !JSAnnot
      | JSBinOpIn !JSAnnot
      | JSBinOpInstanceOf !JSAnnot
      | JSBinOpLe !JSAnnot
      | JSBinOpLsh !JSAnnot
      | JSBinOpLt !JSAnnot
      | JSBinOpMinus !JSAnnot
      | JSBinOpMod !JSAnnot
      | JSBinOpNeq !JSAnnot
      | JSBinOpOf !JSAnnot
      | JSBinOpOr !JSAnnot
      | JSBinOpPlus !JSAnnot
      | JSBinOpRsh !JSAnnot
      | JSBinOpStrictEq !JSAnnot
      | JSBinOpStrictNeq !JSAnnot
      | JSBinOpTimes !JSAnnot
      | JSBinOpUrsh !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSUnaryOp
      = JSUnaryOpDecr !JSAnnot
      | JSUnaryOpDelete !JSAnnot
      | JSUnaryOpIncr !JSAnnot
      | JSUnaryOpMinus !JSAnnot
      | JSUnaryOpNot !JSAnnot
      | JSUnaryOpPlus !JSAnnot
      | JSUnaryOpTilde !JSAnnot
      | JSUnaryOpTypeof !JSAnnot
      | JSUnaryOpVoid !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSSemi
      = JSSemi !JSAnnot
      | JSSemiAuto
      deriving (Data, Eq, Show, Typeable)
  
  data JSAssignOp
      = JSAssign !JSAnnot
      | JSTimesAssign !JSAnnot
      | JSDivideAssign !JSAnnot
      | JSModAssign !JSAnnot
      | JSPlusAssign !JSAnnot
      | JSMinusAssign !JSAnnot
      | JSLshAssign !JSAnnot
      | JSRshAssign !JSAnnot
      | JSUrshAssign !JSAnnot
      | JSBwAndAssign !JSAnnot
      | JSBwXorAssign !JSAnnot
      | JSBwOrAssign !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSTryCatch
      = JSCatch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,rb,block
      | JSCatchIf !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,if,expr,rb,block
      deriving (Data, Eq, Show, Typeable)
  
  data JSTryFinally
      = JSFinally !JSAnnot !JSBlock -- ^finally,block
      | JSNoFinally
      deriving (Data, Eq, Show, Typeable)
  
  data JSBlock
      = JSBlock !JSAnnot ![JSStatement] !JSAnnot -- ^lbrace, stmts, rbrace
      deriving (Data, Eq, Show, Typeable)
  
  data JSSwitchParts
      = JSCase !JSAnnot !JSExpression !JSAnnot ![JSStatement]    -- ^expr,colon,stmtlist
      | JSDefault !JSAnnot !JSAnnot ![JSStatement] -- ^colon,stmtlist
      deriving (Data, Eq, Show, Typeable)
  
  data JSVarInitializer
      = JSVarInit !JSAnnot !JSExpression -- ^ assignop, initializer
      | JSVarInitNone
      deriving (Data, Eq, Show, Typeable)
  
  data JSObjectProperty
      = JSPropertyNameandValue !JSPropertyName !JSAnnot ![JSExpression] -- ^name, colon, value
      | JSPropertyIdentRef !JSAnnot !String
      | JSObjectMethod !JSMethodDefinition
      deriving (Data, Eq, Show, Typeable)
  
  data JSMethodDefinition
      = JSMethodDefinition !JSPropertyName !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- name, lb, params, rb, block
      | JSGeneratorMethodDefinition !JSAnnot !JSPropertyName !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^*, name, lb, params, rb, block
      | JSPropertyAccessor !JSAccessor !JSPropertyName !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^get/set, name, lb, params, rb, block
      deriving (Data, Eq, Show, Typeable)
  
  data JSPropertyName
      = JSPropertyIdent !JSAnnot !String
      | JSPropertyString !JSAnnot !String
      | JSPropertyNumber !JSAnnot !String
      | JSPropertyComputed !JSAnnot !JSExpression !JSAnnot -- ^lb, expr, rb
      deriving (Data, Eq, Show, Typeable)
  
  type JSObjectPropertyList = JSCommaTrailingList JSObjectProperty
  
  -- | Accessors for JSObjectProperty is either 'get' or 'set'.
  data JSAccessor
      = JSAccessorGet !JSAnnot
      | JSAccessorSet !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSIdent
      = JSIdentName !JSAnnot !String
      | JSIdentNone
      deriving (Data, Eq, Show, Typeable)
  
  data JSArrayElement
      = JSArrayElement !JSExpression
      | JSArrayComma !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  data JSCommaList a
      = JSLCons !(JSCommaList a) !JSAnnot !a -- ^head, comma, a
      | JSLOne !a -- ^ single element (no comma)
      | JSLNil
      deriving (Data, Eq, Show, Typeable)
  
  data JSCommaTrailingList a
      = JSCTLComma !(JSCommaList a) !JSAnnot -- ^list, trailing comma
      | JSCTLNone !(JSCommaList a) -- ^list
      deriving (Data, Eq, Show, Typeable)
  
  data JSTemplatePart
      = JSTemplatePart !JSExpression !JSAnnot !String -- ^expr, rb, suffix
      deriving (Data, Eq, Show, Typeable)
  
  data JSClassHeritage
      = JSExtends !JSAnnot !JSExpression
      | JSExtendsNone
      deriving (Data, Eq, Show, Typeable)
  
  data JSClassElement
      = JSClassInstanceMethod !JSMethodDefinition
      | JSClassStaticMethod !JSAnnot !JSMethodDefinition
      | JSClassSemi !JSAnnot
      deriving (Data, Eq, Show, Typeable)
  
  -- -----------------------------------------------------------------------------
  -- | Show the AST elements stripped of their JSAnnot data.
  
  -- Strip out the location info
  showStripped :: JSAST -> String
  showStripped (JSAstProgram xs _) = "JSAstProgram " ++ ss xs
  showStripped (JSAstModule xs _) = "JSAstModule " ++ ss xs
  showStripped (JSAstStatement s _) = "JSAstStatement (" ++ ss s ++ ")"
  showStripped (JSAstExpression e _) = "JSAstExpression (" ++ ss e ++ ")"
  showStripped (JSAstLiteral s _)  = "JSAstLiteral (" ++ ss s ++ ")"
  
  
  class ShowStripped a where
      ss :: a -> String
  
  instance ShowStripped JSStatement where
      ss (JSStatementBlock _ xs _ _) = "JSStatementBlock " ++ ss xs
      ss (JSBreak _ JSIdentNone s) = "JSBreak" ++ commaIf (ss s)
      ss (JSBreak _ (JSIdentName _ n) s) = "JSBreak " ++ singleQuote n ++ commaIf (ss s)
      ss (JSClass _ n h _lb xs _rb _) = "JSClass " ++ ssid n ++ " (" ++ ss h ++ ") " ++ ss xs
      ss (JSContinue _ JSIdentNone s) = "JSContinue" ++ commaIf (ss s)
      ss (JSContinue _ (JSIdentName _ n) s) = "JSContinue " ++ singleQuote n ++ commaIf (ss s)
      ss (JSConstant _ xs _as) = "JSConstant " ++ ss xs
      ss (JSDoWhile _d x1 _w _lb x2 _rb x3) = "JSDoWhile (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSFor _ _lb x1s _s1 x2s _s2 x3s _rb x4) = "JSFor " ++ ss x1s ++ " " ++ ss x2s ++ " " ++ ss x3s ++ " (" ++ ss x4 ++ ")"
      ss (JSForIn _ _lb x1s _i x2 _rb x3) = "JSForIn " ++ ss x1s ++ " (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForVar _ _lb _v x1s _s1 x2s _s2 x3s _rb x4) = "JSForVar " ++ ss x1s ++ " " ++ ss x2s ++ " " ++ ss x3s ++ " (" ++ ss x4 ++ ")"
      ss (JSForVarIn _ _lb _v x1 _i x2 _rb x3) = "JSForVarIn (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForLet _ _lb _v x1s _s1 x2s _s2 x3s _rb x4) = "JSForLet " ++ ss x1s ++ " " ++ ss x2s ++ " " ++ ss x3s ++ " (" ++ ss x4 ++ ")"
      ss (JSForLetIn _ _lb _v x1 _i x2 _rb x3) = "JSForLetIn (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForLetOf _ _lb _v x1 _i x2 _rb x3) = "JSForLetOf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForConst _ _lb _v x1s _s1 x2s _s2 x3s _rb x4) = "JSForConst " ++ ss x1s ++ " " ++ ss x2s ++ " " ++ ss x3s ++ " (" ++ ss x4 ++ ")"
      ss (JSForConstIn _ _lb _v x1 _i x2 _rb x3) = "JSForConstIn (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForConstOf _ _lb _v x1 _i x2 _rb x3) = "JSForConstOf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForOf _ _lb x1s _i x2 _rb x3) = "JSForOf " ++ ss x1s ++ " (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSForVarOf _ _lb _v x1 _i x2 _rb x3) = "JSForVarOf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSFunction _ n _lb pl _rb x3 _) = "JSFunction " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
      ss (JSAsyncFunction _ _ n _lb pl _rb x3 _) = "JSAsyncFunction " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
      ss (JSGenerator _ _ n _lb pl _rb x3 _) = "JSGenerator " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
      ss (JSIf _ _lb x1 _rb x2) = "JSIf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")"
      ss (JSIfElse _ _lb x1 _rb x2 _e x3) = "JSIfElse (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
      ss (JSLabelled x1 _c x2) = "JSLabelled (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")"
      ss (JSLet _ xs _as) = "JSLet " ++ ss xs
      ss (JSEmptyStatement _) = "JSEmptyStatement"
      ss (JSExpressionStatement l s) = ss l ++ (let x = ss s in if not (null x) then ',':x else "")
      ss (JSAssignStatement lhs op rhs s) ="JSOpAssign (" ++ ss op ++ "," ++ ss lhs ++ "," ++ ss rhs ++ (let x = ss s in if not (null x) then "),"++x else ")")
      ss (JSMethodCall e _ a _ s) = "JSMethodCall (" ++ ss e ++ ",JSArguments " ++ ss a ++ (let x = ss s in if not (null x) then "),"++x else ")")
      ss (JSReturn _ (Just me) s) = "JSReturn " ++ ss me ++ " " ++ ss s
      ss (JSReturn _ Nothing s) = "JSReturn " ++ ss s
      ss (JSSwitch _ _lp x _rp _lb x2 _rb _) = "JSSwitch (" ++ ss x ++ ") " ++ ss x2
      ss (JSThrow _ x _) = "JSThrow (" ++ ss x ++ ")"
      ss (JSTry _ xt1 xtc xtf) = "JSTry (" ++ ss xt1 ++ "," ++ ss xtc ++ "," ++ ss xtf ++ ")"
      ss (JSVariable _ xs _as) = "JSVariable " ++ ss xs
      ss (JSWhile _ _lb x1 _rb x2) = "JSWhile (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")"
      ss (JSWith _ _lb x1 _rb x _) = "JSWith (" ++ ss x1 ++ ") (" ++ ss x ++ ")"
  
  instance ShowStripped JSExpression where
      ss (JSArrayLiteral _lb xs _rb) = "JSArrayLiteral " ++ ss xs
      ss (JSAssignExpression lhs op rhs) = "JSOpAssign (" ++ ss op ++ "," ++ ss lhs ++ "," ++ ss rhs ++ ")"
      ss (JSAwaitExpression _ e) = "JSAwaitExpresson " ++ ss e
      ss (JSCallExpression ex _ xs _) = "JSCallExpression ("++ ss ex ++ ",JSArguments " ++ ss xs ++ ")"
      ss (JSCallExpressionDot ex _os xs) = "JSCallExpressionDot (" ++ ss ex ++ "," ++ ss xs ++ ")"
      ss (JSCallExpressionSquare ex _os xs _cs) = "JSCallExpressionSquare (" ++ ss ex ++ "," ++ ss xs ++ ")"
      ss (JSClassExpression _ n h _lb xs _rb) = "JSClassExpression " ++ ssid n ++ " (" ++ ss h ++ ") " ++ ss xs
      ss (JSDecimal _ s) = "JSDecimal " ++ singleQuote s
      ss (JSCommaExpression l _ r) = "JSExpression [" ++ ss l ++ "," ++ ss r ++ "]"
      ss (JSExpressionBinary x2 op x3) = "JSExpressionBinary (" ++ ss op ++ "," ++ ss x2 ++ "," ++ ss x3 ++ ")"
      ss (JSExpressionParen _lp x _rp) = "JSExpressionParen (" ++ ss x ++ ")"
      ss (JSExpressionPostfix xs op) = "JSExpressionPostfix (" ++ ss op ++ "," ++ ss xs ++ ")"
      ss (JSExpressionTernary x1 _q x2 _c x3) = "JSExpressionTernary (" ++ ss x1 ++ "," ++ ss x2 ++ "," ++ ss x3 ++ ")"
      ss (JSArrowExpression ps _ e) = "JSArrowExpression (" ++ ss ps ++ ") => " ++ ss e
      ss (JSFunctionExpression _ n _lb pl _rb x3) = "JSFunctionExpression " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
      ss (JSGeneratorExpression _ _ n _lb pl _rb x3) = "JSGeneratorExpression " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
      ss (JSHexInteger _ s) = "JSHexInteger " ++ singleQuote s
      ss (JSOctal _ s) = "JSOctal " ++ singleQuote s
      ss (JSIdentifier _ s) = "JSIdentifier " ++ singleQuote s
      ss (JSLiteral _ []) = "JSLiteral ''"
      ss (JSLiteral _ s) = "JSLiteral " ++ singleQuote s
      ss (JSMemberDot x1s _d x2 ) = "JSMemberDot (" ++ ss x1s ++ "," ++ ss x2 ++ ")"
      ss (JSMemberExpression e _ a _) = "JSMemberExpression (" ++ ss e ++ ",JSArguments " ++ ss a ++ ")"
      ss (JSMemberNew _a n _ s _) = "JSMemberNew (" ++ ss n ++ ",JSArguments " ++ ss s ++ ")"
      ss (JSMemberSquare x1s _lb x2 _rb) = "JSMemberSquare (" ++ ss x1s ++ "," ++ ss x2 ++ ")"
      ss (JSNewExpression _n e) = "JSNewExpression " ++ ss e
      ss (JSObjectLiteral _lb xs _rb) = "JSObjectLiteral " ++ ss xs
      ss (JSRegEx _ s) = "JSRegEx " ++ singleQuote s
      ss (JSStringLiteral _ s) = "JSStringLiteral " ++ s
      ss (JSUnaryExpression op x) = "JSUnaryExpression (" ++ ss op ++ "," ++ ss x ++ ")"
      ss (JSVarInitExpression x1 x2) = "JSVarInitExpression (" ++ ss x1 ++ ") " ++ ss x2
      ss (JSYieldExpression _ Nothing) = "JSYieldExpression ()"
      ss (JSYieldExpression _ (Just x)) = "JSYieldExpression (" ++ ss x ++ ")"
      ss (JSYieldFromExpression _ _ x) = "JSYieldFromExpression (" ++ ss x ++ ")"
      ss (JSSpreadExpression _ x1) = "JSSpreadExpression (" ++ ss x1 ++ ")"
      ss (JSTemplateLiteral Nothing _ s ps) = "JSTemplateLiteral (()," ++ singleQuote s ++ "," ++ ss ps ++ ")"
      ss (JSTemplateLiteral (Just t) _ s ps) = "JSTemplateLiteral ((" ++ ss t ++ ")," ++ singleQuote s ++ "," ++ ss ps ++ ")"
  
  instance ShowStripped JSArrowParameterList where
      ss (JSUnparenthesizedArrowParameter x) = ss x
      ss (JSParenthesizedArrowParameterList _ xs _) = ss xs
  
  instance ShowStripped JSModuleItem where
      ss (JSModuleExportDeclaration _ x1) = "JSModuleExportDeclaration (" ++ ss x1 ++ ")"
      ss (JSModuleImportDeclaration _ x1) = "JSModuleImportDeclaration (" ++ ss x1 ++ ")"
      ss (JSModuleStatementListItem x1) = "JSModuleStatementListItem (" ++ ss x1 ++ ")"
  
  instance ShowStripped JSImportDeclaration where
      ss (JSImportDeclaration imp from _) = "JSImportDeclaration (" ++ ss imp ++ "," ++ ss from ++ ")"
      ss (JSImportDeclarationBare _ m _) = "JSImportDeclarationBare (" ++ singleQuote m ++ ")"
  
  instance ShowStripped JSImportClause where
      ss (JSImportClauseDefault x) = "JSImportClauseDefault (" ++ ss x ++ ")"
      ss (JSImportClauseNameSpace x) = "JSImportClauseNameSpace (" ++ ss x ++ ")"
      ss (JSImportClauseNamed x) = "JSImportClauseNameSpace (" ++ ss x ++ ")"
      ss (JSImportClauseDefaultNameSpace x1 _ x2) = "JSImportClauseDefaultNameSpace (" ++ ss x1 ++ "," ++ ss x2 ++ ")"
      ss (JSImportClauseDefaultNamed x1 _ x2) = "JSImportClauseDefaultNamed (" ++ ss x1 ++ "," ++ ss x2 ++ ")"
  
  instance ShowStripped JSFromClause where
      ss (JSFromClause _ _ m) = "JSFromClause " ++ singleQuote m
  
  instance ShowStripped JSImportNameSpace where
      ss (JSImportNameSpace _ _ x) = "JSImportNameSpace (" ++ ss x ++ ")"
  
  instance ShowStripped JSImportsNamed where
      ss (JSImportsNamed _ xs _) = "JSImportsNamed (" ++ ss xs ++ ")"
  
  instance ShowStripped JSImportSpecifier where
      ss (JSImportSpecifier x1) = "JSImportSpecifier (" ++ ss x1 ++ ")"
      ss (JSImportSpecifierAs x1 _ x2) = "JSImportSpecifierAs (" ++ ss x1 ++ "," ++ ss x2 ++ ")"
  
  instance ShowStripped JSExportDeclaration where
      ss (JSExportFrom xs from _) = "JSExportFrom (" ++ ss xs ++ "," ++ ss from ++ ")"
      ss (JSExportLocals xs _) = "JSExportLocals (" ++ ss xs ++ ")"
      ss (JSExport x1 _) = "JSExport (" ++ ss x1 ++ ")"
  
  instance ShowStripped JSExportClause where
      ss (JSExportClause _ xs _) = "JSExportClause (" ++ ss xs ++ ")"
  
  instance ShowStripped JSExportSpecifier where
      ss (JSExportSpecifier x1) = "JSExportSpecifier (" ++ ss x1 ++ ")"
      ss (JSExportSpecifierAs x1 _ x2) = "JSExportSpecifierAs (" ++ ss x1 ++ "," ++ ss x2 ++ ")"
  
  instance ShowStripped JSTryCatch where
      ss (JSCatch _ _lb x1 _rb x3) = "JSCatch (" ++ ss x1 ++ "," ++ ss x3 ++ ")"
      ss (JSCatchIf _ _lb x1 _ ex _rb x3) = "JSCatch (" ++ ss x1 ++ ") if " ++ ss ex ++ " (" ++ ss x3 ++ ")"
  
  instance ShowStripped JSTryFinally where
      ss (JSFinally _ x) = "JSFinally (" ++ ss x ++ ")"
      ss JSNoFinally = "JSFinally ()"
  
  instance ShowStripped JSIdent where
      ss (JSIdentName _ s) = "JSIdentifier " ++ singleQuote s
      ss JSIdentNone = "JSIdentNone"
  
  instance ShowStripped JSObjectProperty where
      ss (JSPropertyNameandValue x1 _colon x2s) = "JSPropertyNameandValue (" ++ ss x1 ++ ") " ++ ss x2s
      ss (JSPropertyIdentRef _ s) = "JSPropertyIdentRef " ++ singleQuote s
      ss (JSObjectMethod m) = ss m
  
  instance ShowStripped JSMethodDefinition where
      ss (JSMethodDefinition x1 _lb1 x2s _rb1 x3) = "JSMethodDefinition (" ++ ss x1 ++ ") " ++ ss x2s ++ " (" ++ ss x3 ++ ")"
      ss (JSPropertyAccessor s x1 _lb1 x2s _rb1 x3) = "JSPropertyAccessor " ++ ss s ++ " (" ++ ss x1 ++ ") " ++ ss x2s ++ " (" ++ ss x3 ++ ")"
      ss (JSGeneratorMethodDefinition _ x1 _lb1 x2s _rb1 x3) = "JSGeneratorMethodDefinition (" ++ ss x1 ++ ") " ++ ss x2s ++ " (" ++ ss x3 ++ ")"
  
  instance ShowStripped JSPropertyName where
      ss (JSPropertyIdent _ s) = "JSIdentifier " ++ singleQuote s
      ss (JSPropertyString _ s) = "JSIdentifier " ++ singleQuote s
      ss (JSPropertyNumber _ s) = "JSIdentifier " ++ singleQuote s
      ss (JSPropertyComputed _ x _) = "JSPropertyComputed (" ++ ss x ++ ")"
  
  instance ShowStripped JSAccessor where
      ss (JSAccessorGet _) = "JSAccessorGet"
      ss (JSAccessorSet _) = "JSAccessorSet"
  
  instance ShowStripped JSBlock where
      ss (JSBlock _ xs _) = "JSBlock " ++ ss xs
  
  instance ShowStripped JSSwitchParts where
      ss (JSCase _ x1 _c x2s) = "JSCase (" ++ ss x1 ++ ") (" ++ ss x2s ++ ")"
      ss (JSDefault _ _c xs) = "JSDefault (" ++ ss xs ++ ")"
  
  instance ShowStripped JSBinOp where
      ss (JSBinOpAnd _) = "'&&'"
      ss (JSBinOpBitAnd _) = "'&'"
      ss (JSBinOpBitOr _) = "'|'"
      ss (JSBinOpBitXor _) = "'^'"
      ss (JSBinOpDivide _) = "'/'"
      ss (JSBinOpEq _) = "'=='"
      ss (JSBinOpGe _) = "'>='"
      ss (JSBinOpGt _) = "'>'"
      ss (JSBinOpIn _) = "'in'"
      ss (JSBinOpInstanceOf _) = "'instanceof'"
      ss (JSBinOpLe _) = "'<='"
      ss (JSBinOpLsh _) = "'<<'"
      ss (JSBinOpLt _) = "'<'"
      ss (JSBinOpMinus _) = "'-'"
      ss (JSBinOpMod _) = "'%'"
      ss (JSBinOpNeq _) = "'!='"
      ss (JSBinOpOf _) = "'of'"
      ss (JSBinOpOr _) = "'||'"
      ss (JSBinOpPlus _) = "'+'"
      ss (JSBinOpRsh _) = "'>>'"
      ss (JSBinOpStrictEq _) = "'==='"
      ss (JSBinOpStrictNeq _) = "'!=='"
      ss (JSBinOpTimes _) = "'*'"
      ss (JSBinOpUrsh _) = "'>>>'"
  
  instance ShowStripped JSUnaryOp where
      ss (JSUnaryOpDecr _) = "'--'"
      ss (JSUnaryOpDelete _) = "'delete'"
      ss (JSUnaryOpIncr _) = "'++'"
      ss (JSUnaryOpMinus _) = "'-'"
      ss (JSUnaryOpNot _) = "'!'"
      ss (JSUnaryOpPlus _) = "'+'"
      ss (JSUnaryOpTilde _) = "'~'"
      ss (JSUnaryOpTypeof _) = "'typeof'"
      ss (JSUnaryOpVoid _) = "'void'"
  
  instance ShowStripped JSAssignOp where
      ss (JSAssign _) = "'='"
      ss (JSTimesAssign _) = "'*='"
      ss (JSDivideAssign _) = "'/='"
      ss (JSModAssign _) = "'%='"
      ss (JSPlusAssign _) = "'+='"
      ss (JSMinusAssign _) = "'-='"
      ss (JSLshAssign _) = "'<<='"
      ss (JSRshAssign _) = "'>>='"
      ss (JSUrshAssign _) = "'>>>='"
      ss (JSBwAndAssign _) = "'&='"
      ss (JSBwXorAssign _) = "'^='"
      ss (JSBwOrAssign _) = "'|='"
  
  instance ShowStripped JSVarInitializer where
      ss (JSVarInit _ n) = "[" ++ ss n ++ "]"
      ss JSVarInitNone = ""
  
  instance ShowStripped JSSemi where
      ss (JSSemi _) = "JSSemicolon"
      ss JSSemiAuto = ""
  
  instance ShowStripped JSArrayElement where
      ss (JSArrayElement e) = ss e
      ss (JSArrayComma _) = "JSComma"
  
  instance ShowStripped JSTemplatePart where
      ss (JSTemplatePart e _ s) = "(" ++ ss e ++ "," ++ singleQuote s ++ ")"
  
  instance ShowStripped JSClassHeritage where
      ss JSExtendsNone = ""
      ss (JSExtends _ x) = ss x
  
  instance ShowStripped JSClassElement where
      ss (JSClassInstanceMethod m) = ss m
      ss (JSClassStaticMethod _ m) = "JSClassStaticMethod (" ++ ss m ++ ")"
      ss (JSClassSemi _) = "JSClassSemi"
  
  instance ShowStripped a => ShowStripped (JSCommaList a) where
      ss xs = "(" ++ commaJoin (map ss $ fromCommaList xs) ++ ")"
  
  instance ShowStripped a => ShowStripped (JSCommaTrailingList a) where
      ss (JSCTLComma xs _) = "[" ++ commaJoin (map ss $ fromCommaList xs) ++ ",JSComma]"
      ss (JSCTLNone xs)    = "[" ++ commaJoin (map ss $ fromCommaList xs) ++ "]"
  
  instance ShowStripped a => ShowStripped [a] where
      ss xs = "[" ++ commaJoin (map ss xs) ++ "]"
  
  -- -----------------------------------------------------------------------------
  -- Helpers.
  
  commaJoin :: [String] -> String
  commaJoin s = intercalate "," $ filter (not . null) s
  
  fromCommaList :: JSCommaList a -> [a]
  fromCommaList (JSLCons l _ i) = fromCommaList l ++ [i]
  fromCommaList (JSLOne i)      = [i]
  fromCommaList JSLNil = []
  
  singleQuote :: String -> String
  singleQuote s = '\'' : (s ++ "'")
  
  ssid :: JSIdent -> String
  ssid (JSIdentName _ s) = singleQuote s
  ssid JSIdentNone = "''"
  
  commaIf :: String -> String
  commaIf "" = ""
  commaIf xs = ',' : xs
  
  
  deAnnot :: JSBinOp -> JSBinOp
  deAnnot (JSBinOpAnd _) = JSBinOpAnd JSNoAnnot
  deAnnot (JSBinOpBitAnd _) = JSBinOpBitAnd JSNoAnnot
  deAnnot (JSBinOpBitOr _) = JSBinOpBitOr JSNoAnnot
  deAnnot (JSBinOpBitXor _) = JSBinOpBitXor JSNoAnnot
  deAnnot (JSBinOpDivide _) = JSBinOpDivide JSNoAnnot
  deAnnot (JSBinOpEq _) = JSBinOpEq JSNoAnnot
  deAnnot (JSBinOpGe _) = JSBinOpGe JSNoAnnot
  deAnnot (JSBinOpGt _) = JSBinOpGt JSNoAnnot
  deAnnot (JSBinOpIn _) = JSBinOpIn JSNoAnnot
  deAnnot (JSBinOpInstanceOf _) = JSBinOpInstanceOf JSNoAnnot
  deAnnot (JSBinOpLe _) = JSBinOpLe JSNoAnnot
  deAnnot (JSBinOpLsh _) = JSBinOpLsh JSNoAnnot
  deAnnot (JSBinOpLt _) = JSBinOpLt JSNoAnnot
  deAnnot (JSBinOpMinus _) = JSBinOpMinus JSNoAnnot
  deAnnot (JSBinOpMod _) = JSBinOpMod JSNoAnnot
  deAnnot (JSBinOpNeq _) = JSBinOpNeq JSNoAnnot
  deAnnot (JSBinOpOf _) = JSBinOpOf JSNoAnnot
  deAnnot (JSBinOpOr _) = JSBinOpOr JSNoAnnot
  deAnnot (JSBinOpPlus _) = JSBinOpPlus JSNoAnnot
  deAnnot (JSBinOpRsh _) = JSBinOpRsh JSNoAnnot
  deAnnot (JSBinOpStrictEq _) = JSBinOpStrictEq JSNoAnnot
  deAnnot (JSBinOpStrictNeq _) = JSBinOpStrictNeq JSNoAnnot
  deAnnot (JSBinOpTimes _) = JSBinOpTimes JSNoAnnot
  deAnnot (JSBinOpUrsh _) = JSBinOpUrsh JSNoAnnot
  
  binOpEq :: JSBinOp -> JSBinOp -> Bool
  binOpEq a b = deAnnot a == deAnnot b
  