PageRenderTime 99ms CodeModel.GetById 25ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 2ms

/src/nodes.coffee

http://github.com/jashkenas/coffee-script
CoffeeScript | 6111 lines | 3085 code | 559 blank | 2467 comment | 610 complexity | 75430d71fe3a3205f3c9b75d5441a518 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1# `nodes.coffee` contains all of the node classes for the syntax tree. Most
   2# nodes are created as the result of actions in the [grammar](grammar.html),
   3# but some are created by other nodes as a method of code generation. To convert
   4# the syntax tree into a string of JavaScript code, call `compile()` on the root.
   5
   6Error.stackTraceLimit = Infinity
   7
   8{Scope} = require './scope'
   9{isUnassignable, JS_FORBIDDEN} = require './lexer'
  10
  11# Import the helpers we plan to use.
  12{compact, flatten, extend, merge, del, starts, ends, some,
  13addDataToNode, attachCommentsToNode, locationDataToString,
  14throwSyntaxError, replaceUnicodeCodePointEscapes,
  15isFunction, isPlainObject, isNumber, parseNumber} = require './helpers'
  16
  17# Functions required by parser.
  18exports.extend = extend
  19exports.addDataToNode = addDataToNode
  20
  21# Constant functions for nodes that don’t need customization.
  22YES     = -> yes
  23NO      = -> no
  24THIS    = -> this
  25NEGATE  = -> @negated = not @negated; this
  26
  27#### CodeFragment
  28
  29# The various nodes defined below all compile to a collection of **CodeFragment** objects.
  30# A CodeFragments is a block of generated code, and the location in the source file where the code
  31# came from. CodeFragments can be assembled together into working code just by catting together
  32# all the CodeFragments' `code` snippets, in order.
  33exports.CodeFragment = class CodeFragment
  34  constructor: (parent, code) ->
  35    @code = "#{code}"
  36    @type = parent?.constructor?.name or 'unknown'
  37    @locationData = parent?.locationData
  38    @comments = parent?.comments
  39
  40  toString: ->
  41    # This is only intended for debugging.
  42    "#{@code}#{if @locationData then ": " + locationDataToString(@locationData) else ''}"
  43
  44# Convert an array of CodeFragments into a string.
  45fragmentsToText = (fragments) ->
  46  (fragment.code for fragment in fragments).join('')
  47
  48#### Base
  49
  50# The **Base** is the abstract base class for all nodes in the syntax tree.
  51# Each subclass implements the `compileNode` method, which performs the
  52# code generation for that node. To compile a node to JavaScript,
  53# call `compile` on it, which wraps `compileNode` in some generic extra smarts,
  54# to know when the generated code needs to be wrapped up in a closure.
  55# An options hash is passed and cloned throughout, containing information about
  56# the environment from higher in the tree (such as if a returned value is
  57# being requested by the surrounding function), information about the current
  58# scope, and indentation level.
  59exports.Base = class Base
  60
  61  compile: (o, lvl) ->
  62    fragmentsToText @compileToFragments o, lvl
  63
  64  # Occasionally a node is compiled multiple times, for example to get the name
  65  # of a variable to add to scope tracking. When we know that a “premature”
  66  # compilation won’t result in comments being output, set those comments aside
  67  # so that they’re preserved for a later `compile` call that will result in
  68  # the comments being included in the output.
  69  compileWithoutComments: (o, lvl, method = 'compile') ->
  70    if @comments
  71      @ignoreTheseCommentsTemporarily = @comments
  72      delete @comments
  73    unwrapped = @unwrapAll()
  74    if unwrapped.comments
  75      unwrapped.ignoreTheseCommentsTemporarily = unwrapped.comments
  76      delete unwrapped.comments
  77
  78    fragments = @[method] o, lvl
  79
  80    if @ignoreTheseCommentsTemporarily
  81      @comments = @ignoreTheseCommentsTemporarily
  82      delete @ignoreTheseCommentsTemporarily
  83    if unwrapped.ignoreTheseCommentsTemporarily
  84      unwrapped.comments = unwrapped.ignoreTheseCommentsTemporarily
  85      delete unwrapped.ignoreTheseCommentsTemporarily
  86
  87    fragments
  88
  89  compileNodeWithoutComments: (o, lvl) ->
  90    @compileWithoutComments o, lvl, 'compileNode'
  91
  92  # Common logic for determining whether to wrap this node in a closure before
  93  # compiling it, or to compile directly. We need to wrap if this node is a
  94  # *statement*, and it's not a *pureStatement*, and we're not at
  95  # the top level of a block (which would be unnecessary), and we haven't
  96  # already been asked to return the result (because statements know how to
  97  # return results).
  98  compileToFragments: (o, lvl) ->
  99    o        = extend {}, o
 100    o.level  = lvl if lvl
 101    node     = @unfoldSoak(o) or this
 102    node.tab = o.indent
 103
 104    fragments = if o.level is LEVEL_TOP or not node.isStatement(o)
 105      node.compileNode o
 106    else
 107      node.compileClosure o
 108    @compileCommentFragments o, node, fragments
 109    fragments
 110
 111  compileToFragmentsWithoutComments: (o, lvl) ->
 112    @compileWithoutComments o, lvl, 'compileToFragments'
 113
 114  # Statements converted into expressions via closure-wrapping share a scope
 115  # object with their parent closure, to preserve the expected lexical scope.
 116  compileClosure: (o) ->
 117    @checkForPureStatementInExpression()
 118    o.sharedScope = yes
 119    func = new Code [], Block.wrap [this]
 120    args = []
 121    if @contains ((node) -> node instanceof SuperCall)
 122      func.bound = yes
 123    else if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
 124      args = [new ThisLiteral]
 125      if argumentsNode
 126        meth = 'apply'
 127        args.push new IdentifierLiteral 'arguments'
 128      else
 129        meth = 'call'
 130      func = new Value func, [new Access new PropertyName meth]
 131    parts = (new Call func, args).compileNode o
 132
 133    switch
 134      when func.isGenerator or func.base?.isGenerator
 135        parts.unshift @makeCode "(yield* "
 136        parts.push    @makeCode ")"
 137      when func.isAsync or func.base?.isAsync
 138        parts.unshift @makeCode "(await "
 139        parts.push    @makeCode ")"
 140    parts
 141
 142  compileCommentFragments: (o, node, fragments) ->
 143    return fragments unless node.comments
 144    # This is where comments, that are attached to nodes as a `comments`
 145    # property, become `CodeFragment`s. “Inline block comments,” e.g.
 146    # `/* */`-delimited comments that are interspersed within code on a line,
 147    # are added to the current `fragments` stream. All other fragments are
 148    # attached as properties to the nearest preceding or following fragment,
 149    # to remain stowaways until they get properly output in `compileComments`
 150    # later on.
 151    unshiftCommentFragment = (commentFragment) ->
 152      if commentFragment.unshift
 153        # Find the first non-comment fragment and insert `commentFragment`
 154        # before it.
 155        unshiftAfterComments fragments, commentFragment
 156      else
 157        if fragments.length isnt 0
 158          precedingFragment = fragments[fragments.length - 1]
 159          if commentFragment.newLine and precedingFragment.code isnt '' and
 160             not /\n\s*$/.test precedingFragment.code
 161            commentFragment.code = "\n#{commentFragment.code}"
 162        fragments.push commentFragment
 163
 164    for comment in node.comments when comment not in @compiledComments
 165      @compiledComments.push comment # Don’t output this comment twice.
 166      # For block/here comments, denoted by `###`, that are inline comments
 167      # like `1 + ### comment ### 2`, create fragments and insert them into
 168      # the fragments array.
 169      # Otherwise attach comment fragments to their closest fragment for now,
 170      # so they can be inserted into the output later after all the newlines
 171      # have been added.
 172      if comment.here # Block comment, delimited by `###`.
 173        commentFragment = new HereComment(comment).compileNode o
 174      else # Line comment, delimited by `#`.
 175        commentFragment = new LineComment(comment).compileNode o
 176      if (commentFragment.isHereComment and not commentFragment.newLine) or
 177         node.includeCommentFragments()
 178        # Inline block comments, like `1 + /* comment */ 2`, or a node whose
 179        # `compileToFragments` method has logic for outputting comments.
 180        unshiftCommentFragment commentFragment
 181      else
 182        fragments.push @makeCode '' if fragments.length is 0
 183        if commentFragment.unshift
 184          fragments[0].precedingComments ?= []
 185          fragments[0].precedingComments.push commentFragment
 186        else
 187          fragments[fragments.length - 1].followingComments ?= []
 188          fragments[fragments.length - 1].followingComments.push commentFragment
 189    fragments
 190
 191  # If the code generation wishes to use the result of a complex expression
 192  # in multiple places, ensure that the expression is only ever evaluated once,
 193  # by assigning it to a temporary variable. Pass a level to precompile.
 194  #
 195  # If `level` is passed, then returns `[val, ref]`, where `val` is the compiled value, and `ref`
 196  # is the compiled reference. If `level` is not passed, this returns `[val, ref]` where
 197  # the two values are raw nodes which have not been compiled.
 198  cache: (o, level, shouldCache) ->
 199    complex = if shouldCache? then shouldCache this else @shouldCache()
 200    if complex
 201      ref = new IdentifierLiteral o.scope.freeVariable 'ref'
 202      sub = new Assign ref, this
 203      if level then [sub.compileToFragments(o, level), [@makeCode(ref.value)]] else [sub, ref]
 204    else
 205      ref = if level then @compileToFragments o, level else this
 206      [ref, ref]
 207
 208  # Occasionally it may be useful to make an expression behave as if it was 'hoisted', whereby the
 209  # result of the expression is available before its location in the source, but the expression's
 210  # variable scope corresponds to the source position. This is used extensively to deal with executable
 211  # class bodies in classes.
 212  #
 213  # Calling this method mutates the node, proxying the `compileNode` and `compileToFragments`
 214  # methods to store their result for later replacing the `target` node, which is returned by the
 215  # call.
 216  hoist: ->
 217    @hoisted = yes
 218    target   = new HoistTarget @
 219
 220    compileNode        = @compileNode
 221    compileToFragments = @compileToFragments
 222
 223    @compileNode = (o) ->
 224      target.update compileNode, o
 225
 226    @compileToFragments = (o) ->
 227      target.update compileToFragments, o
 228
 229    target
 230
 231  cacheToCodeFragments: (cacheValues) ->
 232    [fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])]
 233
 234  # Construct a node that returns the current node’s result.
 235  # Note that this is overridden for smarter behavior for
 236  # many statement nodes (e.g. `If`, `For`).
 237  makeReturn: (results, mark) ->
 238    if mark
 239      # Mark this node as implicitly returned, so that it can be part of the
 240      # node metadata returned in the AST.
 241      @canBeReturned = yes
 242      return
 243    node = @unwrapAll()
 244    if results
 245      new Call new Literal("#{results}.push"), [node]
 246    else
 247      new Return node
 248
 249  # Does this node, or any of its children, contain a node of a certain kind?
 250  # Recursively traverses down the *children* nodes and returns the first one
 251  # that verifies `pred`. Otherwise return undefined. `contains` does not cross
 252  # scope boundaries.
 253  contains: (pred) ->
 254    node = undefined
 255    @traverseChildren no, (n) ->
 256      if pred n
 257        node = n
 258        return no
 259    node
 260
 261  # Pull out the last node of a node list.
 262  lastNode: (list) ->
 263    if list.length is 0 then null else list[list.length - 1]
 264
 265  # Debugging representation of the node, for inspecting the parse tree.
 266  # This is what `coffee --nodes` prints out.
 267  toString: (idt = '', name = @constructor.name) ->
 268    tree = '\n' + idt + name
 269    tree += '?' if @soak
 270    @eachChild (node) -> tree += node.toString idt + TAB
 271    tree
 272
 273  checkForPureStatementInExpression: ->
 274    if jumpNode = @jumps()
 275      jumpNode.error 'cannot use a pure statement in an expression'
 276
 277  # Plain JavaScript object representation of the node, that can be serialized
 278  # as JSON. This is what the `ast` option in the Node API returns.
 279  # We try to follow the [Babel AST spec](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md)
 280  # as closely as possible, for improved interoperability with other tools.
 281  # **WARNING: DO NOT OVERRIDE THIS METHOD IN CHILD CLASSES.**
 282  # Only override the component `ast*` methods as needed.
 283  ast: (o, level) ->
 284    # Merge `level` into `o` and perform other universal checks.
 285    o = @astInitialize o, level
 286    # Create serializable representation of this node.
 287    astNode = @astNode o
 288    # Mark AST nodes that correspond to expressions that (implicitly) return.
 289    # We can’t do this as part of `astNode` because we need to assemble child
 290    # nodes first before marking the parent being returned.
 291    if @astNode? and @canBeReturned
 292      Object.assign astNode, {returns: yes}
 293    astNode
 294
 295  astInitialize: (o, level) ->
 296    o = Object.assign {}, o
 297    o.level = level if level?
 298    if o.level > LEVEL_TOP
 299      @checkForPureStatementInExpression()
 300    # `@makeReturn` must be called before `astProperties`, because the latter may call
 301    # `.ast()` for child nodes and those nodes would need the return logic from `makeReturn`
 302    # already executed by then.
 303    @makeReturn null, yes if @isStatement(o) and o.level isnt LEVEL_TOP and o.scope?
 304    o
 305
 306  astNode: (o) ->
 307    # Every abstract syntax tree node object has four categories of properties:
 308    # - type, stored in the `type` field and a string like `NumberLiteral`.
 309    # - location data, stored in the `loc`, `start`, `end` and `range` fields.
 310    # - properties specific to this node, like `parsedValue`.
 311    # - properties that are themselves child nodes, like `body`.
 312    # These fields are all intermixed in the Babel spec; `type` and `start` and
 313    # `parsedValue` are all top level fields in the AST node object. We have
 314    # separate methods for returning each category, that we merge together here.
 315    Object.assign {}, {type: @astType(o)}, @astProperties(o), @astLocationData()
 316
 317  # By default, a node class has no specific properties.
 318  astProperties: -> {}
 319
 320  # By default, a node class’s AST `type` is its class name.
 321  astType: -> @constructor.name
 322
 323  # The AST location data is a rearranged version of our Jison location data,
 324  # mutated into the structure that the Babel spec uses.
 325  astLocationData: ->
 326    jisonLocationDataToAstLocationData @locationData
 327
 328  # Determines whether an AST node needs an `ExpressionStatement` wrapper.
 329  # Typically matches our `isStatement()` logic but this allows overriding.
 330  isStatementAst: (o) ->
 331    @isStatement o
 332
 333  # Passes each child to a function, breaking when the function returns `false`.
 334  eachChild: (func) ->
 335    return this unless @children
 336    for attr in @children when @[attr]
 337      for child in flatten [@[attr]]
 338        return this if func(child) is false
 339    this
 340
 341  traverseChildren: (crossScope, func) ->
 342    @eachChild (child) ->
 343      recur = func(child)
 344      child.traverseChildren(crossScope, func) unless recur is no
 345
 346  # `replaceInContext` will traverse children looking for a node for which `match` returns
 347  # true. Once found, the matching node will be replaced by the result of calling `replacement`.
 348  replaceInContext: (match, replacement) ->
 349    return false unless @children
 350    for attr in @children when children = @[attr]
 351      if Array.isArray children
 352        for child, i in children
 353          if match child
 354            children[i..i] = replacement child, @
 355            return true
 356          else
 357            return true if child.replaceInContext match, replacement
 358      else if match children
 359        @[attr] = replacement children, @
 360        return true
 361      else
 362        return true if children.replaceInContext match, replacement
 363
 364  invert: ->
 365    new Op '!', this
 366
 367  unwrapAll: ->
 368    node = this
 369    continue until node is node = node.unwrap()
 370    node
 371
 372  # Default implementations of the common node properties and methods. Nodes
 373  # will override these with custom logic, if needed.
 374
 375  # `children` are the properties to recurse into when tree walking. The
 376  # `children` list *is* the structure of the AST. The `parent` pointer, and
 377  # the pointer to the `children` are how you can traverse the tree.
 378  children: []
 379
 380  # `isStatement` has to do with “everything is an expression”. A few things
 381  # can’t be expressions, such as `break`. Things that `isStatement` returns
 382  # `true` for are things that can’t be used as expressions. There are some
 383  # error messages that come from `nodes.coffee` due to statements ending up
 384  # in expression position.
 385  isStatement: NO
 386
 387  # Track comments that have been compiled into fragments, to avoid outputting
 388  # them twice.
 389  compiledComments: []
 390
 391  # `includeCommentFragments` lets `compileCommentFragments` know whether this node
 392  # has special awareness of how to handle comments within its output.
 393  includeCommentFragments: NO
 394
 395  # `jumps` tells you if an expression, or an internal part of an expression
 396  # has a flow control construct (like `break`, or `continue`, or `return`,
 397  # or `throw`) that jumps out of the normal flow of control and can’t be
 398  # used as a value. This is important because things like this make no sense;
 399  # we have to disallow them.
 400  jumps: NO
 401
 402  # If `node.shouldCache() is false`, it is safe to use `node` more than once.
 403  # Otherwise you need to store the value of `node` in a variable and output
 404  # that variable several times instead. Kind of like this: `5` need not be
 405  # cached. `returnFive()`, however, could have side effects as a result of
 406  # evaluating it more than once, and therefore we need to cache it. The
 407  # parameter is named `shouldCache` rather than `mustCache` because there are
 408  # also cases where we might not need to cache but where we want to, for
 409  # example a long expression that may well be idempotent but we want to cache
 410  # for brevity.
 411  shouldCache: YES
 412
 413  isChainable: NO
 414  isAssignable: NO
 415  isNumber: NO
 416
 417  unwrap: THIS
 418  unfoldSoak: NO
 419
 420  # Is this node used to assign a certain variable?
 421  assigns: NO
 422
 423  # For this node and all descendents, set the location data to `locationData`
 424  # if the location data is not already set.
 425  updateLocationDataIfMissing: (locationData, force) ->
 426    @forceUpdateLocation = yes if force
 427    return this if @locationData and not @forceUpdateLocation
 428    delete @forceUpdateLocation
 429    @locationData = locationData
 430
 431    @eachChild (child) ->
 432      child.updateLocationDataIfMissing locationData
 433
 434  # Add location data from another node
 435  withLocationDataFrom: ({locationData}) ->
 436    @updateLocationDataIfMissing locationData
 437
 438  # Add location data and comments from another node
 439  withLocationDataAndCommentsFrom: (node) ->
 440    @withLocationDataFrom node
 441    {comments} = node
 442    @comments = comments if comments?.length
 443    this
 444
 445  # Throw a SyntaxError associated with this node’s location.
 446  error: (message) ->
 447    throwSyntaxError message, @locationData
 448
 449  makeCode: (code) ->
 450    new CodeFragment this, code
 451
 452  wrapInParentheses: (fragments) ->
 453    [@makeCode('('), fragments..., @makeCode(')')]
 454
 455  wrapInBraces: (fragments) ->
 456    [@makeCode('{'), fragments..., @makeCode('}')]
 457
 458  # `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be
 459  # concatenated together, with `joinStr` added in between each, to produce a final flat array
 460  # of fragments.
 461  joinFragmentArrays: (fragmentsList, joinStr) ->
 462    answer = []
 463    for fragments, i in fragmentsList
 464      if i then answer.push @makeCode joinStr
 465      answer = answer.concat fragments
 466    answer
 467
 468#### HoistTarget
 469
 470# A **HoistTargetNode** represents the output location in the node tree for a hoisted node.
 471# See Base#hoist.
 472exports.HoistTarget = class HoistTarget extends Base
 473  # Expands hoisted fragments in the given array
 474  @expand = (fragments) ->
 475    for fragment, i in fragments by -1 when fragment.fragments
 476      fragments[i..i] = @expand fragment.fragments
 477    fragments
 478
 479  constructor: (@source) ->
 480    super()
 481
 482    # Holds presentational options to apply when the source node is compiled.
 483    @options = {}
 484
 485    # Placeholder fragments to be replaced by the source node’s compilation.
 486    @targetFragments = { fragments: [] }
 487
 488  isStatement: (o) ->
 489    @source.isStatement o
 490
 491  # Update the target fragments with the result of compiling the source.
 492  # Calls the given compile function with the node and options (overriden with the target
 493  # presentational options).
 494  update: (compile, o) ->
 495    @targetFragments.fragments = compile.call @source, merge o, @options
 496
 497  # Copies the target indent and level, and returns the placeholder fragments
 498  compileToFragments: (o, level) ->
 499    @options.indent = o.indent
 500    @options.level  = level ? o.level
 501    [ @targetFragments ]
 502
 503  compileNode: (o) ->
 504    @compileToFragments o
 505
 506  compileClosure: (o) ->
 507    @compileToFragments o
 508
 509#### Root
 510
 511# The root node of the node tree
 512exports.Root = class Root extends Base
 513  constructor: (@body) ->
 514    super()
 515
 516  children: ['body']
 517
 518  # Wrap everything in a safety closure, unless requested not to. It would be
 519  # better not to generate them in the first place, but for now, clean up
 520  # obvious double-parentheses.
 521  compileNode: (o) ->
 522    o.indent    = if o.bare then '' else TAB
 523    o.level     = LEVEL_TOP
 524    o.compiling = yes
 525    @initializeScope o
 526    fragments = @body.compileRoot o
 527    return fragments if o.bare
 528    [].concat @makeCode("(function() {\n"), fragments, @makeCode("\n}).call(this);\n")
 529
 530  initializeScope: (o) ->
 531    o.scope = new Scope null, @body, null, o.referencedVars ? []
 532    # Mark given local variables in the root scope as parameters so they don’t
 533    # end up being declared on the root block.
 534    o.scope.parameter name for name in o.locals or []
 535
 536  commentsAst: ->
 537    @allComments ?=
 538      for commentToken in (@allCommentTokens ? []) when not commentToken.heregex
 539        if commentToken.here
 540          new HereComment commentToken
 541        else
 542          new LineComment commentToken
 543    comment.ast() for comment in @allComments
 544
 545  astNode: (o) ->
 546    o.level = LEVEL_TOP
 547    @initializeScope o
 548    super o
 549
 550  astType: -> 'File'
 551
 552  astProperties: (o) ->
 553    @body.isRootBlock = yes
 554    return
 555      program: Object.assign @body.ast(o), @astLocationData()
 556      comments: @commentsAst()
 557
 558#### Block
 559
 560# The block is the list of expressions that forms the body of an
 561# indented block of code -- the implementation of a function, a clause in an
 562# `if`, `switch`, or `try`, and so on...
 563exports.Block = class Block extends Base
 564  constructor: (nodes) ->
 565    super()
 566
 567    @expressions = compact flatten nodes or []
 568
 569  children: ['expressions']
 570
 571  # Tack an expression on to the end of this expression list.
 572  push: (node) ->
 573    @expressions.push node
 574    this
 575
 576  # Remove and return the last expression of this expression list.
 577  pop: ->
 578    @expressions.pop()
 579
 580  # Add an expression at the beginning of this expression list.
 581  unshift: (node) ->
 582    @expressions.unshift node
 583    this
 584
 585  # If this Block consists of just a single node, unwrap it by pulling
 586  # it back out.
 587  unwrap: ->
 588    if @expressions.length is 1 then @expressions[0] else this
 589
 590  # Is this an empty block of code?
 591  isEmpty: ->
 592    not @expressions.length
 593
 594  isStatement: (o) ->
 595    for exp in @expressions when exp.isStatement o
 596      return yes
 597    no
 598
 599  jumps: (o) ->
 600    for exp in @expressions
 601      return jumpNode if jumpNode = exp.jumps o
 602
 603  # A Block node does not return its entire body, rather it
 604  # ensures that the final expression is returned.
 605  makeReturn: (results, mark) ->
 606    len = @expressions.length
 607    [..., lastExp] = @expressions
 608    lastExp = lastExp?.unwrap() or no
 609    # We also need to check that we’re not returning a JSX tag if there’s an
 610    # adjacent one at the same level; JSX doesn’t allow that.
 611    if lastExp and lastExp instanceof Parens and lastExp.body.expressions.length > 1
 612      {body:{expressions}} = lastExp
 613      [..., penult, last] = expressions
 614      penult = penult.unwrap()
 615      last = last.unwrap()
 616      if penult instanceof JSXElement and last instanceof JSXElement
 617        expressions[expressions.length - 1].error 'Adjacent JSX elements must be wrapped in an enclosing tag'
 618    if mark
 619      @expressions[len - 1]?.makeReturn results, mark
 620      return
 621    while len--
 622      expr = @expressions[len]
 623      @expressions[len] = expr.makeReturn results
 624      @expressions.splice(len, 1) if expr instanceof Return and not expr.expression
 625      break
 626    this
 627
 628  compile: (o, lvl) ->
 629    return new Root(this).withLocationDataFrom(this).compile o, lvl unless o.scope
 630
 631    super o, lvl
 632
 633  # Compile all expressions within the **Block** body. If we need to return
 634  # the result, and it’s an expression, simply return it. If it’s a statement,
 635  # ask the statement to do so.
 636  compileNode: (o) ->
 637    @tab  = o.indent
 638    top   = o.level is LEVEL_TOP
 639    compiledNodes = []
 640
 641    for node, index in @expressions
 642      if node.hoisted
 643        # This is a hoisted expression.
 644        # We want to compile this and ignore the result.
 645        node.compileToFragments o
 646        continue
 647      node = (node.unfoldSoak(o) or node)
 648      if node instanceof Block
 649        # This is a nested block. We don’t do anything special here like
 650        # enclose it in a new scope; we just compile the statements in this
 651        # block along with our own.
 652        compiledNodes.push node.compileNode o
 653      else if top
 654        node.front = yes
 655        fragments = node.compileToFragments o
 656        unless node.isStatement o
 657          fragments = indentInitial fragments, @
 658          [..., lastFragment] = fragments
 659          unless lastFragment.code is '' or lastFragment.isComment
 660            fragments.push @makeCode ';'
 661        compiledNodes.push fragments
 662      else
 663        compiledNodes.push node.compileToFragments o, LEVEL_LIST
 664    if top
 665      if @spaced
 666        return [].concat @joinFragmentArrays(compiledNodes, '\n\n'), @makeCode('\n')
 667      else
 668        return @joinFragmentArrays(compiledNodes, '\n')
 669    if compiledNodes.length
 670      answer = @joinFragmentArrays(compiledNodes, ', ')
 671    else
 672      answer = [@makeCode 'void 0']
 673    if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInParentheses answer else answer
 674
 675  compileRoot: (o) ->
 676    @spaced = yes
 677    fragments = @compileWithDeclarations o
 678    HoistTarget.expand fragments
 679    @compileComments fragments
 680
 681  # Compile the expressions body for the contents of a function, with
 682  # declarations of all inner variables pushed up to the top.
 683  compileWithDeclarations: (o) ->
 684    fragments = []
 685    post = []
 686    for exp, i in @expressions
 687      exp = exp.unwrap()
 688      break unless exp instanceof Literal
 689    o = merge(o, level: LEVEL_TOP)
 690    if i
 691      rest = @expressions.splice i, 9e9
 692      [spaced,    @spaced] = [@spaced, no]
 693      [fragments, @spaced] = [@compileNode(o), spaced]
 694      @expressions = rest
 695    post = @compileNode o
 696    {scope} = o
 697    if scope.expressions is this
 698      declars = o.scope.hasDeclarations()
 699      assigns = scope.hasAssignments
 700      if declars or assigns
 701        fragments.push @makeCode '\n' if i
 702        fragments.push @makeCode "#{@tab}var "
 703        if declars
 704          declaredVariables = scope.declaredVariables()
 705          for declaredVariable, declaredVariablesIndex in declaredVariables
 706            fragments.push @makeCode declaredVariable
 707            if Object::hasOwnProperty.call o.scope.comments, declaredVariable
 708              fragments.push o.scope.comments[declaredVariable]...
 709            if declaredVariablesIndex isnt declaredVariables.length - 1
 710              fragments.push @makeCode ', '
 711        if assigns
 712          fragments.push @makeCode ",\n#{@tab + TAB}" if declars
 713          fragments.push @makeCode scope.assignedVariables().join(",\n#{@tab + TAB}")
 714        fragments.push @makeCode ";\n#{if @spaced then '\n' else ''}"
 715      else if fragments.length and post.length
 716        fragments.push @makeCode "\n"
 717    fragments.concat post
 718
 719  compileComments: (fragments) ->
 720    for fragment, fragmentIndex in fragments
 721      # Insert comments into the output at the next or previous newline.
 722      # If there are no newlines at which to place comments, create them.
 723      if fragment.precedingComments
 724        # Determine the indentation level of the fragment that we are about
 725        # to insert comments before, and use that indentation level for our
 726        # inserted comments. At this point, the fragments’ `code` property
 727        # is the generated output JavaScript, and CoffeeScript always
 728        # generates output indented by two spaces; so all we need to do is
 729        # search for a `code` property that begins with at least two spaces.
 730        fragmentIndent = ''
 731        for pastFragment in fragments[0...(fragmentIndex + 1)] by -1
 732          indent = /^ {2,}/m.exec pastFragment.code
 733          if indent
 734            fragmentIndent = indent[0]
 735            break
 736          else if '\n' in pastFragment.code
 737            break
 738        code = "\n#{fragmentIndent}" + (
 739            for commentFragment in fragment.precedingComments
 740              if commentFragment.isHereComment and commentFragment.multiline
 741                multident commentFragment.code, fragmentIndent, no
 742              else
 743                commentFragment.code
 744          ).join("\n#{fragmentIndent}").replace /^(\s*)$/gm, ''
 745        for pastFragment, pastFragmentIndex in fragments[0...(fragmentIndex + 1)] by -1
 746          newLineIndex = pastFragment.code.lastIndexOf '\n'
 747          if newLineIndex is -1
 748            # Keep searching previous fragments until we can’t go back any
 749            # further, either because there are no fragments left or we’ve
 750            # discovered that we’re in a code block that is interpolated
 751            # inside a string.
 752            if pastFragmentIndex is 0
 753              pastFragment.code = '\n' + pastFragment.code
 754              newLineIndex = 0
 755            else if pastFragment.isStringWithInterpolations and pastFragment.code is '{'
 756              code = code[1..] + '\n' # Move newline to end.
 757              newLineIndex = 1
 758            else
 759              continue
 760          delete fragment.precedingComments
 761          pastFragment.code = pastFragment.code[0...newLineIndex] +
 762            code + pastFragment.code[newLineIndex..]
 763          break
 764
 765      # Yes, this is awfully similar to the previous `if` block, but if you
 766      # look closely you’ll find lots of tiny differences that make this
 767      # confusing if it were abstracted into a function that both blocks share.
 768      if fragment.followingComments
 769        # Does the first trailing comment follow at the end of a line of code,
 770        # like `; // Comment`, or does it start a new line after a line of code?
 771        trail = fragment.followingComments[0].trail
 772        fragmentIndent = ''
 773        # Find the indent of the next line of code, if we have any non-trailing
 774        # comments to output. We need to first find the next newline, as these
 775        # comments will be output after that; and then the indent of the line
 776        # that follows the next newline.
 777        unless trail and fragment.followingComments.length is 1
 778          onNextLine = no
 779          for upcomingFragment in fragments[fragmentIndex...]
 780            unless onNextLine
 781              if '\n' in upcomingFragment.code
 782                onNextLine = yes
 783              else
 784                continue
 785            else
 786              indent = /^ {2,}/m.exec upcomingFragment.code
 787              if indent
 788                fragmentIndent = indent[0]
 789                break
 790              else if '\n' in upcomingFragment.code
 791                break
 792        # Is this comment following the indent inserted by bare mode?
 793        # If so, there’s no need to indent this further.
 794        code = if fragmentIndex is 1 and /^\s+$/.test fragments[0].code
 795          ''
 796        else if trail
 797          ' '
 798        else
 799          "\n#{fragmentIndent}"
 800        # Assemble properly indented comments.
 801        code += (
 802            for commentFragment in fragment.followingComments
 803              if commentFragment.isHereComment and commentFragment.multiline
 804                multident commentFragment.code, fragmentIndent, no
 805              else
 806                commentFragment.code
 807          ).join("\n#{fragmentIndent}").replace /^(\s*)$/gm, ''
 808        for upcomingFragment, upcomingFragmentIndex in fragments[fragmentIndex...]
 809          newLineIndex = upcomingFragment.code.indexOf '\n'
 810          if newLineIndex is -1
 811            # Keep searching upcoming fragments until we can’t go any
 812            # further, either because there are no fragments left or we’ve
 813            # discovered that we’re in a code block that is interpolated
 814            # inside a string.
 815            if upcomingFragmentIndex is fragments.length - 1
 816              upcomingFragment.code = upcomingFragment.code + '\n'
 817              newLineIndex = upcomingFragment.code.length
 818            else if upcomingFragment.isStringWithInterpolations and upcomingFragment.code is '}'
 819              code = "#{code}\n"
 820              newLineIndex = 0
 821            else
 822              continue
 823          delete fragment.followingComments
 824          # Avoid inserting extra blank lines.
 825          code = code.replace /^\n/, '' if upcomingFragment.code is '\n'
 826          upcomingFragment.code = upcomingFragment.code[0...newLineIndex] +
 827            code + upcomingFragment.code[newLineIndex..]
 828          break
 829
 830    fragments
 831
 832  # Wrap up the given nodes as a **Block**, unless it already happens
 833  # to be one.
 834  @wrap: (nodes) ->
 835    return nodes[0] if nodes.length is 1 and nodes[0] instanceof Block
 836    new Block nodes
 837
 838  astNode: (o) ->
 839    if (o.level? and o.level isnt LEVEL_TOP) and @expressions.length
 840      return (new Sequence(@expressions).withLocationDataFrom @).ast o
 841
 842    super o
 843
 844  astType: ->
 845    if @isRootBlock
 846      'Program'
 847    else if @isClassBody
 848      'ClassBody'
 849    else
 850      'BlockStatement'
 851
 852  astProperties: (o) ->
 853    checkForDirectives = del o, 'checkForDirectives'
 854
 855    sniffDirectives @expressions, notFinalExpression: checkForDirectives if @isRootBlock or checkForDirectives
 856    directives = []
 857    body = []
 858    for expression in @expressions
 859      expressionAst = expression.ast o
 860      # Ignore generated PassthroughLiteral
 861      if not expressionAst?
 862        continue
 863      else if expression instanceof Directive
 864        directives.push expressionAst
 865      # If an expression is a statement, it can be added to the body as is.
 866      else if expression.isStatementAst o
 867        body.push expressionAst
 868      # Otherwise, we need to wrap it in an `ExpressionStatement` AST node.
 869      else
 870        body.push Object.assign
 871            type: 'ExpressionStatement'
 872            expression: expressionAst
 873          ,
 874            expression.astLocationData()
 875
 876    return {
 877      # For now, we’re not including `sourceType` on the `Program` AST node.
 878      # Its value could be either `'script'` or `'module'`, and there’s no way
 879      # for CoffeeScript to always know which it should be. The presence of an
 880      # `import` or `export` statement in source code would imply that it should
 881      # be a `module`, but a project may consist of mostly such files and also
 882      # an outlier file that lacks `import` or `export` but is still imported
 883      # into the project and therefore expects to be treated as a `module`.
 884      # Determining the value of `sourceType` is essentially the same challenge
 885      # posed by determining the parse goal of a JavaScript file, also `module`
 886      # or `script`, and so if Node figures out a way to do so for `.js` files
 887      # then CoffeeScript can copy Node’s algorithm.
 888
 889      # sourceType: 'module'
 890      body, directives
 891    }
 892
 893  astLocationData: ->
 894    return if @isRootBlock and not @locationData?
 895    super()
 896
 897# A directive e.g. 'use strict'.
 898# Currently only used during AST generation.
 899exports.Directive = class Directive extends Base
 900  constructor: (@value) ->
 901    super()
 902
 903  astProperties: (o) ->
 904    return
 905      value: Object.assign {},
 906        @value.ast o
 907        type: 'DirectiveLiteral'
 908
 909#### Literal
 910
 911# `Literal` is a base class for static values that can be passed through
 912# directly into JavaScript without translation, such as: strings, numbers,
 913# `true`, `false`, `null`...
 914exports.Literal = class Literal extends Base
 915  constructor: (@value) ->
 916    super()
 917
 918  shouldCache: NO
 919
 920  assigns: (name) ->
 921    name is @value
 922
 923  compileNode: (o) ->
 924    [@makeCode @value]
 925
 926  astProperties: ->
 927    return
 928      value: @value
 929
 930  toString: ->
 931    # This is only intended for debugging.
 932    " #{if @isStatement() then super() else @constructor.name}: #{@value}"
 933
 934exports.NumberLiteral = class NumberLiteral extends Literal
 935  constructor: (@value, {@parsedValue} = {}) ->
 936    super()
 937    unless @parsedValue?
 938      if isNumber @value
 939        @parsedValue = @value
 940        @value = "#{@value}"
 941      else
 942        @parsedValue = parseNumber @value
 943
 944  isBigInt: ->
 945    /n$/.test @value
 946
 947  astType: ->
 948    if @isBigInt()
 949      'BigIntLiteral'
 950    else
 951      'NumericLiteral'
 952
 953  astProperties: ->
 954    return
 955      value:
 956        if @isBigInt()
 957          @parsedValue.toString()
 958        else
 959          @parsedValue
 960      extra:
 961        rawValue:
 962          if @isBigInt()
 963            @parsedValue.toString()
 964          else
 965            @parsedValue
 966        raw: @value
 967
 968exports.InfinityLiteral = class InfinityLiteral extends NumberLiteral
 969  constructor: (@value, {@originalValue = 'Infinity'} = {}) ->
 970    super()
 971
 972  compileNode: ->
 973    [@makeCode '2e308']
 974
 975  astNode: (o) ->
 976    unless @originalValue is 'Infinity'
 977      return new NumberLiteral(@value).withLocationDataFrom(@).ast o
 978    super o
 979
 980  astType: -> 'Identifier'
 981
 982  astProperties: ->
 983    return
 984      name: 'Infinity'
 985      declaration: no
 986
 987exports.NaNLiteral = class NaNLiteral extends NumberLiteral
 988  constructor: ->
 989    super 'NaN'
 990
 991  compileNode: (o) ->
 992    code = [@makeCode '0/0']
 993    if o.level >= LEVEL_OP then @wrapInParentheses code else code
 994
 995  astType: -> 'Identifier'
 996
 997  astProperties: ->
 998    return
 999      name: 'NaN'
1000      declaration: no
1001
1002exports.StringLiteral = class StringLiteral extends Literal
1003  constructor: (@originalValue, {@quote, @initialChunk, @finalChunk, @indent, @double, @heregex} = {}) ->
1004    super ''
1005    @quote = null if @quote is '///'
1006    @fromSourceString = @quote?
1007    @quote ?= '"'
1008    heredoc = @isFromHeredoc()
1009
1010    val = @originalValue
1011    if @heregex
1012      val = val.replace HEREGEX_OMIT, '$1$2'
1013      val = replaceUnicodeCodePointEscapes val, flags: @heregex.flags
1014    else
1015      val = val.replace STRING_OMIT, '$1'
1016      val =
1017        unless @fromSourceString
1018          val
1019        else if heredoc
1020          indentRegex = /// \n#{@indent} ///g if @indent
1021
1022          val = val.replace indentRegex, '\n' if indentRegex
1023          val = val.replace LEADING_BLANK_LINE,  '' if @initialChunk
1024          val = val.replace TRAILING_BLANK_LINE, '' if @finalChunk
1025          val
1026        else
1027          val.replace SIMPLE_STRING_OMIT, (match, offset) =>
1028            if (@initialChunk and offset is 0) or
1029               (@finalChunk and offset + match.length is val.length)
1030              ''
1031            else
1032              ' '
1033    @delimiter = @quote.charAt 0
1034    @value = makeDelimitedLiteral val, {
1035      @delimiter
1036      @double
1037    }
1038
1039    @unquotedValueForTemplateLiteral = makeDelimitedLiteral val, {
1040      delimiter: '`'
1041      @double
1042      escapeNewlines: no
1043      includeDelimiters: no
1044      convertTrailingNullEscapes: yes
1045    }
1046
1047    @unquotedValueForJSX = makeDelimitedLiteral val, {
1048      @double
1049      escapeNewlines: no
1050      includeDelimiters: no
1051      escapeDelimiter: no
1052    }
1053
1054  compileNode: (o) ->
1055    return StringWithInterpolations.fromStringLiteral(@).compileNode o if @shouldGenerateTemplateLiteral()
1056    return [@makeCode @unquotedValueForJSX] if @jsx
1057    super o
1058
1059  # `StringLiteral`s can represent either entire literal strings
1060  # or pieces of text inside of e.g. an interpolated string.
1061  # When parsed as the former but needing to be treated as the latter
1062  # (e.g. the string part of a tagged template literal), this will return
1063  # a copy of the `StringLiteral` with the quotes trimmed from its location
1064  # data (like it would have if parsed as part of an interpolated string).
1065  withoutQuotesInLocationData: ->
1066    endsWithNewline = @originalValue[-1..] is '\n'
1067    locationData = Object.assign {}, @locationData
1068    locationData.first_column          += @quote.length
1069    if endsWithNewline
1070      locationData.last_line -= 1
1071      locationData.last_column =
1072        if locationData.last_line is locationData.first_line
1073          locationData.first_column + @originalValue.length - '\n'.length
1074        else
1075          @originalValue[...-1].length - '\n'.length - @originalValue[...-1].lastIndexOf('\n')
1076    else
1077      locationData.last_column         -= @quote.length
1078    locationData.last_column_exclusive -= @quote.length
1079    locationData.range = [
1080      locationData.range[0] + @quote.length
1081      locationData.range[1] - @quote.length
1082    ]
1083    copy = new StringLiteral @originalValue, {@quote, @initialChunk, @finalChunk, @indent, @double, @heregex}
1084    copy.locationData = locationData
1085    copy
1086
1087  isFromHeredoc: ->
1088    @quote.length is 3
1089
1090  shouldGenerateTemplateLiteral: ->
1091    @isFromHeredoc()
1092
1093  astNode: (o) ->
1094    return StringWithInterpolations.fromStringLiteral(@).ast o if @shouldGenerateTemplateLiteral()
1095    super o
1096
1097  astProperties: ->
1098    return
1099      value: @originalValue
1100      extra:
1101        raw: "#{@delimiter}#{@originalValue}#{@delimiter}"
1102
1103exports.RegexLiteral = class RegexLiteral extends Literal
1104  constructor: (value, {@delimiter = '/', @heregexCommentTokens = []} = {}) ->
1105    super ''
1106    heregex = @delimiter is '///'
1107    endDelimiterIndex = value.lastIndexOf '/'
1108    @flags = value[endDelimiterIndex + 1..]
1109    val = @originalValue = value[1...endDelimiterIndex]
1110    val = val.replace HEREGEX_OMIT, '$1$2' if heregex
1111    val = replaceUnicodeCodePointEscapes val, {@flags}
1112    @value = "#{makeDelimitedLiteral val, delimiter: '/'}#{@flags}"
1113
1114  REGEX_REGEX: /// ^ / (.*) / \w* $ ///
1115
1116  astType: -> 'RegExpLiteral'
1117
1118  astProperties: (o) ->
1119    [, pattern] = @REGEX_REGEX.exec @value
1120    return {
1121      value: undefined
1122      pattern, @flags, @delimiter
1123      originalPattern: @originalValue
1124      extra:
1125        raw: @value
1126        originalRaw: "#{@delimiter}#{@originalValue}#{@delimiter}#{@flags}"
1127        rawValue: undefined
1128      comments:
1129        for heregexCommentToken in @heregexCommentTokens
1130          if heregexCommentToken.here
1131            new HereComment(heregexCommentToken).ast o
1132          else
1133            new LineComment(heregexCommentToken).ast o
1134    }
1135
1136exports.PassthroughLiteral = class PassthroughLiteral extends Literal
1137  constructor: (@originalValue, {@here, @generated} = {}) ->
1138    super ''
1139    @value = @originalValue.replace /\\+(`|$)/g, (string) ->
1140      # `string` is always a value like '\`', '\\\`', '\\\\\`', etc.
1141      # By reducing it to its latter half, we turn '\`' to '`', '\\\`' to '\`', etc.
1142      string[-Math.ceil(string.length / 2)..]
1143
1144  astNode: (o) ->
1145    return null if @generated
1146    super o
1147
1148  astProperties: ->
1149    return {
1150      value: @originalValue
1151      here: !!@here
1152    }
1153
1154exports.IdentifierLiteral = class IdentifierLiteral extends Literal
1155  isAssignable: YES
1156
1157  eachName: (iterator) ->
1158    iterator @
1159
1160  astType: ->
1161    if @jsx
1162      'JSXIdentifier'
1163    else
1164      'Identifier'
1165
1166  astProperties: ->
1167    return
1168      name: @value
1169      declaration: !!@isDeclaration
1170
1171exports.PropertyName = class PropertyName extends Literal
1172  isAssignable: YES
1173
1174  astType: ->
1175    if @jsx
1176      'JSXIdentifier'
1177    else
1178      'Identifier'
1179
1180  astProperties: ->
1181    return
1182      name: @value
1183      declaration: no
1184
1185exports.ComputedPropertyName = class ComputedPropertyName extends PropertyName
1186  compileNode: (o) ->
1187    [@makeCode('['), @value.compileToFragments(o, LEVEL_LIST)..., @makeCode(']')]
1188
1189  astNode: (o) ->
1190    @value.ast o
1191
1192exports.StatementLiteral = class StatementLiteral extends Literal
1193  isStatement: YES
1194
1195  makeReturn: THIS
1196
1197  jumps: (o) ->
1198    return this if @value is 'break' and not (o?.loop or o?.block)
1199    return this if @value is 'continue' and not o?.loop
1200
1201  compileNode: (o) ->
1202    [@makeCode "#{@tab}#{@value};"]
1203
1204  astType: ->
1205    switch @value
1206      when 'continue' then 'ContinueStatement'
1207      when 'break'    then 'BreakStatement'
1208      when 'debugger' then 'DebuggerStatement'
1209
1210exports.ThisLiteral = class ThisLiteral extends Literal
1211  constructor: (value) ->
1212    super 'this'
1213    @shorthand = value is '@'
1214
1215  compileNode: (o) ->
1216    code = if o.scope.method?.bound then o.scope.method.context else @value
1217    [@makeCode code]
1218
1219  astType: -> 'ThisExpression'
1220
1221  astProperties: ->
1222    return
1223      shorthand: @shorthand
1224
1225exports.UndefinedLiteral = class UndefinedLiteral extends Literal
1226  constructor: ->
1227    super 'undefined'
1228
1229  compileNode: (o) ->
1230    [@makeCode if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0']
1231
1232  astType: -> 'Identifier'
1233
1234  astProperties: ->
1235    return
1236      name: @value
1237      declaration: no
1238
1239exports.NullLiteral = class NullLiteral extends Literal
1240  constructor: ->
1241    super 'null'
1242
1243exports.BooleanLiteral = class BooleanLiteral extends Literal
1244  constructor: (value, {@originalValue} = {}) ->
1245    super value
1246    @originalValue ?= @value
1247
1248  astProperties: ->
1249    value: if @value is 'true' then yes else no
1250    name: @originalValue
1251
1252exports.DefaultLiteral = class DefaultLiteral extends Literal
1253  astType: -> 'Identifier'
1254
1255  astProperties: ->
1256    return
1257      name: 'default'
1258      declaration: no
1259
1260#### Return
1261
1262# A `return` is a *pureStatement*—wrapping it in a closure wouldn’t make sense.
1263exports.Return = class Return extends Base
1264  constructor: (@expression, {@belongsToFuncDirectiveReturn} = {}) ->
1265    super()
1266
1267  children: ['expression']
1268
1269  isStatement:     YES
1270  makeReturn:      THIS
1271  jumps:           THIS
1272
1273  compileToFragments: (o, level) ->
1274    expr = @expression?.makeReturn()
1275    if expr and expr not instanceof Return then expr.compileToFragments o, level else super o, level
1276
1277  compileNode: (o) ->
1278    answer = []
1279    # TODO: If we call `expression.compile()` here twice, we’ll sometimes
1280    # get back different results!
1281    if @expression
1282      answer = @expression.compileToFragments o, LEVEL_PAREN
1283      unshiftAfterComments answer, @makeCode "#{@tab}return "
1284      # Since the `return` got indented by `@tab`, preceding comments that are
1285      # multiline need to be indented.
1286      for fragment in answer
1287        if fragment.isHereComment and '\n' in fragment.code
1288          fragment.code = multident fragment.code, @tab
1289        else if fragment.isLineComment
1290          fragment.code = "#{@tab}#{fragment.code}"
1291        else
1292          break
1293    else
1294      answer.push @makeCode "#{@tab}return"
1295    answer.push @makeCode ';'
1296    answer
1297
1298  checkForPureStatementInExpression: ->
1299    # don’t flag `return` from `await return`/`yield return` as invalid.
1300    return if @belongsToFuncDirectiveReturn
1301    super()
1302
1303  astType: -> 'ReturnStatement'
1304
1305  astProperties: (o) ->
1306    argument: @expression?.ast(o, LEVEL_PAREN) ? null
1307
1308# Parent class for `YieldReturn`/`AwaitReturn`.
1309exports.FuncDirectiveReturn = class FuncDirectiveReturn extends Return
1310  constructor: (expression, {@returnKeyword}) ->
1311    super expression
1312
1313  compileNode: (o) ->
1314    @checkScope o
1315    super o
1316
1317  checkScope: (o) ->
1318    unless o.scope.parent?
1319      @error "#{@keyword} can only occur inside functions"
1320
1321  isStatementAst: NO
1322
1323  astNode: (o) ->
1324    @checkScope o
1325
1326    new Op @keyword,
1327      new Return @expression, belongsToFuncDirectiveReturn: yes
1328      .withLocationDataFrom(
1329        if @expression?
1330          locationData: mergeLocationData @returnKeyword.locationData, @expression.locationData
1331        else
1332          @returnKeyword
1333      )
1334    .withLocationDataFrom @
1335    .ast o
1336
1337# `yield return` works exactly like `return`, except that it turns the function
1338# into a generator.
1339exports.YieldReturn = class YieldReturn extends FuncDirectiveReturn
1340  keyword: 'yield'
1341
1342exports.AwaitReturn = class AwaitReturn extends FuncDirectiveReturn
1343  keyword: 'await'
1344
1345#### Value
1346
1347# A value, variable or literal or parenthesized, indexed or dotted into,
1348# or vanilla.
1349exports.Value = class Value extends Base
1350  constructor: (base, props, tag, isDefaultValue = no) ->
1351    super()
1352    return base if not props and base instanceof Value
1353    @base           = base
1354    @properties     = props or []
1355    @tag            = tag
1356    @[tag]          = yes if tag
1357    @isDefaultValue = isDefaultValue
1358    # If this is a `@foo =` assignment, if there are comments on `@` move them
1359    # to be on `foo`.
1360    if @base?.comments and @base instanceof ThisLiteral and @properties[0]?.name?
1361      moveComments @base, @properties[0].name
1362
1363  children: ['base', 'properties']
1364
1365  # Add a property (or *properties* ) `Access` to the list.
1366  add: (props) ->
1367    @properties = @properties.concat props
1368    @forceUpdateLocation = yes
1369    this
1370
1371  hasProperties: ->
1372    @properties.length isnt 0
1373
1374  bareLiteral: (type) ->
1375    not @properties.length and @base instanceof type
1376
1377  # Some boolean checks for the benefit of other nodes.
1378  isArray        : -> @bareLiteral(Arr)
1379  isRange        : -> @bareLiteral(Range)
1380  shouldCache    : -> @hasProperties() or @base.shouldCache()
1381  isAssignable   : (opts) -> @hasProperties() or @base.isAssignable opts
1382  isNumber       : -> @bareLiteral(NumberLiteral)
1383  isString       : -> @bareLiteral(StringLiteral)
1384  isRegex        : -> @bareLiteral(RegexLiteral)
1385  isUndefined    : -> @bareLiteral(UndefinedLiteral)
1386  isNull         : -> @bareLiteral(NullLiteral)
1387  isBoolean      : -> @bareLiteral(BooleanLiteral)
1388  isAtomic       : ->
1389    for node in @properties.concat @base
1390      return no if node.soak or node instanceof Call or node instanceof Op and node.operator is 'do'
1391    yes
1392
1393  isNotCallable  : -> @isNumber() or @isString() or @isRegex() or
1394                      @isArray() or @isRange() or @isSplice() or @isObject() or
1395                      @isUndefined() or @isNull() or @isBoolean()
1396
1397  isStatement : (o)    -> not @properties.length and @base.isStatement o
1398  isJSXTag    : -> @base instanceof JSXTag
1399  assigns     : (name) -> not @properties.length and @base.assigns name
1400  jumps      

Large files files are truncated, but you can click here to view the full file