mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
merging jcoglan's splat declaration patch.
This commit is contained in:
commit
963adb5230
22 changed files with 491 additions and 220 deletions
7
Cakefile
7
Cakefile
|
@ -94,15 +94,16 @@ task 'build:browser', 'rebuild the merged script for inclusion in the browser',
|
||||||
#{fs.readFileSync "lib/#{name}.js"}
|
#{fs.readFileSync "lib/#{name}.js"}
|
||||||
};
|
};
|
||||||
"""
|
"""
|
||||||
{parser, uglify} = require 'uglify-js'
|
code = """
|
||||||
ast = parser.parse """
|
|
||||||
this.CoffeeScript = function() {
|
this.CoffeeScript = function() {
|
||||||
function require(path){ return require[path]; }
|
function require(path){ return require[path]; }
|
||||||
#{code}
|
#{code}
|
||||||
return require['./coffee-script']
|
return require['./coffee-script']
|
||||||
}()
|
}()
|
||||||
"""
|
"""
|
||||||
code = uglify.gen_code uglify.ast_squeeze uglify.ast_mangle ast, extra: yes
|
unless process.env.MINIFY is 'false'
|
||||||
|
{parser, uglify} = require 'uglify-js'
|
||||||
|
code = uglify.gen_code uglify.ast_squeeze uglify.ast_mangle parser.parse code
|
||||||
fs.writeFileSync 'extras/coffee-script.js', header + '\n' + code
|
fs.writeFileSync 'extras/coffee-script.js', header + '\n' + code
|
||||||
console.log "built ... running browser tests:"
|
console.log "built ... running browser tests:"
|
||||||
invoke 'test:browser'
|
invoke 'test:browser'
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
return compile(content);
|
return compile(content);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.VERSION = '1.0.2-pre';
|
exports.VERSION = '1.1.0-pre';
|
||||||
exports.RESERVED = RESERVED;
|
exports.RESERVED = RESERVED;
|
||||||
exports.helpers = require('./helpers');
|
exports.helpers = require('./helpers');
|
||||||
exports.compile = compile = function(code, options) {
|
exports.compile = compile = function(code, options) {
|
||||||
|
|
|
@ -196,7 +196,7 @@
|
||||||
if (err) {
|
if (err) {
|
||||||
return printLine(err.message);
|
return printLine(err.message);
|
||||||
} else if (opts.compile && opts.watch) {
|
} else if (opts.compile && opts.watch) {
|
||||||
return console.log("" + ((new Date).toTimeString()) + " - compiled " + source);
|
return console.log("" + ((new Date).toLocaleTimeString()) + " - compiled " + source);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -169,13 +169,11 @@
|
||||||
return new Access($2, 'proto');
|
return new Access($2, 'proto');
|
||||||
}), o('::', function() {
|
}), o('::', function() {
|
||||||
return new Access(new Literal('prototype'));
|
return new Access(new Literal('prototype'));
|
||||||
}), o('Index'), o('Slice', function() {
|
}), o('Index')
|
||||||
return new Slice($1);
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
Index: [
|
Index: [
|
||||||
o('INDEX_START Expression INDEX_END', function() {
|
o('INDEX_START IndexValue INDEX_END', function() {
|
||||||
return new Index($2);
|
return $2;
|
||||||
}), o('INDEX_SOAK Index', function() {
|
}), o('INDEX_SOAK Index', function() {
|
||||||
return extend($2, {
|
return extend($2, {
|
||||||
soak: true
|
soak: true
|
||||||
|
@ -186,6 +184,13 @@
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
IndexValue: [
|
||||||
|
o('Expression', function() {
|
||||||
|
return new Index($1);
|
||||||
|
}), o('Slice', function() {
|
||||||
|
return new Slice($1);
|
||||||
|
})
|
||||||
|
],
|
||||||
Object: [
|
Object: [
|
||||||
o('{ AssignList OptComma }', function() {
|
o('{ AssignList OptComma }', function() {
|
||||||
return new Obj($2, $1.generated);
|
return new Obj($2, $1.generated);
|
||||||
|
@ -280,12 +285,12 @@
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
Slice: [
|
Slice: [
|
||||||
o('INDEX_START Expression RangeDots Expression INDEX_END', function() {
|
o('Expression RangeDots Expression', function() {
|
||||||
return new Range($2, $4, $3);
|
return new Range($1, $3, $2);
|
||||||
}), o('INDEX_START Expression RangeDots INDEX_END', function() {
|
}), o('Expression RangeDots', function() {
|
||||||
return new Range($2, null, $3);
|
return new Range($1, null, $2);
|
||||||
}), o('INDEX_START RangeDots Expression INDEX_END', function() {
|
}), o('RangeDots Expression', function() {
|
||||||
return new Range(null, $3, $2);
|
return new Range(null, $2, $1);
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
ArgList: [
|
ArgList: [
|
||||||
|
|
23
lib/lexer.js
23
lib/lexer.js
|
@ -1,5 +1,5 @@
|
||||||
(function() {
|
(function() {
|
||||||
var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, last, op, starts, _ref;
|
var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, last, op, starts, _ref;
|
||||||
var __indexOf = Array.prototype.indexOf || function(item) {
|
var __indexOf = Array.prototype.indexOf || function(item) {
|
||||||
for (var i = 0, l = this.length; i < l; i++) {
|
for (var i = 0, l = this.length; i < l; i++) {
|
||||||
if (this[i] === item) return i;
|
if (this[i] === item) return i;
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
this.token('OWN', id);
|
this.token('OWN', id);
|
||||||
return id.length;
|
return id.length;
|
||||||
}
|
}
|
||||||
forcedIdentifier = colon || (prev = last(this.tokens)) && !prev.spaced && ((_ref = prev[0]) === '.' || _ref === '?.' || _ref === '@' || _ref === '::');
|
forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref = prev[0]) === '.' || _ref === '?.' || _ref === '::') || !prev.spaced && prev[0] === '@');
|
||||||
tag = 'IDENTIFIER';
|
tag = 'IDENTIFIER';
|
||||||
if (__indexOf.call(JS_KEYWORDS, id) >= 0 || !forcedIdentifier && __indexOf.call(COFFEE_KEYWORDS, id) >= 0) {
|
if (__indexOf.call(JS_KEYWORDS, id) >= 0 || !forcedIdentifier && __indexOf.call(COFFEE_KEYWORDS, id) >= 0) {
|
||||||
tag = id.toUpperCase();
|
tag = id.toUpperCase();
|
||||||
|
@ -175,7 +175,6 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
comment = match[0], here = match[1];
|
comment = match[0], here = match[1];
|
||||||
this.line += count(comment, '\n');
|
|
||||||
if (here) {
|
if (here) {
|
||||||
this.token('HERECOMMENT', this.sanitizeHeredoc(here, {
|
this.token('HERECOMMENT', this.sanitizeHeredoc(here, {
|
||||||
herecomment: true,
|
herecomment: true,
|
||||||
|
@ -183,6 +182,7 @@
|
||||||
}));
|
}));
|
||||||
this.token('TERMINATOR', '\n');
|
this.token('TERMINATOR', '\n');
|
||||||
}
|
}
|
||||||
|
this.line += count(comment, '\n');
|
||||||
return comment.length;
|
return comment.length;
|
||||||
};
|
};
|
||||||
Lexer.prototype.jsToken = function() {
|
Lexer.prototype.jsToken = function() {
|
||||||
|
@ -398,10 +398,14 @@
|
||||||
Lexer.prototype.sanitizeHeredoc = function(doc, options) {
|
Lexer.prototype.sanitizeHeredoc = function(doc, options) {
|
||||||
var attempt, herecomment, indent, match, _ref;
|
var attempt, herecomment, indent, match, _ref;
|
||||||
indent = options.indent, herecomment = options.herecomment;
|
indent = options.indent, herecomment = options.herecomment;
|
||||||
if (herecomment && 0 > doc.indexOf('\n')) {
|
if (herecomment) {
|
||||||
return doc;
|
if (HEREDOC_ILLEGAL.test(doc)) {
|
||||||
}
|
throw new Error("block comment cannot contain \"*/\", starting on line " + (this.line + 1));
|
||||||
if (!herecomment) {
|
}
|
||||||
|
if (doc.indexOf('\n') <= 0) {
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
while (match = HEREDOC_INDENT.exec(doc)) {
|
while (match = HEREDOC_INDENT.exec(doc)) {
|
||||||
attempt = match[1];
|
attempt = match[1];
|
||||||
if (indent === null || (0 < (_ref = attempt.length) && _ref < indent.length)) {
|
if (indent === null || (0 < (_ref = attempt.length) && _ref < indent.length)) {
|
||||||
|
@ -435,7 +439,7 @@
|
||||||
case 'CALL_START':
|
case 'CALL_START':
|
||||||
if (stack.length) {
|
if (stack.length) {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
} else {
|
} else if (tok[0] === '(') {
|
||||||
tok[0] = 'PARAM_START';
|
tok[0] = 'PARAM_START';
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -618,8 +622,9 @@
|
||||||
HEREGEX_OMIT = /\s+(?:#.*)?/g;
|
HEREGEX_OMIT = /\s+(?:#.*)?/g;
|
||||||
MULTILINER = /\n/g;
|
MULTILINER = /\n/g;
|
||||||
HEREDOC_INDENT = /\n+([^\n\S]*)/g;
|
HEREDOC_INDENT = /\n+([^\n\S]*)/g;
|
||||||
|
HEREDOC_ILLEGAL = /\*\//;
|
||||||
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/;
|
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/;
|
||||||
LINE_CONTINUER = /^\s*(?:,|\??\.(?!\.)|::)/;
|
LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/;
|
||||||
TRAILING_SPACES = /\s+$/;
|
TRAILING_SPACES = /\s+$/;
|
||||||
NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/;
|
NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/;
|
||||||
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
|
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
|
||||||
|
|
78
lib/nodes.js
78
lib/nodes.js
|
@ -41,7 +41,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Base.prototype.compileClosure = function(o) {
|
Base.prototype.compileClosure = function(o) {
|
||||||
if (this.jumps()) {
|
if (this.jumps() || this instanceof Throw) {
|
||||||
throw SyntaxError('cannot use a pure statement in an expression.');
|
throw SyntaxError('cannot use a pure statement in an expression.');
|
||||||
}
|
}
|
||||||
o.sharedScope = true;
|
o.sharedScope = true;
|
||||||
|
@ -630,6 +630,32 @@
|
||||||
}
|
}
|
||||||
return ifn;
|
return ifn;
|
||||||
};
|
};
|
||||||
|
Call.prototype.filterImplicitObjects = function(list) {
|
||||||
|
var node, nodes, obj, prop, properties, _i, _j, _len, _len2, _ref;
|
||||||
|
nodes = [];
|
||||||
|
for (_i = 0, _len = list.length; _i < _len; _i++) {
|
||||||
|
node = list[_i];
|
||||||
|
if (!((typeof node.isObject == "function" ? node.isObject() : void 0) && node.base.generated)) {
|
||||||
|
nodes.push(node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj = null;
|
||||||
|
_ref = node.base.properties;
|
||||||
|
for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) {
|
||||||
|
prop = _ref[_j];
|
||||||
|
if (prop instanceof Assign) {
|
||||||
|
if (!obj) {
|
||||||
|
nodes.push(obj = new Obj(properties = [], true));
|
||||||
|
}
|
||||||
|
properties.push(prop);
|
||||||
|
} else {
|
||||||
|
nodes.push(prop);
|
||||||
|
obj = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
};
|
||||||
Call.prototype.compileNode = function(o) {
|
Call.prototype.compileNode = function(o) {
|
||||||
var arg, args, code, _ref;
|
var arg, args, code, _ref;
|
||||||
if ((_ref = this.variable) != null) {
|
if ((_ref = this.variable) != null) {
|
||||||
|
@ -638,16 +664,16 @@
|
||||||
if (code = Splat.compileSplattedArray(o, this.args, true)) {
|
if (code = Splat.compileSplattedArray(o, this.args, true)) {
|
||||||
return this.compileSplat(o, code);
|
return this.compileSplat(o, code);
|
||||||
}
|
}
|
||||||
|
args = this.filterImplicitObjects(this.args);
|
||||||
args = ((function() {
|
args = ((function() {
|
||||||
var _i, _len, _ref, _results;
|
var _i, _len, _results;
|
||||||
_ref = this.args;
|
|
||||||
_results = [];
|
_results = [];
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = args.length; _i < _len; _i++) {
|
||||||
arg = _ref[_i];
|
arg = args[_i];
|
||||||
_results.push(arg.compile(o, LEVEL_LIST));
|
_results.push(arg.compile(o, LEVEL_LIST));
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
}).call(this)).join(', ');
|
})()).join(', ');
|
||||||
if (this.isSuper) {
|
if (this.isSuper) {
|
||||||
return this.superReference(o) + (".call(this" + (args && ', ' + args) + ")");
|
return this.superReference(o) + (".call(this" + (args && ', ' + args) + ")");
|
||||||
} else {
|
} else {
|
||||||
|
@ -842,7 +868,7 @@
|
||||||
}
|
}
|
||||||
Obj.prototype.children = ['properties'];
|
Obj.prototype.children = ['properties'];
|
||||||
Obj.prototype.compileNode = function(o) {
|
Obj.prototype.compileNode = function(o) {
|
||||||
var i, idt, indent, join, lastNoncom, obj, prop, props;
|
var i, idt, indent, join, lastNoncom, node, obj, prop, props, _i, _len;
|
||||||
props = this.properties;
|
props = this.properties;
|
||||||
if (!props.length) {
|
if (!props.length) {
|
||||||
if (this.front) {
|
if (this.front) {
|
||||||
|
@ -851,6 +877,14 @@
|
||||||
return '{}';
|
return '{}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.generated) {
|
||||||
|
for (_i = 0, _len = props.length; _i < _len; _i++) {
|
||||||
|
node = props[_i];
|
||||||
|
if (node instanceof Value) {
|
||||||
|
throw new Error('cannot have an implicit value in an implicit object');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
idt = o.indent += TAB;
|
idt = o.indent += TAB;
|
||||||
lastNoncom = this.lastNonComment(this.properties);
|
lastNoncom = this.lastNonComment(this.properties);
|
||||||
props = (function() {
|
props = (function() {
|
||||||
|
@ -900,25 +934,26 @@
|
||||||
this.objects = objs || [];
|
this.objects = objs || [];
|
||||||
}
|
}
|
||||||
Arr.prototype.children = ['objects'];
|
Arr.prototype.children = ['objects'];
|
||||||
|
Arr.prototype.filterImplicitObjects = Call.prototype.filterImplicitObjects;
|
||||||
Arr.prototype.compileNode = function(o) {
|
Arr.prototype.compileNode = function(o) {
|
||||||
var code, obj;
|
var code, obj, objs;
|
||||||
if (!this.objects.length) {
|
if (!this.objects.length) {
|
||||||
return '[]';
|
return '[]';
|
||||||
}
|
}
|
||||||
o.indent += TAB;
|
o.indent += TAB;
|
||||||
if (code = Splat.compileSplattedArray(o, this.objects)) {
|
objs = this.filterImplicitObjects(this.objects);
|
||||||
|
if (code = Splat.compileSplattedArray(o, objs)) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
code = ((function() {
|
code = ((function() {
|
||||||
var _i, _len, _ref, _results;
|
var _i, _len, _results;
|
||||||
_ref = this.objects;
|
|
||||||
_results = [];
|
_results = [];
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = objs.length; _i < _len; _i++) {
|
||||||
obj = _ref[_i];
|
obj = objs[_i];
|
||||||
_results.push(obj.compile(o, LEVEL_LIST));
|
_results.push(obj.compile(o, LEVEL_LIST));
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
}).call(this)).join(', ');
|
})()).join(', ');
|
||||||
if (code.indexOf('\n') >= 0) {
|
if (code.indexOf('\n') >= 0) {
|
||||||
return "[\n" + o.indent + code + "\n" + this.tab + "]";
|
return "[\n" + o.indent + code + "\n" + this.tab + "]";
|
||||||
} else {
|
} else {
|
||||||
|
@ -1280,6 +1315,9 @@
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
param = _ref[_i];
|
param = _ref[_i];
|
||||||
if (param.splat) {
|
if (param.splat) {
|
||||||
|
if (param.name.value) {
|
||||||
|
o.scope.add(param.name.value, 'var');
|
||||||
|
}
|
||||||
splats = new Assign(new Value(new Arr((function() {
|
splats = new Assign(new Value(new Arr((function() {
|
||||||
var _i, _len, _ref, _results;
|
var _i, _len, _ref, _results;
|
||||||
_ref = this.params;
|
_ref = this.params;
|
||||||
|
@ -1517,17 +1555,20 @@
|
||||||
var CONVERSIONS, INVERSIONS;
|
var CONVERSIONS, INVERSIONS;
|
||||||
__extends(Op, Base);
|
__extends(Op, Base);
|
||||||
function Op(op, first, second, flip) {
|
function Op(op, first, second, flip) {
|
||||||
|
var call;
|
||||||
if (op === 'in') {
|
if (op === 'in') {
|
||||||
return new In(first, second);
|
return new In(first, second);
|
||||||
}
|
}
|
||||||
if (op === 'do') {
|
if (op === 'do') {
|
||||||
return new Call(first, first.params || []);
|
call = new Call(first, first.params || []);
|
||||||
|
call["do"] = true;
|
||||||
|
return call;
|
||||||
}
|
}
|
||||||
if (op === 'new') {
|
if (op === 'new') {
|
||||||
if (first instanceof Call) {
|
if (first instanceof Call && !first["do"]) {
|
||||||
return first.newInstance();
|
return first.newInstance();
|
||||||
}
|
}
|
||||||
if (first instanceof Code && first.bound) {
|
if (first instanceof Code && first.bound || first["do"]) {
|
||||||
first = new Parens(first);
|
first = new Parens(first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1635,6 +1676,9 @@
|
||||||
if ((op === 'new' || op === 'typeof' || op === 'delete') || (op === '+' || op === '-') && this.first instanceof Op && this.first.operator === op) {
|
if ((op === 'new' || op === 'typeof' || op === 'delete') || (op === '+' || op === '-') && this.first instanceof Op && this.first.operator === op) {
|
||||||
parts.push(' ');
|
parts.push(' ');
|
||||||
}
|
}
|
||||||
|
if (op === 'new' && this.first.isStatement(o)) {
|
||||||
|
this.first = new Parens(this.first);
|
||||||
|
}
|
||||||
parts.push(this.first.compile(o, LEVEL_OP));
|
parts.push(this.first.compile(o, LEVEL_OP));
|
||||||
if (this.flip) {
|
if (this.flip) {
|
||||||
parts.reverse();
|
parts.reverse();
|
||||||
|
|
242
lib/parser.js
242
lib/parser.js
File diff suppressed because one or more lines are too long
70
lib/repl.js
70
lib/repl.js
|
@ -1,22 +1,24 @@
|
||||||
(function() {
|
(function() {
|
||||||
var CoffeeScript, error, helpers, readline, repl, run, stdin, stdout;
|
var ACCESSOR, CoffeeScript, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, error, getCompletions, getPropertyNames, readline, repl, run, stdin, stdout;
|
||||||
|
var __hasProp = Object.prototype.hasOwnProperty;
|
||||||
CoffeeScript = require('./coffee-script');
|
CoffeeScript = require('./coffee-script');
|
||||||
helpers = require('./helpers');
|
|
||||||
readline = require('readline');
|
readline = require('readline');
|
||||||
|
Script = process.binding('evals').Script;
|
||||||
stdin = process.openStdin();
|
stdin = process.openStdin();
|
||||||
stdout = process.stdout;
|
stdout = process.stdout;
|
||||||
error = function(err) {
|
error = function(err) {
|
||||||
return stdout.write((err.stack || err.toString()) + '\n\n');
|
return stdout.write((err.stack || err.toString()) + '\n\n');
|
||||||
};
|
};
|
||||||
helpers.extend(global, {
|
backlog = '';
|
||||||
quit: function() {
|
|
||||||
return process.exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
run = function(buffer) {
|
run = function(buffer) {
|
||||||
var val;
|
var code, val;
|
||||||
|
code = backlog += '\n' + buffer.toString();
|
||||||
|
if (code[code.length - 1] === '\\') {
|
||||||
|
return backlog = backlog.slice(0, backlog.length - 1);
|
||||||
|
}
|
||||||
|
backlog = '';
|
||||||
try {
|
try {
|
||||||
val = CoffeeScript.eval(buffer.toString(), {
|
val = CoffeeScript.eval(code, {
|
||||||
bare: true,
|
bare: true,
|
||||||
globals: true,
|
globals: true,
|
||||||
filename: 'repl'
|
filename: 'repl'
|
||||||
|
@ -29,14 +31,60 @@
|
||||||
}
|
}
|
||||||
return repl.prompt();
|
return repl.prompt();
|
||||||
};
|
};
|
||||||
|
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
|
||||||
|
SIMPLEVAR = /\s*(\w*)$/i;
|
||||||
|
autocomplete = function(text) {
|
||||||
|
return completeAttribute(text) || completeVariable(text) || [[], text];
|
||||||
|
};
|
||||||
|
completeAttribute = function(text) {
|
||||||
|
var all, completions, match, obj, prefix, val;
|
||||||
|
if (match = text.match(ACCESSOR)) {
|
||||||
|
all = match[0], obj = match[1], prefix = match[2];
|
||||||
|
try {
|
||||||
|
val = Script.runInThisContext(obj);
|
||||||
|
} catch (error) {
|
||||||
|
return [[], text];
|
||||||
|
}
|
||||||
|
completions = getCompletions(prefix, getPropertyNames(val));
|
||||||
|
return [completions, prefix];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
completeVariable = function(text) {
|
||||||
|
var completions, free, scope, _ref;
|
||||||
|
if (free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0) {
|
||||||
|
scope = Script.runInThisContext('this');
|
||||||
|
completions = getCompletions(free, CoffeeScript.RESERVED.concat(getPropertyNames(scope)));
|
||||||
|
return [completions, free];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getCompletions = function(prefix, candidates) {
|
||||||
|
var el, _i, _len, _results;
|
||||||
|
_results = [];
|
||||||
|
for (_i = 0, _len = candidates.length; _i < _len; _i++) {
|
||||||
|
el = candidates[_i];
|
||||||
|
if (el.indexOf(prefix) === 0) {
|
||||||
|
_results.push(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _results;
|
||||||
|
};
|
||||||
|
getPropertyNames = function(obj) {
|
||||||
|
var name, _results;
|
||||||
|
_results = [];
|
||||||
|
for (name in obj) {
|
||||||
|
if (!__hasProp.call(obj, name)) continue;
|
||||||
|
_results.push(name);
|
||||||
|
}
|
||||||
|
return _results;
|
||||||
|
};
|
||||||
process.on('uncaughtException', error);
|
process.on('uncaughtException', error);
|
||||||
if (readline.createInterface.length < 3) {
|
if (readline.createInterface.length < 3) {
|
||||||
repl = readline.createInterface(stdin);
|
repl = readline.createInterface(stdin, autocomplete);
|
||||||
stdin.on('data', function(buffer) {
|
stdin.on('data', function(buffer) {
|
||||||
return repl.write(buffer);
|
return repl.write(buffer);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
repl = readline.createInterface(stdin, stdout);
|
repl = readline.createInterface(stdin, stdout, autocomplete);
|
||||||
}
|
}
|
||||||
repl.setPrompt('coffee> ');
|
repl.setPrompt('coffee> ');
|
||||||
repl.on('close', function() {
|
repl.on('close', function() {
|
||||||
|
|
45
package.json
45
package.json
|
@ -1,22 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "coffee-script",
|
"name": "coffee-script",
|
||||||
"description": "Unfancy JavaScript",
|
"description": "Unfancy JavaScript",
|
||||||
"keywords": ["javascript", "language", "coffeescript", "compiler"],
|
"keywords": ["javascript", "language", "coffeescript", "compiler"],
|
||||||
"author": "Jeremy Ashkenas",
|
"author": "Jeremy Ashkenas",
|
||||||
"version": "1.0.2-pre",
|
"version": "1.1.0-pre",
|
||||||
"licenses": [{
|
"licenses": [{
|
||||||
"type": "MIT",
|
"type": "MIT",
|
||||||
"url": "http://github.com/jashkenas/coffee-script/raw/master/LICENSE"
|
"url": "http://github.com/jashkenas/coffee-script/raw/master/LICENSE"
|
||||||
}],
|
}],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.2.5"
|
"node": ">=0.2.5"
|
||||||
},
|
},
|
||||||
"directories" : {
|
"directories" : {
|
||||||
"lib" : "./lib"
|
"lib" : "./lib"
|
||||||
},
|
},
|
||||||
"main" : "./lib/coffee-script",
|
"main" : "./lib/coffee-script",
|
||||||
"bin": {
|
"bin": {
|
||||||
"coffee": "./bin/coffee",
|
"coffee": "./bin/coffee",
|
||||||
"cake": "./bin/cake"
|
"cake": "./bin/cake"
|
||||||
}
|
},
|
||||||
|
"homepage": "http://coffeescript.org",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/jashkenas/coffee-script.git"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ else if require.registerExtension
|
||||||
require.registerExtension '.coffee', (content) -> compile content
|
require.registerExtension '.coffee', (content) -> compile content
|
||||||
|
|
||||||
# The current CoffeeScript version number.
|
# The current CoffeeScript version number.
|
||||||
exports.VERSION = '1.0.2-pre'
|
exports.VERSION = '1.1.0-pre'
|
||||||
|
|
||||||
# Words that cannot be used as identifiers in CoffeeScript code
|
# Words that cannot be used as identifiers in CoffeeScript code
|
||||||
exports.RESERVED = RESERVED
|
exports.RESERVED = RESERVED
|
||||||
|
|
|
@ -165,7 +165,7 @@ writeJs = (source, js, base) ->
|
||||||
if err
|
if err
|
||||||
printLine err.message
|
printLine err.message
|
||||||
else if opts.compile and opts.watch
|
else if opts.compile and opts.watch
|
||||||
console.log "#{(new Date).toTimeString()} - compiled #{source}"
|
console.log "#{(new Date).toLocaleTimeString()} - compiled #{source}"
|
||||||
path.exists dir, (exists) ->
|
path.exists dir, (exists) ->
|
||||||
if exists then compile() else exec "mkdir -p #{dir}", compile
|
if exists then compile() else exec "mkdir -p #{dir}", compile
|
||||||
|
|
||||||
|
|
|
@ -246,15 +246,19 @@ grammar =
|
||||||
o ':: Identifier', -> new Access $2, 'proto'
|
o ':: Identifier', -> new Access $2, 'proto'
|
||||||
o '::', -> new Access new Literal 'prototype'
|
o '::', -> new Access new Literal 'prototype'
|
||||||
o 'Index'
|
o 'Index'
|
||||||
o 'Slice', -> new Slice $1
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Indexing into an object or array using bracket notation.
|
# Indexing into an object or array using bracket notation.
|
||||||
Index: [
|
Index: [
|
||||||
o 'INDEX_START Expression INDEX_END', -> new Index $2
|
o 'INDEX_START IndexValue INDEX_END', -> $2
|
||||||
o 'INDEX_SOAK Index', -> extend $2, soak : yes
|
o 'INDEX_SOAK Index', -> extend $2, soak : yes
|
||||||
o 'INDEX_PROTO Index', -> extend $2, proto: yes
|
o 'INDEX_PROTO Index', -> extend $2, proto: yes
|
||||||
]
|
]
|
||||||
|
|
||||||
|
IndexValue: [
|
||||||
|
o 'Expression', -> new Index $1
|
||||||
|
o 'Slice', -> new Slice $1
|
||||||
|
]
|
||||||
|
|
||||||
# In CoffeeScript, an object literal is simply a list of assignments.
|
# In CoffeeScript, an object literal is simply a list of assignments.
|
||||||
Object: [
|
Object: [
|
||||||
|
@ -334,9 +338,9 @@ grammar =
|
||||||
|
|
||||||
# Array slice literals.
|
# Array slice literals.
|
||||||
Slice: [
|
Slice: [
|
||||||
o 'INDEX_START Expression RangeDots Expression INDEX_END', -> new Range $2, $4, $3
|
o 'Expression RangeDots Expression', -> new Range $1, $3, $2
|
||||||
o 'INDEX_START Expression RangeDots INDEX_END', -> new Range $2, null, $3
|
o 'Expression RangeDots', -> new Range $1, null, $2
|
||||||
o 'INDEX_START RangeDots Expression INDEX_END', -> new Range null, $3, $2
|
o 'RangeDots Expression', -> new Range null, $2, $1
|
||||||
]
|
]
|
||||||
|
|
||||||
# The **ArgList** is both the list of objects passed into a function call,
|
# The **ArgList** is both the list of objects passed into a function call,
|
||||||
|
|
|
@ -80,7 +80,8 @@ exports.Lexer = class Lexer
|
||||||
@token 'OWN', id
|
@token 'OWN', id
|
||||||
return id.length
|
return id.length
|
||||||
forcedIdentifier = colon or
|
forcedIdentifier = colon or
|
||||||
(prev = last @tokens) and not prev.spaced and prev[0] in ['.', '?.', '@', '::']
|
(prev = last @tokens) and (prev[0] in ['.', '?.', '::'] or
|
||||||
|
not prev.spaced and prev[0] is '@')
|
||||||
tag = 'IDENTIFIER'
|
tag = 'IDENTIFIER'
|
||||||
|
|
||||||
if id in JS_KEYWORDS or
|
if id in JS_KEYWORDS or
|
||||||
|
@ -170,11 +171,11 @@ exports.Lexer = class Lexer
|
||||||
commentToken: ->
|
commentToken: ->
|
||||||
return 0 unless match = @chunk.match COMMENT
|
return 0 unless match = @chunk.match COMMENT
|
||||||
[comment, here] = match
|
[comment, here] = match
|
||||||
@line += count comment, '\n'
|
|
||||||
if here
|
if here
|
||||||
@token 'HERECOMMENT', @sanitizeHeredoc here,
|
@token 'HERECOMMENT', @sanitizeHeredoc here,
|
||||||
herecomment: true, indent: Array(@indent + 1).join(' ')
|
herecomment: true, indent: Array(@indent + 1).join(' ')
|
||||||
@token 'TERMINATOR', '\n'
|
@token 'TERMINATOR', '\n'
|
||||||
|
@line += count comment, '\n'
|
||||||
comment.length
|
comment.length
|
||||||
|
|
||||||
# Matches JavaScript interpolated directly into the source via backticks.
|
# Matches JavaScript interpolated directly into the source via backticks.
|
||||||
|
@ -343,8 +344,11 @@ exports.Lexer = class Lexer
|
||||||
# erasing all external indentation on the left-hand side.
|
# erasing all external indentation on the left-hand side.
|
||||||
sanitizeHeredoc: (doc, options) ->
|
sanitizeHeredoc: (doc, options) ->
|
||||||
{indent, herecomment} = options
|
{indent, herecomment} = options
|
||||||
return doc if herecomment and 0 > doc.indexOf '\n'
|
if herecomment
|
||||||
unless herecomment
|
if HEREDOC_ILLEGAL.test doc
|
||||||
|
throw new Error "block comment cannot contain \"*/\", starting on line #{@line + 1}"
|
||||||
|
return doc if doc.indexOf('\n') <= 0
|
||||||
|
else
|
||||||
while match = HEREDOC_INDENT.exec doc
|
while match = HEREDOC_INDENT.exec doc
|
||||||
attempt = match[1]
|
attempt = match[1]
|
||||||
indent = attempt if indent is null or 0 < attempt.length < indent.length
|
indent = attempt if indent is null or 0 < attempt.length < indent.length
|
||||||
|
@ -367,7 +371,7 @@ exports.Lexer = class Lexer
|
||||||
stack.push tok
|
stack.push tok
|
||||||
when '(', 'CALL_START'
|
when '(', 'CALL_START'
|
||||||
if stack.length then stack.pop()
|
if stack.length then stack.pop()
|
||||||
else
|
else if tok[0] is '('
|
||||||
tok[0] = 'PARAM_START'
|
tok[0] = 'PARAM_START'
|
||||||
return this
|
return this
|
||||||
this
|
this
|
||||||
|
@ -593,9 +597,11 @@ MULTILINER = /\n/g
|
||||||
|
|
||||||
HEREDOC_INDENT = /\n+([^\n\S]*)/g
|
HEREDOC_INDENT = /\n+([^\n\S]*)/g
|
||||||
|
|
||||||
|
HEREDOC_ILLEGAL = /\*\//
|
||||||
|
|
||||||
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/
|
ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/
|
||||||
|
|
||||||
LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?!\.) | :: ) ///
|
LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///
|
||||||
|
|
||||||
TRAILING_SPACES = /\s+$/
|
TRAILING_SPACES = /\s+$/
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ exports.Base = class Base
|
||||||
# Statements converted into expressions via closure-wrapping share a scope
|
# Statements converted into expressions via closure-wrapping share a scope
|
||||||
# object with their parent closure, to preserve the expected lexical scope.
|
# object with their parent closure, to preserve the expected lexical scope.
|
||||||
compileClosure: (o) ->
|
compileClosure: (o) ->
|
||||||
if @jumps()
|
if @jumps() or this instanceof Throw
|
||||||
throw SyntaxError 'cannot use a pure statement in an expression.'
|
throw SyntaxError 'cannot use a pure statement in an expression.'
|
||||||
o.sharedScope = yes
|
o.sharedScope = yes
|
||||||
Closure.wrap(this).compileNode o
|
Closure.wrap(this).compileNode o
|
||||||
|
@ -498,12 +498,31 @@ exports.Call = class Call extends Base
|
||||||
ifn = unfoldSoak o, call, 'variable'
|
ifn = unfoldSoak o, call, 'variable'
|
||||||
ifn
|
ifn
|
||||||
|
|
||||||
|
# Walk through the objects in the arguments, moving over simple values.
|
||||||
|
# This allows syntax like `call a: b, c` into `call({a: b}, c);`
|
||||||
|
filterImplicitObjects: (list) ->
|
||||||
|
nodes = []
|
||||||
|
for node in list
|
||||||
|
unless node.isObject?() and node.base.generated
|
||||||
|
nodes.push node
|
||||||
|
continue
|
||||||
|
obj = null
|
||||||
|
for prop in node.base.properties
|
||||||
|
if prop instanceof Assign
|
||||||
|
nodes.push obj = new Obj properties = [], true if not obj
|
||||||
|
properties.push prop
|
||||||
|
else
|
||||||
|
nodes.push prop
|
||||||
|
obj = null
|
||||||
|
nodes
|
||||||
|
|
||||||
# Compile a vanilla function call.
|
# Compile a vanilla function call.
|
||||||
compileNode: (o) ->
|
compileNode: (o) ->
|
||||||
@variable?.front = @front
|
@variable?.front = @front
|
||||||
if code = Splat.compileSplattedArray o, @args, true
|
if code = Splat.compileSplattedArray o, @args, true
|
||||||
return @compileSplat o, code
|
return @compileSplat o, code
|
||||||
args = (arg.compile o, LEVEL_LIST for arg in @args).join ', '
|
args = @filterImplicitObjects @args
|
||||||
|
args = (arg.compile o, LEVEL_LIST for arg in args).join ', '
|
||||||
if @isSuper
|
if @isSuper
|
||||||
@superReference(o) + ".call(this#{ args and ', ' + args })"
|
@superReference(o) + ".call(this#{ args and ', ' + args })"
|
||||||
else
|
else
|
||||||
|
@ -700,6 +719,9 @@ exports.Obj = class Obj extends Base
|
||||||
compileNode: (o) ->
|
compileNode: (o) ->
|
||||||
props = @properties
|
props = @properties
|
||||||
return (if @front then '({})' else '{}') unless props.length
|
return (if @front then '({})' else '{}') unless props.length
|
||||||
|
if @generated
|
||||||
|
for node in props when node instanceof Value
|
||||||
|
throw new Error 'cannot have an implicit value in an implicit object'
|
||||||
idt = o.indent += TAB
|
idt = o.indent += TAB
|
||||||
lastNoncom = @lastNonComment @properties
|
lastNoncom = @lastNonComment @properties
|
||||||
props = for prop, i in props
|
props = for prop, i in props
|
||||||
|
@ -734,11 +756,14 @@ exports.Arr = class Arr extends Base
|
||||||
|
|
||||||
children: ['objects']
|
children: ['objects']
|
||||||
|
|
||||||
|
filterImplicitObjects: Call::filterImplicitObjects
|
||||||
|
|
||||||
compileNode: (o) ->
|
compileNode: (o) ->
|
||||||
return '[]' unless @objects.length
|
return '[]' unless @objects.length
|
||||||
o.indent += TAB
|
o.indent += TAB
|
||||||
return code if code = Splat.compileSplattedArray o, @objects
|
objs = @filterImplicitObjects @objects
|
||||||
code = (obj.compile o, LEVEL_LIST for obj in @objects).join ', '
|
return code if code = Splat.compileSplattedArray o, objs
|
||||||
|
code = (obj.compile o, LEVEL_LIST for obj in objs).join ', '
|
||||||
if code.indexOf('\n') >= 0
|
if code.indexOf('\n') >= 0
|
||||||
"[\n#{o.indent}#{code}\n#{@tab}]"
|
"[\n#{o.indent}#{code}\n#{@tab}]"
|
||||||
else
|
else
|
||||||
|
@ -1029,6 +1054,7 @@ exports.Code = class Code extends Base
|
||||||
vars = []
|
vars = []
|
||||||
exprs = []
|
exprs = []
|
||||||
for param in @params when param.splat
|
for param in @params when param.splat
|
||||||
|
o.scope.add param.name.value, 'var' if param.name.value
|
||||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||||
new Value new Literal 'arguments'
|
new Value new Literal 'arguments'
|
||||||
break
|
break
|
||||||
|
@ -1187,10 +1213,13 @@ exports.While = class While extends Base
|
||||||
exports.Op = class Op extends Base
|
exports.Op = class Op extends Base
|
||||||
constructor: (op, first, second, flip) ->
|
constructor: (op, first, second, flip) ->
|
||||||
return new In first, second if op is 'in'
|
return new In first, second if op is 'in'
|
||||||
return new Call first, first.params or [] if op is 'do'
|
if op is 'do'
|
||||||
|
call = new Call first, first.params or []
|
||||||
|
call.do = yes
|
||||||
|
return call
|
||||||
if op is 'new'
|
if op is 'new'
|
||||||
return first.newInstance() if first instanceof Call
|
return first.newInstance() if first instanceof Call and not first.do
|
||||||
first = new Parens first if first instanceof Code and first.bound
|
first = new Parens first if first instanceof Code and first.bound or first.do
|
||||||
@operator = CONVERSIONS[op] or op
|
@operator = CONVERSIONS[op] or op
|
||||||
@first = first
|
@first = first
|
||||||
@second = second
|
@second = second
|
||||||
|
@ -1284,6 +1313,7 @@ exports.Op = class Op extends Base
|
||||||
parts = [op = @operator]
|
parts = [op = @operator]
|
||||||
parts.push ' ' if op in ['new', 'typeof', 'delete'] or
|
parts.push ' ' if op in ['new', 'typeof', 'delete'] or
|
||||||
op in ['+', '-'] and @first instanceof Op and @first.operator is op
|
op in ['+', '-'] and @first instanceof Op and @first.operator is op
|
||||||
|
@first = new Parens @first if op is 'new' and @first.isStatement o
|
||||||
parts.push @first.compile o, LEVEL_OP
|
parts.push @first.compile o, LEVEL_OP
|
||||||
parts.reverse() if @flip
|
parts.reverse() if @flip
|
||||||
parts.join ''
|
parts.join ''
|
||||||
|
|
|
@ -6,40 +6,82 @@
|
||||||
|
|
||||||
# Require the **coffee-script** module to get access to the compiler.
|
# Require the **coffee-script** module to get access to the compiler.
|
||||||
CoffeeScript = require './coffee-script'
|
CoffeeScript = require './coffee-script'
|
||||||
helpers = require './helpers'
|
|
||||||
readline = require 'readline'
|
readline = require 'readline'
|
||||||
|
Script = process.binding('evals').Script
|
||||||
|
|
||||||
|
# REPL Setup
|
||||||
|
|
||||||
# Start by opening up `stdin` and `stdout`.
|
# Start by opening up `stdin` and `stdout`.
|
||||||
stdin = process.openStdin()
|
stdin = process.openStdin()
|
||||||
stdout = process.stdout
|
stdout = process.stdout
|
||||||
|
|
||||||
# Log an error.
|
# Log an error.
|
||||||
error = (err) ->
|
error = (err) ->
|
||||||
stdout.write (err.stack or err.toString()) + '\n\n'
|
stdout.write (err.stack or err.toString()) + '\n\n'
|
||||||
|
|
||||||
# Quick alias for quitting the REPL.
|
# The current backlog of multi-line code.
|
||||||
helpers.extend global, quit: -> process.exit(0)
|
backlog = ''
|
||||||
|
|
||||||
# The main REPL function. **run** is called every time a line of code is entered.
|
# 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
|
# Attempt to evaluate the command. If there's an exception, print it out instead
|
||||||
# of exiting.
|
# of exiting.
|
||||||
run = (buffer) ->
|
run = (buffer) ->
|
||||||
|
code = backlog += '\n' + buffer.toString()
|
||||||
|
if code[code.length - 1] is '\\'
|
||||||
|
return backlog = backlog[0...backlog.length - 1]
|
||||||
|
backlog = ''
|
||||||
try
|
try
|
||||||
val = CoffeeScript.eval buffer.toString(), bare: on, globals: on, filename: 'repl'
|
val = CoffeeScript.eval code, bare: on, globals: on, filename: 'repl'
|
||||||
process.stdout.write val + '\n' if val isnt undefined
|
process.stdout.write val + '\n' if val isnt undefined
|
||||||
catch err
|
catch err
|
||||||
error err
|
error err
|
||||||
repl.prompt()
|
repl.prompt()
|
||||||
|
|
||||||
|
## Autocompletion
|
||||||
|
|
||||||
|
# Regexes to match complete-able bits of text.
|
||||||
|
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/
|
||||||
|
SIMPLEVAR = /\s*(\w*)$/i
|
||||||
|
|
||||||
|
# Returns a list of completions, and the completed text.
|
||||||
|
autocomplete = (text) ->
|
||||||
|
completeAttribute(text) or completeVariable(text) or [[], text]
|
||||||
|
|
||||||
|
# Attempt to autocomplete a chained dotted attribute: `one.two.three`.
|
||||||
|
completeAttribute = (text) ->
|
||||||
|
if match = text.match ACCESSOR
|
||||||
|
[all, obj, prefix] = match
|
||||||
|
try
|
||||||
|
val = Script.runInThisContext obj
|
||||||
|
catch error
|
||||||
|
return [[], text]
|
||||||
|
completions = getCompletions prefix, getPropertyNames val
|
||||||
|
[completions, prefix]
|
||||||
|
|
||||||
|
# Attempt to autocomplete an in-scope free variable: `one`.
|
||||||
|
completeVariable = (text) ->
|
||||||
|
if free = text.match(SIMPLEVAR)?[1]
|
||||||
|
scope = Script.runInThisContext 'this'
|
||||||
|
completions = getCompletions free, CoffeeScript.RESERVED.concat(getPropertyNames scope)
|
||||||
|
[completions, free]
|
||||||
|
|
||||||
|
# Return elements of candidates for which `prefix` is a prefix.
|
||||||
|
getCompletions = (prefix, candidates) ->
|
||||||
|
(el for el in candidates when el.indexOf(prefix) is 0)
|
||||||
|
|
||||||
|
# Return all "own" properties of an object.
|
||||||
|
getPropertyNames = (obj) ->
|
||||||
|
(name for own name of obj)
|
||||||
|
|
||||||
# Make sure that uncaught exceptions don't kill the REPL.
|
# Make sure that uncaught exceptions don't kill the REPL.
|
||||||
process.on 'uncaughtException', error
|
process.on 'uncaughtException', error
|
||||||
|
|
||||||
# Create the REPL by listening to **stdin**.
|
# Create the REPL by listening to **stdin**.
|
||||||
if readline.createInterface.length < 3
|
if readline.createInterface.length < 3
|
||||||
repl = readline.createInterface stdin
|
repl = readline.createInterface stdin, autocomplete
|
||||||
stdin.on 'data', (buffer) -> repl.write buffer
|
stdin.on 'data', (buffer) -> repl.write buffer
|
||||||
else
|
else
|
||||||
repl = readline.createInterface stdin, stdout
|
repl = readline.createInterface stdin, stdout, autocomplete
|
||||||
|
|
||||||
repl.setPrompt 'coffee> '
|
repl.setPrompt 'coffee> '
|
||||||
repl.on 'close', -> stdin.destroy()
|
repl.on 'close', -> stdin.destroy()
|
||||||
|
|
|
@ -36,6 +36,28 @@ test "array splat expansions with assignments", ->
|
||||||
eq 4, b
|
eq 4, b
|
||||||
arrayEq [0,1,2,3,4], list
|
arrayEq [0,1,2,3,4], list
|
||||||
|
|
||||||
|
|
||||||
|
test "mixed shorthand objects in array lists", ->
|
||||||
|
|
||||||
|
arr = [
|
||||||
|
a:1
|
||||||
|
'b'
|
||||||
|
c:1
|
||||||
|
]
|
||||||
|
ok arr.length is 3
|
||||||
|
ok arr[2].c is 1
|
||||||
|
|
||||||
|
arr = [b: 1, a: 2, 100]
|
||||||
|
eq arr[1], 100
|
||||||
|
|
||||||
|
arr = [a:0, b:1, (1 + 1)]
|
||||||
|
eq arr[1], 2
|
||||||
|
|
||||||
|
arr = [a:1, 'a', b:1, 'b']
|
||||||
|
eq arr.length, 4
|
||||||
|
eq arr[2].b, 1
|
||||||
|
eq arr[3], 'b'
|
||||||
|
|
||||||
test "array splats with nested arrays", ->
|
test "array splats with nested arrays", ->
|
||||||
nonce = {}
|
nonce = {}
|
||||||
a = [nonce]
|
a = [nonce]
|
||||||
|
|
|
@ -51,3 +51,6 @@ test "#1026", ->
|
||||||
else
|
else
|
||||||
d
|
d
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
test "#1050", ->
|
||||||
|
cantCompile "### */ ###"
|
||||||
|
|
|
@ -73,6 +73,18 @@ test "`?.` and `::` should continue lines", ->
|
||||||
#::
|
#::
|
||||||
#?.foo
|
#?.foo
|
||||||
|
|
||||||
|
doesNotThrow -> CoffeeScript.compile """
|
||||||
|
oh. yes
|
||||||
|
oh?. true
|
||||||
|
oh:: return
|
||||||
|
"""
|
||||||
|
|
||||||
|
doesNotThrow -> CoffeeScript.compile """
|
||||||
|
a?[b..]
|
||||||
|
a?[...b]
|
||||||
|
a?[b..c]
|
||||||
|
"""
|
||||||
|
|
||||||
# Array Literals
|
# Array Literals
|
||||||
|
|
||||||
test "indented array literals don't trigger whitespace rewriting", ->
|
test "indented array literals don't trigger whitespace rewriting", ->
|
||||||
|
|
|
@ -339,6 +339,12 @@ test "passing splats to functions", ->
|
||||||
arrayEq [2..6], others
|
arrayEq [2..6], others
|
||||||
eq 7, last
|
eq 7, last
|
||||||
|
|
||||||
|
test "splat variables are local to the function", ->
|
||||||
|
outer = "x"
|
||||||
|
clobber = (avar, outer...) -> outer
|
||||||
|
clobber "foo", "bar"
|
||||||
|
eq "x", outer
|
||||||
|
|
||||||
|
|
||||||
test "Issue 894: Splatting against constructor-chained functions.", ->
|
test "Issue 894: Splatting against constructor-chained functions.", ->
|
||||||
|
|
||||||
|
@ -431,3 +437,9 @@ test "don't wrap 'pure' statements in a closure", ->
|
||||||
for item in items
|
for item in items
|
||||||
return item if item is nonce
|
return item if item is nonce
|
||||||
eq nonce, fn items
|
eq nonce, fn items
|
||||||
|
|
||||||
|
#### Unusual `new` Usage
|
||||||
|
|
||||||
|
test "usage of `new` is careful about where the invocation parens end up", ->
|
||||||
|
eq 'object', typeof new try Array
|
||||||
|
eq 'object', typeof new do -> ->
|
||||||
|
|
|
@ -147,3 +147,11 @@ test "default values with splatted arguments", ->
|
||||||
eq 5, withSplats(1,1)
|
eq 5, withSplats(1,1)
|
||||||
eq 1, withSplats(1,1,1)
|
eq 1, withSplats(1,1,1)
|
||||||
eq 2, withSplats(1,1,1,1)
|
eq 2, withSplats(1,1,1,1)
|
||||||
|
|
||||||
|
test "default values with function calls", ->
|
||||||
|
doesNotThrow -> CoffeeScript.compile "(x = f()) ->"
|
||||||
|
|
||||||
|
test "arguments vs parameters", ->
|
||||||
|
doesNotThrow -> CoffeeScript.compile "f(x) ->"
|
||||||
|
f = (g) -> g()
|
||||||
|
eq 5, f (x) -> 5
|
||||||
|
|
|
@ -37,3 +37,8 @@ eq Number::toString, .42['toString']
|
||||||
|
|
||||||
eq Number::toString, 4.2.toString
|
eq Number::toString, 4.2.toString
|
||||||
eq Number::toString, .42.toString
|
eq Number::toString, .42.toString
|
||||||
|
|
||||||
|
test '#1168: leading floating point suppresses newline', ->
|
||||||
|
eq 1, do ->
|
||||||
|
1
|
||||||
|
.5 + 0.5
|
||||||
|
|
|
@ -183,6 +183,22 @@ test "invoking functions with implicit object literals", ->
|
||||||
ok result.length is 3
|
ok result.length is 3
|
||||||
ok result[2].c is 1
|
ok result[2].c is 1
|
||||||
|
|
||||||
|
result = getA b: 13, a: 42, 2
|
||||||
|
eq 42, result
|
||||||
|
|
||||||
|
result = getArgs a:1, (1 + 1)
|
||||||
|
ok result[1] is 2
|
||||||
|
|
||||||
|
result = getArgs a:1, b
|
||||||
|
ok result.length is 2
|
||||||
|
ok result[1] is 30
|
||||||
|
|
||||||
|
result = getArgs a:1, b, b:1, a
|
||||||
|
ok result.length is 4
|
||||||
|
ok result[2].b is 1
|
||||||
|
|
||||||
|
throws -> CoffeeScript.compile "a = b:1, c"
|
||||||
|
|
||||||
test "some weird indentation in YAML-style object literals", ->
|
test "some weird indentation in YAML-style object literals", ->
|
||||||
two = (a, b) -> b
|
two = (a, b) -> b
|
||||||
obj = then two 1,
|
obj = then two 1,
|
||||||
|
@ -193,3 +209,4 @@ test "some weird indentation in YAML-style object literals", ->
|
||||||
d: e
|
d: e
|
||||||
f: 1
|
f: 1
|
||||||
eq 1, obj[1]
|
eq 1, obj[1]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue