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