/src/nodes.coffee
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