From 6b0418a74ac651ca208fa01583336ad7d0c41b25 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sun, 25 Jul 2010 00:15:12 -0700 Subject: [PATCH] merging master changes into symbology --- lib/cake.js | 7 +- lib/coffee-script.js | 5 +- lib/command.js | 5 +- lib/grammar.js | 2 +- lib/helpers.js | 2 +- lib/index.js | 2 +- lib/lexer.js | 14 +- lib/nodes.js | 74 +++--- lib/optparse.js | 2 +- lib/repl.js | 2 +- lib/rewriter.js | 488 ++++++++++++++++++------------------- lib/scope.js | 10 +- src/nodes.coffee | 67 ++--- test/test_functions.coffee | 14 +- 14 files changed, 339 insertions(+), 355 deletions(-) diff --git a/lib/cake.js b/lib/cake.js index 7847ed19..2dbc93e0 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var CoffeeScript, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks; fs = require('fs'); path = require('path'); @@ -17,12 +17,11 @@ action = _a[0]; description = _a[1]; } - tasks[name] = { + return (tasks[name] = { name: name, description: description, action: action - }; - return tasks[name]; + }); }, option: function(letter, flag, description) { return switches.push([letter, flag, description]); diff --git a/lib/coffee-script.js b/lib/coffee-script.js index 2cd332f4..a476bb0f 100644 --- a/lib/coffee-script.js +++ b/lib/coffee-script.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var Lexer, compile, helpers, lexer, parser, path, processScripts; if (typeof process !== "undefined" && process !== null) { path = require('path'); @@ -51,8 +51,7 @@ }, setInput: function(tokens) { this.tokens = tokens; - this.pos = 0; - return this.pos; + return (this.pos = 0); }, upcomingInput: function() { return ""; diff --git a/lib/command.js b/lib/command.js index 729cffda..f99355fd 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var BANNER, CoffeeScript, SWITCHES, _a, compileOptions, compileScript, compileScripts, compileStdio, exec, fs, lint, optionParser, options, optparse, parseOptions, path, printTokens, sources, spawn, usage, version, watch, writeJs; fs = require('fs'); path = require('path'); @@ -195,8 +195,7 @@ options.compile = options.compile || !!o.output; options.run = !(o.compile || o.print || o.lint); options.print = !!(o.print || (o.eval || o.stdio && o.compile)); - sources = options.arguments; - return sources; + return (sources = options.arguments); }; compileOptions = function(source) { var o; diff --git a/lib/grammar.js b/lib/grammar.js index e77e8cc8..d0173764 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var Parser, _a, _b, _c, _d, _e, _f, _g, _h, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; var __hasProp = Object.prototype.hasOwnProperty; Parser = require('jison').Parser; diff --git a/lib/helpers.js b/lib/helpers.js index b041ef8b..76cff280 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var compact, count, del, ends, extend, flatten, helpers, include, indexOf, merge, starts; if (!(typeof process !== "undefined" && process !== null)) { this.exports = this; diff --git a/lib/index.js b/lib/index.js index 8e014e91..6bf0e654 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var _a, key, val; var __hasProp = Object.prototype.hasOwnProperty; _a = require('./coffee-script'); diff --git a/lib/lexer.js b/lib/lexer.js index 1bb322fe..06c9d984 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, Lexer, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, _a, _b, _c, compact, count, helpers, include, starts; var __slice = Array.prototype.slice; if (typeof process !== "undefined" && process !== null) { @@ -219,8 +219,7 @@ }; Lexer.prototype.balancedToken = function() { var delimited; - var _d = arguments.length, _e = _d >= 1; - delimited = __slice.call(arguments, 0, _d - 0); + delimited = __slice.call(arguments, 0); return this.balancedString(this.chunk, delimited); }; Lexer.prototype.lineToken = function() { @@ -387,8 +386,7 @@ } else if (_d === ')') { tok[0] = 'PARAM_END'; } else if (_d === '(' || _d === 'CALL_START') { - tok[0] = 'PARAM_START'; - return tok[0]; + return (tok[0] = 'PARAM_START'); } } return true; @@ -543,8 +541,7 @@ return null; } if (typeof newTag !== "undefined" && newTag !== null) { - tok[0] = newTag; - return tok[0]; + return (tok[0] = newTag); } return tok[0]; }; @@ -554,8 +551,7 @@ return null; } if (typeof val !== "undefined" && val !== null) { - tok[1] = val; - return tok[1]; + return (tok[1] = val); } return tok[1]; }; diff --git a/lib/nodes.js b/lib/nodes.js index b4ed6c99..d7f1500c 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1,7 +1,7 @@ -(function(){ +(function() { var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, DOUBLE_PARENS, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NUMBER, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SIMPLENUM, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, ends, flatten, helpers, include, indexOf, literal, merge, starts, utility; var __extends = function(child, parent) { - var ctor = function(){ }; + var ctor = function(){}; ctor.prototype = parent.prototype; child.prototype = new ctor(); child.prototype.constructor = child; @@ -203,11 +203,6 @@ this.expressions[idx] = last.makeReturn(); return this; }; - Expressions.prototype.rewriteThis = function() { - return this.traverseChildren(false, function(child) { - return child instanceof ValueNode && child.base.value === 'this' ? (child.base = literal('_this')) : null; - }); - }; Expressions.prototype.compile = function(o) { o = o || {}; return o.scope ? Expressions.__superClass__.compile.call(this, o) : this.compileRoot(o); @@ -230,7 +225,7 @@ code = this.compileWithDeclarations(o); code = code.replace(TRAILING_WHITESPACE, ''); code = code.replace(DOUBLE_PARENS, '($1)'); - return o.noWrap ? code : ("(function(){\n" + code + "\n})();\n"); + return o.noWrap ? code : ("(function() {\n" + code + "\n})();\n"); }; Expressions.prototype.compileWithDeclarations = function(o) { var code; @@ -399,8 +394,7 @@ part = prop.compile(o); baseline += part; complete += part; - this.last = part; - return this.last; + return (this.last = part); } }).call(this); } @@ -452,7 +446,7 @@ CallNode.prototype.superReference = function(o) { var meth, methname; methname = o.scope.method.name; - meth = (function() { + return (meth = (function() { if (o.scope.method.proto) { return "" + (o.scope.method.proto) + ".__superClass__." + methname; } else if (methname) { @@ -460,8 +454,7 @@ } else { throw new Error("cannot call super on an anonymous function."); } - })(); - return meth; + })()); }; CallNode.prototype.compileNode = function(o) { var _b, _c, _d, _e, _f, _g, _h, arg, args, compilation; @@ -500,7 +493,7 @@ } if (this.isNew) { utility('extends'); - return "(function() {\n" + (this.idt(1)) + "var ctor = function(){ };\n" + (this.idt(1)) + "__extends(ctor, " + meth + ");\n" + (this.idt(1)) + "return " + (meth) + ".apply(new ctor, " + (this.compileSplatArguments(o)) + ");\n" + this.tab + "}).call(this)"; + return "(function() {\n" + (this.idt(1)) + "var ctor = function(){};\n" + (this.idt(1)) + "__extends(ctor, " + meth + ");\n" + (this.idt(1)) + "return " + (meth) + ".apply(new ctor, " + (this.compileSplatArguments(o)) + ");\n" + this.tab + "}).call(this)"; } else { return "" + (this.prefix()) + (meth) + ".apply(" + obj + ", " + (this.compileSplatArguments(o)) + ")"; } @@ -641,7 +634,7 @@ body = ("var " + i + " = " + this.fromVar + "; " + clause + " " + i + " <" + this.equals + " " + this.toVar + " : " + i + " >" + this.equals + " " + this.toVar + "; " + clause + " " + i + " += 1 : " + i + " -= 1"); } post = ("{ " + (result) + ".push(" + i + ") };\n" + (idt) + "return " + result + ";\n" + o.indent); - return "(function(){" + (pre) + "\n" + (idt) + "for (" + body + ")" + post + "}).call(this)"; + return "(function() {" + (pre) + "\n" + (idt) + "for (" + body + ")" + post + "}).call(this)"; }; return RangeNode; })(); @@ -841,7 +834,11 @@ return this.variable instanceof ValueNode; }; AssignNode.prototype.makeReturn = function() { - return new Expressions([this, new ReturnNode(this.variable)]); + if (this.isStatement()) { + return new Expressions([this, new ReturnNode(this.variable)]); + } else { + return AssignNode.__superClass__.makeReturn.call(this); + } }; AssignNode.prototype.isStatement = function() { return this.isValue() && (this.variable.isArray() || this.variable.isObject()); @@ -952,7 +949,7 @@ top = del(o, 'top'); o.scope = sharedScope || new Scope(o.scope, this.body, this); o.top = true; - o.indent = this.idt(this.bound ? 2 : 1); + o.indent = this.idt(1); del(o, 'noWrap'); del(o, 'globals'); i = 0; @@ -983,23 +980,17 @@ return _e; })(); this.body.makeReturn(); - if (this.bound) { - this.body.rewriteThis(); - } _j = params; for (_i = 0, _k = _j.length; _i < _k; _i++) { param = _j[_i]; (o.scope.parameter(param)); } code = this.body.expressions.length ? ("\n" + (this.body.compileWithDeclarations(o)) + "\n") : ''; - func = ("function(" + (params.join(', ')) + ") {" + code + (code && this.idt(this.bound ? 1 : 0)) + "}"); - if (top && !this.bound) { - func = ("(" + func + ")"); + func = ("function(" + (params.join(', ')) + ") {" + code + (code && this.tab) + "}"); + if (this.bound) { + return ("" + (utility('bind')) + "(" + func + ", this)"); } - if (!(this.bound)) { - return func; - } - return "(function(_this) {\n" + (this.idt(1)) + "return " + func + "\n" + this.tab + "})(this)"; + return top ? ("(" + func + ")") : func; }; CodeNode.prototype.topSensitive = function() { return true; @@ -1040,20 +1031,24 @@ return (typeof (_b = this.index) !== "undefined" && _b !== null) ? this.compileParam(o) : this.name.compile(o); }; SplatNode.prototype.compileParam = function(o) { - var _b, _c, idx, len, name, pos, trailing, variadic; + var _b, _c, end, idx, len, name, pos, trailing, variadic; name = this.name.compile(o); o.scope.find(name); - len = o.scope.freeVariable(); - o.scope.assign(len, "arguments.length"); - variadic = o.scope.freeVariable(); - o.scope.assign(variadic, ("" + len + " >= " + this.arglength)); - _b = this.trailings; - for (idx = 0, _c = _b.length; idx < _c; idx++) { - trailing = _b[idx]; - pos = this.trailings.length - idx; - o.scope.assign(trailing.compile(o), ("arguments[" + variadic + " ? " + len + " - " + pos + " : " + (this.index + idx) + "]")); + end = ''; + if (this.trailings.length) { + len = o.scope.freeVariable(); + o.scope.assign(len, "arguments.length"); + variadic = o.scope.freeVariable(); + o.scope.assign(variadic, ("" + len + " >= " + this.arglength)); + end = this.trailings.length ? (", " + len + " - " + (this.trailings.length)) : null; + _b = this.trailings; + for (idx = 0, _c = _b.length; idx < _c; idx++) { + trailing = _b[idx]; + pos = this.trailings.length - idx; + o.scope.assign(trailing.compile(o), ("arguments[" + variadic + " ? " + len + " - " + pos + " : " + (this.index + idx) + "]")); + } } - return "" + name + " = " + (utility('slice')) + ".call(arguments, " + this.index + ", " + len + " - " + (this.trailings.length) + ")"; + return "" + name + " = " + (utility('slice')) + ".call(arguments, " + this.index + end + ")"; }; SplatNode.prototype.compileValue = function(o, name, index, trailings) { var trail; @@ -1681,7 +1676,8 @@ } }); UTILITIES = { - 'extends': "function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n if (typeof parent.extended === \"function\") parent.extended(child);\n child.__superClass__ = parent.prototype;\n }", + 'extends': "function(child, parent) {\n var ctor = function(){};\n ctor.prototype = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n if (typeof parent.extended === \"function\") parent.extended(child);\n child.__superClass__ = parent.prototype;\n }", + bind: "function(func, context) {\n return function(){ return func.apply(context, arguments); };\n }", hasProp: 'Object.prototype.hasOwnProperty', slice: 'Array.prototype.slice' }; diff --git a/lib/optparse.js b/lib/optparse.js index 3db86122..839439f0 100755 --- a/lib/optparse.js +++ b/lib/optparse.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments; exports.OptionParser = (function() { OptionParser = function(rules, banner) { diff --git a/lib/repl.js b/lib/repl.js index b33e3951..ca5584fe 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var CoffeeScript, helpers, readline, repl, run, stdio; CoffeeScript = require('./coffee-script'); helpers = require('./helpers').helpers; diff --git a/lib/rewriter.js b/lib/rewriter.js index c8adc260..8562299b 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -1,6 +1,8 @@ -(function(){ +(function() { var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, helpers, include, pair; - var __hasProp = Object.prototype.hasOwnProperty; + var __bind = function(func, context) { + return function(){ return func.apply(context, arguments); }; + }, __hasProp = Object.prototype.hasOwnProperty; if (typeof process !== "undefined" && process !== null) { _a = require('./helpers'); helpers = _a.helpers; @@ -38,30 +40,28 @@ return true; }; Rewriter.prototype.adjustComments = function() { - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var _c, _d, after, before; - if (!(token[0] === 'HERECOMMENT')) { - return 1; - } - _c = [_this.tokens[i - 2], _this.tokens[i + 2]]; - before = _c[0]; - after = _c[1]; - if (after && after[0] === 'INDENT') { - _this.tokens.splice(i + 2, 1); - before && before[0] === 'OUTDENT' && post && (prev[0] === post[0]) && (post[0] === 'TERMINATOR') ? _this.tokens.splice(i - 2, 1) : _this.tokens.splice(i, 0, after); - } else if (prev && !('TERMINATOR' === (_d = prev[0]) || 'INDENT' === _d || 'OUTDENT' === _d)) { - if (post && post[0] === 'TERMINATOR' && after && after[0] === 'OUTDENT') { - _this.tokens.splice.apply(_this.tokens, [i + 2, 0].concat(_this.tokens.splice(i, 2))); - _this.tokens[i + 2][0] !== 'TERMINATOR' ? _this.tokens.splice(i + 2, 0, ['TERMINATOR', "\n", prev[2]]) : null; - } else { - _this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]]); - } - return 2; - } + return this.scanTokens(__bind(function(prev, token, post, i) { + var _c, _d, after, before; + if (!(token[0] === 'HERECOMMENT')) { return 1; } - })(this)); + _c = [this.tokens[i - 2], this.tokens[i + 2]]; + before = _c[0]; + after = _c[1]; + if (after && after[0] === 'INDENT') { + this.tokens.splice(i + 2, 1); + before && before[0] === 'OUTDENT' && post && (prev[0] === post[0]) && (post[0] === 'TERMINATOR') ? this.tokens.splice(i - 2, 1) : this.tokens.splice(i, 0, after); + } else if (prev && !('TERMINATOR' === (_d = prev[0]) || 'INDENT' === _d || 'OUTDENT' === _d)) { + if (post && post[0] === 'TERMINATOR' && after && after[0] === 'OUTDENT') { + this.tokens.splice.apply(this.tokens, [i + 2, 0].concat(this.tokens.splice(i, 2))); + this.tokens[i + 2][0] !== 'TERMINATOR' ? this.tokens.splice(i + 2, 0, ['TERMINATOR', "\n", prev[2]]) : null; + } else { + this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]]); + } + return 2; + } + return 1; + }, this)); }; Rewriter.prototype.removeLeadingNewlines = function() { var _c; @@ -72,242 +72,226 @@ return _c; }; Rewriter.prototype.removeMidExpressionNewlines = function() { - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) { - return 1; - } - _this.tokens.splice(i, 1); - return 0; + return this.scanTokens(__bind(function(prev, token, post, i) { + if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) { + return 1; } - })(this)); + this.tokens.splice(i, 1); + return 0; + }, this)); }; Rewriter.prototype.closeOpenCallsAndIndexes = function() { var brackets, parens; parens = [0]; brackets = [0]; - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var _c; - if ((_c = token[0]) === 'CALL_START') { - parens.push(0); - } else if (_c === 'INDEX_START') { - brackets.push(0); - } else if (_c === '(') { - parens[parens.length - 1] += 1; - } else if (_c === '[') { - brackets[brackets.length - 1] += 1; - } else if (_c === ')') { - if (parens[parens.length - 1] === 0) { - parens.pop(); - token[0] = 'CALL_END'; - } else { - parens[parens.length - 1] -= 1; - } - } else if (_c === ']') { - if (brackets[brackets.length - 1] === 0) { - brackets.pop(); - token[0] = 'INDEX_END'; - } else { - brackets[brackets.length - 1] -= 1; - } + return this.scanTokens(__bind(function(prev, token, post, i) { + var _c; + if ((_c = token[0]) === 'CALL_START') { + parens.push(0); + } else if (_c === 'INDEX_START') { + brackets.push(0); + } else if (_c === '(') { + parens[parens.length - 1] += 1; + } else if (_c === '[') { + brackets[brackets.length - 1] += 1; + } else if (_c === ')') { + if (parens[parens.length - 1] === 0) { + parens.pop(); + token[0] = 'CALL_END'; + } else { + parens[parens.length - 1] -= 1; + } + } else if (_c === ']') { + if (brackets[brackets.length - 1] === 0) { + brackets.pop(); + token[0] = 'INDEX_END'; + } else { + brackets[brackets.length - 1] -= 1; } - return 1; } - })(this)); + return 1; + }, this)); }; Rewriter.prototype.addImplicitBraces = function() { var closeBrackets, stack; stack = [0]; - closeBrackets = (function(_this) { - return function(i) { - var _c, len, size, tmp; - len = stack.length - 1; - _c = stack[len]; - for (tmp = 0; (0 <= _c ? tmp < _c : tmp > _c); (0 <= _c ? tmp += 1 : tmp -= 1)) { - _this.tokens.splice(i, 0, ['}', '}', _this.tokens[i][2]]); + closeBrackets = __bind(function(i) { + var _c, len, size, tmp; + len = stack.length - 1; + _c = stack[len]; + for (tmp = 0; (0 <= _c ? tmp < _c : tmp > _c); (0 <= _c ? tmp += 1 : tmp -= 1)) { + this.tokens.splice(i, 0, ['}', '}', this.tokens[i][2]]); + } + size = stack[len] + 1; + stack[len] = 0; + return size; + }, this); + return this.scanTokens(__bind(function(prev, token, post, i) { + var after, before, idx, len, open, size, tag; + tag = token[0]; + len = stack.length - 1; + before = this.tokens[i - 2]; + after = this.tokens[i + 2]; + open = stack[len] > 0; + if (include(EXPRESSION_START, tag)) { + stack.push(tag === '{' ? 1 : 0); + if (tag === '{' && post && post[0] === 'INDENT') { + return 2; } - size = stack[len] + 1; - stack[len] = 0; + } else if (include(EXPRESSION_END, tag)) { + if (tag === 'OUTDENT' && post && post[0] === '}') { + return 1; + } + stack[len - 1] += stack.pop(); + if (tag === '}') { + stack[len - 1] -= 1; + } + } else if (tag === ':' && !open) { + idx = before && before[0] === '@' ? i - 2 : i - 1; + this.tokens.splice(idx, 0, ['{', '{', token[2]]); + stack[stack.length - 1] += 1; + return 2; + } else if (tag === 'TERMINATOR' && !((after && after[0] === ':') || (post && post[0] === '@' && this.tokens[i + 3] && this.tokens[i + 3][0] === ':'))) { + size = closeBrackets(i); return size; } - })(this); - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var after, before, idx, len, open, size, tag; - tag = token[0]; - len = stack.length - 1; - before = _this.tokens[i - 2]; - after = _this.tokens[i + 2]; - open = stack[len] > 0; - if (include(EXPRESSION_START, tag)) { - stack.push(tag === '{' ? 1 : 0); - if (tag === '{' && post && post[0] === 'INDENT') { - return 2; - } - } else if (include(EXPRESSION_END, tag)) { - if (tag === 'OUTDENT' && post && post[0] === '}') { - return 1; - } - stack[len - 1] += stack.pop(); - if (tag === '}') { - stack[len - 1] -= 1; - } - } else if (tag === ':' && !open) { - idx = before && before[0] === '@' ? i - 2 : i - 1; - _this.tokens.splice(idx, 0, ['{', '{', token[2]]); - stack[stack.length - 1] += 1; - return 2; - } else if (tag === 'TERMINATOR' && !((after && after[0] === ':') || (post && post[0] === '@' && _this.tokens[i + 3] && _this.tokens[i + 3][0] === ':'))) { - size = closeBrackets(i); - return size; - } - return 1; - } - })(this)); + return 1; + }, this)); }; Rewriter.prototype.addImplicitParentheses = function() { var closeCalls, stack; stack = [0]; - closeCalls = (function(_this) { - return function(i) { - var _c, size, tmp; - _c = stack[stack.length - 1]; - for (tmp = 0; (0 <= _c ? tmp < _c : tmp > _c); (0 <= _c ? tmp += 1 : tmp -= 1)) { - _this.tokens.splice(i, 0, ['CALL_END', ')', _this.tokens[i][2]]); - } - size = stack[stack.length - 1] + 1; - stack[stack.length - 1] = 0; - return size; + closeCalls = __bind(function(i) { + var _c, size, tmp; + _c = stack[stack.length - 1]; + for (tmp = 0; (0 <= _c ? tmp < _c : tmp > _c); (0 <= _c ? tmp += 1 : tmp -= 1)) { + this.tokens.splice(i, 0, ['CALL_END', ')', this.tokens[i][2]]); } - })(this); - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var _c, _d, j, nx, open, size, tag; - tag = token[0]; - if (tag === 'OUTDENT') { - stack[stack.length - 2] += stack.pop(); - } - open = stack[stack.length - 1] > 0; - if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag) && !(tag === '!' && (('IN' === (_c = post[0]) || 'OF' === _c)))) { - _this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]); - stack[stack.length - 1] += 1; - if (include(EXPRESSION_START, tag)) { - stack.push(0); - } - return 2; - } + size = stack[stack.length - 1] + 1; + stack[stack.length - 1] = 0; + return size; + }, this); + return this.scanTokens(__bind(function(prev, token, post, i) { + var _c, _d, j, nx, open, size, tag; + tag = token[0]; + if (tag === 'OUTDENT') { + stack[stack.length - 2] += stack.pop(); + } + open = stack[stack.length - 1] > 0; + if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag) && !(tag === '!' && (('IN' === (_c = post[0]) || 'OF' === _c)))) { + this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]); + stack[stack.length - 1] += 1; if (include(EXPRESSION_START, tag)) { - if (tag === 'INDENT' && !token.generated && open && !(prev && include(IMPLICIT_BLOCK, prev[0]))) { - size = closeCalls(i); - stack.push(0); - return size; - } stack.push(0); - return 1; } - if (open && !token.generated && prev[0] !== ',' && (!post || include(IMPLICIT_END, tag))) { - j = 1; - while ((typeof (_d = (nx = _this.tokens[i + j])) !== "undefined" && _d !== null) && include(IMPLICIT_END, nx[0])) { - j++; - } - if ((typeof nx !== "undefined" && nx !== null) && nx[0] === ',' && _this.tokens[i + j - 1][0] === 'OUTDENT') { - if (tag === 'TERMINATOR') { - _this.tokens.splice(i, 1); - } - } else { - size = closeCalls(i); - if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) { - stack.pop(); - } - return size; - } - } - if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) { - stack[stack.length - 2] += stack.pop(); - return 1; + return 2; + } + if (include(EXPRESSION_START, tag)) { + if (tag === 'INDENT' && !token.generated && open && !(prev && include(IMPLICIT_BLOCK, prev[0]))) { + size = closeCalls(i); + stack.push(0); + return size; } + stack.push(0); return 1; } - })(this)); + if (open && !token.generated && prev[0] !== ',' && (!post || include(IMPLICIT_END, tag))) { + j = 1; + while ((typeof (_d = (nx = this.tokens[i + j])) !== "undefined" && _d !== null) && include(IMPLICIT_END, nx[0])) { + j++; + } + if ((typeof nx !== "undefined" && nx !== null) && nx[0] === ',' && this.tokens[i + j - 1][0] === 'OUTDENT') { + if (tag === 'TERMINATOR') { + this.tokens.splice(i, 1); + } + } else { + size = closeCalls(i); + if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) { + stack.pop(); + } + return size; + } + } + if (tag !== 'OUTDENT' && include(EXPRESSION_END, tag)) { + stack[stack.length - 2] += stack.pop(); + return 1; + } + return 1; + }, this)); }; Rewriter.prototype.addImplicitIndentation = function() { - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var _c, idx, indent, insertion, outdent, parens, pre, starter, tok; - if (token[0] === 'ELSE' && prev[0] !== 'OUTDENT') { - _this.tokens.splice.apply(_this.tokens, [i, 0].concat(_this.indentation(token))); - return 2; - } - if (token[0] === 'CATCH' && (_this.tokens[i + 2][0] === 'TERMINATOR' || _this.tokens[i + 2][0] === 'FINALLY')) { - _this.tokens.splice.apply(_this.tokens, [i + 2, 0].concat(_this.indentation(token))); - return 4; - } - if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) { - return 1; - } - starter = token[0]; - _c = _this.indentation(token); - indent = _c[0]; - outdent = _c[1]; - indent.generated = (outdent.generated = true); - _this.tokens.splice(i + 1, 0, indent); - idx = i + 1; - parens = 0; - while (true) { - idx += 1; - tok = _this.tokens[idx]; - pre = _this.tokens[idx - 1]; - if ((!tok || (include(SINGLE_CLOSERS, tok[0]) && tok[1] !== ';' && parens === 0) || (tok[0] === ')' && parens === 0)) && !(tok[0] === 'ELSE' && !('IF' === starter || 'THEN' === starter))) { - insertion = pre[0] === "," ? idx - 1 : idx; - _this.tokens.splice(insertion, 0, outdent); - break; - } - if (tok[0] === '(') { - parens += 1; - } - if (tok[0] === ')') { - parens -= 1; - } - } - if (!(token[0] === 'THEN')) { - return 1; - } - _this.tokens.splice(i, 1); - return 0; + return this.scanTokens(__bind(function(prev, token, post, i) { + var _c, idx, indent, insertion, outdent, parens, pre, starter, tok; + if (token[0] === 'ELSE' && prev[0] !== 'OUTDENT') { + this.tokens.splice.apply(this.tokens, [i, 0].concat(this.indentation(token))); + return 2; } - })(this)); + if (token[0] === 'CATCH' && (this.tokens[i + 2][0] === 'TERMINATOR' || this.tokens[i + 2][0] === 'FINALLY')) { + this.tokens.splice.apply(this.tokens, [i + 2, 0].concat(this.indentation(token))); + return 4; + } + if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) { + return 1; + } + starter = token[0]; + _c = this.indentation(token); + indent = _c[0]; + outdent = _c[1]; + indent.generated = (outdent.generated = true); + this.tokens.splice(i + 1, 0, indent); + idx = i + 1; + parens = 0; + while (true) { + idx += 1; + tok = this.tokens[idx]; + pre = this.tokens[idx - 1]; + if ((!tok || (include(SINGLE_CLOSERS, tok[0]) && tok[1] !== ';' && parens === 0) || (tok[0] === ')' && parens === 0)) && !(tok[0] === 'ELSE' && !('IF' === starter || 'THEN' === starter))) { + insertion = pre[0] === "," ? idx - 1 : idx; + this.tokens.splice(insertion, 0, outdent); + break; + } + if (tok[0] === '(') { + parens += 1; + } + if (tok[0] === ')') { + parens -= 1; + } + } + if (!(token[0] === 'THEN')) { + return 1; + } + this.tokens.splice(i, 1); + return 0; + }, this)); }; Rewriter.prototype.ensureBalance = function(pairs) { var _c, _d, key, levels, line, open, openLine, unclosed, value; levels = {}; openLine = {}; - this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var _c, _d, _e, _f, close, open, pair; - _d = pairs; - for (_c = 0, _e = _d.length; _c < _e; _c++) { - pair = _d[_c]; - _f = pair; - open = _f[0]; - close = _f[1]; - levels[open] = levels[open] || 0; - if (token[0] === open) { - if (levels[open] === 0) { - openLine[open] = token[2]; - } - levels[open] += 1; - } - if (token[0] === close) { - levels[open] -= 1; - } - if (levels[open] < 0) { - throw new Error(("too many " + (token[1]) + " on line " + (token[2] + 1))); + this.scanTokens(__bind(function(prev, token, post, i) { + var _c, _d, _e, _f, close, open, pair; + _d = pairs; + for (_c = 0, _e = _d.length; _c < _e; _c++) { + pair = _d[_c]; + _f = pair; + open = _f[0]; + close = _f[1]; + levels[open] = levels[open] || 0; + if (token[0] === open) { + if (levels[open] === 0) { + openLine[open] = token[2]; } + levels[open] += 1; + } + if (token[0] === close) { + levels[open] -= 1; + } + if (levels[open] < 0) { + throw new Error(("too many " + (token[1]) + " on line " + (token[2] + 1))); } - return 1; } - })(this)); + return 1; + }, this)); unclosed = (function() { _c = []; _d = levels; for (key in _d) { @@ -333,41 +317,39 @@ val = _c[key]; (debt[key] = 0); } - return this.scanTokens((function(_this) { - return function(prev, token, post, i) { - var inv, match, mtag, oppos, tag; - tag = token[0]; - inv = INVERSES[token[0]]; - if (include(EXPRESSION_START, tag)) { - stack.push(token); - return 1; - } else if (include(EXPRESSION_END, tag)) { - if (debt[inv] > 0) { - debt[inv] -= 1; - _this.tokens.splice(i, 1); - return 0; - } else { - match = stack.pop(); - mtag = match[0]; - oppos = INVERSES[mtag]; - if (tag === oppos) { - return 1; - } - debt[mtag] += 1; - val = [oppos, mtag === 'INDENT' ? match[1] : oppos]; - if ((_this.tokens[i + 2] == undefined ? undefined : _this.tokens[i + 2][0]) === mtag) { - _this.tokens.splice(i + 3, 0, val); - stack.push(match); - } else { - _this.tokens.splice(i, 0, val); - } + return this.scanTokens(__bind(function(prev, token, post, i) { + var inv, match, mtag, oppos, tag; + tag = token[0]; + inv = INVERSES[token[0]]; + if (include(EXPRESSION_START, tag)) { + stack.push(token); + return 1; + } else if (include(EXPRESSION_END, tag)) { + if (debt[inv] > 0) { + debt[inv] -= 1; + this.tokens.splice(i, 1); + return 0; + } else { + match = stack.pop(); + mtag = match[0]; + oppos = INVERSES[mtag]; + if (tag === oppos) { return 1; } - } else { + debt[mtag] += 1; + val = [oppos, mtag === 'INDENT' ? match[1] : oppos]; + if ((this.tokens[i + 2] == undefined ? undefined : this.tokens[i + 2][0]) === mtag) { + this.tokens.splice(i + 3, 0, val); + stack.push(match); + } else { + this.tokens.splice(i, 0, val); + } return 1; } + } else { + return 1; } - })(this)); + }, this)); }; Rewriter.prototype.indentation = function(token) { return [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]]; diff --git a/lib/scope.js b/lib/scope.js index f299511f..44db0477 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -1,4 +1,4 @@ -(function(){ +(function() { var Scope; var __hasProp = Object.prototype.hasOwnProperty; if (!(typeof process !== "undefined" && process !== null)) { @@ -41,8 +41,7 @@ return false; }; Scope.prototype.parameter = function(name) { - this.variables[name] = 'param'; - return this.variables[name]; + return (this.variables[name] = 'param'); }; Scope.prototype.check = function(name) { if (this.variables.hasOwnProperty(name)) { @@ -60,11 +59,10 @@ return this.tempVar; }; Scope.prototype.assign = function(name, value) { - this.variables[name] = { + return (this.variables[name] = { value: value, assigned: true - }; - return this.variables[name]; + }); }; Scope.prototype.hasDeclarations = function(body) { return body === this.expressions && this.any(function(k, val) { diff --git a/src/nodes.coffee b/src/nodes.coffee index f8dfb50f..1abf49e4 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -185,12 +185,6 @@ exports.Expressions = class Expressions extends BaseNode @expressions[idx] = last.makeReturn() this - # A bound function uses a local `_this` variable instead of the real `this`. - rewriteThis: -> - @traverseChildren false, (child) -> - if child instanceof ValueNode and child.base.value is 'this' - child.base = literal '_this' - # An **Expressions** is the only node that can serve as the root. compile: (o) -> o or= {} @@ -204,12 +198,12 @@ exports.Expressions = class Expressions extends BaseNode # It would be better not to generate them in the first place, but for now, # clean up obvious double-parentheses. compileRoot: (o) -> - o.indent = @tab = if o.noWrap then '' else TAB - o.scope = new Scope(null, this, null) - code = @compileWithDeclarations(o) - code = code.replace(TRAILING_WHITESPACE, '') - code = code.replace(DOUBLE_PARENS, '($1)') - if o.noWrap then code else "(function(){\n$code\n})();\n" + o.indent = @tab = if o.noWrap then '' else TAB + o.scope = new Scope(null, this, null) + code = @compileWithDeclarations(o) + code = code.replace(TRAILING_WHITESPACE, '') + code = code.replace(DOUBLE_PARENS, '($1)') + if o.noWrap then code else "(function() {\n$code\n})();\n" # Compile the expressions body for the contents of a function, with # declarations of all inner variables pushed up to the top. @@ -459,7 +453,7 @@ exports.CallNode = class CallNode extends BaseNode utility 'extends' """ (function() { - ${@idt(1)}var ctor = function(){ }; + ${@idt(1)}var ctor = function(){}; ${@idt(1)}__extends(ctor, $meth); ${@idt(1)}return ${meth}.apply(new ctor, ${ @compileSplatArguments(o) }); $@tab}).call(this) @@ -589,8 +583,8 @@ exports.RangeNode = class RangeNode extends BaseNode else clause = "$@fromVar <= $@toVar ?" body = "var $i = $@fromVar; $clause $i <$@equals $@toVar : $i >$@equals $@toVar; $clause $i += 1 : $i -= 1" - post = "{ ${result}.push($i) };\n${idt}return $result;\n$o.indent" - "(function(){${pre}\n${idt}for ($body)$post}).call(this)" + post = "{ ${result}.push($i) };\n${idt}return $result;\n$o.indent" + "(function() {${pre}\n${idt}for ($body)$post}).call(this)" #### SliceNode @@ -764,7 +758,10 @@ exports.AssignNode = class AssignNode extends BaseNode @variable instanceof ValueNode makeReturn: -> - return new Expressions [this, new ReturnNode(@variable)] + if @isStatement() + return new Expressions [this, new ReturnNode(@variable)] + else + super() isStatement: -> @isValue() and (@variable.isArray() or @variable.isObject()) @@ -866,7 +863,7 @@ exports.CodeNode = class CodeNode extends BaseNode top = del o, 'top' o.scope = sharedScope or new Scope(o.scope, @body, this) o.top = true - o.indent = @idt(if @bound then 2 else 1) + o.indent = @idt(1) del o, 'noWrap' del o, 'globals' i = 0 @@ -886,13 +883,11 @@ exports.CodeNode = class CodeNode extends BaseNode i += 1 params = (param.compile(o) for param in params) @body.makeReturn() - @body.rewriteThis() if @bound (o.scope.parameter(param)) for param in params code = if @body.expressions.length then "\n${ @body.compileWithDeclarations(o) }\n" else '' - func = "function(${ params.join(', ') }) {$code${ code and @idt(if @bound then 1 else 0) }}" - func = "($func)" if top and not @bound - return func unless @bound - "(function(_this) {\n${@idt(1)}return $func\n$@tab})(this)" + func = "function(${ params.join(', ') }) {$code${ code and @tab }}" + return "${utility('bind')}($func, this)" if @bound + if top then "($func)" else func topSensitive: -> true @@ -927,14 +922,17 @@ exports.SplatNode = class SplatNode extends BaseNode compileParam: (o) -> name = @name.compile(o) o.scope.find name - len = o.scope.freeVariable() - o.scope.assign len, "arguments.length" - variadic = o.scope.freeVariable() - o.scope.assign variadic, "$len >= $@arglength" - for trailing, idx in @trailings - pos = @trailings.length - idx - o.scope.assign(trailing.compile(o), "arguments[$variadic ? $len - $pos : ${@index + idx}]") - "$name = ${utility('slice')}.call(arguments, $@index, $len - ${@trailings.length})" + end = '' + if @trailings.length + len = o.scope.freeVariable() + o.scope.assign len, "arguments.length" + variadic = o.scope.freeVariable() + o.scope.assign variadic, "$len >= $@arglength" + end = if @trailings.length then ", $len - ${@trailings.length}" + for trailing, idx in @trailings + pos = @trailings.length - idx + o.scope.assign(trailing.compile(o), "arguments[$variadic ? $len - $pos : ${@index + idx}]") + "$name = ${utility('slice')}.call(arguments, $@index$end)" # A compiling a splat as a destructuring assignment means slicing arguments # from the right-hand-side's corresponding array. @@ -1487,7 +1485,7 @@ UTILITIES = { # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206). extends: """ function(child, parent) { - var ctor = function(){ }; + var ctor = function(){}; ctor.prototype = parent.prototype; child.prototype = new ctor(); child.prototype.constructor = child; @@ -1496,6 +1494,13 @@ UTILITIES = { } """ + # Create a function bound to the current value of "this". + bind: """ + function(func, context) { + return function(){ return func.apply(context, arguments); }; + } + """ + # Shortcuts to speed up the lookup time for native functions. hasProp: 'Object.prototype.hasOwnProperty' slice: 'Array.prototype.slice' diff --git a/test/test_functions.coffee b/test/test_functions.coffee index fe25d581..12c5762c 100644 --- a/test/test_functions.coffee +++ b/test/test_functions.coffee @@ -19,17 +19,27 @@ ok y.x() is 3 obj = { - name: "Fred" + name: 'Fred' bound: -> - (=> ok(this.name is "Fred"))() + (=> ok(this.name is 'Fred'))() unbound: -> (-> ok(!this.name?))() + + nested: -> + (=> + (=> + (=> + ok this.name is 'Fred' + )() + )() + )() } obj.unbound() obj.bound() +obj.nested() # Python decorator style wrapper that memoizes any function