Merge remote-tracking branch 'upstream/master' into sourcemaps

Conflicts:
	lib/coffee-script/coffee-script.js
	lib/coffee-script/command.js
	lib/coffee-script/nodes.js
	src/coffee-script.coffee
	src/command.coffee
	src/nodes.coffee
This commit is contained in:
Jason Walton
2013-03-04 09:25:55 -05:00
25 changed files with 601 additions and 591 deletions

View File

@@ -1,8 +1,8 @@
fs = require 'fs'
path = require 'path'
{extend} = require './lib/coffee-script/helpers'
CoffeeScript = require './lib/coffee-script'
{spawn, exec} = require 'child_process'
helpers = require './lib/coffee-script/helpers'
# ANSI Terminal Colors.
bold = red = green = reset = ''
@@ -79,7 +79,7 @@ task 'build:full', 'rebuild the source twice, and run the tests', ->
task 'build:parser', 'rebuild the Jison parser (run build first)', ->
extend global, require('util')
helpers.extend global, require('util')
require 'jison'
parser = require('./lib/coffee-script/grammar').parser
fs.writeFile 'lib/coffee-script/parser.js', parser.generate()
@@ -225,7 +225,7 @@ runTests = (CoffeeScript) ->
# Run every test in the `test` folder, recording failures.
files = fs.readdirSync 'test'
for file in files when file.match /\.(lit)?coffee$/i
literate = path.extname(file) is '.litcoffee'
literate = helpers.isLiterate file
currentFile = filename = path.join 'test', file
code = fs.readFileSync filename
try

View File

@@ -1,7 +1,6 @@
// Generated by CoffeeScript 1.5.0
(function() {
var Lexer, baseFileName, compile, count, ext, extend, extensions, fs, lexer, loadFile, parser, path, sourcemap, vm, _i, _len, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
var Lexer, baseFileName, compile, ext, fs, helpers, lexer, loadFile, parser, path, sourcemap, vm, _i, _len, _ref,
__hasProp = {}.hasOwnProperty;
fs = require('fs');
@@ -12,26 +11,26 @@
parser = require('./parser').parser;
sourcemap = require('./sourcemap');
helpers = require('./helpers');
vm = require('vm');
_ref = require('./helpers'), count = _ref.count, extend = _ref.extend;
extensions = ['.coffee', '.litcoffee'];
sourcemap = require('./sourcemap');
loadFile = function(module, filename) {
var raw, stripped;
raw = fs.readFileSync(filename, 'utf8');
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
return module._compile(compile(stripped, {
filename: filename
filename: filename,
literate: helpers.isLiterate(filename)
}), filename);
};
if (require.extensions) {
for (_i = 0, _len = extensions.length; _i < _len; _i++) {
ext = extensions[_i];
_ref = ['.coffee', '.litcoffee', '.md', '.coffee.md'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
ext = _ref[_i];
require.extensions[ext] = loadFile;
}
}
@@ -65,7 +64,7 @@
noReplace: true
});
}
newLines = count(fragment.code, "\n");
newLines = helpers.count(fragment.code, "\n");
currentLine += newLines;
currentColumn = fragment.code.length - (newLines ? fragment.code.lastIndexOf("\n") : 0);
}
@@ -91,7 +90,7 @@
}
merge = exports.helpers.merge;
try {
options = extend({}, options);
options = helpers.extend({}, options);
options.sourceMap = new sourcemap.SourceMap();
coffeeFile = path.basename(options.filename);
jsFile = baseFileName(options.filename) + ".js";
@@ -123,7 +122,7 @@
};
exports.run = function(code, options) {
var mainModule, _ref1;
var mainModule;
if (options == null) {
options = {};
}
@@ -131,7 +130,7 @@
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
mainModule.moduleCache && (mainModule.moduleCache = {});
mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename)));
if ((_ref1 = path.extname(mainModule.filename), __indexOf.call(extensions, _ref1) < 0) || require.extensions) {
if (!helpers.isCoffee(mainModule.filename) || require.extensions) {
return mainModule._compile(compile(code, options), mainModule.filename);
} else {
return mainModule._compile(code, mainModule.filename);

View File

@@ -1,7 +1,6 @@
// Generated by CoffeeScript 1.5.0
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, coffee_exts, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, lint, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, lint, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref;
fs = require('fs');
@@ -49,8 +48,6 @@
optionParser = null;
coffee_exts = ['.coffee', '.litcoffee'];
exports.run = function() {
var literals, source, _i, _len, _results;
parseOptions();
@@ -91,7 +88,6 @@
compilePath = function(source, topLevel, base) {
return fs.stat(source, function(err, stats) {
var _ref1;
if (err && err.code !== 'ENOENT') {
throw err;
}
@@ -131,7 +127,7 @@
return compilePath(path.join(source, file), false, base);
});
});
} else if (topLevel || (_ref1 = path.extname(source), __indexOf.call(coffee_exts, _ref1) >= 0)) {
} else if (topLevel || helpers.isCoffee(source)) {
if (opts.watch) {
watch(source, base);
}
@@ -375,15 +371,15 @@
};
outputPath = function(source, base, extension) {
var baseDir, dir, filename, srcDir;
var baseDir, basename, dir, srcDir, _ref1;
if (extension == null) {
extension = ".js";
}
filename = path.basename(source, path.extname(source)) + extension;
basename = path.basename(source, ((_ref1 = source.match(/\.((lit)?coffee|coffee\.md)$/)) != null ? _ref1[0] : void 0) || path.extname(source));
srcDir = path.dirname(source);
baseDir = base === '.' ? srcDir : srcDir.substring(base.length);
dir = opts.output ? path.join(opts.output, baseDir) : srcDir;
return path.join(dir, filename);
return path.join(dir, basename + extension);
};
writeJs = function(base, sourcePath, js, generatedSourceMap) {
@@ -449,7 +445,7 @@
};
printTokens = function(tokens) {
var locationData, strings, tag, token, value;
var strings, tag, token, value;
strings = (function() {
var _i, _len, _results;
_results = [];
@@ -457,8 +453,7 @@
token = tokens[_i];
tag = token[0];
value = token[1].toString().replace(/\n/, '\\n');
locationData = helpers.locationDataToString(token[2]);
_results.push("[" + tag + " " + value + " " + locationData + "]");
_results.push("[" + tag + " " + value + "]");
}
return _results;
})();
@@ -480,11 +475,9 @@
};
compileOptions = function(filename) {
var literate;
literate = path.extname(filename) === '.litcoffee';
return {
filename: filename,
literate: literate,
literate: helpers.isLiterate(filename),
bare: opts.bare,
header: opts.compile
};

View File

@@ -16,7 +16,6 @@
action = (match = unwrap.exec(action)) ? match[1] : "(" + action + "())";
action = action.replace(/\bnew /g, '$&yy.');
action = action.replace(/\b(?:Block\.wrap|extend)\b/g, 'yy.$&');
action = action.replace(/\b(Op|Value\.(create|wrap))\b/g, 'yy.$&');
addLocationDataFn = function(first, last) {
if (!last) {
return "yy.addLocationDataFn(@" + first + ")";
@@ -94,12 +93,12 @@
],
AssignObj: [
o('ObjAssignable', function() {
return Value.wrap($1);
return new Value($1);
}), o('ObjAssignable : Expression', function() {
return new Assign(LOC(1)(Value.wrap($1)), $3, 'object');
return new Assign(LOC(1)(new Value($1)), $3, 'object');
}), o('ObjAssignable :\
INDENT Expression OUTDENT', function() {
return new Assign(LOC(1)(Value.wrap($1)), $4, 'object');
return new Assign(LOC(1)(new Value($1)), $4, 'object');
}), o('Comment')
],
ObjAssignable: [o('Identifier'), o('AlphaNumeric'), o('ThisProperty')],
@@ -160,27 +159,27 @@
],
SimpleAssignable: [
o('Identifier', function() {
return Value.wrap($1);
return new Value($1);
}), o('Value Accessor', function() {
return $1.add($2);
}), o('Invocation Accessor', function() {
return Value.wrap($1, [].concat($2));
return new Value($1, [].concat($2));
}), o('ThisProperty')
],
Assignable: [
o('SimpleAssignable'), o('Array', function() {
return Value.wrap($1);
return new Value($1);
}), o('Object', function() {
return Value.wrap($1);
return new Value($1);
})
],
Value: [
o('Assignable'), o('Literal', function() {
return Value.wrap($1);
return new Value($1);
}), o('Parenthetical', function() {
return Value.wrap($1);
return new Value($1);
}), o('Range', function() {
return Value.wrap($1);
return new Value($1);
}), o('This')
],
Accessor: [
@@ -190,6 +189,8 @@
return new Access($2, 'soak');
}), o(':: Identifier', function() {
return [LOC(1)(new Access(new Literal('prototype'))), LOC(2)(new Access($2))];
}), o('?:: Identifier', function() {
return [LOC(1)(new Access(new Literal('prototype'), 'soak')), LOC(2)(new Access($2))];
}), o('::', function() {
return new Access(new Literal('prototype'));
}), o('Index')
@@ -274,14 +275,14 @@
],
This: [
o('THIS', function() {
return Value.wrap(new Literal('this'));
return new Value(new Literal('this'));
}), o('@', function() {
return Value.wrap(new Literal('this'));
return new Value(new Literal('this'));
})
],
ThisProperty: [
o('@ Identifier', function() {
return Value.wrap(LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this');
return new Value(LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this');
})
],
Array: [
@@ -348,7 +349,7 @@
o('CATCH Identifier Block', function() {
return [$2, $3];
}), o('CATCH Object Block', function() {
return [LOC(2)(Value.wrap($2)), $3];
return [LOC(2)(new Value($2)), $3];
})
],
Throw: [
@@ -411,7 +412,7 @@
ForBody: [
o('FOR Range', function() {
return {
source: LOC(2)(Value.wrap($2))
source: LOC(2)(new Value($2))
};
}), o('ForStart ForSource', function() {
$2.own = $1.own;
@@ -430,9 +431,9 @@
],
ForValue: [
o('Identifier'), o('ThisProperty'), o('Array', function() {
return Value.wrap($1);
return new Value($1);
}), o('Object', function() {
return Value.wrap($1);
return new Value($1);
})
],
ForVariables: [
@@ -533,42 +534,42 @@
],
Operation: [
o('UNARY Expression', function() {
return Op.create($1, $2);
return new Op($1, $2);
}), o('- Expression', (function() {
return Op.create('-', $2);
return new Op('-', $2);
}), {
prec: 'UNARY'
}), o('+ Expression', (function() {
return Op.create('+', $2);
return new Op('+', $2);
}), {
prec: 'UNARY'
}), o('-- SimpleAssignable', function() {
return Op.create('--', $2);
return new Op('--', $2);
}), o('++ SimpleAssignable', function() {
return Op.create('++', $2);
return new Op('++', $2);
}), o('SimpleAssignable --', function() {
return Op.create('--', $1, null, true);
return new Op('--', $1, null, true);
}), o('SimpleAssignable ++', function() {
return Op.create('++', $1, null, true);
return new Op('++', $1, null, true);
}), o('Expression ?', function() {
return new Existence($1);
}), o('Expression + Expression', function() {
return Op.create('+', $1, $3);
return new Op('+', $1, $3);
}), o('Expression - Expression', function() {
return Op.create('-', $1, $3);
return new Op('-', $1, $3);
}), o('Expression MATH Expression', function() {
return Op.create($2, $1, $3);
return new Op($2, $1, $3);
}), o('Expression SHIFT Expression', function() {
return Op.create($2, $1, $3);
return new Op($2, $1, $3);
}), o('Expression COMPARE Expression', function() {
return Op.create($2, $1, $3);
return new Op($2, $1, $3);
}), o('Expression LOGIC Expression', function() {
return Op.create($2, $1, $3);
return new Op($2, $1, $3);
}), o('Expression RELATION Expression', function() {
if ($2.charAt(0) === '!') {
return Op.create($2.slice(1), $1, $3).invert();
return new Op($2.slice(1), $1, $3).invert();
} else {
return Op.create($2, $1, $3);
return new Op($2, $1, $3);
}
}), o('SimpleAssignable COMPOUND_ASSIGN\
Expression', function() {
@@ -585,7 +586,7 @@
]
};
operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
tokens = [];

View File

@@ -121,4 +121,12 @@
}
};
exports.isCoffee = function(file) {
return /\.((lit)?coffee|coffee\.md)$/.test(file);
};
exports.isLiterate = function(file) {
return /\.(litcoffee|coffee\.md)$/.test(file);
};
}).call(this);

View File

@@ -17,7 +17,6 @@
opts = {};
}
this.literate = opts.literate;
code = this.clean(code);
this.indent = 0;
this.indebt = 0;
this.outdebt = 0;
@@ -26,6 +25,7 @@
this.tokens = [];
this.chunkLine = opts.line || 0;
this.chunkColumn = opts.column || 0;
code = this.clean(code);
i = 0;
while (this.chunk = code.slice(i)) {
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
@@ -47,10 +47,11 @@
if (code.charCodeAt(0) === BOM) {
code = code.slice(1);
}
code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
if (WHITESPACE.test(code)) {
code = "\n" + code;
this.chunkLine--;
}
code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
if (this.literate) {
lines = (function() {
var _i, _len, _ref2, _results;
@@ -83,7 +84,7 @@
this.token('OWN', id);
return id.length;
}
forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::') || !prev.spaced && prev[0] === '@');
forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::' || _ref2 === '?::') || !prev.spaced && prev[0] === '@');
tag = 'IDENTIFIER';
if (!forcedIdentifier && (__indexOf.call(JS_KEYWORDS, id) >= 0 || __indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
tag = id.toUpperCase();
@@ -768,7 +769,7 @@
Lexer.prototype.unfinished = function() {
var _ref2;
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
};
Lexer.prototype.escapeLines = function(str, heredoc) {
@@ -843,7 +844,7 @@
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
WHITESPACE = /^[^\n\S]+/;

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.5.0
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, CodeFragment, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, PARANOID, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, checkFragments, compact, del, ends, extend, flatten, fragmentsToText, last, locationDataToString, merge, multident, some, starts, unfoldSoak, utility, _ref, _ref1,
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, CodeFragment, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, last, locationDataToString, merge, multident, some, starts, unfoldSoak, utility, _ref, _ref1,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
@@ -35,8 +35,6 @@
return this;
};
PARANOID = false;
exports.CodeFragment = CodeFragment = (function() {
function CodeFragment(parent, code) {
@@ -56,7 +54,6 @@
fragmentsToText = function(fragments) {
var fragment;
checkFragments(fragments);
return ((function() {
var _i, _len, _results;
_results = [];
@@ -68,32 +65,6 @@
})()).join('');
};
checkFragments = function(fragments, node) {
var fragment, i, inspected, nodeName, nodeStr, _i, _len;
if (node == null) {
node = null;
}
if (!PARANOID) {
return fragments;
}
nodeName = node ? " from " + node.constructor.name : "";
if (!fragments) {
throw new Error("Fragments is null" + nodeName + ": " + fragments + "\n");
}
if (fragments instanceof CodeFragment) {
throw new Error("Expected array of fragments but found fragment" + nodeName + ": " + fragments + "\n");
}
for (i = _i = 0, _len = fragments.length; _i < _len; i = ++_i) {
fragment = fragments[i];
if (!(fragment instanceof CodeFragment)) {
inspected = (require('util')).inspect(fragments);
nodeStr = node ? "node: " + (node.toString()) + "\n" : "";
throw new Error("Expected fragment: " + i + " of " + fragments.length + nodeName + ": " + fragment + "\nFragments: " + inspected + "\n" + nodeStr);
}
}
return fragments;
};
exports.Base = Base = (function() {
function Base() {}
@@ -103,7 +74,7 @@
};
Base.prototype.compileToFragments = function(o, lvl) {
var fragments, node;
var node;
o = extend({}, o);
if (lvl) {
o.level = lvl;
@@ -111,12 +82,10 @@
node = this.unfoldSoak(o) || this;
node.tab = o.indent;
if (o.level === LEVEL_TOP || !node.isStatement(o)) {
fragments = node.compileNode(o);
return node.compileNode(o);
} else {
fragments = node.compileClosure(o);
return node.compileClosure(o);
}
checkFragments(fragments, node);
return fragments;
};
Base.prototype.compileClosure = function(o) {
@@ -236,7 +205,7 @@
};
Base.prototype.invert = function() {
return Op.create('!', this);
return new Op('!', this);
};
Base.prototype.unwrapAll = function() {
@@ -289,7 +258,6 @@
answer = [];
for (i = _i = 0, _len = fragmentsList.length; _i < _len; i = ++_i) {
fragments = fragmentsList[i];
checkFragments(fragments, this);
if (i) {
answer.push(this.makeCode(joinStr));
}
@@ -586,7 +554,7 @@
__extends(Undefined, _super);
function Undefined() {
Undefined.__super__.constructor.apply(this, arguments);
return Undefined.__super__.constructor.apply(this, arguments);
}
Undefined.prototype.isAssignable = NO;
@@ -606,7 +574,7 @@
__extends(Null, _super);
function Null() {
Null.__super__.constructor.apply(this, arguments);
return Null.__super__.constructor.apply(this, arguments);
}
Null.prototype.isAssignable = NO;
@@ -688,21 +656,16 @@
__extends(Value, _super);
Value.wrap = function(base, props, tag) {
function Value(base, props, tag) {
if (!props && base instanceof Value) {
return base;
} else {
return new Value(base, props, tag);
}
};
function Value(base, properties, tag) {
this.base = base;
this.properties = properties;
this.properties || (this.properties = []);
if (tag === 'this') {
this["this"] = true;
this.properties = props || [];
if (tag) {
this[tag] = true;
}
return this;
}
Value.prototype.children = ['base', 'properties'];
@@ -785,10 +748,10 @@
if (this.properties.length < 2 && !this.base.isComplex() && !(name != null ? name.isComplex() : void 0)) {
return [this, this];
}
base = Value.wrap(this.base, this.properties.slice(0, -1));
base = new Value(this.base, this.properties.slice(0, -1));
if (base.isComplex()) {
bref = new Literal(o.scope.freeVariable('base'));
base = Value.wrap(new Parens(new Assign(bref, base)));
base = new Value(new Parens(new Assign(bref, base)));
}
if (!name) {
return [base, bref];
@@ -798,7 +761,7 @@
name = new Index(new Assign(nref, name.index));
nref = new Index(nref);
}
return [base.add(name), Value.wrap(bref || base.base, [nref || name])];
return [base.add(name), new Value(bref || base.base, [nref || name])];
};
Value.prototype.compileNode = function(o) {
@@ -832,8 +795,8 @@
continue;
}
prop.soak = false;
fst = Value.wrap(_this.base, _this.properties.slice(0, i));
snd = Value.wrap(_this.base, _this.properties.slice(i));
fst = new Value(_this.base, _this.properties.slice(0, i));
snd = new Value(_this.base, _this.properties.slice(i));
if (fst.isComplex()) {
ref = new Literal(o.scope.freeVariable('ref'));
fst = new Parens(new Assign(ref, fst));
@@ -902,24 +865,19 @@
};
Call.prototype.superReference = function(o) {
var accesses, method, name;
var accesses, method;
method = o.scope.namedMethod();
if (!method) {
throw SyntaxError('cannot call super outside of a function.');
}
name = method.name;
if (name == null) {
throw SyntaxError('cannot call super on an anonymous function.');
}
if (method.klass) {
if (method != null ? method.klass : void 0) {
accesses = [new Access(new Literal('__super__'))];
if (method["static"]) {
accesses.push(new Access(new Literal('constructor')));
}
accesses.push(new Access(new Literal(name)));
return (Value.wrap(new Literal(method.klass), accesses)).compile(o);
accesses.push(new Access(new Literal(method.name)));
return (new Value(new Literal(method.klass), accesses)).compile(o);
} else if (method != null ? method.ctor : void 0) {
return "" + method.name + ".__super__.constructor";
} else {
return "" + name + ".__super__.constructor";
throw SyntaxError('cannot call super outside of an instance method.');
}
};
@@ -936,15 +894,15 @@
if (ifn = unfoldSoak(o, this, 'variable')) {
return ifn;
}
_ref2 = Value.wrap(this.variable).cacheReference(o), left = _ref2[0], rite = _ref2[1];
_ref2 = new Value(this.variable).cacheReference(o), left = _ref2[0], rite = _ref2[1];
} else {
left = new Literal(this.superReference(o));
rite = Value.wrap(left);
rite = new Value(left);
}
rite = new Call(rite, this.args);
rite.isNew = this.isNew;
left = new Literal("typeof " + (left.compile(o)) + " === \"function\"");
return new If(left, Value.wrap(rite), {
return new If(left, new Value(rite), {
soak: true
});
}
@@ -1026,7 +984,7 @@
return [].concat(this.makeCode("(function(func, args, ctor) {\n" + idt + "ctor.prototype = func.prototype;\n" + idt + "var child = new ctor, result = func.apply(child, args);\n" + idt + "return Object(result) === result ? result : child;\n" + this.tab + "})("), this.variable.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), splatArgs, this.makeCode(", function(){})"));
}
answer = [];
base = Value.wrap(this.variable);
base = new Value(this.variable);
if ((name = base.properties.pop()) && base.isComplex()) {
ref = o.scope.freeVariable('ref');
answer = answer.concat(this.makeCode("(" + ref + " = "), base.compileToFragments(o, LEVEL_LIST), this.makeCode(")"), name.compileToFragments(o));
@@ -1062,7 +1020,7 @@
Extends.prototype.children = ['child', 'parent'];
Extends.prototype.compileToFragments = function(o) {
return new Call(Value.wrap(new Literal(utility('extends'))), [this.child, this.parent]).compileToFragments(o);
return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compileToFragments(o);
};
return Extends;
@@ -1432,12 +1390,18 @@
};
Class.prototype.addBoundFunctions = function(o) {
var bvar, lhs, _i, _len, _ref2;
_ref2 = this.boundFuncs;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
bvar = _ref2[_i];
lhs = (Value.wrap(new Literal("this"), [new Access(bvar)])).compile(o);
this.ctor.body.unshift(new Literal("" + lhs + " = " + (utility('bind')) + "(" + lhs + ", this)"));
var body, bound, func, lhs, name, rhs, _i, _len, _ref2, _ref3;
if (this.boundFuncs.length) {
o.scope.assign('_this', 'this');
_ref2 = this.boundFuncs;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
_ref3 = _ref2[_i], name = _ref3[0], func = _ref3[1];
lhs = new Value(new Literal("this"), [new Access(name)]);
body = new Block([new Return(new Literal("" + this.ctor.name + ".prototype." + name.value + ".apply(_this, arguments)"))]);
rhs = new Code(func.params, body, 'boundfunc');
bound = new Assign(lhs, rhs);
this.ctor.body.unshift(bound);
}
}
};
@@ -1472,9 +1436,9 @@
func.context = name;
}
} else {
assign.variable = Value.wrap(new Literal(name), [new Access(new Literal('prototype')), new Access(base)]);
assign.variable = new Value(new Literal(name), [new Access(new Literal('prototype')), new Access(base)]);
if (func instanceof Code && func.bound) {
this.boundFuncs.push(base);
this.boundFuncs.push([base, func]);
func.bound = false;
}
}
@@ -1490,7 +1454,8 @@
Class.prototype.walkBody = function(name, o) {
var _this = this;
return this.traverseChildren(false, function(child) {
var exps, i, node, _i, _len, _ref2;
var cont, exps, i, node, _i, _len, _ref2;
cont = true;
if (child instanceof Class) {
return false;
}
@@ -1499,11 +1464,13 @@
for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) {
node = _ref2[i];
if (node instanceof Value && node.isObject(true)) {
cont = false;
exps[i] = _this.addProperties(node, name, o);
}
}
return child.expressions = exps = flatten(exps);
child.expressions = exps = flatten(exps);
}
return cont && !(child instanceof Class);
});
};
@@ -1517,8 +1484,7 @@
return this.directives = expressions.splice(0, index);
};
Class.prototype.ensureConstructor = function(name, o) {
var returnExpr;
Class.prototype.ensureConstructor = function(name) {
if (!this.ctor) {
this.ctor = new Code;
if (this.parent) {
@@ -1527,20 +1493,12 @@
if (this.externalCtor) {
this.ctor.body.push(new Literal("" + this.externalCtor + ".apply(this, arguments)"));
}
this.ctor.body.makeReturn();
this.body.expressions.unshift(this.ctor);
}
this.ctor.ctor = this.ctor.name = name;
this.ctor.klass = null;
this.ctor.noReturn = true;
returnExpr = null;
this.ctor.body.traverseChildren(false, function(node) {
if (node instanceof Return && (returnExpr = node.expression)) {
return false;
}
});
if (returnExpr) {
throw SyntaxError("cannot return a value from a constructor: \"" + (returnExpr.compileNode(o)) + "\" in class " + name);
}
return this.ctor.noReturn = true;
};
Class.prototype.compileNode = function(o) {
@@ -1554,7 +1512,7 @@
this.hoistDirectivePrologue();
this.setContext(name);
this.walkBody(name, o);
this.ensureConstructor(name, o);
this.ensureConstructor(name);
this.body.spaced = true;
if (!(this.ctor instanceof Code)) {
this.body.expressions.unshift(this.ctor);
@@ -1678,7 +1636,7 @@
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0);
}
acc = IDENTIFIER.test(idx.unwrap().value || 0);
value = Value.wrap(value);
value = new Value(value);
value.properties.push(new (acc ? Access : Index)(idx));
if (_ref4 = obj.unwrap().value, __indexOf.call(RESERVED, _ref4) >= 0) {
throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (value.compile(o)));
@@ -1704,7 +1662,7 @@
_ref5 = obj, (_ref6 = _ref5.variable, idx = _ref6.base), obj = _ref5.value;
} else {
if (obj.base instanceof Parens) {
_ref7 = Value.wrap(obj.unwrapAll()).cacheReference(o), obj = _ref7[0], idx = _ref7[1];
_ref7 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref7[0], idx = _ref7[1];
} else {
idx = obj["this"] ? obj.properties[0].name : obj;
}
@@ -1734,7 +1692,7 @@
} else {
acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0);
}
val = Value.wrap(new Literal(vvarText), [new (acc ? Access : Index)(idx)]);
val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]);
}
if ((name != null) && __indexOf.call(RESERVED, name) >= 0) {
throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (val.compile(o)));
@@ -1764,7 +1722,7 @@
if (__indexOf.call(this.context, "?") >= 0) {
o.isExistentialEquals = true;
}
return Op.create(this.context.slice(0, -1), left, new Assign(right, this.value, '=')).compileToFragments(o);
return new Op(this.context.slice(0, -1), left, new Assign(right, this.value, '=')).compileToFragments(o);
};
Assign.prototype.compileSplice = function(o) {
@@ -1857,7 +1815,7 @@
o.scope.add(p.value, 'var', true);
}
}
splats = new Assign(Value.wrap(new Arr((function() {
splats = new Assign(new Value(new Arr((function() {
var _l, _len3, _ref5, _results;
_ref5 = this.params;
_results = [];
@@ -1866,7 +1824,7 @@
_results.push(p.asReference(o));
}
return _results;
}).call(this))), Value.wrap(new Literal('arguments')));
}).call(this))), new Value(new Literal('arguments')));
break;
}
_ref5 = this.params;
@@ -1875,16 +1833,16 @@
if (param.isComplex()) {
val = ref = param.asReference(o);
if (param.value) {
val = Op.create('?', ref, param.value);
val = new Op('?', ref, param.value);
}
exprs.push(new Assign(Value.wrap(param.name), val, '=', {
exprs.push(new Assign(new Value(param.name), val, '=', {
param: true
}));
} else {
ref = param;
if (param.value) {
lit = new Literal(ref.name.value + ' == null');
val = new Assign(Value.wrap(param.name), param.value, '=');
val = new Assign(new Value(param.name), param.value, '=');
exprs.push(new If(lit, val));
}
}
@@ -2007,7 +1965,7 @@
} else if (node.isComplex()) {
node = new Literal(o.scope.freeVariable('arg'));
}
node = Value.wrap(node);
node = new Value(node);
if (this.splat) {
node = new Splat(node);
}
@@ -2221,7 +2179,7 @@
__extends(Op, _super);
Op.create = function(op, first, second, flip) {
function Op(op, first, second, flip) {
if (op === 'in') {
return new In(first, second);
}
@@ -2236,14 +2194,11 @@
first = new Parens(first);
}
}
return new Op(op, first, second, flip);
};
function Op(op, first, second, flip) {
this.operator = CONVERSIONS[op] || op;
this.first = first;
this.second = second;
this.operator = CONVERSIONS[op] || op;
this.flip = !!flip;
return this;
}
CONVERSIONS = {
@@ -2305,7 +2260,7 @@
} else if (this.operator === '!' && (fst = this.first.unwrap()) instanceof Op && ((_ref2 = fst.operator) === '!' || _ref2 === 'in' || _ref2 === 'instanceof')) {
return fst;
} else {
return Op.create('!', this);
return new Op('!', this);
}
};
@@ -2314,7 +2269,7 @@
return ((_ref2 = this.operator) === '++' || _ref2 === '--' || _ref2 === 'delete') && unfoldSoak(o, this, 'first');
};
Op.generateDo = function(exp) {
Op.prototype.generateDo = function(exp) {
var call, func, param, passedParams, ref, _i, _len, _ref2;
passedParams = [];
func = exp instanceof Assign && (ref = exp.value.unwrap()) instanceof Code ? ref : exp;
@@ -2808,7 +2763,7 @@
}
fn = ((_ref6 = val.base) != null ? _ref6.unwrapAll() : void 0) || val;
ref = new Literal(o.scope.freeVariable('fn'));
base = Value.wrap(ref);
base = new Value(ref);
if (val.base) {
_ref7 = [base, val], val.base = _ref7[0], base = _ref7[1];
}
@@ -2981,7 +2936,7 @@
};
If.prototype.compileStatement = function(o) {
var answer, body, child, cond, exeq, ifPart;
var answer, body, child, cond, exeq, ifPart, indent;
child = del(o, 'chainChild');
exeq = del(o, 'isExistentialEquals');
if (exeq) {
@@ -2989,10 +2944,12 @@
type: 'if'
}).compileToFragments(o);
}
indent = o.indent + TAB;
body = this.ensureBlock(this.body).compileToFragments(merge(o, {
indent: indent
}));
cond = this.condition.compileToFragments(o, LEVEL_PAREN);
o.indent += TAB;
body = this.ensureBlock(this.body);
ifPart = [].concat(this.makeCode("if ("), cond, this.makeCode(") {\n"), body.compileToFragments(o), this.makeCode("\n" + this.tab + "}"));
ifPart = [].concat(this.makeCode("if ("), cond, this.makeCode(") {\n"), body, this.makeCode("\n" + this.tab + "}"));
if (!child) {
ifPart.unshift(this.makeCode(this.tab));
}
@@ -3001,19 +2958,20 @@
}
answer = ifPart.concat(this.makeCode(' else '));
if (this.isChain) {
o.indent = this.tab;
o.chainChild = true;
answer = answer.concat(this.elseBody.unwrap().compileToFragments(o, LEVEL_TOP));
} else {
answer = answer.concat(this.makeCode("{\n"), this.elseBody.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}"));
answer = answer.concat(this.makeCode("{\n"), this.elseBody.compileToFragments(merge(o, {
indent: indent
}), LEVEL_TOP), this.makeCode("\n" + this.tab + "}"));
}
return answer;
};
If.prototype.compileExpression = function(o) {
var alt, body, cond, fragments;
cond = this.condition.compileToFragments(o, LEVEL_COND);
body = this.bodyNode().compileToFragments(o, LEVEL_LIST);
cond = this.condition.compileToFragments(o, LEVEL_COND);
alt = this.elseBodyNode() ? this.elseBodyNode().compileToFragments(o, LEVEL_LIST) : [this.makeCode('void 0')];
fragments = cond.concat(this.makeCode(" ? "), body, this.makeCode(" : "), alt);
if (o.level >= LEVEL_COND) {
@@ -3048,7 +3006,7 @@
if (mentionsArgs) {
args.push(new Literal('arguments'));
}
func = Value.wrap(func, [new Access(meth)]);
func = new Value(func, [new Access(meth)]);
}
func.noReturn = noReturn;
call = new Call(func, args);
@@ -3072,7 +3030,7 @@
return;
}
parent[name] = ifn.body;
ifn.body = Value.wrap(parent);
ifn.body = new Value(parent);
return ifn;
};
@@ -3080,9 +3038,6 @@
"extends": function() {
return "function(child, parent) { for (var key in parent) { if (" + (utility('hasProp')) + ".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }";
},
bind: function() {
return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }';
},
indexOf: function() {
return "[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }";
},

File diff suppressed because one or more lines are too long

View File

@@ -162,7 +162,7 @@
var stack;
stack = [];
return this.scanTokens(function(token, i, tokens) {
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
tag = token[0];
prevTag = (i > 0 ? tokens[i - 1] : [])[0];
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
@@ -225,12 +225,13 @@
return i += 1;
}
};
endImplicitObject = function() {
endImplicitObject = function(j) {
j = j != null ? j : i;
stack.pop();
tokens.splice(i, 0, generate('}', '}'));
tokens.splice(j, 0, generate('}', '}'));
return i += 1;
};
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH' || tag === 'CATCH')) {
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH')) {
stack.push([
'CONTROL', i, {
ours: true
@@ -273,7 +274,7 @@
startImplicitCall(i + 1);
return forward(2);
}
if (this.matchTags(i, IMPLICIT_FUNC, 'INDENT') && ((_ref2 = stackTop()) != null ? _ref2[0] : void 0) !== '[' && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) {
if (this.matchTags(i, IMPLICIT_FUNC, 'INDENT', null, ':') && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) {
startImplicitCall(i + 1);
stack.push(['INDENT', i + 2]);
return forward(3);
@@ -287,9 +288,9 @@
while (this.tag(s - 2) === 'HERECOMMENT') {
s -= 2;
}
startsLine = s === 0 || (_ref3 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref3) >= 0) || tokens[s - 1].newLine;
startsLine = s === 0 || (_ref2 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref2) >= 0) || tokens[s - 1].newLine;
if (stackTop()) {
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1];
_ref3 = stackTop(), stackTag = _ref3[0], stackIdx = _ref3[1];
if ((stackTag === '{' || stackTag === 'INDENT' && this.tag(stackIdx - 1) === '{') && (startsLine || this.tag(s - 1) === ',' || this.tag(s - 1) === '{')) {
return forward(1);
}
@@ -297,7 +298,7 @@
startImplicitObject(s, !!startsLine);
return forward(2);
}
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::')) {
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::' || tag === '?::')) {
endImplicitCall();
return forward(1);
}
@@ -306,7 +307,7 @@
}
if (__indexOf.call(IMPLICIT_END, tag) >= 0) {
while (inImplicit()) {
_ref5 = stackTop(), stackTag = _ref5[0], stackIdx = _ref5[1], (_ref6 = _ref5[2], sameLine = _ref6.sameLine, startsLine = _ref6.startsLine);
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1], (_ref5 = _ref4[2], sameLine = _ref5.sameLine, startsLine = _ref5.startsLine);
if (inImplicitCall() && prevTag !== ',') {
endImplicitCall();
} else if (inImplicitObject() && sameLine && !startsLine) {
@@ -319,11 +320,9 @@
}
}
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
if (nextTag === 'OUTDENT') {
i += 1;
}
offset = nextTag === 'OUTDENT' ? 1 : 0;
while (inImplicitObject()) {
endImplicitObject();
endImplicitObject(i + offset);
}
}
return forward(1);

View File

@@ -39,7 +39,8 @@
};
Scope.prototype.namedMethod = function() {
if (this.method.name || !this.parent) {
var _ref1;
if (((_ref1 = this.method) != null ? _ref1.name : void 0) || !this.parent) {
return this.method;
}
return this.parent.namedMethod();

View File

@@ -6,25 +6,22 @@
# If included on a webpage, it will automatically sniff out, compile, and
# execute all scripts present in `text/coffeescript` tags.
fs = require 'fs'
path = require 'path'
{Lexer} = require './lexer'
{parser} = require './parser'
fs = require 'fs'
path = require 'path'
{Lexer} = require './lexer'
{parser} = require './parser'
helpers = require './helpers'
vm = require 'vm'
sourcemap = require './sourcemap'
vm = require 'vm'
{count, extend} = require './helpers'
# The file extensions that are considered to be CoffeeScript.
extensions = ['.coffee', '.litcoffee']
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
loadFile = (module, filename) ->
raw = fs.readFileSync filename, 'utf8'
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
module._compile compile(stripped, {filename}), filename
module._compile compile(stripped, {filename, literate: helpers.isLiterate filename}), filename
if require.extensions
for ext in extensions
for ext in ['.coffee', '.litcoffee', '.md', '.coffee.md']
require.extensions[ext] = loadFile
# The current CoffeeScript version number.
@@ -55,7 +52,7 @@ exports.compile = compile = (code, options = {}) ->
[fragment.locationData.first_line, fragment.locationData.first_column],
[currentLine, currentColumn],
{noReplace: true})
newLines = count fragment.code, "\n"
newLines = helpers.count fragment.code, "\n"
currentLine += newLines
currentColumn = fragment.code.length - (if newLines then fragment.code.lastIndexOf "\n" else 0)
@@ -76,7 +73,7 @@ exports.compile = compile = (code, options = {}) ->
exports.compileWithSourceMap = (code, options={}) ->
{merge} = exports.helpers
try
options = extend {}, options
options = helpers.extend {}, options
options.sourceMap = new sourcemap.SourceMap()
coffeeFile = path.basename options.filename
jsFile = baseFileName(options.filename) + ".js"
@@ -121,7 +118,7 @@ exports.run = (code, options = {}) ->
mainModule.paths = require('module')._nodeModulePaths path.dirname fs.realpathSync options.filename
# Compile.
if (path.extname(mainModule.filename) not in extensions) or require.extensions
if not helpers.isCoffee(mainModule.filename) or require.extensions
mainModule._compile compile(code, options), mainModule.filename
else
mainModule._compile code, mainModule.filename

View File

@@ -57,7 +57,6 @@ sourceCode = []
notSources = {}
watchers = {}
optionParser = null
coffee_exts = ['.coffee', '.litcoffee']
# Run `coffee` by parsing passed options and determining what action to take.
# Many flags cause us to divert before compiling anything. Flags passed after
@@ -80,8 +79,8 @@ exports.run = ->
compilePath source, yes, path.normalize source
# Compile a path, which could be a script or a directory. If a directory
# is passed, recursively compile all '.coffee' and '.litcoffee' extension source
# files in it and all subdirectories.
# is passed, recursively compile all '.coffee', '.litcoffee', and '.coffee.md'
# extension source files in it and all subdirectories.
compilePath = (source, topLevel, base) ->
fs.stat source, (err, stats) ->
throw err if err and err.code isnt 'ENOENT'
@@ -99,7 +98,7 @@ compilePath = (source, topLevel, base) ->
sourceCode[index..index] = files.map -> null
files.forEach (file) ->
compilePath (path.join source, file), no, base
else if topLevel or path.extname(source) in coffee_exts
else if topLevel or helpers.isCoffee source
watch source, base if opts.watch
fs.readFile source, (err, code) ->
throw err if err and err.code isnt 'ENOENT'
@@ -251,11 +250,11 @@ removeSource = (source, base, removeJs) ->
# Get the corresponding output JavaScript path for a source file.
outputPath = (source, base, extension=".js") ->
filename = path.basename(source, path.extname(source)) + extension
basename = path.basename source, source.match(/\.((lit)?coffee|coffee\.md)$/)?[0] or path.extname(source)
srcDir = path.dirname source
baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
path.join dir, filename
path.join dir, basename + extension
# Write out a JavaScript source file with the compiled code. By default, files
# are written out in `cwd` as `.js` files with the same name, but the output
@@ -301,13 +300,12 @@ lint = (file, js) ->
jsl.stdin.write js
jsl.stdin.end()
# Pretty-print a stream of tokens.
# Pretty-print a stream of tokens, sans location data.
printTokens = (tokens) ->
strings = for token in tokens
tag = token[0]
value = token[1].toString().replace(/\n/, '\\n')
locationData = helpers.locationDataToString token[2]
"[#{tag} #{value} #{locationData}]"
"[#{tag} #{value}]"
printLine strings.join(' ')
# Use the [OptionParser module](optparse.html) to extract all options from
@@ -324,8 +322,7 @@ parseOptions = ->
# The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename) ->
literate = path.extname(filename) is '.litcoffee'
{filename, literate, bare: opts.bare, header: opts.compile}
{filename, literate: helpers.isLiterate(filename), bare: opts.bare, header: opts.compile}
# Start up a new Node.js instance with the arguments in `--nodejs` passed to
# the `node` binary, preserving the other options.

View File

@@ -39,7 +39,6 @@ o = (patternString, action, options) ->
# All runtime functions we need are defined on "yy"
action = action.replace /\bnew /g, '$&yy.'
action = action.replace /\b(?:Block\.wrap|extend)\b/g, 'yy.$&'
action = action.replace /\b(Op|Value\.(create|wrap))\b/g, 'yy.$&'
# Returns a function which adds location data to the first parameter passed
# in, and returns the parameter. If the parameter is not a node, it will
@@ -159,10 +158,10 @@ grammar =
# Assignment when it happens within an object literal. The difference from
# the ordinary **Assign** is that these allow numbers and strings as keys.
AssignObj: [
o 'ObjAssignable', -> Value.wrap $1
o 'ObjAssignable : Expression', -> new Assign LOC(1)(Value.wrap($1)), $3, 'object'
o 'ObjAssignable', -> new Value $1
o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value($1)), $3, 'object'
o 'ObjAssignable :
INDENT Expression OUTDENT', -> new Assign LOC(1)(Value.wrap($1)), $4, 'object'
INDENT Expression OUTDENT', -> new Assign LOC(1)(new Value($1)), $4, 'object'
o 'Comment'
]
@@ -236,26 +235,26 @@ grammar =
# Variables and properties that can be assigned to.
SimpleAssignable: [
o 'Identifier', -> Value.wrap $1
o 'Identifier', -> new Value $1
o 'Value Accessor', -> $1.add $2
o 'Invocation Accessor', -> Value.wrap $1, [].concat $2
o 'Invocation Accessor', -> new Value $1, [].concat $2
o 'ThisProperty'
]
# Everything that can be assigned to.
Assignable: [
o 'SimpleAssignable'
o 'Array', -> Value.wrap $1
o 'Object', -> Value.wrap $1
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
# The types of things that can be treated as values -- assigned to, invoked
# as functions, indexed into, named as a class, etc.
Value: [
o 'Assignable'
o 'Literal', -> Value.wrap $1
o 'Parenthetical', -> Value.wrap $1
o 'Range', -> Value.wrap $1
o 'Literal', -> new Value $1
o 'Parenthetical', -> new Value $1
o 'Range', -> new Value $1
o 'This'
]
@@ -264,7 +263,8 @@ grammar =
Accessor: [
o '. Identifier', -> new Access $2
o '?. Identifier', -> new Access $2, 'soak'
o ':: Identifier', -> [LOC(1)(new Access new Literal 'prototype'), LOC(2)(new Access $2)]
o ':: Identifier', -> [LOC(1)(new Access new Literal('prototype')), LOC(2)(new Access $2)]
o '?:: Identifier', -> [LOC(1)(new Access new Literal('prototype'), 'soak'), LOC(2)(new Access $2)]
o '::', -> new Access new Literal 'prototype'
o 'Index'
]
@@ -330,13 +330,13 @@ grammar =
# A reference to the *this* current object.
This: [
o 'THIS', -> Value.wrap new Literal 'this'
o '@', -> Value.wrap new Literal 'this'
o 'THIS', -> new Value new Literal 'this'
o '@', -> new Value new Literal 'this'
]
# A reference to a property on *this*.
ThisProperty: [
o '@ Identifier', -> Value.wrap LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this'
o '@ Identifier', -> new Value LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this'
]
# The array literal.
@@ -400,7 +400,7 @@ grammar =
# A catch clause names its error and runs a block of code.
Catch: [
o 'CATCH Identifier Block', -> [$2, $3]
o 'CATCH Object Block', -> [LOC(2)(Value.wrap($2)), $3]
o 'CATCH Object Block', -> [LOC(2)(new Value($2)), $3]
]
# Throw an exception object.
@@ -449,7 +449,7 @@ grammar =
]
ForBody: [
o 'FOR Range', -> source: LOC(2) Value.wrap($2)
o 'FOR Range', -> source: LOC(2) new Value($2)
o 'ForStart ForSource', -> $2.own = $1.own; $2.name = $1[0]; $2.index = $1[1]; $2
]
@@ -463,8 +463,8 @@ grammar =
ForValue: [
o 'Identifier'
o 'ThisProperty'
o 'Array', -> Value.wrap $1
o 'Object', -> Value.wrap $1
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
# An array or range comprehension has variables for the current element
@@ -530,30 +530,30 @@ grammar =
# -type rule, but in order to make the precedence binding possible, separate
# rules are necessary.
Operation: [
o 'UNARY Expression', -> Op.create $1 , $2
o '- Expression', (-> Op.create '-', $2), prec: 'UNARY'
o '+ Expression', (-> Op.create '+', $2), prec: 'UNARY'
o 'UNARY Expression', -> new Op $1 , $2
o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
o '-- SimpleAssignable', -> Op.create '--', $2
o '++ SimpleAssignable', -> Op.create '++', $2
o 'SimpleAssignable --', -> Op.create '--', $1, null, true
o 'SimpleAssignable ++', -> Op.create '++', $1, null, true
o '-- SimpleAssignable', -> new Op '--', $2
o '++ SimpleAssignable', -> new Op '++', $2
o 'SimpleAssignable --', -> new Op '--', $1, null, true
o 'SimpleAssignable ++', -> new Op '++', $1, null, true
# [The existential operator](http://jashkenas.github.com/coffee-script/#existence).
o 'Expression ?', -> new Existence $1
o 'Expression + Expression', -> Op.create '+' , $1, $3
o 'Expression - Expression', -> Op.create '-' , $1, $3
o 'Expression + Expression', -> new Op '+' , $1, $3
o 'Expression - Expression', -> new Op '-' , $1, $3
o 'Expression MATH Expression', -> Op.create $2, $1, $3
o 'Expression SHIFT Expression', -> Op.create $2, $1, $3
o 'Expression COMPARE Expression', -> Op.create $2, $1, $3
o 'Expression LOGIC Expression', -> Op.create $2, $1, $3
o 'Expression MATH Expression', -> new Op $2, $1, $3
o 'Expression SHIFT Expression', -> new Op $2, $1, $3
o 'Expression COMPARE Expression', -> new Op $2, $1, $3
o 'Expression LOGIC Expression', -> new Op $2, $1, $3
o 'Expression RELATION Expression', ->
if $2.charAt(0) is '!'
Op.create($2[1..], $1, $3).invert()
new Op($2[1..], $1, $3).invert()
else
Op.create $2, $1, $3
new Op $2, $1, $3
o 'SimpleAssignable COMPOUND_ASSIGN
Expression', -> new Assign $1, $3, $2
@@ -577,7 +577,7 @@ grammar =
#
# (2 + 3) * 4
operators = [
['left', '.', '?.', '::']
['left', '.', '?.', '::', '?::']
['left', 'CALL_START', 'CALL_END']
['nonassoc', '++', '--']
['left', '?']

View File

@@ -92,4 +92,10 @@ exports.locationDataToString = (obj) ->
else
"No location data"
# Determine if a filename represents a CoffeeScript file.
exports.isCoffee = (file) -> /\.((lit)?coffee|coffee\.md)$/.test file
# Determine if a filename represents a Literate CoffeeScript file.
exports.isLiterate = (file) -> /\.(litcoffee|coffee\.md)$/.test file

View File

@@ -35,7 +35,6 @@ exports.Lexer = class Lexer
# unless explicitly asked not to.
tokenize: (code, opts = {}) ->
@literate = opts.literate # Are we lexing literate CoffeeScript?
code = @clean code # The stripped, cleaned original source code.
@indent = 0 # The current indentation level.
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@@ -47,6 +46,7 @@ exports.Lexer = class Lexer
opts.line or 0 # The start line for the current @chunk.
@chunkColumn =
opts.column or 0 # The start column of the current @chunk.
code = @clean code # The stripped, cleaned original source code.
# At every position, run through this list of attempted matches,
# short-circuiting if any of them succeed. Their order determines precedence:
@@ -80,8 +80,10 @@ exports.Lexer = class Lexer
# by removing all lines that aren't indented by at least four spaces or a tab.
clean: (code) ->
code = code.slice(1) if code.charCodeAt(0) is BOM
code = "\n#{code}" if WHITESPACE.test code
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
if WHITESPACE.test code
code = "\n#{code}"
@chunkLine--
if @literate
lines = for line in code.split('\n')
if match = LITERATE.exec line
@@ -112,7 +114,7 @@ exports.Lexer = class Lexer
@token 'OWN', id
return id.length
forcedIdentifier = colon or
(prev = last @tokens) and (prev[0] in ['.', '?.', '::'] or
(prev = last @tokens) and (prev[0] in ['.', '?.', '::', '?::'] or
not prev.spaced and prev[0] is '@')
tag = 'IDENTIFIER'
@@ -676,7 +678,7 @@ exports.Lexer = class Lexer
# Are we in the midst of an unfinished expression?
unfinished: ->
LINE_CONTINUER.test(@chunk) or
@tag() in ['\\', '.', '?.', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
# Converts newlines for string literals.
@@ -769,7 +771,7 @@ OPERATOR = /// ^ (
| >>>=? # zero-fill right shift
| ([-+:])\1 # doubles
| ([&|<>])\2=? # logic / shift
| \?\. # soak access
| \?(\.|::) # soak access
| \.{2,3} # range or splat
) ///

View File

@@ -154,7 +154,7 @@ exports.Base = class Base
child.traverseChildren crossScope, func
invert: ->
Op.create '!', this
new Op '!', this
unwrapAll: ->
node = this
@@ -448,15 +448,12 @@ exports.Return = class Return extends Base
# A value, variable or literal or parenthesized, indexed or dotted into,
# or vanilla.
exports.Value = class Value extends Base
@wrap: (base, props, tag) ->
if not props and base instanceof Value
base
else
new Value base, props, tag
constructor: (@base, @properties, tag) ->
@properties or= []
@this = true if tag is 'this'
constructor: (base, props, tag) ->
return base if not props and base instanceof Value
@base = base
@properties = props or []
@[tag] = true if tag
return this
children: ['base', 'properties']
@@ -502,16 +499,16 @@ exports.Value = class Value extends Base
name = last @properties
if @properties.length < 2 and not @base.isComplex() and not name?.isComplex()
return [this, this] # `a` `a.b`
base = Value.wrap @base, @properties[...-1]
base = new Value @base, @properties[...-1]
if base.isComplex() # `a().b`
bref = new Literal o.scope.freeVariable 'base'
base = Value.wrap new Parens new Assign bref, base
base = new Value new Parens new Assign bref, base
return [base, bref] unless name # `a()`
if name.isComplex() # `a[b()]`
nref = new Literal o.scope.freeVariable 'name'
name = new Index new Assign nref, name.index
nref = new Index nref
[base.add(name), Value.wrap(bref or base.base, [nref or name])]
[base.add(name), new Value(bref or base.base, [nref or 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*
@@ -535,8 +532,8 @@ exports.Value = class Value extends Base
return ifn
for prop, i in @properties when prop.soak
prop.soak = off
fst = Value.wrap @base, @properties[...i]
snd = Value.wrap @base, @properties[i..]
fst = new Value @base, @properties[...i]
snd = new Value @base, @properties[i..]
if fst.isComplex()
ref = new Literal o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, fst
@@ -584,16 +581,15 @@ exports.Call = class Call extends Base
# method.
superReference: (o) ->
method = o.scope.namedMethod()
throw SyntaxError 'cannot call super outside of a function.' unless method
{name} = method
throw SyntaxError 'cannot call super on an anonymous function.' unless name?
if method.klass
if method?.klass
accesses = [new Access(new Literal '__super__')]
accesses.push new Access new Literal 'constructor' if method.static
accesses.push new Access new Literal name
(Value.wrap (new Literal method.klass), accesses).compile o
accesses.push new Access new Literal method.name
(new Value (new Literal method.klass), accesses).compile o
else if method?.ctor
"#{method.name}.__super__.constructor"
else
"#{name}.__super__.constructor"
throw SyntaxError 'cannot call super outside of an instance method.'
# The appropriate `this` value for a `super` call.
superThis : (o) ->
@@ -605,14 +601,14 @@ exports.Call = class Call extends Base
if @soak
if @variable
return ifn if ifn = unfoldSoak o, this, 'variable'
[left, rite] = Value.wrap(@variable).cacheReference o
[left, rite] = new Value(@variable).cacheReference o
else
left = new Literal @superReference o
rite = Value.wrap left
rite = new Value left
rite = new Call rite, @args
rite.isNew = @isNew
left = new Literal "typeof #{ left.compile o } === \"function\""
return new If left, Value.wrap(rite), soak: yes
return new If left, new Value(rite), soak: yes
call = this
list = []
loop
@@ -679,7 +675,7 @@ exports.Call = class Call extends Base
@makeCode(", "), splatArgs, @makeCode(", function(){})")
answer = []
base = Value.wrap @variable
base = new Value @variable
if (name = base.properties.pop()) and base.isComplex()
ref = o.scope.freeVariable 'ref'
answer = answer.concat @makeCode("(#{ref} = "),
@@ -709,7 +705,7 @@ exports.Extends = class Extends extends Base
# Hooks one constructor into another's prototype chain.
compileToFragments: (o) ->
new Call(Value.wrap(new Literal utility 'extends'), [@child, @parent]).compileToFragments o
new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compileToFragments o
#### Access
@@ -986,9 +982,22 @@ exports.Class = class Class extends Base
# Ensure that all functions bound to the instance are proxied in the
# constructor.
addBoundFunctions: (o) ->
for bvar in @boundFuncs
lhs = (Value.wrap (new Literal "this"), [new Access bvar]).compile o
@ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"
if @boundFuncs.length
o.scope.assign '_this', 'this'
for [name, func] in @boundFuncs
lhs = new Value (new Literal "this"), [new Access name]
body = new Block [new Return new Literal "#{@ctor.name}.prototype.#{name.value}.apply(_this, arguments)"]
rhs = new Code func.params, body, 'boundfunc'
bound = new Assign lhs, rhs
@ctor.body.unshift bound
# {base} = assign.variable
# lhs = (new Value (new Literal "this"), [new Access base]).compile o
# @ctor.body.unshift new Literal """#{lhs} = function() {
# #{o.indent} return #{@ctor.name}.prototype.#{base.value}.apply(_this, arguments);
# #{o.indent}}\n
# """
return
# Merge the properties from a top-level object as prototypal properties
@@ -1016,9 +1025,9 @@ exports.Class = class Class extends Base
if func.bound
func.context = name
else
assign.variable = Value.wrap(new Literal(name), [(new Access new Literal 'prototype'), new Access base ])
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), new Access base ])
if func instanceof Code and func.bound
@boundFuncs.push base
@boundFuncs.push [base, func]
func.bound = no
assign
compact exprs
@@ -1026,12 +1035,15 @@ exports.Class = class Class extends Base
# Walk the body of the class, looking for prototype properties to be converted.
walkBody: (name, o) ->
@traverseChildren false, (child) =>
cont = true
return false if child instanceof Class
if child instanceof Block
for node, i in exps = child.expressions
if node instanceof Value and node.isObject(true)
cont = false
exps[i] = @addProperties node, name, o
child.expressions = exps = flatten exps
cont and child not instanceof Class
# `use strict` (and other directives) must be the first expression statement(s)
# of a function body. This method ensures the prologue is correctly positioned
@@ -1045,23 +1057,17 @@ exports.Class = class Class extends Base
# Make sure that a constructor is defined for the class, and properly
# configured.
ensureConstructor: (name, o) ->
ensureConstructor: (name) ->
if not @ctor
@ctor = new Code
@ctor.body.push new Literal "#{name}.__super__.constructor.apply(this, arguments)" if @parent
@ctor.body.push new Literal "#{@externalCtor}.apply(this, arguments)" if @externalCtor
@ctor.body.makeReturn()
@body.expressions.unshift @ctor
@ctor.ctor = @ctor.name = name
@ctor.klass = null
@ctor.noReturn = yes
# Prevent constructor from returning a value.
returnExpr = null
@ctor.body.traverseChildren no, (node) ->
return no if node instanceof Return and (returnExpr = node.expression)
if returnExpr
throw SyntaxError "cannot return a value from a constructor: \"#{returnExpr.compileNode o}\" in class #{name}"
# Instead of generating the JavaScript string directly, we build up the
# equivalent syntax tree and compile that, in pieces. You can see the
# constructor, property assignments, and inheritance getting built out below.
@@ -1074,7 +1080,7 @@ exports.Class = class Class extends Base
@hoistDirectivePrologue()
@setContext name
@walkBody name, o
@ensureConstructor name, o
@ensureConstructor name
@body.spaced = yes
@body.expressions.unshift @ctor unless @ctor instanceof Code
@body.expressions.push lname
@@ -1166,7 +1172,7 @@ exports.Assign = class Assign extends Base
else
new Literal 0
acc = IDENTIFIER.test idx.unwrap().value or 0
value = Value.wrap value
value = new Value value
value.properties.push new (if acc then Access else Index) idx
if obj.unwrap().value in RESERVED
throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{value.compile o}"
@@ -1190,7 +1196,7 @@ exports.Assign = class Assign extends Base
else
# A shorthand `{a, b, @c} = val` pattern-match.
if obj.base instanceof Parens
[obj, idx] = Value.wrap(obj.unwrapAll()).cacheReference o
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
else
idx = if obj.this then obj.properties[0].name else obj
if not splat and obj instanceof Splat
@@ -1215,7 +1221,7 @@ exports.Assign = class Assign extends Base
acc = no
else
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
val = Value.wrap new Literal(vvarText), [new (if acc then Access else Index) idx]
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
if name? and name in RESERVED
throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{val.compile o}"
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
@@ -1233,7 +1239,7 @@ exports.Assign = class Assign extends Base
left.base.value != "this" and not o.scope.check left.base.value
throw new Error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been defined."
if "?" in @context then o.isExistentialEquals = true
Op.create(@context[...-1], left, new Assign(right, @value, '=') ).compileToFragments o
new Op(@context[...-1], left, new Assign(right, @value, '=') ).compileToFragments o
# Compile the assignment from an array splice literal, using JavaScript's
# `Array#splice` method.
@@ -1294,19 +1300,19 @@ exports.Code = class Code extends Base
for {name: p} in @params
if p.this then p = p.properties[0].name
if p.value then o.scope.add p.value, 'var', yes
splats = new Assign Value.wrap(new Arr(p.asReference o for p in @params)),
Value.wrap new Literal 'arguments'
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
new Value new Literal 'arguments'
break
for param in @params
if param.isComplex()
val = ref = param.asReference o
val = Op.create '?', ref, param.value if param.value
exprs.push new Assign Value.wrap(param.name), val, '=', param: yes
val = new Op '?', ref, param.value if param.value
exprs.push new Assign new Value(param.name), val, '=', param: yes
else
ref = param
if param.value
lit = new Literal ref.name.value + ' == null'
val = new Assign Value.wrap(param.name), param.value, '='
val = new Assign new Value(param.name), param.value, '='
exprs.push new If lit, val
params.push ref unless splats
wasEmpty = @body.isEmpty()
@@ -1375,7 +1381,7 @@ exports.Param = class Param extends Base
node = new Literal o.scope.freeVariable node.value
else if node.isComplex()
node = new Literal o.scope.freeVariable 'arg'
node = Value.wrap node
node = new Value node
node = new Splat node if @splat
@reference = node
@@ -1526,19 +1532,18 @@ exports.While = class While extends Base
# Simple Arithmetic and logical operations. Performs some conversion from
# CoffeeScript operations into their JavaScript equivalents.
exports.Op = class Op extends Base
@create: (op, first, second, flip) ->
if op is 'in'
return new In first, second
constructor: (op, first, second, flip ) ->
return new In first, second if op is 'in'
if op is 'do'
return @generateDo first
if op is 'new'
return first.newInstance() if first instanceof Call and not first.do and not first.isNew
first = new Parens first if first instanceof Code and first.bound or first.do
return new Op op, first, second, flip
constructor: (op, @first, @second, flip ) ->
@operator = CONVERSIONS[op] or op
@first = first
@second = second
@flip = !!flip
return this
# The map of conversions from CoffeeScript to JavaScript symbols.
CONVERSIONS =
@@ -1591,12 +1596,12 @@ exports.Op = class Op extends Base
fst.operator in ['!', 'in', 'instanceof']
fst
else
Op.create '!', this
new Op '!', this
unfoldSoak: (o) ->
@operator in ['++', '--', 'delete'] and unfoldSoak o, this, 'first'
@generateDo: (exp) ->
generateDo: (exp) ->
passedParams = []
func = if exp instanceof Assign and (ref = exp.value.unwrap()) instanceof Code
ref
@@ -1933,7 +1938,7 @@ exports.For = class For extends While
val.properties[0].name?.value in ['call', 'apply'])
fn = val.base?.unwrapAll() or val
ref = new Literal o.scope.freeVariable 'fn'
base = Value.wrap ref
base = new Value ref
if val.base
[val.base, base] = [base, val]
body.expressions[idx] = new Call base, expr.args
@@ -2038,25 +2043,24 @@ exports.If = class If extends Base
if exeq
return new If(@condition.invert(), @elseBodyNode(), type: 'if').compileToFragments o
indent = o.indent + TAB
body = @ensureBlock(@body).compileToFragments merge o, {indent}
cond = @condition.compileToFragments o, LEVEL_PAREN
o.indent += TAB
body = @ensureBlock(@body)
ifPart = [].concat @makeCode("if ("), cond, @makeCode(") {\n"), body.compileToFragments(o), @makeCode("\n#{@tab}}")
ifPart = [].concat @makeCode("if ("), cond, @makeCode(") {\n"), body, @makeCode("\n#{@tab}}")
ifPart.unshift @makeCode @tab unless child
return ifPart unless @elseBody
answer = ifPart.concat @makeCode(' else ')
if @isChain
o.indent = @tab
o.chainChild = yes
answer = answer.concat @elseBody.unwrap().compileToFragments o, LEVEL_TOP
else
answer = answer.concat @makeCode("{\n"), @elseBody.compileToFragments(o, LEVEL_TOP), @makeCode("\n#{@tab}}")
answer = answer.concat @makeCode("{\n"), @elseBody.compileToFragments(merge(o, {indent}), LEVEL_TOP), @makeCode("\n#{@tab}}")
answer
# Compile the `If` as a conditional operator.
compileExpression: (o) ->
cond = @condition.compileToFragments o, LEVEL_COND
body = @bodyNode().compileToFragments o, LEVEL_LIST
cond = @condition.compileToFragments o, LEVEL_COND
alt = if @elseBodyNode() then @elseBodyNode().compileToFragments(o, LEVEL_LIST) else [@makeCode('void 0')]
fragments = cond.concat @makeCode(" ? "), body, @makeCode(" : "), alt
if o.level >= LEVEL_COND then @wrapInBraces fragments else fragments
@@ -2087,7 +2091,7 @@ Closure =
meth = new Literal if mentionsArgs then 'apply' else 'call'
args = [new Literal 'this']
args.push new Literal 'arguments' if mentionsArgs
func = Value.wrap func, [new Access meth]
func = new Value func, [new Access meth]
func.noReturn = noReturn
call = new Call func, args
if statement then Block.wrap [call] else call
@@ -2104,7 +2108,7 @@ Closure =
unfoldSoak = (o, parent, name) ->
return unless ifn = parent[name].unfoldSoak o
parent[name] = ifn.body
ifn.body = Value.wrap parent
ifn.body = new Value parent
ifn
# Constants
@@ -2118,11 +2122,6 @@ UTILITIES =
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
"""
# Create a function bound to the current value of "this".
bind: -> '''
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
'''
# Discover if an item is in an array.
indexOf: -> """
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }

View File

@@ -173,14 +173,15 @@ class exports.Rewriter
tokens.splice idx, 0, generate '{', generate(new String('{'))
i += 1 if not j?
endImplicitObject = ->
endImplicitObject = (j) ->
j = j ? i
stack.pop()
tokens.splice i, 0, generate '}', '}'
tokens.splice j, 0, generate '}', '}'
i += 1
# Don't end an implicit call on next indent if any of these are in an argument
if inImplicitCall() and tag in ['IF', 'TRY', 'FINALLY', 'CATCH',
'CLASS', 'SWITCH', 'CATCH']
'CLASS', 'SWITCH']
stack.push ['CONTROL', i, ours: true]
return forward(1)
@@ -239,8 +240,7 @@ class exports.Rewriter
# which is probably always unintended.
# Furthermore don't allow this in literal arrays, as
# that creates grammatical ambiguities.
if @matchTags(i, IMPLICIT_FUNC, 'INDENT') and
stackTop()?[0] isnt '[' and
if @matchTags(i, IMPLICIT_FUNC, 'INDENT', null, ':') and
not @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH',
'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])
startImplicitCall i + 1
@@ -271,7 +271,7 @@ class exports.Rewriter
# .g b, ->
# c
# .h a
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::']
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::', '?::']
endImplicitCall()
return forward(1)
@@ -314,10 +314,10 @@ class exports.Rewriter
#
# When it isn't the comma go on to play a role in a call or
# array further up the stack, so give it a chance.
if nextTag is 'OUTDENT'
i += 1
offset = if nextTag is 'OUTDENT' then 1 else 0
while inImplicitObject()
endImplicitObject()
endImplicitObject i + offset
return forward(1)
# Add location data to all tokens generated by the rewriter.

View File

@@ -41,7 +41,7 @@ can get complicated if super is being called from an inner function.
function object that has a name filled in, or bottoms out.
namedMethod: ->
return @method if @method.name or !@parent
return @method if @method?.name or !@parent
@parent.namedMethod()
Look up a variable name in lexical scope, and declare it if it does not

View File

@@ -117,24 +117,23 @@ test "basic classes, again, but in the manual prototype style", ->
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
test "super with plain ol' functions as the original constructors", ->
test "super with plain ol' prototypes", ->
TopClass = (arg) ->
@prop = 'top-' + arg
this
TopClass = ->
TopClass::func = (arg) ->
'top-' + arg
SuperClass = (arg) ->
SuperClass = ->
SuperClass extends TopClass
SuperClass::func = (arg) ->
super 'super-' + arg
this
SubClass = ->
super 'sub'
this
SuperClass extends TopClass
SubClass extends SuperClass
SubClass::func = ->
super 'sub'
ok (new SubClass).prop is 'top-super-sub'
eq (new SubClass).func(), 'top-super-sub'
test "'@' referring to the current instance, and not being coerced into a call", ->
@@ -313,7 +312,7 @@ test "classes with value'd constructors", ->
eq (new Two).value, 2
test "exectuable class bodies", ->
test "executable class bodies", ->
class A
if true
@@ -327,6 +326,17 @@ test "exectuable class bodies", ->
eq a.c, undefined
test "#2502: parenthesizing inner object values", ->
class A
category: (type: 'string')
sections: (type: 'number', default: 0)
eq (new A).category.type, 'string'
eq (new A).sections.default, 0
test "mild metaprogramming", ->
class Base
@@ -595,6 +605,11 @@ test "#1813: Passing class definitions as expressions", ->
eq result, B
test "#1966: external constructors should produce their return value", ->
ctor = -> {}
class A then constructor: ctor
ok (new A) not instanceof A
test "#1980: regression with an inherited class with static function members", ->
class A
@@ -676,27 +691,6 @@ test "#2630: class bodies can't reference arguments", ->
throws ->
CoffeeScript.compile('class Test then arguments')
test "#2359: instanceof should work when extending native objects", ->
class MyError extends Error
ok new MyError instanceof MyError
test '#2359: external constructors returning "other typed" objets', ->
ctor = -> {}
class A then constructor: ctor
ok new A instanceof A
ok new ctor not instanceof A
ok new ctor not instanceof ctor
test "#2359: constructors should not return an explicit value", ->
throws -> CoffeeScript.run "class then constructor: -> return 5"
throws -> CoffeeScript.run """
class
constructor: ->
if foo
return bar: 7
baz()
"""
test "#2319: fn class n extends o.p [INDENT] x = 123", ->
first = ->
@@ -706,4 +700,43 @@ test "#2319: fn class n extends o.p [INDENT] x = 123", ->
one = 1
one: -> one
eq new OneKeeper().one(), 1
eq new OneKeeper().one(), 1
test "#2599: other typed constructors should be inherited", ->
class Base
constructor: -> return {}
class Derived extends Base
ok (new Derived) not instanceof Derived
ok (new Derived) not instanceof Base
ok (new Base) not instanceof Base
test "#2359: extending native objects that use other typed constructors requires defining a constructor", ->
class BrokenArray extends Array
method: -> 'no one will call me'
brokenArray = new BrokenArray
ok brokenArray not instanceof BrokenArray
ok typeof brokenArray.method is 'undefined'
class WorkingArray extends Array
constructor: -> super
method: -> 'yes!'
workingArray = new WorkingArray
ok workingArray instanceof WorkingArray
eq 'yes!', workingArray.method()
test "#2489: removing __bind", ->
class Thing
foo: (a, b, c) ->
bar: (a, b, c) =>
thing = new Thing
eq thing.foo.length, 3
eq thing.bar.length, 3

View File

@@ -129,9 +129,3 @@ test "Try catch finally as implicit arguments", ->
bar = yes
catch e
eq bar, yes
baz = first
try iamwhoiam() catch e
"bar"
try iamwhoiam() catch e
eq baz, "bar"

View File

@@ -605,15 +605,6 @@ test "#2297, Different behaviors on interpreting literal", ->
eq xyzzy.four, 4
eq xyzzy.h, 2
thud = foo
1
one: 1
two: 2
three: 3
2
3
eq thud.two, 2
test "#2715, Chained implicit calls", ->
first = (x) -> x
second = (x, y) -> y
@@ -631,30 +622,6 @@ test "#2715, Chained implicit calls", ->
2
eq baz, 2
qux = first second
1
2
3
4
eq qux, 2
test "More implicit calls", ->
first = (x) -> x
second = (x, y) -> y
foo = no
if (first
yes)
foo = yes
eq foo, yes
foo = no
if not first
no
foo = yes
eq foo, no
test "Implicit calls and new", ->
first = (x) -> x
foo = (@x) ->
@@ -662,12 +629,23 @@ test "Implicit calls and new", ->
eq bar.x, 1
third = (x, y, z) -> z
baz = first new foo
new
foo third
baz = first new foo new foo third
one: 1
two: 2
1
three: 3
2
eq baz.x.x.three, 3
eq baz.x.x.three, 3
test "Loose tokens inside of explicit call lists", ->
first = (x) -> x
second = (x, y) -> y
one = 1
foo = second( one
2)
eq foo, 2
bar = first( first
one: 1)
eq bar.one, 1

View File

@@ -38,6 +38,26 @@ test "Verify location of generated tokens", ->
eq numberToken[2].last_line, 0
eq numberToken[2].last_column, 5
test "Verify location of generated tokens (with indented first line)", ->
tokens = CoffeeScript.tokens " a = 83"
eq tokens.length, 6
[IndentToken, aToken, equalsToken, numberToken] = tokens
eq aToken[2].first_line, 0
eq aToken[2].first_column, 2
eq aToken[2].last_line, 0
eq aToken[2].last_column, 2
eq equalsToken[2].first_line, 0
eq equalsToken[2].first_column, 4
eq equalsToken[2].last_line, 0
eq equalsToken[2].last_column, 4
eq numberToken[2].first_line, 0
eq numberToken[2].first_column, 6
eq numberToken[2].last_line, 0
eq numberToken[2].last_column, 7
test "Verify all tokens get a location", ->
doesNotThrow ->

View File

@@ -323,6 +323,29 @@ test "#2549, Brace-less Object Literal as a Second Operand on a New Line", ->
two: 2
eq baz.two, 2
test "#2757, Nested", ->
foo =
bar:
one: 1,
eq foo.bar.one, 1
baz =
qux:
one: 1,
corge:
two: 2,
three: three: three: 3,
xyzzy:
thud:
four:
four: 4,
five: 5,
eq baz.qux.one, 1
eq baz.corge.three.three.three, 3
eq baz.xyzzy.thud.four.four, 4
eq baz.xyzzy.five, 5
test "#1865, syntax regression 1.1.3", ->
foo = (x, y) -> y

View File

@@ -293,4 +293,6 @@ test "#2567: Optimization of negated existential produces correct result", ->
ok !(!a?)
ok !b?
test "#2508: Existential access of the prototype", ->
eq NonExistent?::nothing, undefined
ok Object?::toString

View File

@@ -23,6 +23,9 @@ test "vlqDecodeValue with offset", ->
# Try with an offset, and some cruft at the end.
arrayEq (sourcemap.vlqDecodeValue ("abc" + pair[1] + "efg"), 3), [pair[0], pair[1].length]
eqJson = (a, b) ->
eq (JSON.stringify JSON.parse a), (JSON.stringify JSON.parse b)
test "SourceMap tests", ->
map = new sourcemap.SourceMap()
map.addMapping [0, 0], [0, 0]
@@ -31,8 +34,8 @@ test "SourceMap tests", ->
map.addMapping [1, 9], [2, 8]
map.addMapping [3, 0], [3, 4]
eq (sourcemap.generateV3SourceMap map, "source.coffee", "source.js"), '{"version":3,"file":"source.js","sourceRoot":"","sources":["source.coffee"],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}'
eq (sourcemap.generateV3SourceMap map), '{"version":3,"file":null,"sourceRoot":"","sources":[],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}'
eqJson (sourcemap.generateV3SourceMap map, "source.coffee", "source.js"), '{"version":3,"file":"source.js","sourceRoot":"","sources":["source.coffee"],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}'
eqJson (sourcemap.generateV3SourceMap map), '{"version":3,"file":null,"sourceRoot":"","sources":[],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}'
# Look up a generated column - should get back the original source position.
arrayEq map.getSourcePosition([2,8]), [1,9]