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:
commit
ecfe49adca
36 changed files with 1157 additions and 485 deletions
8
Cakefile
8
Cakefile
|
@ -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 = """
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Generated by CoffeeScript 1.2.1-pre
|
||||
(function() {
|
||||
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Generated by CoffeeScript 1.2.1-pre
|
||||
(function() {
|
||||
var extend, flatten;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Generated by CoffeeScript 1.2.1-pre
|
||||
(function() {
|
||||
var key, val, _ref;
|
||||
|
||||
|
|
|
@ -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
|
@ -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,})/;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Generated by CoffeeScript 1.2.1-pre
|
||||
(function() {
|
||||
var Scope, extend, last, _ref;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
110
src/nodes.coffee
110
src/nodes.coffee
|
@ -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.
|
||||
|
|
|
@ -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+(\*?))\]/
|
||||
|
||||
|
|
140
src/repl.coffee
140
src/repl.coffee
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
155
test/strict.coffee
Normal 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
|
Loading…
Reference in a new issue