1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Merge branch 'master' of github.com:jashkenas/coffee-script

This commit is contained in:
Jeremy Ashkenas 2012-02-23 22:05:39 -05:00
commit ecfe49adca
36 changed files with 1157 additions and 485 deletions

View file

@ -11,10 +11,10 @@ unless process.platform is 'win32'
bold = red = green = reset = ''
if enableColors
bold = '\033[0;1m'
red = '\033[0;31m'
green = '\033[0;32m'
reset = '\033[0m'
bold = '\x1B[0;1m'
red = '\x1B[0;31m'
green = '\x1B[0;32m'
reset = '\x1B[0m'
# Built file header.
header = """

View file

@ -529,7 +529,7 @@ Expressions
<p>
If you would like to iterate over just the keys that are defined on the
object itself, by adding a <tt>hasOwnProperty</tt>
check to avoid properties that may be interited from the prototype, use<br />
check to avoid properties that may be inherited from the prototype, use<br />
<tt>for own key, value of object</tt>
</p>
<p>

View file

@ -943,7 +943,7 @@ ages = (function() {
<p>
If you would like to iterate over just the keys that are defined on the
object itself, by adding a <tt>hasOwnProperty</tt>
check to avoid properties that may be interited from the prototype, use<br />
check to avoid properties that may be inherited from the prototype, use<br />
<tt>for own key, value of object</tt>
</p>
<p>

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var CoffeeScript, runScripts;
@ -5,7 +6,7 @@
CoffeeScript.require = require;
CoffeeScript.eval = function(code, options) {
CoffeeScript["eval"] = function(code, options) {
return eval(CoffeeScript.compile(code, options));
};

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
@ -55,7 +56,7 @@
} catch (e) {
return fatalError("" + e);
}
_ref = options.arguments;
_ref = options["arguments"];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
arg = _ref[_i];
@ -65,8 +66,9 @@
};
printTasks = function() {
var cakefilePath, desc, name, spaces, task;
cakefilePath = path.join(path.relative(__originalDirname, process.cwd()), 'Cakefile');
var cakefilePath, desc, name, relative, spaces, task;
relative = path.relative || path.resolve;
cakefilePath = path.join(relative(__originalDirname, process.cwd()), 'Cakefile');
console.log("" + cakefilePath + " defines the following tasks:\n");
for (name in tasks) {
task = tasks[name];

View file

@ -1,6 +1,7 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var Lexer, RESERVED, compile, fs, lexer, parser, path, vm, _ref,
__hasProp = Object.prototype.hasOwnProperty;
__hasProp = {}.hasOwnProperty;
fs = require('fs');
@ -33,17 +34,20 @@
exports.helpers = require('./helpers');
exports.compile = compile = function(code, options) {
var merge;
var header, js, merge;
if (options == null) options = {};
merge = exports.helpers.merge;
try {
return (parser.parse(lexer.tokenize(code))).compile(merge({}, options));
js = (parser.parse(lexer.tokenize(code))).compile(options);
if (!options.header) return js;
} catch (err) {
if (options.filename) {
err.message = "In " + options.filename + ", " + err.message;
}
throw err;
}
header = "Generated by CoffeeScript " + this.VERSION;
return "// " + header + "\n" + js;
};
exports.tokens = function(code, options) {
@ -60,6 +64,7 @@
exports.run = function(code, options) {
var mainModule;
if (options == null) options = {};
mainModule = require.main;
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
mainModule.moduleCache && (mainModule.moduleCache = {});
@ -71,7 +76,7 @@
}
};
exports.eval = function(code, options) {
exports["eval"] = function(code, options) {
var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref2, _ref3, _require;
if (options == null) options = {};
if (!(code = code.trim())) return;

View file

@ -1,5 +1,6 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, joinTimeout, lint, loadRequires, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref;
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, hidden, joinTimeout, lint, loadRequires, 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');
@ -25,6 +26,10 @@
return process.stderr.write(line + '\n');
};
hidden = function(file) {
return /^\./.test(file);
};
BANNER = 'Usage: coffee [options] path/to/script.coffee\n\nIf called without options, `coffee` will run your script.';
SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']];
@ -42,7 +47,7 @@
optionParser = null;
exports.run = function() {
var source, _i, _len, _results;
var literals, source, _i, _len, _results;
parseOptions();
if (opts.nodejs) return forkNode();
if (opts.help) return usage();
@ -53,10 +58,10 @@
return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + ".");
}
if (opts.stdio) return compileStdio();
if (opts.eval) return compileScript(null, sources[0]);
if (opts["eval"]) return compileScript(null, sources[0]);
if (!sources.length) return require('./repl');
if (opts.run) opts.literals = sources.splice(1).concat(opts.literals);
process.argv = process.argv.slice(0, 2).concat(opts.literals);
literals = opts.run ? sources.splice(1) : [];
process.argv = process.argv.slice(0, 2).concat(literals);
process.argv[0] = 'coffee';
process.execPath = require.main.filename;
_results = [];
@ -84,21 +89,28 @@
if (stats.isDirectory()) {
if (opts.watch) watchDir(source, base);
return fs.readdir(source, function(err, files) {
var file, index, _i, _len, _ref2, _results;
var file, index, _i, _len, _ref2, _ref3, _results;
if (err && err.code !== 'ENOENT') throw err;
if ((err != null ? err.code : void 0) === 'ENOENT') return;
files = files.map(function(file) {
return path.join(source, file);
});
index = sources.indexOf(source);
[].splice.apply(sources, [index, index - index + 1].concat(files)), files;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
[].splice.apply(sources, [index, index - index + 1].concat(_ref2 = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(path.join(source, file));
}
return _results;
})())), _ref2;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref3 = files.map(function() {
return null;
}))), _ref2;
}))), _ref3;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(compilePath(file, false, base));
if (!hidden(file)) {
_results.push(compilePath(path.join(source, file), false, base));
}
}
return _results;
});
@ -150,7 +162,7 @@
} catch (err) {
CoffeeScript.emit('failure', err, task);
if (CoffeeScript.listeners('failure').length) return;
if (o.watch) return printLine(err.message);
if (o.watch) return printLine(err.message + '\x07');
printWarn(err instanceof Error && err.stack || ("ERROR: " + err));
return process.exit(1);
}
@ -254,13 +266,11 @@
watcher.close();
return unwatchDir(source, base);
}
files = files.map(function(file) {
return path.join(source, file);
});
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
if (!(!notSources[file])) continue;
if (!(!hidden(file) && !notSources[file])) continue;
file = path.join(source, file);
if (sources.some(function(s) {
return s.indexOf(file) >= 0;
})) {
@ -395,8 +405,8 @@
o = opts = optionParser.parse(process.argv.slice(2));
o.compile || (o.compile = !!o.output);
o.run = !(o.compile || o.print || o.lint);
o.print = !!(o.print || (o.eval || o.stdio && o.compile));
sources = o.arguments;
o.print = !!(o.print || (o["eval"] || o.stdio && o.compile));
sources = o["arguments"];
for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) {
source = sources[i];
sourceCode[i] = null;
@ -406,7 +416,8 @@
compileOptions = function(filename) {
return {
filename: filename,
bare: opts.bare
bare: opts.bare,
header: opts.compile
};
};

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var extend, flatten;

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var key, val, _ref;

View file

@ -1,6 +1,7 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref2,
__indexOf = Array.prototype.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 BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref2,
__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; };
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
@ -68,7 +69,7 @@
}
}
}
if (__indexOf.call(['eval', 'arguments'].concat(JS_FORBIDDEN), id) >= 0) {
if (__indexOf.call(JS_FORBIDDEN, id) >= 0) {
if (forcedIdentifier) {
tag = 'IDENTIFIER';
id = new String(id);
@ -108,10 +109,22 @@
};
Lexer.prototype.numberToken = function() {
var binaryLiteral, lexedLength, match, number;
var binaryLiteral, lexedLength, match, number, octalLiteral;
if (!(match = NUMBER.exec(this.chunk))) return 0;
number = match[0];
if (/E/.test(number)) {
this.error("exponential notation '" + number + "' must be indicated with a lowercase 'e'");
} else if (/[BOX]/.test(number)) {
this.error("radix prefixes must be lowercase '" + number + "'");
} else if (/^0[89]/.test(number)) {
this.error("decimal literals '" + number + "' must not be prefixed with '0'");
} else if (/^0[0-7]/.test(number)) {
this.error("octal literals '" + number + "' must be prefixed with '0o'");
}
lexedLength = number.length;
if (octalLiteral = /0o([0-7]+)/.exec(number)) {
number = (parseInt(octalLiteral[1], 8)).toString();
}
if (binaryLiteral = /0b([01]+)/.exec(number)) {
number = (parseInt(binaryLiteral[1], 2)).toString();
}
@ -120,7 +133,7 @@
};
Lexer.prototype.stringToken = function() {
var match, string;
var match, octalEsc, string;
switch (this.chunk.charAt(0)) {
case "'":
if (!(match = SIMPLESTR.exec(this.chunk))) return 0;
@ -137,6 +150,9 @@
default:
return 0;
}
if (octalEsc = /^(?:\\.|[^\\])*\\[0-7]/.test(string)) {
this.error("octal escape sequences " + string + " are not allowed");
}
this.line += count(string, '\n');
return string.length;
};
@ -170,7 +186,6 @@
herecomment: true,
indent: Array(this.indent + 1).join(' ')
}));
this.token('TERMINATOR', '\n');
}
this.line += count(comment, '\n');
return comment.length;
@ -622,15 +637,19 @@
COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf'];
RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf', 'implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', 'yield'];
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
STRICT_PROSCRIBED = ['arguments', 'eval'];
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS);
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED);
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED;
IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/;
NUMBER = /^0x[\da-f]+|^0b[01]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;
@ -11,10 +12,9 @@
}
OptionParser.prototype.parse = function(args) {
var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, skippingArgument, value, _i, _j, _len, _len2, _ref;
var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, seenNonOptionArg, skippingArgument, value, _i, _j, _len, _len2, _ref;
options = {
arguments: [],
literals: []
"arguments": []
};
skippingArgument = false;
originalArgs = args;
@ -27,29 +27,32 @@
}
if (arg === '--') {
pos = originalArgs.indexOf('--');
options.arguments = [originalArgs[1 + pos]];
options.literals = originalArgs.slice(2 + pos);
options["arguments"] = options["arguments"].concat(originalArgs.slice(pos + 1));
break;
}
isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG));
matchedRule = false;
_ref = this.rules;
for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
rule = _ref[_j];
if (rule.shortFlag === arg || rule.longFlag === arg) {
value = rule.hasArgument ? (skippingArgument = true, args[i + 1]) : true;
options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value;
matchedRule = true;
break;
seenNonOptionArg = options["arguments"].length > 0;
if (!seenNonOptionArg) {
matchedRule = false;
_ref = this.rules;
for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
rule = _ref[_j];
if (rule.shortFlag === arg || rule.longFlag === arg) {
value = true;
if (rule.hasArgument) {
skippingArgument = true;
value = args[i + 1];
}
options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value;
matchedRule = true;
break;
}
}
if (isOption && !matchedRule) {
throw new Error("unrecognized option: " + arg);
}
}
if (isOption && !matchedRule) {
throw new Error("unrecognized option: " + arg);
}
if (!isOption) {
options.arguments = originalArgs.slice(originalArgs.indexOf(arg));
break;
}
if (seenNonOptionArg || !isOption) options["arguments"].push(arg);
}
return options;
};
@ -73,9 +76,9 @@
})();
LONG_FLAG = /^(--\w[\w\-]+)/;
LONG_FLAG = /^(--\w[\w\-]*)/;
SHORT_FLAG = /^(-\w)/;
SHORT_FLAG = /^(-\w)$/;
MULTI_FLAG = /^-(\w{2,})/;

View file

@ -1,5 +1,10 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, readline, repl, run, stdin, stdout;
var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, REPL_PROMPT_MULTILINE, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, multilineMode, pipedInput, readline, repl, run, stdin, stdout;
stdin = process.openStdin();
stdout = process.stdout;
CoffeeScript = require('./coffee-script');
@ -13,6 +18,8 @@
REPL_PROMPT = 'coffee> ';
REPL_PROMPT_MULTILINE = '------> ';
REPL_PROMPT_CONTINUATION = '......> ';
enableColours = false;
@ -21,45 +28,10 @@
enableColours = !process.env.NODE_DISABLE_COLORS;
}
stdin = process.openStdin();
stdout = process.stdout;
error = function(err) {
return stdout.write((err.stack || err.toString()) + '\n');
};
backlog = '';
run = function(buffer) {
var code, returnValue, _;
if (!buffer.toString().trim() && !backlog) {
repl.prompt();
return;
}
code = backlog += buffer;
if (code[code.length - 1] === '\\') {
backlog = "" + backlog.slice(0, -1) + "\n";
repl.setPrompt(REPL_PROMPT_CONTINUATION);
repl.prompt();
return;
}
repl.setPrompt(REPL_PROMPT);
backlog = '';
try {
_ = global._;
returnValue = CoffeeScript.eval("_=(" + code + "\n)", {
filename: 'repl',
modulename: 'repl'
});
if (returnValue === void 0) global._ = _;
process.stdout.write(inspect(returnValue, false, 2, enableColours) + '\n');
} catch (err) {
error(err);
}
return repl.prompt();
};
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
SIMPLEVAR = /(\w+)$/i;
@ -116,19 +88,115 @@
process.on('uncaughtException', error);
if (readline.createInterface.length < 3) {
repl = readline.createInterface(stdin, autocomplete);
stdin.on('data', function(buffer) {
return repl.write(buffer);
backlog = '';
run = function(buffer) {
var code, returnValue, _;
buffer = buffer.replace(/[\r\n]+$/, "");
if (multilineMode) {
backlog += "" + buffer + "\n";
repl.setPrompt(REPL_PROMPT_CONTINUATION);
repl.prompt();
return;
}
if (!buffer.toString().trim() && !backlog) {
repl.prompt();
return;
}
code = backlog += buffer;
if (code[code.length - 1] === '\\') {
backlog = "" + backlog.slice(0, -1) + "\n";
repl.setPrompt(REPL_PROMPT_CONTINUATION);
repl.prompt();
return;
}
repl.setPrompt(REPL_PROMPT);
backlog = '';
try {
_ = global._;
returnValue = CoffeeScript["eval"]("_=(" + code + "\n)", {
filename: 'repl',
modulename: 'repl'
});
if (returnValue === void 0) global._ = _;
repl.output.write("" + (inspect(returnValue, false, 2, enableColours)) + "\n");
} catch (err) {
error(err);
}
return repl.prompt();
};
if (stdin.readable) {
pipedInput = '';
repl = {
prompt: function() {
return stdout.write(this._prompt);
},
setPrompt: function(p) {
return this._prompt = p;
},
input: stdin,
output: stdout,
on: function() {}
};
stdin.on('data', function(chunk) {
return pipedInput += chunk;
});
stdin.on('end', function() {
var line, _i, _len, _ref;
_ref = pipedInput.trim().split("\n");
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
line = _ref[_i];
stdout.write("" + line + "\n");
run(line);
}
stdout.write('\n');
return process.exit(0);
});
} else {
repl = readline.createInterface(stdin, stdout, autocomplete);
if (readline.createInterface.length < 3) {
repl = readline.createInterface(stdin, autocomplete);
stdin.on('data', function(buffer) {
return repl.write(buffer);
});
} else {
repl = readline.createInterface(stdin, stdout, autocomplete);
}
}
multilineMode = false;
repl.input.on('keypress', function(char, key) {
var cursorPos, newPrompt;
if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'v')) return;
cursorPos = repl.cursor;
repl.output.cursorTo(0);
repl.output.clearLine(1);
multilineMode = !multilineMode;
backlog = '';
repl.setPrompt((newPrompt = multilineMode ? REPL_PROMPT_MULTILINE : REPL_PROMPT));
repl.prompt();
return repl.output.cursorTo(newPrompt.length + (repl.cursor = cursorPos));
});
repl.input.on('keypress', function(char, key) {
if (!(multilineMode && repl.line)) return;
if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'd')) return;
multilineMode = false;
return repl._line();
});
repl.on('attemptClose', function() {
if (multilineMode) {
multilineMode = false;
repl.output.cursorTo(0);
repl.output.clearLine(1);
repl._onLine(repl.line);
return;
}
if (backlog) {
backlog = '';
process.stdout.write('\n');
repl.output.write('\n');
repl.setPrompt(REPL_PROMPT);
return repl.prompt();
} else {
@ -137,8 +205,8 @@
});
repl.on('close', function() {
process.stdout.write('\n');
return stdin.destroy();
repl.output.write('\n');
return repl.input.destroy();
});
repl.on('line', run);

View file

@ -1,7 +1,8 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref,
__indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = Array.prototype.slice;
__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; },
__slice = [].slice;
exports.Rewriter = (function() {
@ -176,7 +177,9 @@
return this.scanTokens(function(token, i, tokens) {
var callObject, current, next, prev, tag, _ref, _ref2, _ref3;
tag = token[0];
if (tag === 'CLASS' || tag === 'IF') noCall = true;
if (tag === 'CLASS' || tag === 'IF' || tag === 'FOR' || tag === 'WHILE') {
noCall = true;
}
_ref = tokens.slice(i - 1, (i + 1) + 1 || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0);
seenSingle = false;

View file

@ -1,3 +1,4 @@
// Generated by CoffeeScript 1.2.1-pre
(function() {
var Scope, extend, last, _ref;

View file

@ -58,7 +58,8 @@ exports.run = ->
# Display the list of Cake tasks in a format similar to `rake -T`
printTasks = ->
cakefilePath = path.join path.relative(__originalDirname, process.cwd()), 'Cakefile'
relative = path.relative or path.resolve
cakefilePath = path.join relative(__originalDirname, process.cwd()), 'Cakefile'
console.log "#{cakefilePath} defines the following tasks:\n"
for name, task of tasks
spaces = 20 - name.length

View file

@ -34,10 +34,13 @@ exports.helpers = require './helpers'
exports.compile = compile = (code, options = {}) ->
{merge} = exports.helpers
try
(parser.parse lexer.tokenize code).compile merge {}, options
js = (parser.parse lexer.tokenize code).compile options
return js unless options.header
catch err
err.message = "In #{options.filename}, #{err.message}" if options.filename
throw err
header = "Generated by CoffeeScript #{@VERSION}"
"// #{header}\n#{js}"
# Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = (code, options) ->
@ -54,7 +57,7 @@ exports.nodes = (source, options) ->
# Compile and execute a string of CoffeeScript (on the server), correctly
# setting `__filename`, `__dirname`, and relative `require()`.
exports.run = (code, options) ->
exports.run = (code, options = {}) ->
mainModule = require.main
# Set the filename.

View file

@ -19,12 +19,14 @@ helpers.extend CoffeeScript, new EventEmitter
printLine = (line) -> process.stdout.write line + '\n'
printWarn = (line) -> process.stderr.write line + '\n'
hidden = (file) -> /^\.|~$/.test file
# The help banner that is printed when `coffee` is called without arguments.
BANNER = '''
Usage: coffee [options] path/to/script.coffee
Usage: coffee [options] path/to/script.coffee -- [args]
If called without options, `coffee` will run your script.
'''
'''
# The list of all the valid option flags that `coffee` knows how to handle.
SWITCHES = [
@ -69,9 +71,8 @@ exports.run = ->
return compileStdio() if opts.stdio
return compileScript null, sources[0] if opts.eval
return require './repl' unless sources.length
if opts.run
opts.literals = sources.splice(1).concat opts.literals
process.argv = process.argv[0..1].concat opts.literals
literals = if opts.run then sources.splice 1 else []
process.argv = process.argv[0..1].concat literals
process.argv[0] = 'coffee'
process.execPath = require.main.filename
for source in sources
@ -96,11 +97,11 @@ compilePath = (source, topLevel, base) ->
fs.readdir source, (err, files) ->
throw err if err and err.code isnt 'ENOENT'
return if err?.code is 'ENOENT'
files = files.map (file) -> path.join source, file
index = sources.indexOf source
sources[index..index] = files
sources[index..index] = (path.join source, file for file in files)
sourceCode[index..index] = files.map -> null
compilePath file, no, base for file in files
for file in files when not hidden file
compilePath (path.join source, file), no, base
else if topLevel or path.extname(source) is '.coffee'
watch source, base if opts.watch
fs.readFile source, (err, code) ->
@ -136,7 +137,7 @@ compileScript = (file, input, base) ->
catch err
CoffeeScript.emit 'failure', err, task
return if CoffeeScript.listeners('failure').length
return printLine err.message if o.watch
return printLine err.message + '\x07' if o.watch
printWarn err instanceof Error and err.stack or "ERROR: #{err}"
process.exit 1
@ -221,8 +222,8 @@ watchDir = (source, base) ->
throw err unless err.code is 'ENOENT'
watcher.close()
return unwatchDir source, base
files = files.map (file) -> path.join source, file
for file in files when not notSources[file]
for file in files when not hidden(file) and not notSources[file]
file = path.join source, file
continue if sources.some (s) -> s.indexOf(file) >= 0
sources.push file
sourceCode.push null
@ -313,7 +314,8 @@ parseOptions = ->
return
# The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename) -> {filename, bare: opts.bare}
compileOptions = (filename) ->
{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

@ -106,7 +106,7 @@ exports.Lexer = class Lexer
@tokens.pop()
id = '!' + id
if id in ['eval', 'arguments'].concat JS_FORBIDDEN
if id in JS_FORBIDDEN
if forcedIdentifier
tag = 'IDENTIFIER'
id = new String id
@ -133,7 +133,17 @@ exports.Lexer = class Lexer
numberToken: ->
return 0 unless match = NUMBER.exec @chunk
number = match[0]
if /E/.test number
@error "exponential notation '#{number}' must be indicated with a lowercase 'e'"
else if /[BOX]/.test number
@error "radix prefixes must be lowercase '#{number}'"
else if /^0[89]/.test number
@error "decimal literals '#{number}' must not be prefixed with '0'"
else if /^0[0-7]/.test number
@error "octal literals '#{number}' must be prefixed with '0o'"
lexedLength = number.length
if octalLiteral = /0o([0-7]+)/.exec number
number = (parseInt octalLiteral[1], 8).toString()
if binaryLiteral = /0b([01]+)/.exec number
number = (parseInt binaryLiteral[1], 2).toString()
@token 'NUMBER', number
@ -154,6 +164,8 @@ exports.Lexer = class Lexer
@token 'STRING', @escapeLines string
else
return 0
if octalEsc = /^(?:\\.|[^\\])*\\[0-7]/.test string
@error "octal escape sequences #{string} are not allowed"
@line += count string, '\n'
string.length
@ -178,7 +190,6 @@ exports.Lexer = class Lexer
if here
@token 'HERECOMMENT', @sanitizeHeredoc here,
herecomment: true, indent: Array(@indent + 1).join(' ')
@token 'TERMINATOR', '\n'
@line += count comment, '\n'
comment.length
@ -336,8 +347,8 @@ exports.Lexer = class Lexer
prev[1] += '='
return value.length
if value is ';'
@seenFor = no
tag = 'TERMINATOR'
@seenFor = no
tag = 'TERMINATOR'
else if value in MATH then tag = 'MATH'
else if value in COMPARE then tag = 'COMPARE'
else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
@ -569,13 +580,18 @@ RESERVED = [
'case', 'default', 'function', 'var', 'void', 'with'
'const', 'let', 'enum', 'export', 'import', 'native'
'__hasProp', '__extends', '__slice', '__bind', '__indexOf'
'implements', 'interface', 'let', 'package',
'private', 'protected', 'public', 'static', 'yield'
]
STRICT_PROSCRIBED = ['arguments', 'eval']
# The superset of both JavaScript keywords and reserved words, none of which may
# be used as identifiers or properties.
JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS)
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED)
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED
# Token matching regexes.
IDENTIFIER = /// ^
@ -584,8 +600,9 @@ IDENTIFIER = /// ^
///
NUMBER = ///
^ 0x[\da-f]+ | # hex
^ 0b[01]+ | # binary
^ 0b[01]+ | # binary
^ 0o[0-7]+ | # octal
^ 0x[\da-f]+ | # hex
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
///i

View file

@ -4,7 +4,7 @@
# the syntax tree into a string of JavaScript code, call `compile()` on the root.
{Scope} = require './scope'
{RESERVED} = require './lexer'
{RESERVED, STRICT_PROSCRIBED} = require './lexer'
# Import the helpers we plan to use.
{compact, flatten, extend, merge, del, starts, ends, last} = require './helpers'
@ -329,7 +329,7 @@ exports.Literal = class Literal extends Base
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
else if @value is 'this'
if o.scope.method?.bound then o.scope.method.context else @value
else if @value.reserved and "#{@value}" not in ['eval', 'arguments']
else if @value.reserved
"\"#{@value}\""
else
@value
@ -386,6 +386,7 @@ exports.Value = class Value extends Base
isComplex : -> @hasProperties() or @base.isComplex()
isAssignable : -> @hasProperties() or @base.isAssignable()
isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value
isString : -> @base instanceof Literal and IS_STRING.test @base.value
isAtomic : ->
for node in @properties.concat @base
return no if node.soak or node instanceof Call
@ -467,7 +468,7 @@ exports.Comment = class Comment extends Base
makeReturn: THIS
compileNode: (o, level) ->
code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/"
code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/\n"
code = o.indent + code if (level or o.level) is LEVEL_TOP
code
@ -758,13 +759,14 @@ exports.Slice = class Slice extends Base
compileNode: (o) ->
{to, from} = @range
fromStr = from and from.compile(o, LEVEL_PAREN) or '0'
compiled = to and to.compile o, LEVEL_ACCESS
compiled = to and to.compile o, LEVEL_PAREN
if to and not (not @range.exclusive and +compiled is -1)
toStr = ', ' + if @range.exclusive
compiled
else if SIMPLENUM.test compiled
(+compiled + 1).toString()
"#{+compiled + 1}"
else
compiled = to.compile o, LEVEL_ACCESS
"#{compiled} + 1 || 9e9"
".slice(#{ fromStr }#{ toStr or '' })"
@ -779,6 +781,14 @@ exports.Obj = class Obj extends Base
compileNode: (o) ->
props = @properties
propNames = []
for prop in @properties
prop = prop.variable if prop.isComplex()
if prop?
propName = prop.unwrapAll().value.toString()
if propName in propNames
throw SyntaxError "multiple object literal properties named \"#{propName}\""
propNames.push propName
return (if @front then '({})' else '{}') unless props.length
if @generated
for node in props when node instanceof Value
@ -853,6 +863,8 @@ exports.Class = class Class extends Base
tail instanceof Access and tail.name.value
else
@variable.base.value
if decl in STRICT_PROSCRIBED
throw SyntaxError "variable name may not be #{decl}"
decl and= IDENTIFIER.test(decl) and decl
# For all `this`-references and bound functions in the class definition,
@ -916,6 +928,16 @@ exports.Class = class Class extends Base
exps[i] = @addProperties node, name, o
child.expressions = exps = flatten exps
# `use strict` (and other directives) must be the first expression statement(s)
# of a function body. This method ensures the prologue is correctly positioned
# above the `constructor`.
hoistDirectivePrologue: ->
index = 0
{expressions} = @body
++index while (node = expressions[index]) and node instanceof Comment or
node instanceof Value and node.isString()
@directives = expressions.splice 0, index
# Make sure that a constructor is defined for the class, and properly
# configured.
ensureConstructor: (name) ->
@ -938,6 +960,7 @@ exports.Class = class Class extends Base
name = "_#{name}" if name.reserved
lname = new Literal name
@hoistDirectivePrologue()
@setContext name
@walkBody name, o
@ensureConstructor name
@ -946,6 +969,7 @@ exports.Class = class Class extends Base
if decl
@body.expressions.unshift new Assign (new Value (new Literal name), [new Access new Literal 'name']), (new Literal "'#{name}'")
@body.expressions.push lname
@body.expressions.unshift @directives...
@addBoundFunctions o
call = Closure.wrap @body
@ -969,6 +993,9 @@ exports.Assign = class Assign extends Base
constructor: (@variable, @value, @context, options) ->
@param = options and options.param
@subpattern = options and options.subpattern
forbidden = (name = @variable.unwrapAll().value) in STRICT_PROSCRIBED
if forbidden and @context isnt 'object'
throw SyntaxError "variable name may not be \"#{name}\""
children: ['variable', 'value']
@ -1034,7 +1061,7 @@ exports.Assign = class Assign extends Base
acc = IDENTIFIER.test idx.unwrap().value or 0
value = new Value value
value.properties.push new (if acc then Access else Index) idx
if obj.unwrap().value in ['arguments','eval'].concat RESERVED
if obj.unwrap().value in RESERVED
throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{value.compile o}"
return new Assign(obj, value, null, param: @param).compile o, LEVEL_TOP
vvar = value.compile o, LEVEL_LIST
@ -1079,7 +1106,7 @@ exports.Assign = class Assign extends Base
else
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
if name? and name in ['arguments','eval'].concat RESERVED
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).compile o, LEVEL_LIST
assigns.push vvar unless top or @subpattern
@ -1141,8 +1168,10 @@ exports.Code = class Code extends Base
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
vars = []
params = []
exprs = []
for name in @paramNames() # this step must be performed before the others
unless o.scope.check name then o.scope.parameter name
for param in @params when param.splat
o.scope.add p.name.value, 'var', yes for p in @params when p.name.value
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
@ -1159,11 +1188,15 @@ exports.Code = class Code extends Base
lit = new Literal ref.name.value + ' == null'
val = new Assign new Value(param.name), param.value, '='
exprs.push new If lit, val
vars.push ref unless splats
params.push ref unless splats
wasEmpty = @body.isEmpty()
exprs.unshift splats if splats
@body.expressions.unshift exprs... if exprs.length
o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
o.scope.parameter params[i] = p.compile o for p, i in params
uniqs = []
for name in @paramNames()
throw SyntaxError "multiple parameters named '#{name}'" if name in uniqs
uniqs.push name
@body.makeReturn() unless wasEmpty or @noReturn
if @bound
if o.scope.parent.method?.bound
@ -1173,12 +1206,18 @@ exports.Code = class Code extends Base
idt = o.indent
code = 'function'
code += ' ' + @name if @ctor
code += '(' + vars.join(', ') + ') {'
code += '(' + params.join(', ') + ') {'
code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
code += '}'
return @tab + code if @ctor
if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
# A list of parameter names, excluding those generated by the compiler.
paramNames: ->
names = []
names.push param.names()... for param in @params
names
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
# unless `crossScope` is `true`.
traverseChildren: (crossScope, func) ->
@ -1191,6 +1230,8 @@ exports.Code = class Code extends Base
# as well as be a splat, gathering up a group of parameters into an array.
exports.Param = class Param extends Base
constructor: (@name, @value, @splat) ->
if (name = @name.unwrapAll().value) in STRICT_PROSCRIBED
throw SyntaxError "parameter name \"#{name}\" is not allowed"
children: ['name', 'value']
@ -1202,7 +1243,8 @@ exports.Param = class Param extends Base
node = @name
if node.this
node = node.properties[0].name
node = new Literal '_' + node.value if node.value.reserved
if node.value.reserved
node = new Literal o.scope.freeVariable node.value
else if node.isComplex()
node = new Literal o.scope.freeVariable 'arg'
node = new Value node
@ -1212,6 +1254,36 @@ exports.Param = class Param extends Base
isComplex: ->
@name.isComplex()
# Finds the name or names of a `Param`; useful for detecting duplicates.
# In a sense, a destructured parameter represents multiple JS parameters,
# thus this method returns an `Array` of names.
# Reserved words used as param names, as well as the Object and Array
# literals used for destructured params, get a compiler generated name
# during the `Code` compilation step, so this is necessarily an incomplete
# list of a parameter's names.
names: (name = @name)->
atParam = (obj) ->
{value} = obj.properties[0].name
return if value.reserved then [] else [value]
# * simple literals `foo`
return [name.value] if name instanceof Literal
# * at-params `@foo`
return atParam(name) if name instanceof Value
names = []
for obj in name.objects
# * assignments within destructured parameters `{foo:bar}`
if obj instanceof Assign
names.push obj.variable.base.value
# * destructured parameters within destructured parameters `[{a}]`
else if obj.isArray() or obj.isObject()
names.push @names(obj.base)...
# * at-params within destructured parameters `{@foo}`
else if obj.this
names.push atParam(obj)...
# * simple destructured parameters {foo}
else names.push obj.base.value
names
#### Splat
# A splat, either as a parameter to a function, an argument to a call,
@ -1403,6 +1475,10 @@ exports.Op = class Op extends Base
# In chains, there's no need to wrap bare obj literals in parens,
# as the chained expression is wrapped.
@first.front = @front unless isChain
if @operator is 'delete' and o.scope.check(@first.unwrapAll().value)
throw SyntaxError 'delete operand may not be argument or var'
if @operator in ['--', '++'] and @first.unwrapAll().value in STRICT_PROSCRIBED
throw SyntaxError 'prefix increment/decrement may not have eval or arguments operand'
return @compileUnary o if @isUnary()
return @compileChain o if isChain
return @compileExistence o if @operator is '?'
@ -1432,6 +1508,8 @@ exports.Op = class Op extends Base
# Compile a unary **Op**.
compileUnary: (o) ->
if o.level >= LEVEL_ACCESS
return (new Parens this).compile o
parts = [op = @operator]
plusMinus = op in ['+', '-']
parts.push ' ' if op in ['new', 'typeof', 'delete'] or
@ -1507,6 +1585,8 @@ exports.Try = class Try extends Base
tryPart = @attempt.compile o, LEVEL_TOP
catchPart = if @recovery
if @error.value in STRICT_PROSCRIBED
throw SyntaxError "catch variable may not be \"#{@error.value}\""
o.scope.add @error.value, 'param' unless o.scope.check @error.value
" catch#{errorPart}{\n#{ @recovery.compile o, LEVEL_TOP }\n#{@tab}}"
else unless @ensure or @recovery
@ -1876,12 +1956,12 @@ UTILITIES =
# Discover if an item is in an array.
indexOf: -> """
Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
"""
# Shortcuts to speed up the lookup time for native functions.
hasProp: -> 'Object.prototype.hasOwnProperty'
slice : -> 'Array.prototype.slice'
hasProp: -> '{}.hasOwnProperty'
slice : -> '[].slice'
# Levels indicate a node's position in the AST. Useful for knowing if
# parens are necessary or superfluous.

View file

@ -17,14 +17,13 @@ exports.OptionParser = class OptionParser
@rules = buildRules rules
# Parse the list of arguments, populating an `options` object with all of the
# specified options, and return it. `options.arguments` will be an array
# containing the remaining non-option arguments. `options.literals` will be
# an array of options that are meant to be passed through directly to the
# executing script. This is a simpler API than many option parsers that allow
# you to attach callback actions for every flag. Instead, you're responsible
# for interpreting the options object.
# specified options, and return it. Options after the first non-option
# argument are treated as arguments. `options.arguments` will be an array
# containing the remaining arguments. This is a simpler API than many option
# parsers that allow you to attach callback actions for every flag. Instead,
# you're responsible for interpreting the options object.
parse: (args) ->
options = arguments: [], literals: []
options = arguments: []
skippingArgument = no
originalArgs = args
args = normalizeArguments args
@ -34,25 +33,26 @@ exports.OptionParser = class OptionParser
continue
if arg is '--'
pos = originalArgs.indexOf '--'
options.arguments = [originalArgs[1 + pos]]
options.literals = originalArgs[(2 + pos)..]
options.arguments = options.arguments.concat originalArgs[(pos + 1)..]
break
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
matchedRule = no
for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = if rule.hasArgument
skippingArgument = yes
args[i + 1]
else
true
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if not isOption
options.arguments = originalArgs[(originalArgs.indexOf arg)..]
break
# the CS option parser is a little odd; options after the first
# non-option argument are treated as non-option arguments themselves
seenNonOptionArg = options.arguments.length > 0
unless seenNonOptionArg
matchedRule = no
for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = true
if rule.hasArgument
skippingArgument = yes
value = args[i + 1]
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if seenNonOptionArg or not isOption
options.arguments.push arg
options
# Return the help text for this **OptionParser**, listing and describing all
@ -71,8 +71,8 @@ exports.OptionParser = class OptionParser
# -------
# Regex matchers for option flags.
LONG_FLAG = /^(--\w[\w\-]+)/
SHORT_FLAG = /^(-\w)/
LONG_FLAG = /^(--\w[\w\-]*)/
SHORT_FLAG = /^(-\w)$/
MULTI_FLAG = /^-(\w{2,})/
OPTIONAL = /\[(\w+(\*?))\]/

View file

@ -4,6 +4,10 @@
#
# coffee> console.log "#{num} bottles of beer" for num in [99..1]
# Start by opening up `stdin` and `stdout`.
stdin = process.openStdin()
stdout = process.stdout
# Require the **coffee-script** module to get access to the compiler.
CoffeeScript = require './coffee-script'
readline = require 'readline'
@ -15,50 +19,16 @@ Module = require 'module'
# Config
REPL_PROMPT = 'coffee> '
REPL_PROMPT_MULTILINE = '------> '
REPL_PROMPT_CONTINUATION = '......> '
enableColours = no
unless process.platform is 'win32'
enableColours = not process.env.NODE_DISABLE_COLORS
# Start by opening up `stdin` and `stdout`.
stdin = process.openStdin()
stdout = process.stdout
# Log an error.
error = (err) ->
stdout.write (err.stack or err.toString()) + '\n'
# The current backlog of multi-line code.
backlog = ''
# The main REPL function. **run** is called every time a line of code is entered.
# Attempt to evaluate the command. If there's an exception, print it out instead
# of exiting.
run = (buffer) ->
if !buffer.toString().trim() and !backlog
repl.prompt()
return
code = backlog += buffer
if code[code.length - 1] is '\\'
backlog = "#{backlog[...-1]}\n"
repl.setPrompt REPL_PROMPT_CONTINUATION
repl.prompt()
return
repl.setPrompt REPL_PROMPT
backlog = ''
try
_ = global._
returnValue = CoffeeScript.eval "_=(#{code}\n)", {
filename: 'repl'
modulename: 'repl'
}
if returnValue is undefined
global._ = _
process.stdout.write inspect(returnValue, no, 2, enableColours) + '\n'
catch err
error err
repl.prompt()
## Autocompletion
# Regexes to match complete-able bits of text.
@ -98,25 +68,109 @@ getCompletions = (prefix, candidates) ->
# Make sure that uncaught exceptions don't kill the REPL.
process.on 'uncaughtException', error
# Create the REPL by listening to **stdin**.
if readline.createInterface.length < 3
repl = readline.createInterface stdin, autocomplete
stdin.on 'data', (buffer) -> repl.write buffer
# The current backlog of multi-line code.
backlog = ''
# The main REPL function. **run** is called every time a line of code is entered.
# Attempt to evaluate the command. If there's an exception, print it out instead
# of exiting.
run = (buffer) ->
buffer = buffer.replace /[\r\n]+$/, ""
if multilineMode
backlog += "#{buffer}\n"
repl.setPrompt REPL_PROMPT_CONTINUATION
repl.prompt()
return
if !buffer.toString().trim() and !backlog
repl.prompt()
return
code = backlog += buffer
if code[code.length - 1] is '\\'
backlog = "#{backlog[...-1]}\n"
repl.setPrompt REPL_PROMPT_CONTINUATION
repl.prompt()
return
repl.setPrompt REPL_PROMPT
backlog = ''
try
_ = global._
returnValue = CoffeeScript.eval "_=(#{code}\n)", {
filename: 'repl'
modulename: 'repl'
}
if returnValue is undefined
global._ = _
repl.output.write "#{inspect returnValue, no, 2, enableColours}\n"
catch err
error err
repl.prompt()
if stdin.readable
# handle piped input
pipedInput = ''
repl =
prompt: -> stdout.write @_prompt
setPrompt: (p) -> @_prompt = p
input: stdin
output: stdout
on: ->
stdin.on 'data', (chunk) ->
pipedInput += chunk
stdin.on 'end', ->
for line in pipedInput.trim().split "\n"
stdout.write "#{line}\n"
run line
stdout.write '\n'
process.exit 0
else
repl = readline.createInterface stdin, stdout, autocomplete
# Create the REPL by listening to **stdin**.
if readline.createInterface.length < 3
repl = readline.createInterface stdin, autocomplete
stdin.on 'data', (buffer) -> repl.write buffer
else
repl = readline.createInterface stdin, stdout, autocomplete
multilineMode = off
# Handle multi-line mode switch
repl.input.on 'keypress', (char, key) ->
# test for Ctrl-v
return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'v'
cursorPos = repl.cursor
repl.output.cursorTo 0
repl.output.clearLine 1
multilineMode = not multilineMode
backlog = ''
repl.setPrompt (newPrompt = if multilineMode then REPL_PROMPT_MULTILINE else REPL_PROMPT)
repl.prompt()
repl.output.cursorTo newPrompt.length + (repl.cursor = cursorPos)
# Handle Ctrl-d press at end of last line in multiline mode
repl.input.on 'keypress', (char, key) ->
return unless multilineMode and repl.line
# test for Ctrl-d
return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'd'
multilineMode = off
repl._line()
repl.on 'attemptClose', ->
if multilineMode
multilineMode = off
repl.output.cursorTo 0
repl.output.clearLine 1
repl._onLine repl.line
return
if backlog
backlog = ''
process.stdout.write '\n'
repl.output.write '\n'
repl.setPrompt REPL_PROMPT
repl.prompt()
else
repl.close()
repl.on 'close', ->
process.stdout.write '\n'
stdin.destroy()
repl.output.write '\n'
repl.input.destroy()
repl.on 'line', run

View file

@ -167,7 +167,7 @@ class exports.Rewriter
@scanTokens (token, i, tokens) ->
tag = token[0]
noCall = yes if tag in ['CLASS', 'IF']
noCall = yes if tag in ['CLASS', 'IF', 'FOR', 'WHILE']
[prev, current, next] = tokens[i - 1 .. i + 1]
callObject = not noCall and tag is 'INDENT' and
next and next.generated and next[0] is '{' and

View file

@ -267,6 +267,10 @@ test "#1005: invalid identifiers allowed on LHS of destructuring assignment", ->
CoffeeScript.compile "[@#{v}] = x"
CoffeeScript.compile "[@#{v}...] = x"
test "#2055: destructuring assignment with `new`", ->
{length} = new Array
eq 0, length
# Existential Assignment

View file

@ -612,10 +612,70 @@ test "#1966: external constructors should produce their return value", ->
ok (new A) not instanceof A
test "#1980: regression with an inherited class with static function members", ->
class A
class B extends A
@static: => 'value'
eq B.static(), 'value'
eq B.static(), 'value'
test "#1534: class then 'use strict'", ->
# [14.1 Directive Prologues and the Use Strict Directive](http://es5.github.com/#x14.1)
nonce = {}
error = 'do -> ok this'
strictTest = "do ->'use strict';#{error}"
return unless (try CoffeeScript.run strictTest, bare: yes catch e then nonce) is nonce
throws -> CoffeeScript.run "class then 'use strict';#{error}", bare: yes
doesNotThrow -> CoffeeScript.run "class then #{error}", bare: yes
doesNotThrow -> CoffeeScript.run "class then #{error};'use strict'", bare: yes
# comments are ignored in the Directive Prologue
comments = ["""
class
### comment ###
'use strict'
#{error}""",
"""
class
### comment 1 ###
### comment 2 ###
'use strict'
#{error}""",
"""
class
### comment 1 ###
### comment 2 ###
'use strict'
#{error}
### comment 3 ###"""
]
throws (-> CoffeeScript.run comment, bare: yes) for comment in comments
# [ES5 §14.1](http://es5.github.com/#x14.1) allows for other directives
directives = ["""
class
'directive 1'
'use strict'
#{error}""",
"""
class
'use strict'
'directive 2'
#{error}""",
"""
class
### comment 1 ###
'directive 1'
'use strict'
#{error}""",
"""
class
### comment 1 ###
'directive 1'
### comment 2 ###
'use strict'
#{error}"""
]
throws (-> CoffeeScript.run directive, bare: yes) for directive in directives

View file

@ -202,3 +202,6 @@ test "block comments inside class bodies", ->
b: ->
ok B.prototype.a instanceof Function
test "#2037: herecomments shouldn't imply line terminators", ->
do (-> ### ###; fail)

View file

@ -13,6 +13,14 @@ test "--bare", ->
eq -1, CoffeeScript.compile('x = y', bare: on).indexOf 'function'
ok 'passed' is CoffeeScript.eval '"passed"', bare: on, filename: 'test'
test "header (#1778)", ->
header = "// Generated by CoffeeScript #{CoffeeScript.VERSION}\n"
eq 0, CoffeeScript.compile('x = y', header: on).indexOf header
test "header is disabled by default", ->
header = "// Generated by CoffeeScript #{CoffeeScript.VERSION}\n"
eq -1, CoffeeScript.compile('x = y').indexOf header
test "multiple generated references", ->
a = {b: []}
a.b[true] = -> this == a.b

View file

@ -485,3 +485,17 @@ test "#1910: loop index should be mutable within a loop iteration and immutable
++iterations
eq 6, k
eq 5, iterations
test "#2007: Return object literal from comprehension", ->
y = for x in [1, 2]
foo: "foo" + x
eq 2, y.length
eq "foo1", y[0].foo
eq "foo2", y[1].foo
x = 2
y = while x
x: --x
eq 2, y.length
eq 1, y[0].x
eq 0, y[1].x

View file

@ -123,17 +123,17 @@ test "#1492: Nested blocks don't cause double semicolons", ->
eq -1, js.indexOf ';;'
test "#1195 Ignore trailing semicolons (before newlines or as the last char in a program)", ->
preNewline = (numSemicolons) ->
"""
nonce = {}; nonce2 = {}
f = -> nonce#{Array(numSemicolons+1).join(';')}
nonce2
unless f() is nonce then throw new Error('; before linebreak should = newline')
"""
CoffeeScript.run(preNewline(n), bare: true) for n in [1,2,3]
preNewline = (numSemicolons) ->
"""
nonce = {}; nonce2 = {}
f = -> nonce#{Array(numSemicolons+1).join(';')}
nonce2
unless f() is nonce then throw new Error('; before linebreak should = newline')
"""
CoffeeScript.run(preNewline(n), bare: true) for n in [1,2,3]
lastChar = '-> lastChar;'
doesNotThrow -> CoffeeScript.compile lastChar, bare: true
lastChar = '-> lastChar;'
doesNotThrow -> CoffeeScript.compile lastChar, bare: true
test "#1299: Disallow token misnesting", ->
try

View file

@ -91,9 +91,9 @@ test "self-referencing functions", ->
test "splats", ->
arrayEq [0, 1, 2], (((splat...) -> splat) 0, 1, 2)
arrayEq [2, 3], (((_, _, splat...) -> splat) 0, 1, 2, 3)
arrayEq [0, 1], (((splat..., _, _) -> splat) 0, 1, 2, 3)
arrayEq [2], (((_, _, splat..., _) -> splat) 0, 1, 2, 3)
arrayEq [2, 3], (((_, _1, splat...) -> splat) 0, 1, 2, 3)
arrayEq [0, 1], (((splat..., _, _1) -> splat) 0, 1, 2, 3)
arrayEq [2], (((_, _1, splat..., _2) -> splat) 0, 1, 2, 3)
test "@-parameters: automatically assign an argument's value to a property of the context", ->
nonce = {}
@ -131,7 +131,7 @@ test "destructuring in function definition", ->
test "default values", ->
nonceA = {}
nonceB = {}
a = (_,_,arg=nonceA) -> arg
a = (_,_1,arg=nonceA) -> arg
eq nonceA, a()
eq nonceA, a(0)
eq nonceB, a(0,0,nonceB)
@ -139,7 +139,7 @@ test "default values", ->
eq nonceA, a(0,0,null)
eq false , a(0,0,false)
eq nonceB, a(undefined,undefined,nonceB,undefined)
b = (_,arg=nonceA,_,_) -> arg
b = (_,arg=nonceA,_1,_2) -> arg
eq nonceA, b()
eq nonceA, b(0)
eq nonceB, b(0,nonceB)
@ -147,7 +147,7 @@ test "default values", ->
eq nonceA, b(0,null)
eq false , b(0,false)
eq nonceB, b(undefined,nonceB,undefined)
c = (arg=nonceA,_,_) -> arg
c = (arg=nonceA,_,_1) -> arg
eq nonceA, c()
eq 0, c(0)
eq nonceB, c(nonceB)

View file

@ -50,6 +50,22 @@ eq Number::toString, .42.toString
eq Number::toString, (4).toString
test '#1168: leading floating point suppresses newline', ->
eq 1, do ->
1
.5 + 0.5
eq 1, do ->
1
.5 + 0.5
test "Python-style octal literal notation '0o777'", ->
eq 511, 0o777
eq 1, 0o1
eq 1, 0o00001
eq parseInt('0777', 8), 0o777
eq '777', 0o777.toString 8
eq 4, 0o4.valueOf()
eq Number::toString, 0o777['toString']
eq Number::toString, 0o777.toString
test "#2060: Disallow uppercase radix prefixes and exponential notation", ->
for char in ['b', 'o', 'x', 'e']
program = "0#{char}0"
doesNotThrow -> CoffeeScript.compile program, bare: yes
throws -> CoffeeScript.compile program.toUpperCase(), bare: yes

View file

@ -253,20 +253,19 @@ test "#1871: Special case for IMPLICIT_END in the middle of an implicit object",
eq result.two.join(' '), '2 2 2'
test "#1961, #1974, regression with compound assigning to an implicit object", ->
obj = null
obj ?=
obj ?=
one: 1
two: 2
eq obj.two, 2
obj = null
obj or=
three: 3
four: 4
eq obj.four, 4

View file

@ -13,20 +13,31 @@ opt = new OptionParser [
['-l', '--list [FILES*]', 'desc list']
]
result = opt.parse ['one', 'two', 'three', '-r', 'dir']
test "basic arguments", ->
args = ['one', 'two', 'three', '-r', 'dir']
result = opt.parse args
arrayEq args, result.arguments
eq undefined, result.required
eq 5, result.arguments.length
eq '-r', result.arguments[3]
test "boolean and parameterised options", ->
result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']
ok result.optional
eq 'folder', result.required
arrayEq ['one', 'two'], result.arguments
result = opt.parse ['--optional', '-r', 'folder', 'one', 'two']
test "list options", ->
result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']
arrayEq ['one.txt', 'two.txt'], result.list
arrayEq ['three'], result.arguments
ok result.optional
eq 'folder', result.required
eq 'one two', result.arguments.join ' '
result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three']
ok result.list instanceof Array
ok result.list.join(' ') is 'one.txt two.txt'
ok result.arguments.join(' ') is 'three'
test "-- and interesting combinations", ->
result = opt.parse ['-o','-r','a','-r','b','-o','--','-a','b','--c','d']
arrayEq ['-a', 'b', '--c', 'd'], result.arguments
ok result.optional
eq 'b', result.required
args = ['--','-o','a','-r','c','-o','--','-a','arg0','-b','arg1']
result = opt.parse args
eq undefined, result.optional
eq undefined, result.required
arrayEq args[1..], result.arguments

View file

@ -32,3 +32,12 @@ test "catch statements should introduce their argument to scope", ->
catch e
do -> e = 5
eq 5, e
class Array then slice: fail # needs to be global
class Object then hasOwnProperty: fail
test "#1973: redefining Array/Object constructors shouldn't confuse __X helpers", ->
arr = [1..4]
arrayEq [3, 4], arr[2..]
obj = {arr}
for own k of obj
eq arr, obj[k]

155
test/strict.coffee Normal file
View file

@ -0,0 +1,155 @@
# Strict Early Errors
# -------------------
# The following are prohibited under ES5's `strict` mode
# * `Octal Integer Literals`
# * `Octal Escape Sequences`
# * duplicate property definitions in `Object Literal`s
# * duplicate formal parameter
# * `delete` operand is a variable
# * `delete` operand is a parameter
# * `delete` operand is `undefined`
# * `Future Reserved Word`s as identifiers: implements, interface, let, package, private, protected, public, static, yield
# * `eval` or `arguments` as `catch` identifier
# * `eval` or `arguments` as formal parameter
# * `eval` or `arguments` as function declaration identifier
# * `eval` or `arguments` as LHS of assignment
# * `eval` or `arguments` as the operand of a post/pre-fix inc/dec-rement expression
# helper to assert that code complies with strict prohibitions
strict = (code, msg) ->
throws (-> CoffeeScript.compile code), null, msg
strictOk = (code, msg) ->
doesNotThrow (-> CoffeeScript.compile code), msg
test "octal integer literals prohibited", ->
strict '01'
strict '07777'
# decimals with a leading '0' are also prohibited
strict '09'
strict '079'
strictOk '`01`'
test "octal escape sequences prohibited", ->
strict '"\\0"'
strict '"\\7"'
strict '"\\000"'
strict '"\\777"'
strict '"_\\0"'
strict '"\\0_"'
strict '"_\\0_"'
strict '"\\08"'
strict '"\\\\\\0"'
strictOk '"\\8"'
strictOk '"\\\\0"'
strictOk '"\\\\\\\\0"'
strictOk "`'\\0'`"
test "duplicate property definitions in object literals are prohibited", ->
strict 'o = {x:1,x:1}'
strict 'x = 1; o = {x, x: 2}'
test "duplicate formal parameter are prohibited", ->
nonce = {}
# a Param can be an Identifier, ThisProperty( @-param ), Array, or Object
# a Param can also be a splat (...) or an assignment (param=value)
# the following function expressions should throw errors
strict '(_,_)->', 'param, param'
strict '(_,@_)->', 'param, @param'
strict '(_,_...)->', 'param, param...'
strict '(@_,_...)->', '@param, param...'
strict '(_,_ = true)->', 'param, param='
strict '(@_,@_)->', 'two @params'
strict '(_,@_ = true)->', 'param, @param='
strict '(_,{_})->', 'param, {param}'
strict '(@_,{_})->', '@param, {param}'
strict '({_,_})->', '{param, param}'
strict '({_,@_})->', '{param, @param}'
strict '(_,[_])->', 'param, [param]'
strict '([_,_])->', '[param, param]'
strict '([_,@_])->', '[param, @param]'
strict '(_,[_]=true)->', 'param, [param]='
strict '(_,[@_,{_}])->', 'param, [@param, {param}]'
strict '(_,[_,{@_}])->', 'param, [param, {@param}]'
strict '(_,[_,{_}])->', 'param, [param, {param}]'
strict '(_,[_,{__}])->', 'param, [param, {param2}]'
strict '(_,[__,{_}])->', 'param, [param2, {param}]'
strict '(__,[_,{_}])->', 'param, [param2, {param2}]'
# the following function expressions should **not** throw errors
strictOk '({},_arg)->'
strictOk '({},{})->'
strictOk '([]...,_arg)->'
strictOk '({}...,_arg)->'
strictOk '({}...,[],_arg)->'
strictOk '([]...,{},_arg)->'
strictOk '(@case,_case)->'
strictOk '(@case,_case...)->'
strictOk '(@case...,_case)->'
strictOk '(_case,@case)->'
strictOk '(_case,@case...)->'
test "`delete` operand restrictions", ->
strict 'a = 1; delete a'
strictOk 'delete a' #noop
strict '(a) -> delete a'
strict '(@a) -> delete a'
strict '(a...) -> delete a'
strict '(a = 1) -> delete a'
strict '([a]) -> delete a'
strict '({a}) -> delete a'
test "`Future Reserved Word`s, `eval` and `arguments` restrictions", ->
access = (keyword, check = strict) ->
check "#{keyword}.a = 1"
check "#{keyword}[0] = 1"
assign = (keyword, check = strict) ->
check "#{keyword} = 1"
check "#{keyword} += 1"
check "#{keyword} -= 1"
check "#{keyword} *= 1"
check "#{keyword} /= 1"
check "#{keyword} ?= 1"
check "{keyword}++"
check "++{keyword}"
check "{keyword}--"
check "--{keyword}"
destruct = (keyword, check = strict) ->
check "{#{keyword}}"
check "o = {#{keyword}}"
invoke = (keyword, check = strict) ->
check "#{keyword} yes"
check "do #{keyword}"
fnDecl = (keyword, check = strict) ->
check "class #{keyword}"
param = (keyword, check = strict) ->
check "(#{keyword}) ->"
check "({#{keyword}}) ->"
prop = (keyword, check = strict) ->
check "a.#{keyword} = 1"
tryCatch = (keyword, check = strict) ->
check "try new Error catch #{keyword}"
future = 'implements interface let package private protected public static yield'.split ' '
for keyword in future
access keyword
assign keyword
destruct keyword
invoke keyword
fnDecl keyword
param keyword
prop keyword, strictOk
tryCatch keyword
for keyword in ['eval', 'arguments']
access keyword, strictOk
assign keyword
destruct keyword, strictOk
invoke keyword, strictOk
fnDecl keyword
param keyword
prop keyword, strictOk
tryCatch keyword