// Generated by CoffeeScript 2.1.0 (function() { // `nodes.coffee` contains all of the node classes for the syntax tree. Most // nodes are created as the result of actions in the [grammar](grammar.html), // but some are created by other nodes as a method of code generation. To convert // the syntax tree into a string of JavaScript code, call `compile()` on the root. var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, ComputedPropertyName, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, moveComments, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, indexOf = [].indexOf, splice = [].splice, slice = [].slice; Error.stackTraceLimit = 2e308; ({Scope} = require('./scope')); ({isUnassignable, JS_FORBIDDEN} = require('./lexer')); // Import the helpers we plan to use. ({compact, flatten, extend, merge, del, starts, ends, some, addDataToNode, attachCommentsToNode, locationDataToString, throwSyntaxError} = require('./helpers')); // Functions required by parser. exports.extend = extend; exports.addDataToNode = addDataToNode; // Constant functions for nodes that don’t need customization. YES = function() { return true; }; NO = function() { return false; }; THIS = function() { return this; }; NEGATE = function() { this.negated = !this.negated; return this; }; //### CodeFragment // The various nodes defined below all compile to a collection of **CodeFragment** objects. // A CodeFragments is a block of generated code, and the location in the source file where the code // came from. CodeFragments can be assembled together into working code just by catting together // all the CodeFragments' `code` snippets, in order. exports.CodeFragment = CodeFragment = class CodeFragment { constructor(parent, code) { var ref1; this.code = `${code}`; this.type = (parent != null ? (ref1 = parent.constructor) != null ? ref1.name : void 0 : void 0) || 'unknown'; this.locationData = parent != null ? parent.locationData : void 0; this.comments = parent != null ? parent.comments : void 0; } toString() { // This is only intended for debugging. return `${this.code}${(this.locationData ? ": " + locationDataToString(this.locationData) : '')}`; } }; // Convert an array of CodeFragments into a string. fragmentsToText = function(fragments) { var fragment; return ((function() { var j, len1, results; results = []; for (j = 0, len1 = fragments.length; j < len1; j++) { fragment = fragments[j]; results.push(fragment.code); } return results; })()).join(''); }; //### Base // The **Base** is the abstract base class for all nodes in the syntax tree. // Each subclass implements the `compileNode` method, which performs the // code generation for that node. To compile a node to JavaScript, // call `compile` on it, which wraps `compileNode` in some generic extra smarts, // to know when the generated code needs to be wrapped up in a closure. // An options hash is passed and cloned throughout, containing information about // the environment from higher in the tree (such as if a returned value is // being requested by the surrounding function), information about the current // scope, and indentation level. exports.Base = Base = (function() { class Base { compile(o, lvl) { return fragmentsToText(this.compileToFragments(o, lvl)); } // Occasionally a node is compiled multiple times, for example to get the name // of a variable to add to scope tracking. When we know that a “premature” // compilation won’t result in comments being output, set those comments aside // so that they’re preserved for a later `compile` call that will result in // the comments being included in the output. compileWithoutComments(o, lvl, method = 'compile') { var fragments, unwrapped; if (this.comments) { this.ignoreTheseCommentsTemporarily = this.comments; delete this.comments; } unwrapped = this.unwrapAll(); if (unwrapped.comments) { unwrapped.ignoreTheseCommentsTemporarily = unwrapped.comments; delete unwrapped.comments; } fragments = this[method](o, lvl); if (this.ignoreTheseCommentsTemporarily) { this.comments = this.ignoreTheseCommentsTemporarily; delete this.ignoreTheseCommentsTemporarily; } if (unwrapped.ignoreTheseCommentsTemporarily) { unwrapped.comments = unwrapped.ignoreTheseCommentsTemporarily; delete unwrapped.ignoreTheseCommentsTemporarily; } return fragments; } compileNodeWithoutComments(o, lvl) { return this.compileWithoutComments(o, lvl, 'compileNode'); } // Common logic for determining whether to wrap this node in a closure before // compiling it, or to compile directly. We need to wrap if this node is a // *statement*, and it's not a *pureStatement*, and we're not at // the top level of a block (which would be unnecessary), and we haven't // already been asked to return the result (because statements know how to // return results). compileToFragments(o, lvl) { var fragments, node; o = extend({}, o); if (lvl) { o.level = lvl; } node = this.unfoldSoak(o) || this; node.tab = o.indent; fragments = o.level === LEVEL_TOP || !node.isStatement(o) ? node.compileNode(o) : node.compileClosure(o); this.compileCommentFragments(o, node, fragments); return fragments; } compileToFragmentsWithoutComments(o, lvl) { return this.compileWithoutComments(o, lvl, 'compileToFragments'); } // Statements converted into expressions via closure-wrapping share a scope // object with their parent closure, to preserve the expected lexical scope. compileClosure(o) { var args, argumentsNode, func, jumpNode, meth, parts, ref1, ref2; if (jumpNode = this.jumps()) { jumpNode.error('cannot use a pure statement in an expression'); } o.sharedScope = true; func = new Code([], Block.wrap([this])); args = []; if (this.contains((function(node) { return node instanceof SuperCall; }))) { func.bound = true; } else if ((argumentsNode = this.contains(isLiteralArguments)) || this.contains(isLiteralThis)) { args = [new ThisLiteral]; if (argumentsNode) { meth = 'apply'; args.push(new IdentifierLiteral('arguments')); } else { meth = 'call'; } func = new Value(func, [new Access(new PropertyName(meth))]); } parts = (new Call(func, args)).compileNode(o); switch (false) { case !(func.isGenerator || ((ref1 = func.base) != null ? ref1.isGenerator : void 0)): parts.unshift(this.makeCode("(yield* ")); parts.push(this.makeCode(")")); break; case !(func.isAsync || ((ref2 = func.base) != null ? ref2.isAsync : void 0)): parts.unshift(this.makeCode("(await ")); parts.push(this.makeCode(")")); } return parts; } compileCommentFragments(o, node, fragments) { var base1, base2, comment, commentFragment, j, len1, ref1, unshiftCommentFragment; if (!node.comments) { return fragments; } // This is where comments, that are attached to nodes as a `comments` // property, become `CodeFragment`s. “Inline block comments,” e.g. // `/* */`-delimited comments that are interspersed within code on a line, // are added to the current `fragments` stream. All other fragments are // attached as properties to the nearest preceding or following fragment, // to remain stowaways until they get properly output in `compileComments` // later on. unshiftCommentFragment = function(commentFragment) { var precedingFragment; if (commentFragment.unshift) { // Find the first non-comment fragment and insert `commentFragment` // before it. return unshiftAfterComments(fragments, commentFragment); } else { if (fragments.length !== 0) { precedingFragment = fragments[fragments.length - 1]; if (commentFragment.newLine && precedingFragment.code !== '' && !/\n\s*$/.test(precedingFragment.code)) { commentFragment.code = `\n${commentFragment.code}`; } } return fragments.push(commentFragment); } }; ref1 = node.comments; for (j = 0, len1 = ref1.length; j < len1; j++) { comment = ref1[j]; if (!(indexOf.call(this.compiledComments, comment) < 0)) { continue; } this.compiledComments.push(comment); // Don’t output this comment twice. // For block/here comments, denoted by `###`, that are inline comments // like `1 + ### comment ### 2`, create fragments and insert them into // the fragments array. // Otherwise attach comment fragments to their closest fragment for now, // so they can be inserted into the output later after all the newlines // have been added. if (comment.here) { // Block comment, delimited by `###`. commentFragment = new HereComment(comment).compileNode(o); // Line comment, delimited by `#`. } else { commentFragment = new LineComment(comment).compileNode(o); } if ((commentFragment.isHereComment && !commentFragment.newLine) || node.includeCommentFragments()) { // Inline block comments, like `1 + /* comment */ 2`, or a node whose // `compileToFragments` method has logic for outputting comments. unshiftCommentFragment(commentFragment); } else { if (fragments.length === 0) { fragments.push(this.makeCode('')); } if (commentFragment.unshift) { if ((base1 = fragments[0]).precedingComments == null) { base1.precedingComments = []; } fragments[0].precedingComments.push(commentFragment); } else { if ((base2 = fragments[fragments.length - 1]).followingComments == null) { base2.followingComments = []; } fragments[fragments.length - 1].followingComments.push(commentFragment); } } } return fragments; } // If the code generation wishes to use the result of a complex expression // in multiple places, ensure that the expression is only ever evaluated once, // by assigning it to a temporary variable. Pass a level to precompile. // If `level` is passed, then returns `[val, ref]`, where `val` is the compiled value, and `ref` // is the compiled reference. If `level` is not passed, this returns `[val, ref]` where // the two values are raw nodes which have not been compiled. cache(o, level, shouldCache) { var complex, ref, sub; complex = shouldCache != null ? shouldCache(this) : this.shouldCache(); if (complex) { ref = new IdentifierLiteral(o.scope.freeVariable('ref')); sub = new Assign(ref, this); if (level) { return [sub.compileToFragments(o, level), [this.makeCode(ref.value)]]; } else { return [sub, ref]; } } else { ref = level ? this.compileToFragments(o, level) : this; return [ref, ref]; } } // Occasionally it may be useful to make an expression behave as if it was 'hoisted', whereby the // result of the expression is available before its location in the source, but the expression's // variable scope corresponds the source position. This is used extensively to deal with executable // class bodies in classes. // Calling this method mutates the node, proxying the `compileNode` and `compileToFragments` // methods to store their result for later replacing the `target` node, which is returned by the // call. hoist() { var compileNode, compileToFragments, target; this.hoisted = true; target = new HoistTarget(this); compileNode = this.compileNode; compileToFragments = this.compileToFragments; this.compileNode = function(o) { return target.update(compileNode, o); }; this.compileToFragments = function(o) { return target.update(compileToFragments, o); }; return target; } cacheToCodeFragments(cacheValues) { return [fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])]; } // Construct a node that returns the current node's result. // Note that this is overridden for smarter behavior for // many statement nodes (e.g. If, For)... makeReturn(res) { var me; me = this.unwrapAll(); if (res) { return new Call(new Literal(`${res}.push`), [me]); } else { return new Return(me); } } // Does this node, or any of its children, contain a node of a certain kind? // Recursively traverses down the *children* nodes and returns the first one // that verifies `pred`. Otherwise return undefined. `contains` does not cross // scope boundaries. contains(pred) { var node; node = void 0; this.traverseChildren(false, function(n) { if (pred(n)) { node = n; return false; } }); return node; } // Pull out the last node of a node list. lastNode(list) { if (list.length === 0) { return null; } else { return list[list.length - 1]; } } // `toString` representation of the node, for inspecting the parse tree. // This is what `coffee --nodes` prints out. toString(idt = '', name = this.constructor.name) { var tree; tree = '\n' + idt + name; if (this.soak) { tree += '?'; } this.eachChild(function(node) { return tree += node.toString(idt + TAB); }); return tree; } // Passes each child to a function, breaking when the function returns `false`. eachChild(func) { var attr, child, j, k, len1, len2, ref1, ref2; if (!this.children) { return this; } ref1 = this.children; for (j = 0, len1 = ref1.length; j < len1; j++) { attr = ref1[j]; if (this[attr]) { ref2 = flatten([this[attr]]); for (k = 0, len2 = ref2.length; k < len2; k++) { child = ref2[k]; if (func(child) === false) { return this; } } } } return this; } traverseChildren(crossScope, func) { return this.eachChild(function(child) { var recur; recur = func(child); if (recur !== false) { return child.traverseChildren(crossScope, func); } }); } // `replaceInContext` will traverse children looking for a node for which `match` returns // true. Once found, the matching node will be replaced by the result of calling `replacement`. replaceInContext(match, replacement) { var attr, child, children, i, j, k, len1, len2, ref1, ref2; if (!this.children) { return false; } ref1 = this.children; for (j = 0, len1 = ref1.length; j < len1; j++) { attr = ref1[j]; if (children = this[attr]) { if (Array.isArray(children)) { for (i = k = 0, len2 = children.length; k < len2; i = ++k) { child = children[i]; if (match(child)) { splice.apply(children, [i, i - i + 1].concat(ref2 = replacement(child, this))), ref2; return true; } else { if (child.replaceInContext(match, replacement)) { return true; } } } } else if (match(children)) { this[attr] = replacement(children, this); return true; } else { if (children.replaceInContext(match, replacement)) { return true; } } } } } invert() { return new Op('!', this); } unwrapAll() { var node; node = this; while (node !== (node = node.unwrap())) { continue; } return node; } // For this node and all descendents, set the location data to `locationData` // if the location data is not already set. updateLocationDataIfMissing(locationData) { if (this.locationData && !this.forceUpdateLocation) { return this; } delete this.forceUpdateLocation; this.locationData = locationData; return this.eachChild(function(child) { return child.updateLocationDataIfMissing(locationData); }); } // Throw a SyntaxError associated with this node’s location. error(message) { return throwSyntaxError(message, this.locationData); } makeCode(code) { return new CodeFragment(this, code); } wrapInParentheses(fragments) { return [this.makeCode('('), ...fragments, this.makeCode(')')]; } wrapInBraces(fragments) { return [this.makeCode('{'), ...fragments, this.makeCode('}')]; } // `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be // concatenated together, with `joinStr` added in between each, to produce a final flat array // of fragments. joinFragmentArrays(fragmentsList, joinStr) { var answer, fragments, i, j, len1; answer = []; for (i = j = 0, len1 = fragmentsList.length; j < len1; i = ++j) { fragments = fragmentsList[i]; if (i) { answer.push(this.makeCode(joinStr)); } answer = answer.concat(fragments); } return answer; } }; // Default implementations of the common node properties and methods. Nodes // will override these with custom logic, if needed. // `children` are the properties to recurse into when tree walking. The // `children` list *is* the structure of the AST. The `parent` pointer, and // the pointer to the `children` are how you can traverse the tree. Base.prototype.children = []; // `isStatement` has to do with “everything is an expression”. A few things // can’t be expressions, such as `break`. Things that `isStatement` returns // `true` for are things that can’t be used as expressions. There are some // error messages that come from `nodes.coffee` due to statements ending up // in expression position. Base.prototype.isStatement = NO; // Track comments that have been compiled into fragments, to avoid outputting // them twice. Base.prototype.compiledComments = []; // `includeCommentFragments` lets `compileCommentFragments` know whether this node // has special awareness of how to handle comments within its output. Base.prototype.includeCommentFragments = NO; // `jumps` tells you if an expression, or an internal part of an expression // has a flow control construct (like `break`, or `continue`, or `return`, // or `throw`) that jumps out of the normal flow of control and can’t be // used as a value. This is important because things like this make no sense; // we have to disallow them. Base.prototype.jumps = NO; // If `node.shouldCache() is false`, it is safe to use `node` more than once. // Otherwise you need to store the value of `node` in a variable and output // that variable several times instead. Kind of like this: `5` need not be // cached. `returnFive()`, however, could have side effects as a result of // evaluating it more than once, and therefore we need to cache it. The // parameter is named `shouldCache` rather than `mustCache` because there are // also cases where we might not need to cache but where we want to, for // example a long expression that may well be idempotent but we want to cache // for brevity. Base.prototype.shouldCache = YES; Base.prototype.isChainable = NO; Base.prototype.isAssignable = NO; Base.prototype.isNumber = NO; Base.prototype.unwrap = THIS; Base.prototype.unfoldSoak = NO; // Is this node used to assign a certain variable? Base.prototype.assigns = NO; return Base; }).call(this); //### HoistTarget // A **HoistTargetNode** represents the output location in the node tree for a hoisted node. // See Base#hoist. exports.HoistTarget = HoistTarget = class HoistTarget extends Base { // Expands hoisted fragments in the given array static expand(fragments) { var fragment, i, j, ref1; for (i = j = fragments.length - 1; j >= 0; i = j += -1) { fragment = fragments[i]; if (fragment.fragments) { splice.apply(fragments, [i, i - i + 1].concat(ref1 = this.expand(fragment.fragments))), ref1; } } return fragments; } constructor(source1) { super(); this.source = source1; // Holds presentational options to apply when the source node is compiled. this.options = {}; // Placeholder fragments to be replaced by the source node’s compilation. this.targetFragments = { fragments: [] }; } isStatement(o) { return this.source.isStatement(o); } // Update the target fragments with the result of compiling the source. // Calls the given compile function with the node and options (overriden with the target // presentational options). update(compile, o) { return this.targetFragments.fragments = compile.call(this.source, merge(o, this.options)); } // Copies the target indent and level, and returns the placeholder fragments compileToFragments(o, level) { this.options.indent = o.indent; this.options.level = level != null ? level : o.level; return [this.targetFragments]; } compileNode(o) { return this.compileToFragments(o); } compileClosure(o) { return this.compileToFragments(o); } }; //### Block // The block is the list of expressions that forms the body of an // indented block of code -- the implementation of a function, a clause in an // `if`, `switch`, or `try`, and so on... exports.Block = Block = (function() { class Block extends Base { constructor(nodes) { super(); this.expressions = compact(flatten(nodes || [])); } // Tack an expression on to the end of this expression list. push(node) { this.expressions.push(node); return this; } // Remove and return the last expression of this expression list. pop() { return this.expressions.pop(); } // Add an expression at the beginning of this expression list. unshift(node) { this.expressions.unshift(node); return this; } // If this Block consists of just a single node, unwrap it by pulling // it back out. unwrap() { if (this.expressions.length === 1) { return this.expressions[0]; } else { return this; } } // Is this an empty block of code? isEmpty() { return !this.expressions.length; } isStatement(o) { var exp, j, len1, ref1; ref1 = this.expressions; for (j = 0, len1 = ref1.length; j < len1; j++) { exp = ref1[j]; if (exp.isStatement(o)) { return true; } } return false; } jumps(o) { var exp, j, jumpNode, len1, ref1; ref1 = this.expressions; for (j = 0, len1 = ref1.length; j < len1; j++) { exp = ref1[j]; if (jumpNode = exp.jumps(o)) { return jumpNode; } } } // A Block node does not return its entire body, rather it // ensures that the final expression is returned. makeReturn(res) { var expr, len; len = this.expressions.length; while (len--) { expr = this.expressions[len]; this.expressions[len] = expr.makeReturn(res); if (expr instanceof Return && !expr.expression) { this.expressions.splice(len, 1); } break; } return this; } // A **Block** is the only node that can serve as the root. compileToFragments(o = {}, level) { if (o.scope) { return super.compileToFragments(o, level); } else { return this.compileRoot(o); } } // Compile all expressions within the **Block** body. If we need to return // the result, and it’s an expression, simply return it. If it’s a statement, // ask the statement to do so. compileNode(o) { var answer, compiledNodes, fragments, index, j, lastFragment, len1, node, ref1, top; this.tab = o.indent; top = o.level === LEVEL_TOP; compiledNodes = []; ref1 = this.expressions; for (index = j = 0, len1 = ref1.length; j < len1; index = ++j) { node = ref1[index]; if (node.hoisted) { // This is a hoisted expression. // We want to compile this and ignore the result. node.compileToFragments(o); continue; } node = node.unfoldSoak(o) || node; if (node instanceof Block) { // This is a nested block. We don’t do anything special here like // enclose it in a new scope; we just compile the statements in this // block along with our own. compiledNodes.push(node.compileNode(o)); } else if (top) { node.front = true; fragments = node.compileToFragments(o); if (!node.isStatement(o)) { fragments = indentInitial(fragments, this); lastFragment = fragments[fragments.length - 1]; if (!(lastFragment.code === '' || lastFragment.isComment)) { fragments.push(this.makeCode(';')); } } compiledNodes.push(fragments); } else { compiledNodes.push(node.compileToFragments(o, LEVEL_LIST)); } } if (top) { if (this.spaced) { return [].concat(this.joinFragmentArrays(compiledNodes, '\n\n'), this.makeCode('\n')); } else { return this.joinFragmentArrays(compiledNodes, '\n'); } } if (compiledNodes.length) { answer = this.joinFragmentArrays(compiledNodes, ', '); } else { answer = [this.makeCode('void 0')]; } if (compiledNodes.length > 1 && o.level >= LEVEL_LIST) { return this.wrapInParentheses(answer); } else { return answer; } } // If we happen to be the top-level **Block**, wrap everything in a safety // closure, unless requested not to. It would be better not to generate them // in the first place, but for now, clean up obvious double-parentheses. compileRoot(o) { var fragments, j, len1, name, ref1, ref2; o.indent = o.bare ? '' : TAB; o.level = LEVEL_TOP; this.spaced = true; o.scope = new Scope(null, this, null, (ref1 = o.referencedVars) != null ? ref1 : []); ref2 = o.locals || []; for (j = 0, len1 = ref2.length; j < len1; j++) { name = ref2[j]; // Mark given local variables in the root scope as parameters so they don’t // end up being declared on this block. o.scope.parameter(name); } fragments = this.compileWithDeclarations(o); HoistTarget.expand(fragments); fragments = this.compileComments(fragments); if (o.bare) { return fragments; } return [].concat(this.makeCode("(function() {\n"), fragments, this.makeCode("\n}).call(this);\n")); } // Compile the expressions body for the contents of a function, with // declarations of all inner variables pushed up to the top. compileWithDeclarations(o) { var assigns, declaredVariable, declaredVariables, declaredVariablesIndex, declars, exp, fragments, i, j, k, len1, len2, post, ref1, rest, scope, spaced; fragments = []; post = []; ref1 = this.expressions; for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { exp = ref1[i]; exp = exp.unwrap(); if (!(exp instanceof Literal)) { break; } } o = merge(o, { level: LEVEL_TOP }); if (i) { rest = this.expressions.splice(i, 9e9); [spaced, this.spaced] = [this.spaced, false]; [fragments, this.spaced] = [this.compileNode(o), spaced]; this.expressions = rest; } post = this.compileNode(o); ({scope} = o); if (scope.expressions === this) { declars = o.scope.hasDeclarations(); assigns = scope.hasAssignments; if (declars || assigns) { if (i) { fragments.push(this.makeCode('\n')); } fragments.push(this.makeCode(`${this.tab}var `)); if (declars) { declaredVariables = scope.declaredVariables(); for (declaredVariablesIndex = k = 0, len2 = declaredVariables.length; k < len2; declaredVariablesIndex = ++k) { declaredVariable = declaredVariables[declaredVariablesIndex]; fragments.push(this.makeCode(declaredVariable)); if (Object.prototype.hasOwnProperty.call(o.scope.comments, declaredVariable)) { fragments.push(...o.scope.comments[declaredVariable]); } if (declaredVariablesIndex !== declaredVariables.length - 1) { fragments.push(this.makeCode(', ')); } } } if (assigns) { if (declars) { fragments.push(this.makeCode(`,\n${this.tab + TAB}`)); } fragments.push(this.makeCode(scope.assignedVariables().join(`,\n${this.tab + TAB}`))); } fragments.push(this.makeCode(`;\n${(this.spaced ? '\n' : '')}`)); } else if (fragments.length && post.length) { fragments.push(this.makeCode("\n")); } } return fragments.concat(post); } compileComments(fragments) { var code, commentFragment, fragment, fragmentIndent, fragmentIndex, indent, j, k, l, len1, len2, len3, newLineIndex, onNextLine, pastFragment, pastFragmentIndex, q, r, ref1, ref2, ref3, ref4, trail, upcomingFragment, upcomingFragmentIndex; for (fragmentIndex = j = 0, len1 = fragments.length; j < len1; fragmentIndex = ++j) { fragment = fragments[fragmentIndex]; // Insert comments into the output at the next or previous newline. // If there are no newlines at which to place comments, create them. if (fragment.precedingComments) { // Determine the indentation level of the fragment that we are about // to insert comments before, and use that indentation level for our // inserted comments. At this point, the fragments’ `code` property // is the generated output JavaScript, and CoffeeScript always // generates output indented by two spaces; so all we need to do is // search for a `code` property that begins with at least two spaces. fragmentIndent = ''; ref1 = fragments.slice(0, fragmentIndex + 1); for (k = ref1.length - 1; k >= 0; k += -1) { pastFragment = ref1[k]; indent = /^ {2,}/m.exec(pastFragment.code); if (indent) { fragmentIndent = indent[0]; break; } else if (indexOf.call(pastFragment.code, '\n') >= 0) { break; } } code = `\n${fragmentIndent}` + ((function() { var l, len2, ref2, results; ref2 = fragment.precedingComments; results = []; for (l = 0, len2 = ref2.length; l < len2; l++) { commentFragment = ref2[l]; if (commentFragment.isHereComment && commentFragment.multiline) { results.push(multident(commentFragment.code, fragmentIndent, false)); } else { results.push(commentFragment.code); } } return results; })()).join(`\n${fragmentIndent}`).replace(/^(\s*)$/gm, ''); ref2 = fragments.slice(0, fragmentIndex + 1); for (pastFragmentIndex = l = ref2.length - 1; l >= 0; pastFragmentIndex = l += -1) { pastFragment = ref2[pastFragmentIndex]; newLineIndex = pastFragment.code.lastIndexOf('\n'); if (newLineIndex === -1) { // Keep searching previous fragments until we can’t go back any // further, either because there are no fragments left or we’ve // discovered that we’re in a code block that is interpolated // inside a string. if (pastFragmentIndex === 0) { pastFragment.code = '\n' + pastFragment.code; newLineIndex = 0; } else if (pastFragment.isStringWithInterpolations && pastFragment.code === '{') { code = code.slice(1) + '\n'; // Move newline to end. newLineIndex = 1; } else { continue; } } delete fragment.precedingComments; pastFragment.code = pastFragment.code.slice(0, newLineIndex) + code + pastFragment.code.slice(newLineIndex); break; } } // Yes, this is awfully similar to the previous `if` block, but if you // look closely you’ll find lots of tiny differences that make this // confusing if it were abstracted into a function that both blocks share. if (fragment.followingComments) { // Does the first trailing comment follow at the end of a line of code, // like `; // Comment`, or does it start a new line after a line of code? trail = fragment.followingComments[0].trail; fragmentIndent = ''; // Find the indent of the next line of code, if we have any non-trailing // comments to output. We need to first find the next newline, as these // comments will be output after that; and then the indent of the line // that follows the next newline. if (!(trail && fragment.followingComments.length === 1)) { onNextLine = false; ref3 = fragments.slice(fragmentIndex); for (q = 0, len2 = ref3.length; q < len2; q++) { upcomingFragment = ref3[q]; if (!onNextLine) { if (indexOf.call(upcomingFragment.code, '\n') >= 0) { onNextLine = true; } else { continue; } } else { indent = /^ {2,}/m.exec(upcomingFragment.code); if (indent) { fragmentIndent = indent[0]; break; } else if (indexOf.call(upcomingFragment.code, '\n') >= 0) { break; } } } } // Is this comment following the indent inserted by bare mode? // If so, there’s no need to indent this further. code = fragmentIndex === 1 && /^\s+$/.test(fragments[0].code) ? '' : trail ? ' ' : `\n${fragmentIndent}`; // Assemble properly indented comments. code += ((function() { var len3, r, ref4, results; ref4 = fragment.followingComments; results = []; for (r = 0, len3 = ref4.length; r < len3; r++) { commentFragment = ref4[r]; if (commentFragment.isHereComment && commentFragment.multiline) { results.push(multident(commentFragment.code, fragmentIndent, false)); } else { results.push(commentFragment.code); } } return results; })()).join(`\n${fragmentIndent}`).replace(/^(\s*)$/gm, ''); ref4 = fragments.slice(fragmentIndex); for (upcomingFragmentIndex = r = 0, len3 = ref4.length; r < len3; upcomingFragmentIndex = ++r) { upcomingFragment = ref4[upcomingFragmentIndex]; newLineIndex = upcomingFragment.code.indexOf('\n'); if (newLineIndex === -1) { // Keep searching upcoming fragments until we can’t go any // further, either because there are no fragments left or we’ve // discovered that we’re in a code block that is interpolated // inside a string. if (upcomingFragmentIndex === fragments.length - 1) { upcomingFragment.code = upcomingFragment.code + '\n'; newLineIndex = upcomingFragment.code.length; } else if (upcomingFragment.isStringWithInterpolations && upcomingFragment.code === '}') { code = `${code}\n`; newLineIndex = 0; } else { continue; } } delete fragment.followingComments; if (upcomingFragment.code === '\n') { // Avoid inserting extra blank lines. code = code.replace(/^\n/, ''); } upcomingFragment.code = upcomingFragment.code.slice(0, newLineIndex) + code + upcomingFragment.code.slice(newLineIndex); break; } } } return fragments; } // Wrap up the given nodes as a **Block**, unless it already happens // to be one. static wrap(nodes) { if (nodes.length === 1 && nodes[0] instanceof Block) { return nodes[0]; } return new Block(nodes); } }; Block.prototype.children = ['expressions']; return Block; }).call(this); //### Literal // `Literal` is a base class for static values that can be passed through // directly into JavaScript without translation, such as: strings, numbers, // `true`, `false`, `null`... exports.Literal = Literal = (function() { class Literal extends Base { constructor(value1) { super(); this.value = value1; } assigns(name) { return name === this.value; } compileNode(o) { return [this.makeCode(this.value)]; } toString() { // This is only intended for debugging. return ` ${(this.isStatement() ? super.toString() : this.constructor.name)}: ${this.value}`; } }; Literal.prototype.shouldCache = NO; return Literal; }).call(this); exports.NumberLiteral = NumberLiteral = class NumberLiteral extends Literal {}; exports.InfinityLiteral = InfinityLiteral = class InfinityLiteral extends NumberLiteral { compileNode() { return [this.makeCode('2e308')]; } }; exports.NaNLiteral = NaNLiteral = class NaNLiteral extends NumberLiteral { constructor() { super('NaN'); } compileNode(o) { var code; code = [this.makeCode('0/0')]; if (o.level >= LEVEL_OP) { return this.wrapInParentheses(code); } else { return code; } } }; exports.StringLiteral = StringLiteral = class StringLiteral extends Literal { compileNode(o) { var res; return res = this.csx ? [this.makeCode(this.unquote(true, true))] : super.compileNode(); } unquote(doubleQuote = false, newLine = false) { var unquoted; unquoted = this.value.slice(1, -1); if (doubleQuote) { unquoted = unquoted.replace(/\\"/g, '"'); } if (newLine) { unquoted = unquoted.replace(/\\n/g, '\n'); } return unquoted; } }; exports.RegexLiteral = RegexLiteral = class RegexLiteral extends Literal {}; exports.PassthroughLiteral = PassthroughLiteral = class PassthroughLiteral extends Literal {}; exports.IdentifierLiteral = IdentifierLiteral = (function() { class IdentifierLiteral extends Literal { eachName(iterator) { return iterator(this); } }; IdentifierLiteral.prototype.isAssignable = YES; return IdentifierLiteral; }).call(this); exports.CSXTag = CSXTag = class CSXTag extends IdentifierLiteral {}; exports.PropertyName = PropertyName = (function() { class PropertyName extends Literal {}; PropertyName.prototype.isAssignable = YES; return PropertyName; }).call(this); exports.ComputedPropertyName = ComputedPropertyName = class ComputedPropertyName extends PropertyName { compileNode(o) { return [this.makeCode('['), ...this.value.compileToFragments(o, LEVEL_LIST), this.makeCode(']')]; } }; exports.StatementLiteral = StatementLiteral = (function() { class StatementLiteral extends Literal { jumps(o) { if (this.value === 'break' && !((o != null ? o.loop : void 0) || (o != null ? o.block : void 0))) { return this; } if (this.value === 'continue' && !(o != null ? o.loop : void 0)) { return this; } } compileNode(o) { return [this.makeCode(`${this.tab}${this.value};`)]; } }; StatementLiteral.prototype.isStatement = YES; StatementLiteral.prototype.makeReturn = THIS; return StatementLiteral; }).call(this); exports.ThisLiteral = ThisLiteral = class ThisLiteral extends Literal { constructor() { super('this'); } compileNode(o) { var code, ref1; code = ((ref1 = o.scope.method) != null ? ref1.bound : void 0) ? o.scope.method.context : this.value; return [this.makeCode(code)]; } }; exports.UndefinedLiteral = UndefinedLiteral = class UndefinedLiteral extends Literal { constructor() { super('undefined'); } compileNode(o) { return [this.makeCode(o.level >= LEVEL_ACCESS ? '(void 0)' : 'void 0')]; } }; exports.NullLiteral = NullLiteral = class NullLiteral extends Literal { constructor() { super('null'); } }; exports.BooleanLiteral = BooleanLiteral = class BooleanLiteral extends Literal {}; //### Return // A `return` is a *pureStatement*—wrapping it in a closure wouldn’t make sense. exports.Return = Return = (function() { class Return extends Base { constructor(expression1) { super(); this.expression = expression1; } compileToFragments(o, level) { var expr, ref1; expr = (ref1 = this.expression) != null ? ref1.makeReturn() : void 0; if (expr && !(expr instanceof Return)) { return expr.compileToFragments(o, level); } else { return super.compileToFragments(o, level); } } compileNode(o) { var answer, fragment, j, len1; answer = []; // TODO: If we call `expression.compile()` here twice, we’ll sometimes // get back different results! if (this.expression) { answer = this.expression.compileToFragments(o, LEVEL_PAREN); unshiftAfterComments(answer, this.makeCode(`${this.tab}return `)); // Since the `return` got indented by `@tab`, preceding comments that are // multiline need to be indented. for (j = 0, len1 = answer.length; j < len1; j++) { fragment = answer[j]; if (fragment.isHereComment && indexOf.call(fragment.code, '\n') >= 0) { fragment.code = multident(fragment.code, this.tab); } else if (fragment.isLineComment) { fragment.code = `${this.tab}${fragment.code}`; } else { break; } } } else { answer.push(this.makeCode(`${this.tab}return`)); } answer.push(this.makeCode(';')); return answer; } }; Return.prototype.children = ['expression']; Return.prototype.isStatement = YES; Return.prototype.makeReturn = THIS; Return.prototype.jumps = THIS; return Return; }).call(this); // `yield return` works exactly like `return`, except that it turns the function // into a generator. exports.YieldReturn = YieldReturn = class YieldReturn extends Return { compileNode(o) { if (o.scope.parent == null) { this.error('yield can only occur inside functions'); } return super.compileNode(o); } }; exports.AwaitReturn = AwaitReturn = class AwaitReturn extends Return { compileNode(o) { if (o.scope.parent == null) { this.error('await can only occur inside functions'); } return super.compileNode(o); } }; //### Value // A value, variable or literal or parenthesized, indexed or dotted into, // or vanilla. exports.Value = Value = (function() { class Value extends Base { constructor(base, props, tag, isDefaultValue = false) { var ref1, ref2; super(); if (!props && base instanceof Value) { return base; } this.base = base; this.properties = props || []; if (tag) { this[tag] = true; } this.isDefaultValue = isDefaultValue; // If this is a `@foo =` assignment, if there are comments on `@` move them // to be on `foo`. if (((ref1 = this.base) != null ? ref1.comments : void 0) && this.base instanceof ThisLiteral && (((ref2 = this.properties[0]) != null ? ref2.name : void 0) != null)) { moveComments(this.base, this.properties[0].name); } } // Add a property (or *properties* ) `Access` to the list. add(props) { this.properties = this.properties.concat(props); this.forceUpdateLocation = true; return this; } hasProperties() { return this.properties.length !== 0; } bareLiteral(type) { return !this.properties.length && this.base instanceof type; } // Some boolean checks for the benefit of other nodes. isArray() { return this.bareLiteral(Arr); } isRange() { return this.bareLiteral(Range); } shouldCache() { return this.hasProperties() || this.base.shouldCache(); } isAssignable() { return this.hasProperties() || this.base.isAssignable(); } isNumber() { return this.bareLiteral(NumberLiteral); } isString() { return this.bareLiteral(StringLiteral); } isRegex() { return this.bareLiteral(RegexLiteral); } isUndefined() { return this.bareLiteral(UndefinedLiteral); } isNull() { return this.bareLiteral(NullLiteral); } isBoolean() { return this.bareLiteral(BooleanLiteral); } isAtomic() { var j, len1, node, ref1; ref1 = this.properties.concat(this.base); for (j = 0, len1 = ref1.length; j < len1; j++) { node = ref1[j]; if (node.soak || node instanceof Call) { return false; } } return true; } isNotCallable() { return this.isNumber() || this.isString() || this.isRegex() || this.isArray() || this.isRange() || this.isSplice() || this.isObject() || this.isUndefined() || this.isNull() || this.isBoolean(); } isStatement(o) { return !this.properties.length && this.base.isStatement(o); } assigns(name) { return !this.properties.length && this.base.assigns(name); } jumps(o) { return !this.properties.length && this.base.jumps(o); } isObject(onlyGenerated) { if (this.properties.length) { return false; } return (this.base instanceof Obj) && (!onlyGenerated || this.base.generated); } isElision() { if (!(this.base instanceof Arr)) { return false; } return this.base.hasElision(); } isSplice() { var lastProp, ref1; ref1 = this.properties, lastProp = ref1[ref1.length - 1]; return lastProp instanceof Slice; } looksStatic(className) { var ref1; return (this.this || this.base instanceof ThisLiteral || this.base.value === className) && this.properties.length === 1 && ((ref1 = this.properties[0].name) != null ? ref1.value : void 0) !== 'prototype'; } // The value can be unwrapped as its inner node, if there are no attached // properties. unwrap() { if (this.properties.length) { return this; } else { return this.base; } } // A reference has base part (`this` value) and name part. // We cache them separately for compiling complex expressions. // `a()[b()] ?= c` -> `(_base = a())[_name = b()] ? _base[_name] = c` cacheReference(o) { var base, bref, name, nref, ref1; ref1 = this.properties, name = ref1[ref1.length - 1]; if (this.properties.length < 2 && !this.base.shouldCache() && !(name != null ? name.shouldCache() : void 0)) { return [this, this]; // `a` `a.b` } base = new Value(this.base, this.properties.slice(0, -1)); if (base.shouldCache()) { // `a().b` bref = new IdentifierLiteral(o.scope.freeVariable('base')); base = new Value(new Parens(new Assign(bref, base))); } if (!name) { // `a()` return [base, bref]; } if (name.shouldCache()) { // `a[b()]` nref = new IdentifierLiteral(o.scope.freeVariable('name')); name = new Index(new Assign(nref, name.index)); nref = new Index(nref); } return [base.add(name), new Value(bref || base.base, [nref || name])]; } // We compile a value to JavaScript by compiling and joining each property. // Things get much more interesting if the chain of properties has *soak* // operators `?.` interspersed. Then we have to take care not to accidentally // evaluate anything twice when building the soak chain. compileNode(o) { var fragments, j, len1, prop, props; this.base.front = this.front; props = this.properties; fragments = this.base.compileToFragments(o, (props.length ? LEVEL_ACCESS : null)); if (props.length && SIMPLENUM.test(fragmentsToText(fragments))) { fragments.push(this.makeCode('.')); } for (j = 0, len1 = props.length; j < len1; j++) { prop = props[j]; fragments.push(...(prop.compileToFragments(o))); } return fragments; } // Unfold a soak into an `If`: `a?.b` -> `a.b if a?` unfoldSoak(o) { return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => { var fst, i, ifn, j, len1, prop, ref, ref1, snd; ifn = this.base.unfoldSoak(o); if (ifn) { ifn.body.properties.push(...this.properties); return ifn; } ref1 = this.properties; for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { prop = ref1[i]; if (!prop.soak) { continue; } prop.soak = false; fst = new Value(this.base, this.properties.slice(0, i)); snd = new Value(this.base, this.properties.slice(i)); if (fst.shouldCache()) { ref = new IdentifierLiteral(o.scope.freeVariable('ref')); fst = new Parens(new Assign(ref, fst)); snd.base = ref; } return new If(new Existence(fst), snd, { soak: true }); } return false; })(); } eachName(iterator) { if (this.hasProperties()) { return iterator(this); } else if (this.base.isAssignable()) { return this.base.eachName(iterator); } else { return this.error('tried to assign to unassignable value'); } } }; Value.prototype.children = ['base', 'properties']; return Value; }).call(this); //### HereComment // Comment delimited by `###` (becoming `/* */`). exports.HereComment = HereComment = class HereComment extends Base { constructor({ content: content1, newLine: newLine1, unshift: unshift }) { super(); this.content = content1; this.newLine = newLine1; this.unshift = unshift; } compileNode(o) { var fragment, hasLeadingMarks, j, largestIndent, leadingWhitespace, len1, line, multiline, ref1; multiline = indexOf.call(this.content, '\n') >= 0; hasLeadingMarks = /\n\s*[#|\*]/.test(this.content); if (hasLeadingMarks) { this.content = this.content.replace(/^([ \t]*)#(?=\s)/gm, ' *'); } // Unindent multiline comments. They will be reindented later. if (multiline) { largestIndent = ''; ref1 = this.content.split('\n'); for (j = 0, len1 = ref1.length; j < len1; j++) { line = ref1[j]; leadingWhitespace = /^\s*/.exec(line)[0]; if (leadingWhitespace.length > largestIndent.length) { largestIndent = leadingWhitespace; } } this.content = this.content.replace(RegExp(`^(${leadingWhitespace})`, "gm"), ''); } this.content = `/*${this.content}${(hasLeadingMarks ? ' ' : '')}*/`; fragment = this.makeCode(this.content); fragment.newLine = this.newLine; fragment.unshift = this.unshift; fragment.multiline = multiline; // Don’t rely on `fragment.type`, which can break when the compiler is minified. fragment.isComment = fragment.isHereComment = true; return fragment; } }; //### LineComment // Comment running from `#` to the end of a line (becoming `//`). exports.LineComment = LineComment = class LineComment extends Base { constructor({ content: content1, newLine: newLine1, unshift: unshift }) { super(); this.content = content1; this.newLine = newLine1; this.unshift = unshift; } compileNode(o) { var fragment; fragment = this.makeCode(/^\s*$/.test(this.content) ? '' : `//${this.content}`); fragment.newLine = this.newLine; fragment.unshift = this.unshift; fragment.trail = !this.newLine && !this.unshift; // Don’t rely on `fragment.type`, which can break when the compiler is minified. fragment.isComment = fragment.isLineComment = true; return fragment; } }; //### Call // Node for a function invocation. exports.Call = Call = (function() { class Call extends Base { constructor(variable1, args1 = [], soak1, token1) { var ref1; super(); this.variable = variable1; this.args = args1; this.soak = soak1; this.token = token1; this.isNew = false; if (this.variable instanceof Value && this.variable.isNotCallable()) { this.variable.error("literal is not a function"); } this.csx = this.variable.base instanceof CSXTag; // `@variable` never gets output as a result of this node getting created as // part of `RegexWithInterpolations`, so for that case move any comments to // the `args` property that gets passed into `RegexWithInterpolations` via // the grammar. if (((ref1 = this.variable.base) != null ? ref1.value : void 0) === 'RegExp' && this.args.length !== 0) { moveComments(this.variable, this.args[0]); } } // When setting the location, we sometimes need to update the start location to // account for a newly-discovered `new` operator to the left of us. This // expands the range on the left, but not the right. updateLocationDataIfMissing(locationData) { var base, ref1; if (this.locationData && this.needsUpdatedStartLocation) { this.locationData.first_line = locationData.first_line; this.locationData.first_column = locationData.first_column; base = ((ref1 = this.variable) != null ? ref1.base : void 0) || this.variable; if (base.needsUpdatedStartLocation) { this.variable.locationData.first_line = locationData.first_line; this.variable.locationData.first_column = locationData.first_column; base.updateLocationDataIfMissing(locationData); } delete this.needsUpdatedStartLocation; } return super.updateLocationDataIfMissing(locationData); } // Tag this invocation as creating a new instance. newInstance() { var base, ref1; base = ((ref1 = this.variable) != null ? ref1.base : void 0) || this.variable; if (base instanceof Call && !base.isNew) { base.newInstance(); } else { this.isNew = true; } this.needsUpdatedStartLocation = true; return this; } // Soaked chained invocations unfold into if/else ternary structures. unfoldSoak(o) { var call, ifn, j, left, len1, list, ref1, rite; if (this.soak) { if (this.variable instanceof Super) { left = new Literal(this.variable.compile(o)); rite = new Value(left); if (this.variable.accessor == null) { this.variable.error("Unsupported reference to 'super'"); } } else { if (ifn = unfoldSoak(o, this, 'variable')) { return ifn; } [left, rite] = new Value(this.variable).cacheReference(o); } rite = new Call(rite, this.args); rite.isNew = this.isNew; left = new Literal(`typeof ${left.compile(o)} === "function"`); return new If(left, new Value(rite), { soak: true }); } call = this; list = []; while (true) { if (call.variable instanceof Call) { list.push(call); call = call.variable; continue; } if (!(call.variable instanceof Value)) { break; } list.push(call); if (!((call = call.variable.base) instanceof Call)) { break; } } ref1 = list.reverse(); for (j = 0, len1 = ref1.length; j < len1; j++) { call = ref1[j]; if (ifn) { if (call.variable instanceof Call) { call.variable = ifn; } else { call.variable.base = ifn; } } ifn = unfoldSoak(o, call, 'variable'); } return ifn; } // Compile a vanilla function call. compileNode(o) { var arg, argIndex, compiledArgs, fragments, j, len1, ref1, ref2; if (this.csx) { return this.compileCSX(o); } if ((ref1 = this.variable) != null) { ref1.front = this.front; } compiledArgs = []; ref2 = this.args; for (argIndex = j = 0, len1 = ref2.length; j < len1; argIndex = ++j) { arg = ref2[argIndex]; if (argIndex) { compiledArgs.push(this.makeCode(", ")); } compiledArgs.push(...(arg.compileToFragments(o, LEVEL_LIST))); } fragments = []; if (this.isNew) { if (this.variable instanceof Super) { this.variable.error("Unsupported reference to 'super'"); } fragments.push(this.makeCode('new ')); } fragments.push(...this.variable.compileToFragments(o, LEVEL_ACCESS)); fragments.push(this.makeCode('('), ...compiledArgs, this.makeCode(')')); return fragments; } compileCSX(o) { var attr, attrProps, attributes, content, fragments, j, len1, obj, ref1, tag; [attributes, content] = this.args; attributes.base.csx = true; if (content != null) { content.base.csx = true; } fragments = [this.makeCode('<')]; fragments.push(...(tag = this.variable.compileToFragments(o, LEVEL_ACCESS))); if (attributes.base instanceof Arr) { ref1 = attributes.base.objects; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; attr = obj.base; attrProps = (attr != null ? attr.properties : void 0) || []; // Catch invalid CSX attributes:
if (!(attr instanceof Obj || attr instanceof IdentifierLiteral) || (attr instanceof Obj && !attr.generated && (attrProps.length > 1 || !(attrProps[0] instanceof Splat)))) { obj.error("Unexpected token. Allowed CSX attributes are: id=\"val\", src={source}, {props...} or attribute."); } if (obj.base instanceof Obj) { obj.base.csx = true; } fragments.push(this.makeCode(' ')); fragments.push(...obj.compileToFragments(o, LEVEL_PAREN)); } } if (content) { fragments.push(this.makeCode('>')); fragments.push(...content.compileNode(o, LEVEL_LIST)); fragments.push(...[this.makeCode(''), ...tag, this.makeCode('>')]); } else { fragments.push(this.makeCode(' />')); } return fragments; } }; Call.prototype.children = ['variable', 'args']; return Call; }).call(this); //### Super // Takes care of converting `super()` calls into calls against the prototype's // function of the same name. // When `expressions` are set the call will be compiled in such a way that the // expressions are evaluated without altering the return value of the `SuperCall` // expression. exports.SuperCall = SuperCall = (function() { class SuperCall extends Call { isStatement(o) { var ref1; return ((ref1 = this.expressions) != null ? ref1.length : void 0) && o.level === LEVEL_TOP; } compileNode(o) { var ref, ref1, replacement, superCall; if (!((ref1 = this.expressions) != null ? ref1.length : void 0)) { return super.compileNode(o); } superCall = new Literal(fragmentsToText(super.compileNode(o))); replacement = new Block(this.expressions.slice()); if (o.level > LEVEL_TOP) { // If we might be in an expression we need to cache and return the result [superCall, ref] = superCall.cache(o, null, YES); replacement.push(ref); } replacement.unshift(superCall); return replacement.compileToFragments(o, o.level === LEVEL_TOP ? o.level : LEVEL_LIST); } }; SuperCall.prototype.children = Call.prototype.children.concat(['expressions']); return SuperCall; }).call(this); exports.Super = Super = (function() { class Super extends Base { constructor(accessor) { super(); this.accessor = accessor; } compileNode(o) { var fragments, method, name, nref, ref1, ref2, salvagedComments, variable; method = o.scope.namedMethod(); if (!(method != null ? method.isMethod : void 0)) { this.error('cannot use super outside of an instance method'); } if (!((method.ctor != null) || (this.accessor != null))) { ({name, variable} = method); if (name.shouldCache() || (name instanceof Index && name.index.isAssignable())) { nref = new IdentifierLiteral(o.scope.parent.freeVariable('name')); name.index = new Assign(nref, name.index); } this.accessor = nref != null ? new Index(nref) : name; } if ((ref1 = this.accessor) != null ? (ref2 = ref1.name) != null ? ref2.comments : void 0 : void 0) { // A `super()` call gets compiled to e.g. `super.method()`, which means // the `method` property name gets compiled for the first time here, and // again when the `method:` property of the class gets compiled. Since // this compilation happens first, comments attached to `method:` would // get incorrectly output near `super.method()`, when we want them to // get output on the second pass when `method:` is output. So set them // aside during this compilation pass, and put them back on the object so // that they’re there for the later compilation. salvagedComments = this.accessor.name.comments; delete this.accessor.name.comments; } fragments = (new Value(new Literal('super'), this.accessor ? [this.accessor] : [])).compileToFragments(o); if (salvagedComments) { attachCommentsToNode(salvagedComments, this.accessor.name); } return fragments; } }; Super.prototype.children = ['accessor']; return Super; }).call(this); //### RegexWithInterpolations // Regexes with interpolations are in fact just a variation of a `Call` (a // `RegExp()` call to be precise) with a `StringWithInterpolations` inside. exports.RegexWithInterpolations = RegexWithInterpolations = class RegexWithInterpolations extends Call { constructor(args = []) { super(new Value(new IdentifierLiteral('RegExp')), args, false); } }; //### TaggedTemplateCall exports.TaggedTemplateCall = TaggedTemplateCall = class TaggedTemplateCall extends Call { constructor(variable, arg, soak) { if (arg instanceof StringLiteral) { arg = new StringWithInterpolations(Block.wrap([new Value(arg)])); } super(variable, [arg], soak); } compileNode(o) { return this.variable.compileToFragments(o, LEVEL_ACCESS).concat(this.args[0].compileToFragments(o, LEVEL_LIST)); } }; //### Extends // Node to extend an object's prototype with an ancestor object. // After `goog.inherits` from the // [Closure Library](https://github.com/google/closure-library/blob/master/closure/goog/base.js). exports.Extends = Extends = (function() { class Extends extends Base { constructor(child1, parent1) { super(); this.child = child1; this.parent = parent1; } // Hooks one constructor into another's prototype chain. compileToFragments(o) { return new Call(new Value(new Literal(utility('extend', o))), [this.child, this.parent]).compileToFragments(o); } }; Extends.prototype.children = ['child', 'parent']; return Extends; }).call(this); //### Access // A `.` access into a property of a value, or the `::` shorthand for // an access into the object's prototype. exports.Access = Access = (function() { class Access extends Base { constructor(name1, tag) { super(); this.name = name1; this.soak = tag === 'soak'; } compileToFragments(o) { var name, node; name = this.name.compileToFragments(o); node = this.name.unwrap(); if (node instanceof PropertyName) { return [this.makeCode('.'), ...name]; } else { return [this.makeCode('['), ...name, this.makeCode(']')]; } } }; Access.prototype.children = ['name']; Access.prototype.shouldCache = NO; return Access; }).call(this); //### Index // A `[ ... ]` indexed access into an array or object. exports.Index = Index = (function() { class Index extends Base { constructor(index1) { super(); this.index = index1; } compileToFragments(o) { return [].concat(this.makeCode("["), this.index.compileToFragments(o, LEVEL_PAREN), this.makeCode("]")); } shouldCache() { return this.index.shouldCache(); } }; Index.prototype.children = ['index']; return Index; }).call(this); //### Range // A range literal. Ranges can be used to extract portions (slices) of arrays, // to specify a range for comprehensions, or as a value, to be expanded into the // corresponding array of integers at runtime. exports.Range = Range = (function() { class Range extends Base { constructor(from1, to1, tag) { super(); this.from = from1; this.to = to1; this.exclusive = tag === 'exclusive'; this.equals = this.exclusive ? '' : '='; } // Compiles the range's source variables -- where it starts and where it ends. // But only if they need to be cached to avoid double evaluation. compileVariables(o) { var shouldCache, step; o = merge(o, { top: true }); shouldCache = del(o, 'shouldCache'); [this.fromC, this.fromVar] = this.cacheToCodeFragments(this.from.cache(o, LEVEL_LIST, shouldCache)); [this.toC, this.toVar] = this.cacheToCodeFragments(this.to.cache(o, LEVEL_LIST, shouldCache)); if (step = del(o, 'step')) { [this.step, this.stepVar] = this.cacheToCodeFragments(step.cache(o, LEVEL_LIST, shouldCache)); } this.fromNum = this.from.isNumber() ? Number(this.fromVar) : null; this.toNum = this.to.isNumber() ? Number(this.toVar) : null; return this.stepNum = (step != null ? step.isNumber() : void 0) ? Number(this.stepVar) : null; } // When compiled normally, the range returns the contents of the *for loop* // needed to iterate over the values in the range. Used by comprehensions. compileNode(o) { var cond, condPart, from, gt, idx, idxName, known, lt, namedIndex, stepPart, to, varPart; if (!this.fromVar) { this.compileVariables(o); } if (!o.index) { return this.compileArray(o); } // Set up endpoints. known = (this.fromNum != null) && (this.toNum != null); idx = del(o, 'index'); idxName = del(o, 'name'); namedIndex = idxName && idxName !== idx; varPart = `${idx} = ${this.fromC}`; if (this.toC !== this.toVar) { varPart += `, ${this.toC}`; } if (this.step !== this.stepVar) { varPart += `, ${this.step}`; } [lt, gt] = [`${idx} <${this.equals}`, `${idx} >${this.equals}`]; // Generate the condition. condPart = this.stepNum != null ? this.stepNum > 0 ? `${lt} ${this.toVar}` : `${gt} ${this.toVar}` : known ? ([from, to] = [this.fromNum, this.toNum], from <= to ? `${lt} ${to}` : `${gt} ${to}`) : (cond = this.stepVar ? `${this.stepVar} > 0` : `${this.fromVar} <= ${this.toVar}`, `${cond} ? ${lt} ${this.toVar} : ${gt} ${this.toVar}`); // Generate the step. stepPart = this.stepVar ? `${idx} += ${this.stepVar}` : known ? namedIndex ? from <= to ? `++${idx}` : `--${idx}` : from <= to ? `${idx}++` : `${idx}--` : namedIndex ? `${cond} ? ++${idx} : --${idx}` : `${cond} ? ${idx}++ : ${idx}--`; if (namedIndex) { varPart = `${idxName} = ${varPart}`; } if (namedIndex) { stepPart = `${idxName} = ${stepPart}`; } // The final loop body. return [this.makeCode(`${varPart}; ${condPart}; ${stepPart}`)]; } // When used as a value, expand the range into the equivalent array. compileArray(o) { var args, body, cond, hasArgs, i, idt, j, known, post, pre, range, ref1, ref2, result, results, vars; known = (this.fromNum != null) && (this.toNum != null); if (known && Math.abs(this.fromNum - this.toNum) <= 20) { range = (function() { results = []; for (var j = ref1 = this.fromNum, ref2 = this.toNum; ref1 <= ref2 ? j <= ref2 : j >= ref2; ref1 <= ref2 ? j++ : j--){ results.push(j); } return results; }).apply(this); if (this.exclusive) { range.pop(); } return [this.makeCode(`[${range.join(', ')}]`)]; } idt = this.tab + TAB; i = o.scope.freeVariable('i', { single: true }); result = o.scope.freeVariable('results'); pre = `\n${idt}${result} = [];`; if (known) { o.index = i; body = fragmentsToText(this.compileNode(o)); } else { vars = `${i} = ${this.fromC}` + (this.toC !== this.toVar ? `, ${this.toC}` : ''); cond = `${this.fromVar} <= ${this.toVar}`; body = `var ${vars}; ${cond} ? ${i} <${this.equals} ${this.toVar} : ${i} >${this.equals} ${this.toVar}; ${cond} ? ${i}++ : ${i}--`; } post = `{ ${result}.push(${i}); }\n${idt}return ${result};\n${o.indent}`; hasArgs = function(node) { return node != null ? node.contains(isLiteralArguments) : void 0; }; if (hasArgs(this.from) || hasArgs(this.to)) { args = ', arguments'; } return [this.makeCode(`(function() {${pre}\n${idt}for (${body})${post}}).apply(this${args != null ? args : ''})`)]; } }; Range.prototype.children = ['from', 'to']; return Range; }).call(this); //### Slice // An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter // specifies the index of the end of the slice, just as the first parameter // is the index of the beginning. exports.Slice = Slice = (function() { class Slice extends Base { constructor(range1) { super(); this.range = range1; } // We have to be careful when trying to slice through the end of the array, // `9e9` is used because not all implementations respect `undefined` or `1/0`. // `9e9` should be safe because `9e9` > `2**32`, the max array length. compileNode(o) { var compiled, compiledText, from, fromCompiled, to, toStr; ({to, from} = this.range); fromCompiled = from && from.compileToFragments(o, LEVEL_PAREN) || [this.makeCode('0')]; // TODO: jwalton - move this into the 'if'? if (to) { compiled = to.compileToFragments(o, LEVEL_PAREN); compiledText = fragmentsToText(compiled); if (!(!this.range.exclusive && +compiledText === -1)) { toStr = ', ' + (this.range.exclusive ? compiledText : to.isNumber() ? `${+compiledText + 1}` : (compiled = to.compileToFragments(o, LEVEL_ACCESS), `+${fragmentsToText(compiled)} + 1 || 9e9`)); } } return [this.makeCode(`.slice(${fragmentsToText(fromCompiled)}${toStr || ''})`)]; } }; Slice.prototype.children = ['range']; return Slice; }).call(this); //### Obj // An object literal, nothing fancy. exports.Obj = Obj = (function() { class Obj extends Base { constructor(props, generated = false, lhs1 = false) { super(); this.generated = generated; this.lhs = lhs1; this.objects = this.properties = props || []; } isAssignable() { var j, len1, message, prop, ref1; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; // Check for reserved words. message = isUnassignable(prop.unwrapAll().value); if (message) { prop.error(message); } if (prop instanceof Assign && prop.context === 'object') { prop = prop.value; } if (!prop.isAssignable()) { return false; } } return true; } shouldCache() { return !this.isAssignable(); } // Check if object contains splat. hasSplat() { var j, len1, prop, ref1; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; if (prop instanceof Splat) { return true; } } return false; } compileNode(o) { var answer, i, idt, indent, isCompact, j, join, k, key, l, lastNode, len1, len2, len3, len4, node, prop, props, q, ref1, unwrappedVal, value; props = this.properties; if (this.generated) { for (j = 0, len1 = props.length; j < len1; j++) { node = props[j]; if (node instanceof Value) { node.error('cannot have an implicit value in an implicit object'); } } } if (this.hasSplat() && !this.csx) { // Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md return this.compileSpread(o); } idt = o.indent += TAB; lastNode = this.lastNode(this.properties); if (this.csx) { // CSX attributes return this.compileCSXAttributes(o); } // If this object is the left-hand side of an assignment, all its children // are too. if (this.lhs) { for (k = 0, len2 = props.length; k < len2; k++) { prop = props[k]; if (!(prop instanceof Assign)) { continue; } ({value} = prop); unwrappedVal = value.unwrapAll(); if (unwrappedVal instanceof Arr || unwrappedVal instanceof Obj) { unwrappedVal.lhs = true; } else if (unwrappedVal instanceof Assign) { unwrappedVal.nestedLhs = true; } } } isCompact = true; ref1 = this.properties; for (l = 0, len3 = ref1.length; l < len3; l++) { prop = ref1[l]; if (prop instanceof Assign && prop.context === 'object') { isCompact = false; } } answer = []; answer.push(this.makeCode(isCompact ? '' : '\n')); for (i = q = 0, len4 = props.length; q < len4; i = ++q) { prop = props[i]; join = i === props.length - 1 ? '' : isCompact ? ', ' : prop === lastNode ? '\n' : ',\n'; indent = isCompact ? '' : idt; key = prop instanceof Assign && prop.context === 'object' ? prop.variable : prop instanceof Assign ? (!this.lhs ? prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`) : void 0, prop.variable) : prop; if (key instanceof Value && key.hasProperties()) { if (prop.context === 'object' || !key.this) { key.error('invalid object key'); } key = key.properties[0].name; prop = new Assign(key, prop, 'object'); } if (key === prop) { if (prop.shouldCache()) { [key, value] = prop.base.cache(o); if (key instanceof IdentifierLiteral) { key = new PropertyName(key.value); } prop = new Assign(key, value, 'object'); } else if (key instanceof Value && key.base instanceof ComputedPropertyName) { // `{ [foo()] }` output as `{ [ref = foo()]: ref }`. if (prop.base.value.shouldCache()) { [key, value] = prop.base.value.cache(o); if (key instanceof IdentifierLiteral) { key = new ComputedPropertyName(key.value); } prop = new Assign(key, value, 'object'); } else { // `{ [expression] }` output as `{ [expression]: expression }`. prop = new Assign(key, prop.base.value, 'object'); } } else if (!(typeof prop.bareLiteral === "function" ? prop.bareLiteral(IdentifierLiteral) : void 0)) { prop = new Assign(prop, prop, 'object'); } } if (indent) { answer.push(this.makeCode(indent)); } answer.push(...prop.compileToFragments(o, LEVEL_TOP)); if (join) { answer.push(this.makeCode(join)); } } answer.push(this.makeCode(isCompact ? '' : `\n${this.tab}`)); answer = this.wrapInBraces(answer); if (this.front) { return this.wrapInParentheses(answer); } else { return answer; } } assigns(name) { var j, len1, prop, ref1; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; if (prop.assigns(name)) { return true; } } return false; } eachName(iterator) { var j, len1, prop, ref1, results; ref1 = this.properties; results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; if (prop instanceof Assign && prop.context === 'object') { prop = prop.value; } prop = prop.unwrapAll(); if (prop.eachName != null) { results.push(prop.eachName(iterator)); } else { results.push(void 0); } } return results; } // Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md // `obj2 = {a: 1, obj..., c: 3, d: 4}` → `obj2 = _extends({}, {a: 1}, obj, {c: 3, d: 4})` compileSpread(o) { var _extends, addSlice, j, len1, prop, propSlices, props, slices, splatSlice; props = this.properties; // Store object spreads. splatSlice = []; propSlices = []; slices = []; addSlice = function() { if (propSlices.length) { slices.push(new Obj(propSlices)); } if (splatSlice.length) { slices.push(...splatSlice); } splatSlice = []; return propSlices = []; }; for (j = 0, len1 = props.length; j < len1; j++) { prop = props[j]; if (prop instanceof Splat) { splatSlice.push(new Value(prop.name)); addSlice(); } else { propSlices.push(prop); } } addSlice(); if (!(slices[0] instanceof Obj)) { slices.unshift(new Obj); } _extends = new Value(new Literal(utility('_extends', o))); return (new Call(_extends, slices)).compileToFragments(o); } compileCSXAttributes(o) { var answer, i, j, join, len1, prop, props; props = this.properties; answer = []; for (i = j = 0, len1 = props.length; j < len1; i = ++j) { prop = props[i]; prop.csx = true; join = i === props.length - 1 ? '' : ' '; if (prop instanceof Splat) { prop = new Literal(`{${prop.compile(o)}}`); } answer.push(...prop.compileToFragments(o, LEVEL_TOP)); answer.push(this.makeCode(join)); } if (this.front) { return this.wrapInParentheses(answer); } else { return answer; } } }; Obj.prototype.children = ['properties']; return Obj; }).call(this); //### Arr // An array literal. exports.Arr = Arr = (function() { class Arr extends Base { constructor(objs, lhs1 = false) { super(); this.lhs = lhs1; this.objects = objs || []; } hasElision() { var j, len1, obj, ref1; ref1 = this.objects; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; if (obj instanceof Elision) { return true; } } return false; } isAssignable() { var i, j, len1, obj, ref1; if (!this.objects.length) { return false; } ref1 = this.objects; for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { obj = ref1[i]; if (obj instanceof Splat && i + 1 !== this.objects.length) { return false; } if (!(obj.isAssignable() && (!obj.isAtomic || obj.isAtomic()))) { return false; } } return true; } shouldCache() { return !this.isAssignable(); } compileNode(o) { var answer, compiledObjs, fragment, fragmentIndex, fragmentIsElision, fragments, includesLineCommentsOnNonFirstElement, index, j, k, l, len1, len2, len3, len4, len5, obj, objIndex, olen, passedElision, q, r, ref1, unwrappedObj; if (!this.objects.length) { return [this.makeCode('[]')]; } o.indent += TAB; fragmentIsElision = function(fragment) { return fragmentsToText(fragment).trim() === ','; }; // Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]). passedElision = false; answer = []; ref1 = this.objects; for (objIndex = j = 0, len1 = ref1.length; j < len1; objIndex = ++j) { obj = ref1[objIndex]; unwrappedObj = obj.unwrapAll(); // Let `compileCommentFragments` know to intersperse block comments // into the fragments created when compiling this array. if (unwrappedObj.comments && unwrappedObj.comments.filter(function(comment) { return !comment.here; }).length === 0) { unwrappedObj.includeCommentFragments = YES; } // If this array is the left-hand side of an assignment, all its children // are too. if (this.lhs) { if (unwrappedObj instanceof Arr || unwrappedObj instanceof Obj) { unwrappedObj.lhs = true; } } } compiledObjs = (function() { var k, len2, ref2, results; ref2 = this.objects; results = []; for (k = 0, len2 = ref2.length; k < len2; k++) { obj = ref2[k]; results.push(obj.compileToFragments(o, LEVEL_LIST)); } return results; }).call(this); olen = compiledObjs.length; // If `compiledObjs` includes newlines, we will output this as a multiline // array (i.e. with a newline and indentation after the `[`). If an element // contains line comments, that should also trigger multiline output since // by definition line comments will introduce newlines into our output. // The exception is if only the first element has line comments; in that // case, output as the compact form if we otherwise would have, so that the // first element’s line comments get output before or after the array. includesLineCommentsOnNonFirstElement = false; for (index = k = 0, len2 = compiledObjs.length; k < len2; index = ++k) { fragments = compiledObjs[index]; for (l = 0, len3 = fragments.length; l < len3; l++) { fragment = fragments[l]; if (fragment.isHereComment) { fragment.code = fragment.code.trim(); } else if (index !== 0 && includesLineCommentsOnNonFirstElement === false && hasLineComments(fragment)) { includesLineCommentsOnNonFirstElement = true; } } // Add ', ' if all `Elisions` from the beginning of the array are processed (e.g. [, , , a]) and // element isn't `Elision` or last element is `Elision` (e.g. [a,,b,,]) if (index !== 0 && passedElision && (!fragmentIsElision(fragments) || index === olen - 1)) { answer.push(this.makeCode(', ')); } passedElision = passedElision || !fragmentIsElision(fragments); answer.push(...fragments); } if (includesLineCommentsOnNonFirstElement || indexOf.call(fragmentsToText(answer), '\n') >= 0) { for (fragmentIndex = q = 0, len4 = answer.length; q < len4; fragmentIndex = ++q) { fragment = answer[fragmentIndex]; if (fragment.isHereComment) { fragment.code = `${multident(fragment.code, o.indent, false)}\n${o.indent}`; } else if (fragment.code === ', ' && !(fragment != null ? fragment.isElision : void 0)) { fragment.code = `,\n${o.indent}`; } } answer.unshift(this.makeCode(`[\n${o.indent}`)); answer.push(this.makeCode(`\n${this.tab}]`)); } else { for (r = 0, len5 = answer.length; r < len5; r++) { fragment = answer[r]; if (fragment.isHereComment) { fragment.code = `${fragment.code} `; } } answer.unshift(this.makeCode('[')); answer.push(this.makeCode(']')); } return answer; } assigns(name) { var j, len1, obj, ref1; ref1 = this.objects; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; if (obj.assigns(name)) { return true; } } return false; } eachName(iterator) { var j, len1, obj, ref1, results; ref1 = this.objects; results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; obj = obj.unwrapAll(); results.push(obj.eachName(iterator)); } return results; } }; Arr.prototype.children = ['objects']; return Arr; }).call(this); //### Class // The CoffeeScript class definition. // Initialize a **Class** with its name, an optional superclass, and a body. exports.Class = Class = (function() { class Class extends Base { constructor(variable1, parent1, body1 = new Block) { super(); this.variable = variable1; this.parent = parent1; this.body = body1; } compileNode(o) { var executableBody, node, parentName; this.name = this.determineName(); executableBody = this.walkBody(); if (this.parent instanceof Value && !this.parent.hasProperties()) { // Special handling to allow `class expr.A extends A` declarations parentName = this.parent.base.value; } this.hasNameClash = (this.name != null) && this.name === parentName; node = this; if (executableBody || this.hasNameClash) { node = new ExecutableClassBody(node, executableBody); } else if ((this.name == null) && o.level === LEVEL_TOP) { // Anonymous classes are only valid in expressions node = new Parens(node); } if (this.boundMethods.length && this.parent) { if (this.variable == null) { this.variable = new IdentifierLiteral(o.scope.freeVariable('_class')); } if (this.variableRef == null) { [this.variable, this.variableRef] = this.variable.cache(o); } } if (this.variable) { node = new Assign(this.variable, node, null, {moduleDeclaration: this.moduleDeclaration}); } this.compileNode = this.compileClassDeclaration; try { return node.compileToFragments(o); } finally { delete this.compileNode; } } compileClassDeclaration(o) { var ref1, ref2, result; if (this.externalCtor || this.boundMethods.length) { if (this.ctor == null) { this.ctor = this.makeDefaultConstructor(); } } if ((ref1 = this.ctor) != null) { ref1.noReturn = true; } if (this.boundMethods.length) { this.proxyBoundMethods(); } o.indent += TAB; result = []; result.push(this.makeCode("class ")); if (this.name) { result.push(this.makeCode(this.name)); } if (((ref2 = this.variable) != null ? ref2.comments : void 0) != null) { this.compileCommentFragments(o, this.variable, result); } if (this.name) { result.push(this.makeCode(' ')); } if (this.parent) { result.push(this.makeCode('extends '), ...this.parent.compileToFragments(o), this.makeCode(' ')); } result.push(this.makeCode('{')); if (!this.body.isEmpty()) { this.body.spaced = true; result.push(this.makeCode('\n')); result.push(...this.body.compileToFragments(o, LEVEL_TOP)); result.push(this.makeCode(`\n${this.tab}`)); } result.push(this.makeCode('}')); return result; } // Figure out the appropriate name for this class determineName() { var message, name, node, ref1, tail; if (!this.variable) { return null; } ref1 = this.variable.properties, tail = ref1[ref1.length - 1]; node = tail ? tail instanceof Access && tail.name : this.variable.base; if (!(node instanceof IdentifierLiteral || node instanceof PropertyName)) { return null; } name = node.value; if (!tail) { message = isUnassignable(name); if (message) { this.variable.error(message); } } if (indexOf.call(JS_FORBIDDEN, name) >= 0) { return `_${name}`; } else { return name; } } walkBody() { var assign, end, executableBody, expression, expressions, exprs, i, initializer, initializerExpression, j, k, len1, len2, method, properties, pushSlice, ref1, start; this.ctor = null; this.boundMethods = []; executableBody = null; initializer = []; ({expressions} = this.body); i = 0; ref1 = expressions.slice(); for (j = 0, len1 = ref1.length; j < len1; j++) { expression = ref1[j]; if (expression instanceof Value && expression.isObject(true)) { ({properties} = expression.base); exprs = []; end = 0; start = 0; pushSlice = function() { if (end > start) { return exprs.push(new Value(new Obj(properties.slice(start, end), true))); } }; while (assign = properties[end]) { if (initializerExpression = this.addInitializerExpression(assign)) { pushSlice(); exprs.push(initializerExpression); initializer.push(initializerExpression); start = end + 1; } end++; } pushSlice(); splice.apply(expressions, [i, i - i + 1].concat(exprs)), exprs; i += exprs.length; } else { if (initializerExpression = this.addInitializerExpression(expression)) { initializer.push(initializerExpression); expressions[i] = initializerExpression; } i += 1; } } for (k = 0, len2 = initializer.length; k < len2; k++) { method = initializer[k]; if (method instanceof Code) { if (method.ctor) { if (this.ctor) { method.error('Cannot define more than one constructor in a class'); } this.ctor = method; } else if (method.isStatic && method.bound) { method.context = this.name; } else if (method.bound) { this.boundMethods.push(method); } } } if (initializer.length !== expressions.length) { this.body.expressions = (function() { var l, len3, results; results = []; for (l = 0, len3 = initializer.length; l < len3; l++) { expression = initializer[l]; results.push(expression.hoist()); } return results; })(); return new Block(expressions); } } // Add an expression to the class initializer // This is the key method for determining whether an expression in a class // body should appear in the initializer or the executable body. If the given // `node` is valid in a class body the method will return a (new, modified, // or identical) node for inclusion in the class initializer, otherwise // nothing will be returned and the node will appear in the executable body. // At time of writing, only methods (instance and static) are valid in ES // class initializers. As new ES class features (such as class fields) reach // Stage 4, this method will need to be updated to support them. We // additionally allow `PassthroughLiteral`s (backticked expressions) in the // initializer as an escape hatch for ES features that are not implemented // (e.g. getters and setters defined via the `get` and `set` keywords as // opposed to the `Object.defineProperty` method). addInitializerExpression(node) { if (node.unwrapAll() instanceof PassthroughLiteral) { return node; } else if (this.validInitializerMethod(node)) { return this.addInitializerMethod(node); } else { return null; } } // Checks if the given node is a valid ES class initializer method. validInitializerMethod(node) { if (!(node instanceof Assign && node.value instanceof Code)) { return false; } if (node.context === 'object' && !node.variable.hasProperties()) { return true; } return node.variable.looksStatic(this.name) && (this.name || !node.value.bound); } // Returns a configured class initializer method addInitializerMethod(assign) { var method, methodName, variable; ({ variable, value: method } = assign); method.isMethod = true; method.isStatic = variable.looksStatic(this.name); if (method.isStatic) { method.name = variable.properties[0]; } else { methodName = variable.base; method.name = new (methodName.shouldCache() ? Index : Access)(methodName); method.name.updateLocationDataIfMissing(methodName.locationData); if (methodName.value === 'constructor') { method.ctor = (this.parent ? 'derived' : 'base'); } if (method.bound && method.ctor) { method.error('Cannot define a constructor as a bound (fat arrow) function'); } } return method; } makeDefaultConstructor() { var applyArgs, applyCtor, ctor; ctor = this.addInitializerMethod(new Assign(new Value(new PropertyName('constructor')), new Code)); this.body.unshift(ctor); if (this.parent) { ctor.body.push(new SuperCall(new Super, [new Splat(new IdentifierLiteral('arguments'))])); } if (this.externalCtor) { applyCtor = new Value(this.externalCtor, [new Access(new PropertyName('apply'))]); applyArgs = [new ThisLiteral, new IdentifierLiteral('arguments')]; ctor.body.push(new Call(applyCtor, applyArgs)); ctor.body.makeReturn(); } return ctor; } proxyBoundMethods() { var method, name; this.ctor.thisAssignments = (function() { var j, len1, ref1, results; ref1 = this.boundMethods; results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { method = ref1[j]; if (this.parent) { method.classVariable = this.variableRef; } name = new Value(new ThisLiteral, [method.name]); results.push(new Assign(name, new Call(new Value(name, [new Access(new PropertyName('bind'))]), [new ThisLiteral]))); } return results; }).call(this); return null; } }; Class.prototype.children = ['variable', 'parent', 'body']; return Class; }).call(this); exports.ExecutableClassBody = ExecutableClassBody = (function() { class ExecutableClassBody extends Base { constructor(_class, body1 = new Block) { super(); this.class = _class; this.body = body1; } compileNode(o) { var args, argumentsNode, directives, externalCtor, ident, jumpNode, klass, params, parent, ref1, wrapper; if (jumpNode = this.body.jumps()) { jumpNode.error('Class bodies cannot contain pure statements'); } if (argumentsNode = this.body.contains(isLiteralArguments)) { argumentsNode.error("Class bodies shouldn't reference arguments"); } this.name = (ref1 = this.class.name) != null ? ref1 : this.defaultClassVariableName; directives = this.walkBody(); this.setContext(); ident = new IdentifierLiteral(this.name); params = []; args = [new ThisLiteral]; wrapper = new Code(params, this.body); klass = new Parens(new Call(new Value(wrapper, [new Access(new PropertyName('call'))]), args)); this.body.spaced = true; o.classScope = wrapper.makeScope(o.scope); if (this.class.hasNameClash) { parent = new IdentifierLiteral(o.classScope.freeVariable('superClass')); wrapper.params.push(new Param(parent)); args.push(this.class.parent); this.class.parent = parent; } if (this.externalCtor) { externalCtor = new IdentifierLiteral(o.classScope.freeVariable('ctor', { reserve: false })); this.class.externalCtor = externalCtor; this.externalCtor.variable.base = externalCtor; } if (this.name !== this.class.name) { this.body.expressions.unshift(new Assign(new IdentifierLiteral(this.name), this.class)); } else { this.body.expressions.unshift(this.class); } this.body.expressions.unshift(...directives); this.body.push(ident); return klass.compileToFragments(o); } // Traverse the class's children and: // - Hoist valid ES properties into `@properties` // - Hoist static assignments into `@properties` // - Convert invalid ES properties into class or prototype assignments walkBody() { var directives, expr, index; directives = []; index = 0; while (expr = this.body.expressions[index]) { if (!(expr instanceof Value && expr.isString())) { break; } if (expr.hoisted) { index++; } else { directives.push(...this.body.expressions.splice(index, 1)); } } this.traverseChildren(false, (child) => { var cont, i, j, len1, node, ref1; if (child instanceof Class || child instanceof HoistTarget) { return false; } cont = true; if (child instanceof Block) { ref1 = child.expressions; for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { node = ref1[i]; if (node instanceof Value && node.isObject(true)) { cont = false; child.expressions[i] = this.addProperties(node.base.properties); } else if (node instanceof Assign && node.variable.looksStatic(this.name)) { node.value.isStatic = true; } } child.expressions = flatten(child.expressions); } return cont; }); return directives; } setContext() { return this.body.traverseChildren(false, (node) => { if (node instanceof ThisLiteral) { return node.value = this.name; } else if (node instanceof Code && node.bound && node.isStatic) { return node.context = this.name; } }); } // Make class/prototype assignments for invalid ES properties addProperties(assigns) { var assign, base, name, prototype, result, value, variable; result = (function() { var j, len1, results; results = []; for (j = 0, len1 = assigns.length; j < len1; j++) { assign = assigns[j]; variable = assign.variable; base = variable != null ? variable.base : void 0; value = assign.value; delete assign.context; if (base.value === 'constructor') { if (value instanceof Code) { base.error('constructors must be defined at the top level of a class body'); } // The class scope is not available yet, so return the assignment to update later assign = this.externalCtor = new Assign(new Value, value); } else if (!assign.variable.this) { name = new (base.shouldCache() ? Index : Access)(base); prototype = new Access(new PropertyName('prototype')); variable = new Value(new ThisLiteral(), [prototype, name]); assign.variable = variable; } else if (assign.value instanceof Code) { assign.value.isStatic = true; } results.push(assign); } return results; }).call(this); return compact(result); } }; ExecutableClassBody.prototype.children = ['class', 'body']; ExecutableClassBody.prototype.defaultClassVariableName = '_Class'; return ExecutableClassBody; }).call(this); //### Import and Export exports.ModuleDeclaration = ModuleDeclaration = (function() { class ModuleDeclaration extends Base { constructor(clause, source1) { super(); this.clause = clause; this.source = source1; this.checkSource(); } checkSource() { if ((this.source != null) && this.source instanceof StringWithInterpolations) { return this.source.error('the name of the module to be imported from must be an uninterpolated string'); } } checkScope(o, moduleDeclarationType) { if (o.indent.length !== 0) { return this.error(`${moduleDeclarationType} statements must be at top-level scope`); } } }; ModuleDeclaration.prototype.children = ['clause', 'source']; ModuleDeclaration.prototype.isStatement = YES; ModuleDeclaration.prototype.jumps = THIS; ModuleDeclaration.prototype.makeReturn = THIS; return ModuleDeclaration; }).call(this); exports.ImportDeclaration = ImportDeclaration = class ImportDeclaration extends ModuleDeclaration { compileNode(o) { var code, ref1; this.checkScope(o, 'import'); o.importedSymbols = []; code = []; code.push(this.makeCode(`${this.tab}import `)); if (this.clause != null) { code.push(...this.clause.compileNode(o)); } if (((ref1 = this.source) != null ? ref1.value : void 0) != null) { if (this.clause !== null) { code.push(this.makeCode(' from ')); } code.push(this.makeCode(this.source.value)); } code.push(this.makeCode(';')); return code; } }; exports.ImportClause = ImportClause = (function() { class ImportClause extends Base { constructor(defaultBinding, namedImports) { super(); this.defaultBinding = defaultBinding; this.namedImports = namedImports; } compileNode(o) { var code; code = []; if (this.defaultBinding != null) { code.push(...this.defaultBinding.compileNode(o)); if (this.namedImports != null) { code.push(this.makeCode(', ')); } } if (this.namedImports != null) { code.push(...this.namedImports.compileNode(o)); } return code; } }; ImportClause.prototype.children = ['defaultBinding', 'namedImports']; return ImportClause; }).call(this); exports.ExportDeclaration = ExportDeclaration = class ExportDeclaration extends ModuleDeclaration { compileNode(o) { var code, ref1; this.checkScope(o, 'export'); code = []; code.push(this.makeCode(`${this.tab}export `)); if (this instanceof ExportDefaultDeclaration) { code.push(this.makeCode('default ')); } if (!(this instanceof ExportDefaultDeclaration) && (this.clause instanceof Assign || this.clause instanceof Class)) { // Prevent exporting an anonymous class; all exported members must be named if (this.clause instanceof Class && !this.clause.variable) { this.clause.error('anonymous classes cannot be exported'); } code.push(this.makeCode('var ')); this.clause.moduleDeclaration = 'export'; } if ((this.clause.body != null) && this.clause.body instanceof Block) { code = code.concat(this.clause.compileToFragments(o, LEVEL_TOP)); } else { code = code.concat(this.clause.compileNode(o)); } if (((ref1 = this.source) != null ? ref1.value : void 0) != null) { code.push(this.makeCode(` from ${this.source.value}`)); } code.push(this.makeCode(';')); return code; } }; exports.ExportNamedDeclaration = ExportNamedDeclaration = class ExportNamedDeclaration extends ExportDeclaration {}; exports.ExportDefaultDeclaration = ExportDefaultDeclaration = class ExportDefaultDeclaration extends ExportDeclaration {}; exports.ExportAllDeclaration = ExportAllDeclaration = class ExportAllDeclaration extends ExportDeclaration {}; exports.ModuleSpecifierList = ModuleSpecifierList = (function() { class ModuleSpecifierList extends Base { constructor(specifiers) { super(); this.specifiers = specifiers; } compileNode(o) { var code, compiledList, fragments, index, j, len1, specifier; code = []; o.indent += TAB; compiledList = (function() { var j, len1, ref1, results; ref1 = this.specifiers; results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { specifier = ref1[j]; results.push(specifier.compileToFragments(o, LEVEL_LIST)); } return results; }).call(this); if (this.specifiers.length !== 0) { code.push(this.makeCode(`{\n${o.indent}`)); for (index = j = 0, len1 = compiledList.length; j < len1; index = ++j) { fragments = compiledList[index]; if (index) { code.push(this.makeCode(`,\n${o.indent}`)); } code.push(...fragments); } code.push(this.makeCode("\n}")); } else { code.push(this.makeCode('{}')); } return code; } }; ModuleSpecifierList.prototype.children = ['specifiers']; return ModuleSpecifierList; }).call(this); exports.ImportSpecifierList = ImportSpecifierList = class ImportSpecifierList extends ModuleSpecifierList {}; exports.ExportSpecifierList = ExportSpecifierList = class ExportSpecifierList extends ModuleSpecifierList {}; exports.ModuleSpecifier = ModuleSpecifier = (function() { class ModuleSpecifier extends Base { constructor(original, alias, moduleDeclarationType1) { var ref1, ref2; super(); this.original = original; this.alias = alias; this.moduleDeclarationType = moduleDeclarationType1; if (this.original.comments || ((ref1 = this.alias) != null ? ref1.comments : void 0)) { this.comments = []; if (this.original.comments) { this.comments.push(...this.original.comments); } if ((ref2 = this.alias) != null ? ref2.comments : void 0) { this.comments.push(...this.alias.comments); } } // The name of the variable entering the local scope this.identifier = this.alias != null ? this.alias.value : this.original.value; } compileNode(o) { var code; o.scope.find(this.identifier, this.moduleDeclarationType); code = []; code.push(this.makeCode(this.original.value)); if (this.alias != null) { code.push(this.makeCode(` as ${this.alias.value}`)); } return code; } }; ModuleSpecifier.prototype.children = ['original', 'alias']; return ModuleSpecifier; }).call(this); exports.ImportSpecifier = ImportSpecifier = class ImportSpecifier extends ModuleSpecifier { constructor(imported, local) { super(imported, local, 'import'); } compileNode(o) { var ref1; // Per the spec, symbols can’t be imported multiple times // (e.g. `import { foo, foo } from 'lib'` is invalid) if ((ref1 = this.identifier, indexOf.call(o.importedSymbols, ref1) >= 0) || o.scope.check(this.identifier)) { this.error(`'${this.identifier}' has already been declared`); } else { o.importedSymbols.push(this.identifier); } return super.compileNode(o); } }; exports.ImportDefaultSpecifier = ImportDefaultSpecifier = class ImportDefaultSpecifier extends ImportSpecifier {}; exports.ImportNamespaceSpecifier = ImportNamespaceSpecifier = class ImportNamespaceSpecifier extends ImportSpecifier {}; exports.ExportSpecifier = ExportSpecifier = class ExportSpecifier extends ModuleSpecifier { constructor(local, exported) { super(local, exported, 'export'); } }; //### Assign // The **Assign** is used to assign a local variable to value, or to set the // property of an object -- including within object literals. exports.Assign = Assign = (function() { class Assign extends Base { constructor(variable1, value1, context1, options = {}) { super(); this.variable = variable1; this.value = value1; this.context = context1; ({param: this.param, subpattern: this.subpattern, operatorToken: this.operatorToken, moduleDeclaration: this.moduleDeclaration} = options); } isStatement(o) { return (o != null ? o.level : void 0) === LEVEL_TOP && (this.context != null) && (this.moduleDeclaration || indexOf.call(this.context, "?") >= 0); } checkAssignability(o, varBase) { if (Object.prototype.hasOwnProperty.call(o.scope.positions, varBase.value) && o.scope.variables[o.scope.positions[varBase.value]].type === 'import') { return varBase.error(`'${varBase.value}' is read-only`); } } assigns(name) { return this[this.context === 'object' ? 'value' : 'variable'].assigns(name); } unfoldSoak(o) { return unfoldSoak(o, this, 'variable'); } // Compile an assignment, delegating to `compileDestructuring` or // `compileSplice` if appropriate. Keep track of the name of the base object // we've been assigned to, for correct internal references. If the variable // has not been seen yet within the current scope, declare it. compileNode(o) { var answer, compiledName, hasSplat, isValue, j, name, objDestructAnswer, properties, prototype, ref1, ref2, ref3, ref4, ref5, val, varBase; isValue = this.variable instanceof Value; if (isValue) { // When compiling `@variable`, remember if it is part of a function parameter. this.variable.param = this.param; // If `@variable` is an array or an object, we’re destructuring; // if it’s also `isAssignable()`, the destructuring syntax is supported // in ES and we can output it as is; otherwise we `@compileDestructuring` // and convert this ES-unsupported destructuring into acceptable output. if (this.variable.isArray() || this.variable.isObject()) { // This is the left-hand side of an assignment; let `Arr` and `Obj` // know that, so that those nodes know that they’re assignable as // destructured variables. this.variable.base.lhs = true; // Check if @variable contains Obj with splats. hasSplat = this.variable.contains(function(node) { return node instanceof Obj && node.hasSplat(); }); if (!this.variable.isAssignable() || this.variable.isArray() && hasSplat) { return this.compileDestructuring(o); } if (this.variable.isObject() && hasSplat) { // Object destructuring. Can be removed once ES proposal hits Stage 4. objDestructAnswer = this.compileObjectDestruct(o); } if (objDestructAnswer) { return objDestructAnswer; } } if (this.variable.isSplice()) { return this.compileSplice(o); } if ((ref1 = this.context) === '||=' || ref1 === '&&=' || ref1 === '?=') { return this.compileConditional(o); } if ((ref2 = this.context) === '**=' || ref2 === '//=' || ref2 === '%%=') { return this.compileSpecialMath(o); } } if (!this.context) { varBase = this.variable.unwrapAll(); if (!varBase.isAssignable()) { this.variable.error(`'${this.variable.compile(o)}' can't be assigned`); } varBase.eachName((name) => { var commentFragments, commentsNode, message; if (typeof name.hasProperties === "function" ? name.hasProperties() : void 0) { return; } message = isUnassignable(name.value); if (message) { name.error(message); } // `moduleDeclaration` can be `'import'` or `'export'`. this.checkAssignability(o, name); if (this.moduleDeclaration) { return o.scope.add(name.value, this.moduleDeclaration); } else if (this.param) { return o.scope.add(name.value, this.param === 'alwaysDeclare' ? 'var' : 'param'); } else { o.scope.find(name.value); // If this assignment identifier has one or more herecomments // attached, output them as part of the declarations line (unless // other herecomments are already staged there) for compatibility // with Flow typing. Don’t do this if this assignment is for a // class, e.g. `ClassName = class ClassName {`, as Flow requires // the comment to be between the class name and the `{`. if (name.comments && !o.scope.comments[name.value] && !(this.value instanceof Class) && name.comments.every(function(comment) { return comment.here && !comment.multiline; })) { commentsNode = new IdentifierLiteral(name.value); commentsNode.comments = name.comments; commentFragments = []; this.compileCommentFragments(o, commentsNode, commentFragments); return o.scope.comments[name.value] = commentFragments; } } }); } if (this.value instanceof Code) { if (this.value.isStatic) { this.value.name = this.variable.properties[0]; } else if (((ref3 = this.variable.properties) != null ? ref3.length : void 0) >= 2) { ref4 = this.variable.properties, properties = 3 <= ref4.length ? slice.call(ref4, 0, j = ref4.length - 2) : (j = 0, []), prototype = ref4[j++], name = ref4[j++]; if (((ref5 = prototype.name) != null ? ref5.value : void 0) === 'prototype') { this.value.name = name; } } } if (this.csx) { this.value.base.csxAttribute = true; } val = this.value.compileToFragments(o, LEVEL_LIST); compiledName = this.variable.compileToFragments(o, LEVEL_LIST); if (this.context === 'object') { if (this.variable.shouldCache()) { compiledName.unshift(this.makeCode('[')); compiledName.push(this.makeCode(']')); } return compiledName.concat(this.makeCode(this.csx ? '=' : ': '), val); } answer = compiledName.concat(this.makeCode(` ${this.context || '='} `), val); // Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration, // if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses. // The assignment is wrapped in parentheses if 'o.level' has lower precedence than LEVEL_LIST (3) // (i.e. LEVEL_COND (4), LEVEL_OP (5) or LEVEL_ACCESS (6)), or if we're destructuring object, e.g. {a,b} = obj. if (o.level > LEVEL_LIST || isValue && this.variable.base instanceof Obj && !this.nestedLhs && !(this.param === true)) { return this.wrapInParentheses(answer); } else { return answer; } } // Check object destructuring variable for rest elements; // can be removed once ES proposal hits Stage 4. compileObjectDestruct(o) { var fragments, getPropKey, getPropName, j, len1, restElement, restElements, result, traverseRest, value, valueRef, valueRefTemp; // Returns a safe (cached) reference to the key for a given property getPropKey = function(prop) { var key; if (prop instanceof Assign) { [prop.variable, key] = prop.variable.cache(o); return key; } else { return prop; } }; // Returns the name of a given property for use with excludeProps // Property names are quoted (e.g. `a: b` -> 'a'), and everything else uses the key reference // (e.g. `'a': b -> 'a'`, `"#{a}": b` ->