Merge remote-tracking branch 'origin/master'

Conflicts:
	lib/coffee-script/coffee-script.js
	lib/coffee-script/lexer.js
	lib/coffee-script/parser.js
	src/lexer.coffee
This commit is contained in:
Jason Walton 2013-01-14 14:26:06 -05:00
commit a1ba0a89f8
31 changed files with 613 additions and 445 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
raw
presentation
test.coffee
test.litcoffee
parser.output
test/fixtures/underscore
test/*.js

View File

@ -68,7 +68,7 @@ task 'install', 'install CoffeeScript into /usr/local (or --prefix)', (options)
task 'build', 'build the CoffeeScript language from source', build = (cb) ->
files = fs.readdirSync 'src'
files = ('src/' + file for file in files when file.match(/\.coffee$/))
files = ('src/' + file for file in files when file.match(/\.(lit)?coffee$/))
run ['-c', '-o', 'lib/coffee-script'].concat(files), cb
@ -119,8 +119,7 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
}(this));
"""
unless process.env.MINIFY is 'false'
{parser, uglify} = require 'uglify-js'
code = uglify.gen_code uglify.ast_squeeze uglify.ast_mangle parser.parse code
{code} = require('uglify-js').minify code, fromString: true
fs.writeFileSync 'extras/coffee-script.js', header + '\n' + code
console.log "built ... running browser tests:"
invoke 'test:browser'
@ -227,11 +226,12 @@ runTests = (CoffeeScript) ->
# Run every test in the `test` folder, recording failures.
files = fs.readdirSync 'test'
for file in files when file.match /\.coffee$/i
for file in files when file.match /\.(lit)?coffee$/i
literate = path.extname(file) is '.litcoffee'
currentFile = filename = path.join 'test', file
code = fs.readFileSync filename
try
CoffeeScript.run code.toString(), {filename}
CoffeeScript.run code.toString(), {filename, literate}
catch error
failures.push {filename, error}
return !failures.length

View File

@ -127,8 +127,8 @@
The golden rule of CoffeeScript is: <i>"It's just JavaScript"</i>. The code
compiles one-to-one into the equivalent JS, and there is
no interpretation at runtime. You can use any existing JavaScript library
seamlessly from CoffeeScript (and vice-versa). The compiled output is
readable and pretty-printed, passes through
seamlessly from CoffeeScript (and vice-versa). The compiled output is
readable and pretty-printed, passes through
<a href="http://www.javascriptlint.com/">JavaScript Lint</a>
without warnings, will work in every JavaScript runtime, and tends
to run as fast or faster than the equivalent handwritten JavaScript.
@ -176,7 +176,7 @@ npm install -g coffee-script</pre>
<p>
(Leave off the <tt>-g</tt> if you don't wish to install globally.)
</p>
<p>
If you'd prefer to install the latest <b>master</b> version of CoffeeScript, you
can clone the CoffeeScript
@ -190,7 +190,7 @@ npm install -g coffee-script</pre>
npm install -g http://github.com/jashkenas/coffee-script/tarball/master</pre>
<p>
Or, if you want to install to <tt>/usr/local</tt>, and don't want to use
Or, if you want to install to <tt>/usr/local</tt>, and don't want to use
npm to manage it, open the <tt>coffee-script</tt> directory and run:
</p>
@ -288,7 +288,7 @@ sudo bin/cake install</pre>
<tr>
<td><code>-b, --bare</code></td>
<td>
Compile the JavaScript without the
Compile the JavaScript without the
<a href="#lexical_scope">top-level function safety wrapper</a>.
</td>
</tr>
@ -318,8 +318,9 @@ Expressions
<td><code>--nodejs</code></td>
<td>
The <tt>node</tt> executable has some useful options you can set,
such as<br /> <tt>--debug</tt>, <tt>--debug-brk</tt> and <tt>--max-stack-size</tt>. Use this
flag to forward options directly to Node.js.
such as<br /> <tt>--debug</tt>, <tt>--debug-brk</tt>, <tt>--max-stack-size</tt>,
and <tt>--expose-gc</tt>. Use this flag to forward options directly to Node.js.
To pass multiple flags, use <tt>--nodejs</tt> multiple times.
</td>
</tr>
</table>
@ -980,11 +981,11 @@ Expressions
portions in CoffeeScript.
</li>
</ul>
<h2>
Screencasts
</h2>
<ul>
<li>
<a href="http://coffeescript.codeschool.com">A Sip of CoffeeScript</a> is a <a href="http://www.codeschool.com">Code School Course</a>
@ -1007,9 +1008,9 @@ Expressions
<h2>
Examples
</h2>
<p>
The <a href="https://github.com/languages/coffeescript">best list of
The <a href="https://github.com/languages/coffeescript">best list of
open-source CoffeeScript examples</a> can be found on GitHub. But just
to throw out few more:
</p>
@ -1115,7 +1116,7 @@ Expressions
<span id="changelog" class="bookmark"></span>
Change Log
</h2>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.3.3...1.4.0">1.4.0</a>
@ -1123,7 +1124,7 @@ Expressions
</b>
<ul>
<li>
The CoffeeScript compiler now strips Microsoft's UTF-8 BOM if it
The CoffeeScript compiler now strips Microsoft's UTF-8 BOM if it
exists, allowing you to compile BOM-borked source files.
</li>
<li>
@ -1131,12 +1132,12 @@ Expressions
and moving from <tt>path.exists</tt> to <tt>fs.exists</tt>.
</li>
<li>
Small tweaks to splat compilation, backticks, slicing, and the
Small tweaks to splat compilation, backticks, slicing, and the
error for duplicate keys in object literals.
</li>
</ul>
</p>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.3.1...1.3.3">1.3.3</a>
@ -1146,7 +1147,7 @@ Expressions
<li>
Due to the new semantics of JavaScript's strict mode, CoffeeScript no
longer guarantees that constructor functions have names in all runtimes.
See <a href="https://github.com/jashkenas/coffee-script/issues/2052">#2052</a>
See <a href="https://github.com/jashkenas/coffee-script/issues/2052">#2052</a>
for discussion.
</li>
<li>
@ -1165,7 +1166,7 @@ Expressions
</li>
</ul>
</p>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.2.0...1.3.1">1.3.1</a>
@ -1173,26 +1174,26 @@ Expressions
</b>
<ul>
<li>
CoffeeScript now enforces all of JavaScript's <b>Strict Mode</b> early syntax
errors at compile time. This includes old-style octal literals,
duplicate property names in object literals, duplicate parameters in
CoffeeScript now enforces all of JavaScript's <b>Strict Mode</b> early syntax
errors at compile time. This includes old-style octal literals,
duplicate property names in object literals, duplicate parameters in
a function definition, deleting naked variables, setting the value of
<tt>eval</tt> or <tt>arguments</tt>, and more.
See a full discussion at
See a full discussion at
<a href="https://github.com/jashkenas/coffee-script/issues/1547">#1547</a>.
</li>
<li>
The REPL now has a handy new multi-line mode for entering large
blocks of code. It's useful when copy-and-pasting examples into the
REPL. Enter multi-line mode with <tt>Ctrl-V</tt>. You may also now
REPL. Enter multi-line mode with <tt>Ctrl-V</tt>. You may also now
pipe input directly into the REPL.
</li>
<li>
CoffeeScript now prints a <tt>Generated by CoffeeScript VERSION</tt>
CoffeeScript now prints a <tt>Generated by CoffeeScript VERSION</tt>
header at the top of each compiled file.
</li>
<li>
Conditional assignment of previously undefined variables
Conditional assignment of previously undefined variables
<tt>a or= b</tt> is now considered a syntax error.
</li>
<li>
@ -1204,18 +1205,18 @@ Expressions
between them.
</li>
<li>
Both endpoints of a slice are now allowed to be omitted for consistency,
Both endpoints of a slice are now allowed to be omitted for consistency,
effectively creating a shallow copy of the list.
</li>
<li>
Additional tweaks and improvments to <tt>coffee --watch</tt> under
Node's "new" file watching API. Watch will now beep by default
Additional tweaks and improvments to <tt>coffee --watch</tt> under
Node's "new" file watching API. Watch will now beep by default
if you introduce a syntax error into a watched script. We also now
ignore hidden directories by default when watching recursively.
</li>
</ul>
</p>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.1.3...1.2.0">1.2.0</a>
@ -1242,7 +1243,7 @@ Expressions
</li>
</ul>
</p>
<p>
<b class="header" style="margin-top: 20px;">
<a href="https://github.com/jashkenas/coffee-script/compare/1.1.2...1.1.3">1.1.3</a>
@ -1254,7 +1255,7 @@ Expressions
out and keep it readable, as you can see in the examples on this page.
</li>
<li>
You can now call <tt>super</tt> in class level methods in class bodies,
You can now call <tt>super</tt> in class level methods in class bodies,
and bound class methods now preserve their correct context.
</li>
<li>
@ -1263,7 +1264,7 @@ Expressions
also supports binary numbers: <tt>0b10 is 2</tt>.
</li>
<li>
The CoffeeScript module has been nested under a subdirectory to make
The CoffeeScript module has been nested under a subdirectory to make
it easier to <tt>require</tt> individual components separately, without
having to use <b>npm</b>. For example, after adding the CoffeeScript
folder to your path: <tt>require('coffee-script/lexer')</tt>
@ -1273,11 +1274,11 @@ Expressions
it to get a shareable permalink for your example script.
</li>
<li>
The <tt>coffee --watch</tt> feature now only works on Node.js 0.6.0
The <tt>coffee --watch</tt> feature now only works on Node.js 0.6.0
and higher, but now also works properly on Windows.
</li>
<li>
Lots of small bug fixes from
Lots of small bug fixes from
<b><a href="https://github.com/michaelficarra">@michaelficarra</a></b>,
<b><a href="https://github.com/geraldalewis">@geraldalewis</a></b>,
<b><a href="https://github.com/satyr">@satyr</a></b>, and

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var CoffeeScript, runScripts;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var CoffeeScript, cakefileDirectory, existsSync, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;

View File

@ -1,39 +1,38 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var Lexer, RESERVED, compile, fs, lexer, parser, path, stripBOM, vm, _ref,
var Lexer, compile, ext, extensions, fs, lexer, loadFile, parser, path, vm, _i, _len,
__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; },
__hasProp = {}.hasOwnProperty;
fs = require('fs');
path = require('path');
_ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED;
Lexer = require('./lexer').Lexer;
parser = require('./parser').parser;
vm = require('vm');
stripBOM = function(content) {
if (content.charCodeAt(0) === 0xFEFF) {
return content.substring(1);
} else {
return content;
}
extensions = ['.coffee', '.litcoffee'];
loadFile = function(module, filename) {
var raw, stripped;
raw = fs.readFileSync(filename, 'utf8');
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
return module._compile(compile(stripped, {
filename: filename
}), filename);
};
if (require.extensions) {
require.extensions['.coffee'] = function(module, filename) {
var content;
content = compile(stripBOM(fs.readFileSync(filename, 'utf8')), {
filename: filename
});
return module._compile(content, filename);
};
for (_i = 0, _len = extensions.length; _i < _len; _i++) {
ext = extensions[_i];
require.extensions[ext] = loadFile;
}
}
exports.VERSION = '1.4.0';
exports.RESERVED = RESERVED;
exports.VERSION = '1.5.0-pre';
exports.helpers = require('./helpers');
@ -44,7 +43,7 @@
}
merge = exports.helpers.merge;
try {
js = (parser.parse(lexer.tokenize(code))).compile(options);
js = (parser.parse(lexer.tokenize(code, options))).compile(options);
if (!options.header) {
return js;
}
@ -71,7 +70,7 @@
};
exports.run = function(code, options) {
var mainModule;
var mainModule, _ref;
if (options == null) {
options = {};
}
@ -79,7 +78,7 @@
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
mainModule.moduleCache && (mainModule.moduleCache = {});
mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename)));
if (path.extname(mainModule.filename) !== '.coffee' || require.extensions) {
if ((_ref = path.extname(mainModule.filename), __indexOf.call(extensions, _ref) < 0) || require.extensions) {
return mainModule._compile(compile(code, options), mainModule.filename);
} else {
return mainModule._compile(code, mainModule.filename);
@ -87,7 +86,7 @@
};
exports["eval"] = function(code, options) {
var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref1, _ref2, _require;
var Module, Script, js, k, o, r, sandbox, v, _j, _len1, _module, _ref, _ref1, _require;
if (options == null) {
options = {};
}
@ -101,10 +100,10 @@
sandbox = options.sandbox;
} else {
sandbox = Script.createContext();
_ref1 = options.sandbox;
for (k in _ref1) {
if (!__hasProp.call(_ref1, k)) continue;
v = _ref1[k];
_ref = options.sandbox;
for (k in _ref) {
if (!__hasProp.call(_ref, k)) continue;
v = _ref[k];
sandbox[k] = v;
}
}
@ -121,9 +120,9 @@
return Module._load(path, _module, true);
};
_module.filename = sandbox.__filename;
_ref2 = Object.getOwnPropertyNames(require);
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
r = _ref2[_i];
_ref1 = Object.getOwnPropertyNames(require);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
r = _ref1[_j];
if (r !== 'paths') {
_require[r] = require[r];
}

View File

@ -1,6 +1,7 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, 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;
var BANNER, CoffeeScript, EventEmitter, SWITCHES, coffee_exts, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, 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,
__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; };
fs = require('fs');
@ -48,6 +49,8 @@
optionParser = null;
coffee_exts = ['.coffee', '.litcoffee'];
exports.run = function() {
var literals, source, _i, _len, _results;
parseOptions();
@ -92,11 +95,12 @@
compilePath = function(source, topLevel, base) {
return fs.stat(source, function(err, stats) {
var _ref1, _ref2;
if (err && err.code !== 'ENOENT') {
throw err;
}
if ((err != null ? err.code : void 0) === 'ENOENT') {
if (topLevel && source.slice(-7) !== '.coffee') {
if (topLevel && (_ref1 = path.extname(source), __indexOf.call(coffee_exts, _ref1) < 0)) {
source = sources[sources.indexOf(source)] = "" + source + ".coffee";
return compilePath(source, topLevel, base);
}
@ -111,7 +115,7 @@
watchDir(source, base);
}
return fs.readdir(source, function(err, files) {
var file, index, _ref1, _ref2;
var file, index, _ref2, _ref3;
if (err && err.code !== 'ENOENT') {
throw err;
}
@ -122,7 +126,7 @@
files = files.filter(function(file) {
return !hidden(file);
});
[].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (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++) {
@ -130,15 +134,15 @@
_results.push(path.join(source, file));
}
return _results;
})())), _ref1;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() {
})())), _ref2;
[].splice.apply(sourceCode, [index, index - index + 1].concat(_ref3 = files.map(function() {
return null;
}))), _ref2;
}))), _ref3;
return files.forEach(function(file) {
return compilePath(path.join(source, file), false, base);
});
});
} else if (topLevel || path.extname(source) === '.coffee') {
} else if (topLevel || (_ref2 = path.extname(source), __indexOf.call(coffee_exts, _ref2) >= 0)) {
if (opts.watch) {
watch(source, base);
}
@ -170,9 +174,9 @@
};
CoffeeScript.emit('compile', task);
if (o.tokens) {
return printTokens(CoffeeScript.tokens(t.input));
return printTokens(CoffeeScript.tokens(t.input, t.options));
} else if (o.nodes) {
return printLine(CoffeeScript.nodes(t.input).toString().trim());
return printLine(CoffeeScript.nodes(t.input, t.options).toString().trim());
} else if (o.run) {
return CoffeeScript.run(t.input, t.options);
} else if (o.join && t.file !== o.join) {
@ -472,8 +476,11 @@
};
compileOptions = function(filename) {
var literate;
literate = path.extname(filename) === '.litcoffee';
return {
filename: filename,
literate: literate,
bare: opts.bare,
header: opts.compile
};

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
@ -575,6 +575,9 @@
}), o('SimpleAssignable COMPOUND_ASSIGN\
INDENT Expression OUTDENT', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR\
Expression', function() {
return new Assign($1, $4, $2);
}), o('SimpleAssignable EXTENDS Expression', function() {
return new Extends($1, $3);
})

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var buildLocationData, extend, flatten, _ref;

View File

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

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-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, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, locationDataToString, starts, _ref, _ref1,
var BOM, 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, LITERATE, 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, locationDataToString, starts, _ref, _ref1,
__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;
@ -16,19 +16,16 @@
if (opts == null) {
opts = {};
}
if (WHITESPACE.test(code)) {
code = "\n" + code;
}
code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
this.code = code;
this.chunkLine = opts.line || 0;
this.chunkColumn = opts.column || 0;
this.literate = opts.literate;
code = this.clean(code);
this.indent = 0;
this.indebt = 0;
this.outdebt = 0;
this.indents = [];
this.ends = [];
this.tokens = [];
this.chunkLine = opts.line || 0;
this.chunkColumn = opts.column || 0;
i = 0;
while (this.chunk = code.slice(i)) {
consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
@ -45,6 +42,35 @@
return (new Rewriter).rewrite(this.tokens);
};
Lexer.prototype.clean = function(code) {
var line, lines, match;
if (code.charCodeAt(0) === BOM) {
code = code.slice(1);
}
if (WHITESPACE.test(code)) {
code = "\n" + code;
}
code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
if (this.literate) {
lines = (function() {
var _i, _len, _ref2, _results;
_ref2 = code.split('\n');
_results = [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
line = _ref2[_i];
if (match = LITERATE.exec(line)) {
_results.push(line.slice(match[0].length));
} else {
_results.push('# ' + line);
}
}
return _results;
})();
code = lines.join('\n');
}
return code;
};
Lexer.prototype.identifierToken = function() {
var colon, colonOffset, forcedIdentifier, id, idLength, input, match, poppedToken, prev, tag, tagToken, _ref2, _ref3, _ref4;
if (!(match = IDENTIFIER.exec(this.chunk))) {
@ -486,7 +512,7 @@
if (HEREDOC_ILLEGAL.test(doc)) {
this.error("block comment cannot contain \"*/\", starting");
}
if (doc.indexOf('\n') <= 0) {
if (doc.indexOf('\n') < 0) {
return doc;
}
} else {
@ -804,6 +830,8 @@
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED;
BOM = 65279;
IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/;
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
@ -814,7 +842,9 @@
WHITESPACE = /^[^\n\S]+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
LITERATE = /^([ ]{4}|\t)/;
CODE = /^[-=]>/;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, last, merge, multident, some, starts, unfoldSoak, utility, _ref, _ref1,
__hasProp = {}.hasOwnProperty,
@ -1937,7 +1937,7 @@
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
obj = _ref2[_i];
if (obj instanceof Assign) {
names.push(obj.value.unwrap().value);
names.push.apply(names, this.names(obj.value.unwrap()));
} else if (obj instanceof Splat) {
names.push(obj.name.unwrap().value);
} else if (obj instanceof Value) {
@ -2904,6 +2904,9 @@
func = new Code([], Block.wrap([expressions]));
args = [];
if ((mentionsArgs = expressions.contains(this.literalArgs)) || expressions.contains(this.literalThis)) {
if (mentionsArgs && expressions.classBody) {
throw SyntaxError("Class bodies shouldn't reference arguments");
}
meth = new Literal(mentionsArgs ? 'apply' : 'call');
args = [new Literal('this')];
if (mentionsArgs) {

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
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,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-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 = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },

View File

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

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.4.0
// Generated by CoffeeScript 1.5.0-pre
(function() {
var BASE64_CHARS, LineMapping, MAX_BASE64_VALUE, VLQ_CONTINUATION_BIT, VLQ_MASK, VLQ_SHIFT, encodeBase64Char;

View File

@ -3,7 +3,7 @@
"description": "Unfancy JavaScript",
"keywords": ["javascript", "language", "coffeescript", "compiler"],
"author": "Jeremy Ashkenas",
"version": "1.4.0",
"version": "1.5.0-pre",
"licenses": [{
"type": "MIT",
"url": "https://raw.github.com/jashkenas/coffee-script/master/LICENSE"
@ -29,7 +29,7 @@
"url": "git://github.com/jashkenas/coffee-script.git"
},
"devDependencies": {
"uglify-js": ">=1.0.0",
"uglify-js": "~2.2",
"jison": ">=0.2.0"
}
}

View File

@ -6,25 +6,27 @@
# If included on a webpage, it will automatically sniff out, compile, and
# execute all scripts present in `text/coffeescript` tags.
fs = require 'fs'
path = require 'path'
{Lexer,RESERVED} = require './lexer'
{parser} = require './parser'
vm = require 'vm'
fs = require 'fs'
path = require 'path'
{Lexer} = require './lexer'
{parser} = require './parser'
vm = require 'vm'
stripBOM = (content) ->
if content.charCodeAt(0) is 0xFEFF then content.substring 1 else content
# The file extensions that are considered to be CoffeeScript.
extensions = ['.coffee', '.litcoffee']
# Load and run a CoffeeScript file for Node, stripping any `BOM`s.
loadFile = (module, filename) ->
raw = fs.readFileSync filename, 'utf8'
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
module._compile compile(stripped, {filename}), filename
if require.extensions
require.extensions['.coffee'] = (module, filename) ->
content = compile stripBOM(fs.readFileSync filename, 'utf8'), {filename}
module._compile content, filename
for ext in extensions
require.extensions[ext] = loadFile
# The current CoffeeScript version number.
exports.VERSION = '1.4.0'
# Words that cannot be used as identifiers in CoffeeScript code
exports.RESERVED = RESERVED
exports.VERSION = '1.5.0-pre'
# Expose helpers for testing.
exports.helpers = require './helpers'
@ -34,7 +36,7 @@ exports.helpers = require './helpers'
exports.compile = compile = (code, options = {}) ->
{merge} = exports.helpers
try
js = (parser.parse lexer.tokenize code).compile options
js = (parser.parse lexer.tokenize(code, options)).compile options
return js unless options.header
catch err
err.message = "In #{options.filename}, #{err.message}" if options.filename
@ -71,7 +73,7 @@ exports.run = (code, options = {}) ->
mainModule.paths = require('module')._nodeModulePaths path.dirname fs.realpathSync options.filename
# Compile.
if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
if (path.extname(mainModule.filename) not in extensions) or require.extensions
mainModule._compile compile(code, options), mainModule.filename
else
mainModule._compile code, mainModule.filename

View File

@ -57,6 +57,7 @@ sourceCode = []
notSources = {}
watchers = {}
optionParser = null
coffee_exts = ['.coffee', '.litcoffee']
# Run `coffee` by parsing passed options and determining what action to take.
# Many flags cause us to divert before compiling anything. Flags passed after
@ -81,13 +82,13 @@ exports.run = ->
compilePath source, yes, path.normalize source
# Compile a path, which could be a script or a directory. If a directory
# is passed, recursively compile all '.coffee' extension source files in it
# and all subdirectories.
# is passed, recursively compile all '.coffee' and '.litcoffee' extension source
# files in it and all subdirectories.
compilePath = (source, topLevel, base) ->
fs.stat source, (err, stats) ->
throw err if err and err.code isnt 'ENOENT'
if err?.code is 'ENOENT'
if topLevel and source[-7..] isnt '.coffee'
if topLevel and path.extname(source) not in coffee_exts
source = sources[sources.indexOf(source)] = "#{source}.coffee"
return compilePath source, topLevel, base
if topLevel
@ -105,7 +106,7 @@ compilePath = (source, topLevel, base) ->
sourceCode[index..index] = files.map -> null
files.forEach (file) ->
compilePath (path.join source, file), no, base
else if topLevel or path.extname(source) is '.coffee'
else if topLevel or path.extname(source) in coffee_exts
watch source, base if opts.watch
fs.readFile source, (err, code) ->
throw err if err and err.code isnt 'ENOENT'
@ -125,8 +126,8 @@ compileScript = (file, input, base) ->
try
t = task = {file, input, options}
CoffeeScript.emit 'compile', task
if o.tokens then printTokens CoffeeScript.tokens t.input
else if o.nodes then printLine CoffeeScript.nodes(t.input).toString().trim()
if o.tokens then printTokens CoffeeScript.tokens t.input, t.options
else if o.nodes then printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
else if o.run then CoffeeScript.run t.input, t.options
else if o.join and t.file isnt o.join
sourceCode[sources.indexOf(t.file)] = t.input
@ -318,7 +319,8 @@ parseOptions = ->
# The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (filename) ->
{filename, bare: opts.bare, header: opts.compile}
literate = path.extname(filename) is '.litcoffee'
{filename, literate, 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

@ -558,6 +558,8 @@ grammar =
Expression', -> new Assign $1, $3, $2
o 'SimpleAssignable COMPOUND_ASSIGN
INDENT Expression OUTDENT', -> new Assign $1, $4, $2
o 'SimpleAssignable COMPOUND_ASSIGN TERMINATOR
Expression', -> new Assign $1, $4, $2
o 'SimpleAssignable EXTENDS Expression', -> new Extends $1, $3
]

View File

@ -32,20 +32,19 @@ exports.Lexer = class Lexer
# Before returning the token stream, run it through the [Rewriter](rewriter.html)
# unless explicitly asked not to.
tokenize: (code, opts = {}) ->
code = "\n#{code}" if WHITESPACE.test code
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
@literate = opts.literate # Are we lexing literate CoffeeScript?
code = @clean code # The stripped, cleaned original source code.
@indent = 0 # The current indentation level.
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
@code = code # The source code.
@chunkLine =
opts.line or 0 # The start line for the current chunk.
opts.line or 0 # The start line for the current @chunk.
@chunkColumn =
opts.column or 0 # The start column of the current chunk.
@indent = 0 # The current indentation level.
@indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@ends = [] # The stack for pairing up tokens.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
opts.column or 0 # The start column of the current @chunk.
# At every position, run through this list of attempted matches,
# short-circuiting if any of them succeed. Their order determines precedence:
@ -74,6 +73,22 @@ exports.Lexer = class Lexer
return @tokens if opts.rewrite is off
(new Rewriter).rewrite @tokens
# Preprocess the code to remove leading and trailing whitespace, carriage
# returns, etc. If we're lexing literate CoffeeScript, strip external Markdown
# by removing all lines that aren't indented by at least four spaces or a tab.
clean: (code) ->
code = code.slice(1) if code.charCodeAt(0) is BOM
code = "\n#{code}" if WHITESPACE.test code
code = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
if @literate
lines = for line in code.split('\n')
if match = LITERATE.exec line
line[match[0].length..]
else
'# ' + line
code = lines.join '\n'
code
# Tokenizers
# ----------
@ -87,6 +102,7 @@ exports.Lexer = class Lexer
return 0 unless match = IDENTIFIER.exec @chunk
[input, id, colon] = match
# Preserve lenght of id for location data
idLength = id.length
poppedToken = undefined
@ -417,7 +433,7 @@ exports.Lexer = class Lexer
if herecomment
if HEREDOC_ILLEGAL.test doc
@error "block comment cannot contain \"*/\", starting"
return doc if doc.indexOf('\n') <= 0
return doc if doc.indexOf('\n') < 0
else
while match = HEREDOC_INDENT.exec doc
attempt = match[1]
@ -596,6 +612,8 @@ exports.Lexer = class Lexer
# -------
# Returns the line and column number from an offset into the current chunk.
#
# `offset` is a number of characters into @chunk.
getLineAndColumnFromChunk: (offset) ->
if offset is 0
return [@chunkLine, @chunkColumn]
@ -724,6 +742,9 @@ 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
# The character code of the nasty Microsoft madness otherwise known as the BOM.
BOM = 65279
# Token matching regexes.
IDENTIFIER = /// ^
( [$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]* )
@ -751,7 +772,9 @@ OPERATOR = /// ^ (
WHITESPACE = /^[^\n\S]+/
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/
LITERATE = /^([ ]{4}|\t)/
CODE = /^[-=]>/

View File

@ -1307,7 +1307,7 @@ exports.Param = class Param extends Base
for obj in name.objects
# * assignments within destructured parameters `{foo:bar}`
if obj instanceof Assign
names.push obj.value.unwrap().value
names.push @names(obj.value.unwrap())...
# * splats within destructured parameters `[xs...]`
else if obj instanceof Splat
names.push obj.name.unwrap().value
@ -1952,6 +1952,8 @@ Closure =
func = new Code [], Block.wrap [expressions]
args = []
if (mentionsArgs = expressions.contains @literalArgs) or expressions.contains @literalThis
if mentionsArgs and expressions.classBody
throw SyntaxError "Class bodies shouldn't reference arguments"
meth = new Literal if mentionsArgs then 'apply' else 'call'
args = [new Literal 'this']
args.push new Literal 'arguments' if mentionsArgs

View File

@ -1,101 +0,0 @@
# The **Scope** class regulates lexical scoping within CoffeeScript. As you
# generate code, you create a tree of scopes in the same shape as the nested
# function bodies. Each scope knows about the variables declared within it,
# and has a reference to its parent enclosing scope. In this way, we know which
# variables are new and need to be declared with `var`, and which are shared
# with the outside.
# Import the helpers we plan to use.
{extend, last} = require './helpers'
exports.Scope = class Scope
# The top-level **Scope** object.
@root: null
# Initialize a scope with its parent, for lookups up the chain,
# as well as a reference to the **Block** node it belongs to, which is
# where it should declare its variables, and a reference to the function that
# it wraps.
constructor: (@parent, @expressions, @method) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@positions = {}
Scope.root = this unless @parent
# Adds a new variable or overrides an existing one.
add: (name, type, immediate) ->
return @parent.add name, type, immediate if @shared and not immediate
if Object::hasOwnProperty.call @positions, name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.push({name, type}) - 1
# When `super` is called, we need to find the name of the current method we're
# in, so that we know how to invoke the same method of the parent class. This
# can get complicated if super is being called from an inner function.
# `namedMethod` will walk up the scope tree until it either finds the first
# function object that has a name filled in, or bottoms out.
namedMethod: ->
return @method if @method.name or !@parent
@parent.namedMethod()
# Look up a variable name in lexical scope, and declare it if it does not
# already exist.
find: (name) ->
return yes if @check name
@add name, 'var'
no
# Reserve a variable name as originating from a function parameter for this
# scope. No `var` required for internal references.
parameter: (name) ->
return if @shared and @parent.check name, yes
@add name, 'param'
# Just check to see if a variable has already been declared, without reserving,
# walks up to the root scope.
check: (name) ->
!!(@type(name) or @parent?.check(name))
# Generate a temporary variable name at the given index.
temporary: (name, index) ->
if name.length > 1
'_' + name + if index > 1 then index - 1 else ''
else
'_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'
# Gets the type of a variable.
type: (name) ->
return v.type for v in @variables when v.name is name
null
# If we need to store an intermediate result, find an available name for a
# compiler-generated variable. `_var`, `_var2`, and so on...
freeVariable: (name, reserve=true) ->
index = 0
index++ while @check((temp = @temporary name, index))
@add temp, 'var', yes if reserve
temp
# Ensure that an assignment is made at the top of this scope
# (or at the top-level scope, if requested).
assign: (name, value) ->
@add name, {value, assigned: yes}, yes
@hasAssignments = yes
# Does this scope have any declared variables?
hasDeclarations: ->
!!@declaredVariables().length
# Return the list of variables first declared in this scope.
declaredVariables: ->
realVars = []
tempVars = []
for v in @variables when v.type is 'var'
(if v.name.charAt(0) is '_' then tempVars else realVars).push v.name
realVars.sort().concat tempVars.sort()
# Return the list of assignments that are supposed to be made at the top
# of this scope.
assignedVariables: ->
"#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned

117
src/scope.litcoffee Normal file
View File

@ -0,0 +1,117 @@
The **Scope** class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with `var`, and which are shared
with external scopes.
Import the helpers we plan to use.
{extend, last} = require './helpers'
exports.Scope = class Scope
The `root` is the top-level **Scope** object for a given file.
@root: null
Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the **Block** node it belongs to, which is
where it should declare its variables, and a reference to the function that
it belongs to.
constructor: (@parent, @expressions, @method) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@positions = {}
Scope.root = this unless @parent
Adds a new variable or overrides an existing one.
add: (name, type, immediate) ->
return @parent.add name, type, immediate if @shared and not immediate
if Object::hasOwnProperty.call @positions, name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.push({name, type}) - 1
When `super` is called, we need to find the name of the current method we're
in, so that we know how to invoke the same method of the parent class. This
can get complicated if super is being called from an inner function.
`namedMethod` will walk up the scope tree until it either finds the first
function object that has a name filled in, or bottoms out.
namedMethod: ->
return @method if @method.name or !@parent
@parent.namedMethod()
Look up a variable name in lexical scope, and declare it if it does not
already exist.
find: (name) ->
return yes if @check name
@add name, 'var'
no
Reserve a variable name as originating from a function parameter for this
scope. No `var` required for internal references.
parameter: (name) ->
return if @shared and @parent.check name, yes
@add name, 'param'
Just check to see if a variable has already been declared, without reserving,
walks up to the root scope.
check: (name) ->
!!(@type(name) or @parent?.check(name))
Generate a temporary variable name at the given index.
temporary: (name, index) ->
if name.length > 1
'_' + name + if index > 1 then index - 1 else ''
else
'_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'
Gets the type of a variable.
type: (name) ->
return v.type for v in @variables when v.name is name
null
If we need to store an intermediate result, find an available name for a
compiler-generated variable. `_var`, `_var2`, and so on...
freeVariable: (name, reserve=true) ->
index = 0
index++ while @check((temp = @temporary name, index))
@add temp, 'var', yes if reserve
temp
Ensure that an assignment is made at the top of this scope
(or at the top-level scope, if requested).
assign: (name, value) ->
@add name, {value, assigned: yes}, yes
@hasAssignments = yes
Does this scope have any declared variables?
hasDeclarations: ->
!!@declaredVariables().length
Return the list of variables first declared in this scope.
declaredVariables: ->
realVars = []
tempVars = []
for v in @variables when v.type is 'var'
(if v.name.charAt(0) is '_' then tempVars else realVars).push v.name
realVars.sort().concat tempVars.sort()
Return the list of assignments that are supposed to be made at the top
of this scope.
assignedVariables: ->
"#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned

View File

@ -365,3 +365,13 @@ test '#2213: invocations within destructured parameters', ->
throws -> CoffeeScript.compile '({a()})->'
throws -> CoffeeScript.compile '({a:b()})->'
throws -> CoffeeScript.compile '({a:b.c()})->'
test '#2532: compound assignment with terminator', ->
doesNotThrow -> CoffeeScript.compile """
a = "hello"
a +=
"
world
!
"
"""

View File

@ -676,3 +676,7 @@ test "#2052: classes should work in strict mode", ->
class A
catch e
ok no
test "#2630: class bodies can't reference arguments", ->
throws ->
CoffeeScript.compile('class Test then arguments')

View File

@ -199,10 +199,17 @@ test "#2258: allow whitespace-style parameter lists in function definitions", ->
a, b, c
) -> c
eq func(1, 2, 3), 3
func = (
a
b
c
) -> b
eq func(1, 2, 3), 2
eq func(1, 2, 3), 2
test "#2621: fancy destructuring in parameter lists", ->
func = ({ prop1: { key1 }, prop2: { key2, key3: [a, b, c] } }) ->
eq(key2, 'key2')
eq(a, 'a')
func({prop1: {key1: 'key1'}, prop2: {key2: 'key2', key3: ['a', 'b', 'c']}})

48
test/literate.litcoffee Normal file
View File

@ -0,0 +1,48 @@
Literate CoffeeScript Test
--------------------------
comment comment
test "basic literate CoffeeScript parsing", ->
ok yes
now with a...
test "broken up indentation", ->
... broken up ...
do ->
... nested block.
ok yes
Code in `backticks is not parsed` and...
test "comments in indented blocks work", ->
do ->
do ->
# Regular comment.
###
Block comment.
###
ok yes
Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:
* I
* Am
* A
* List
Tabs work too:
test "tabbed code", ->
ok yes

View File

@ -28,6 +28,12 @@ test "operators should respect new lines as spaced", ->
test "multiple operators should space themselves", ->
eq (+ +1), (- -1)
test "compound operators on successive lines", ->
a = 1
a +=
1
eq a, 2
test "bitwise operators", ->
eq 2, (10 & 3)
eq 11, (10 | 3)
@ -275,16 +281,16 @@ test "#2155 ... conditional assignment to a closure", ->
func = -> x ?= (-> if true then 'hi')
func()
eq x(), 'hi'
test "#2197: Existential existential double trouble", ->
counter = 0
func = -> counter++
func()? ? 100
eq counter, 1
test "#2567: Optimization of negated existential produces correct result", ->
a = 1
ok !(!a?)
ok !b?