mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Applied the utility factoring into a "Coffeescript" object to the core. All tests pass fast.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
(function(){
|
||||
var CoffeeScript, fs, helpers, no_such_task, oparse, options, optparse, path, print_tasks, switches, tasks;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, Coffeescript = {
|
||||
hasProp: Object.prototype.hasOwnProperty
|
||||
};
|
||||
// `cake` is a simplified version of [Make](http://www.gnu.org/software/make/)
|
||||
// ([Rake](http://rake.rubyforge.org/), [Jake](http://github.com/280north/jake))
|
||||
// for CoffeeScript. You define tasks with names and descriptions in a Cakefile,
|
||||
@@ -75,7 +77,7 @@
|
||||
var _a, _b, _c, _d, _e, i, name, spaces, task;
|
||||
puts('');
|
||||
_a = tasks;
|
||||
for (name in _a) { if (__hasProp.call(_a, name)) {
|
||||
for (name in _a) { if (Coffeescript.hasProp.call(_a, name)) {
|
||||
task = _a[name];
|
||||
spaces = 20 - name.length;
|
||||
spaces = spaces > 0 ? (function() {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
(function(){
|
||||
var Parser, _a, _b, _c, _d, _e, _f, _g, _h, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, Coffeescript = {
|
||||
hasProp: Object.prototype.hasOwnProperty
|
||||
};
|
||||
// The CoffeeScript parser is generated by [Jison](http://github.com/zaach/jison)
|
||||
// from this grammar file. Jison is a bottom-up parser generator, similar in
|
||||
// style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript.
|
||||
@@ -664,7 +666,7 @@
|
||||
// as "tokens".
|
||||
tokens = [];
|
||||
_a = grammar;
|
||||
for (name in _a) { if (__hasProp.call(_a, name)) {
|
||||
for (name in _a) { if (Coffeescript.hasProp.call(_a, name)) {
|
||||
alternatives = _a[name];
|
||||
grammar[name] = (function() {
|
||||
_b = []; _d = alternatives;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
(function(){
|
||||
var balanced_string, compact, count, del, extend, flatten, helpers, include, merge, starts;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, Coffeescript = {
|
||||
hasProp: Object.prototype.hasOwnProperty
|
||||
};
|
||||
// This file contains the common helper functions that we'd like to share among
|
||||
// the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
|
||||
// arrays, count characters, that sort of thing.
|
||||
@@ -45,13 +47,13 @@
|
||||
var _a, _b, fresh, key, val;
|
||||
fresh = {};
|
||||
_a = options;
|
||||
for (key in _a) { if (__hasProp.call(_a, key)) {
|
||||
for (key in _a) { if (Coffeescript.hasProp.call(_a, key)) {
|
||||
val = _a[key];
|
||||
(fresh[key] = val);
|
||||
}}
|
||||
if (overrides) {
|
||||
_b = overrides;
|
||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
val = _b[key];
|
||||
(fresh[key] = val);
|
||||
}}
|
||||
@@ -63,7 +65,7 @@
|
||||
helpers.extend = (extend = function extend(object, properties) {
|
||||
var _a, _b, key, val;
|
||||
_a = []; _b = properties;
|
||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
val = _b[key];
|
||||
_a.push((object[key] = val));
|
||||
}}
|
||||
|
||||
119
lib/nodes.js
119
lib/nodes.js
@@ -1,11 +1,21 @@
|
||||
(function(){
|
||||
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, CurryNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, ValueNode, WhileNode, compact, del, flatten, helpers, literal, merge, statement;
|
||||
var __extends = function(child, parent) {
|
||||
var ctor = function(){ };
|
||||
ctor.prototype = parent.prototype;
|
||||
child.__superClass__ = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.prototype.constructor = child;
|
||||
var Coffeescript = {
|
||||
extend: function(child, parent) {
|
||||
var ctor = function(){ };
|
||||
ctor.prototype = parent.prototype;
|
||||
child.__superClass__ = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.prototype.constructor = child;
|
||||
},
|
||||
bind: function(func, obj, args) {
|
||||
obj = obj || {};
|
||||
return (typeof args !== "undefined" && args !== null) ? function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
} : function() {
|
||||
return func.apply(obj, arguments);
|
||||
};
|
||||
}
|
||||
};
|
||||
// `nodes.coffee` contains all of the node classes for the syntax tree. Most
|
||||
// nodes are created as the result of actions in the [grammar](grammar.html),
|
||||
@@ -189,7 +199,7 @@
|
||||
this.children = (this.expressions = compact(flatten(nodes || [])));
|
||||
return this;
|
||||
};
|
||||
__extends(Expressions, BaseNode);
|
||||
Coffeescript.extend(Expressions, BaseNode);
|
||||
Expressions.prototype.type = 'Expressions';
|
||||
// Tack an expression on to the end of this expression list.
|
||||
Expressions.prototype.push = function push(node) {
|
||||
@@ -275,7 +285,7 @@
|
||||
var code;
|
||||
code = this.compile_node(o);
|
||||
if (o.scope.has_assignments(this)) {
|
||||
code = "" + (this.tab) + "var " + (o.scope.compiled_assignments()) + ";\n" + code;
|
||||
code = "" + (this.tab) + "var " + (o.scope.compiled_assignments(this.tab)) + ";\n" + code;
|
||||
}
|
||||
if (o.scope.has_declarations(this)) {
|
||||
code = "" + (this.tab) + "var " + (o.scope.compiled_declarations()) + ";\n" + code;
|
||||
@@ -317,7 +327,7 @@
|
||||
this.value = value;
|
||||
return this;
|
||||
};
|
||||
__extends(LiteralNode, BaseNode);
|
||||
Coffeescript.extend(LiteralNode, BaseNode);
|
||||
LiteralNode.prototype.type = 'Literal';
|
||||
// Break and continue must be treated as pure statements -- they lose their
|
||||
// meaning when wrapped in a closure.
|
||||
@@ -344,7 +354,7 @@
|
||||
this.children = [(this.expression = expression)];
|
||||
return this;
|
||||
};
|
||||
__extends(ReturnNode, BaseNode);
|
||||
Coffeescript.extend(ReturnNode, BaseNode);
|
||||
ReturnNode.prototype.type = 'Return';
|
||||
ReturnNode.prototype.top_sensitive = function top_sensitive() {
|
||||
return true;
|
||||
@@ -372,7 +382,7 @@
|
||||
this.children = flatten([(this.base = base), (this.properties = (properties || []))]);
|
||||
return this;
|
||||
};
|
||||
__extends(ValueNode, BaseNode);
|
||||
Coffeescript.extend(ValueNode, BaseNode);
|
||||
ValueNode.prototype.type = 'Value';
|
||||
ValueNode.prototype.SOAK = " == undefined ? undefined : ";
|
||||
// A **ValueNode** has a base and a list of property accesses.
|
||||
@@ -466,7 +476,7 @@
|
||||
this;
|
||||
return this;
|
||||
};
|
||||
__extends(CommentNode, BaseNode);
|
||||
Coffeescript.extend(CommentNode, BaseNode);
|
||||
CommentNode.prototype.type = 'Comment';
|
||||
CommentNode.prototype.make_return = function make_return() {
|
||||
return this;
|
||||
@@ -486,14 +496,10 @@
|
||||
this.is_super = variable === 'super';
|
||||
this.variable = this.is_super ? null : variable;
|
||||
this.children = compact(flatten([this.variable, (this.args = (args || []))]));
|
||||
this.compile_splat_arguments = (function(func, obj, args) {
|
||||
return function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
};
|
||||
}(SplatNode.compile_mixed_array, this, [this.args]));
|
||||
this.compile_splat_arguments = Coffeescript.bind(SplatNode.compile_mixed_array, this, [this.args]);
|
||||
return this;
|
||||
};
|
||||
__extends(CallNode, BaseNode);
|
||||
Coffeescript.extend(CallNode, BaseNode);
|
||||
CallNode.prototype.type = 'Call';
|
||||
// Tag this invocation as creating a new instance.
|
||||
CallNode.prototype.new_instance = function new_instance() {
|
||||
@@ -560,14 +566,10 @@
|
||||
exports.CurryNode = (function() {
|
||||
CurryNode = function CurryNode(meth, args) {
|
||||
this.children = flatten([(this.meth = meth), (this.context = args[0]), (this.args = (args.slice(1) || []))]);
|
||||
this.compile_splat_arguments = (function(func, obj, args) {
|
||||
return function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
};
|
||||
}(SplatNode.compile_mixed_array, this, [this.args]));
|
||||
this.compile_splat_arguments = Coffeescript.bind(SplatNode.compile_mixed_array, this, [this.args]);
|
||||
return this;
|
||||
};
|
||||
__extends(CurryNode, CallNode);
|
||||
Coffeescript.extend(CurryNode, CallNode);
|
||||
CurryNode.prototype.type = 'Curry';
|
||||
CurryNode.prototype.body = 'func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)))';
|
||||
CurryNode.prototype.arguments = function arguments(o) {
|
||||
@@ -582,11 +584,9 @@
|
||||
return (new ArrayNode(this.args)).compile(o);
|
||||
};
|
||||
CurryNode.prototype.compile_node = function compile_node(o) {
|
||||
var body, curried, curry;
|
||||
body = Expressions.wrap([literal(this.body)]);
|
||||
curried = new CodeNode([], body);
|
||||
curry = new CodeNode([literal('func'), literal('obj'), literal('args')], Expressions.wrap([curried]));
|
||||
return (new ParentheticalNode(new CallNode(curry, [this.meth, this.context, literal(this.arguments(o))]))).compile(o);
|
||||
var ref;
|
||||
ref = new ValueNode(literal(o.scope.utility('bind')));
|
||||
return (new CallNode(ref, [this.meth, this.context, literal(this.arguments(o))])).compile(o);
|
||||
};
|
||||
return CurryNode;
|
||||
}).call(this);
|
||||
@@ -599,16 +599,13 @@
|
||||
this.children = [(this.child = child), (this.parent = parent)];
|
||||
return this;
|
||||
};
|
||||
__extends(ExtendsNode, BaseNode);
|
||||
Coffeescript.extend(ExtendsNode, BaseNode);
|
||||
ExtendsNode.prototype.type = 'Extends';
|
||||
ExtendsNode.prototype.code = 'function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.__superClass__ = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n }';
|
||||
// Hooks one constructor into another's prototype chain.
|
||||
ExtendsNode.prototype.compile_node = function compile_node(o) {
|
||||
var call, ref;
|
||||
o.scope.assign('__extends', this.code, true);
|
||||
ref = new ValueNode(literal('__extends'));
|
||||
call = new CallNode(ref, [this.child, this.parent]);
|
||||
return call.compile(o);
|
||||
var ref;
|
||||
ref = new ValueNode(literal(o.scope.utility('extend')));
|
||||
return (new CallNode(ref, [this.child, this.parent])).compile(o);
|
||||
};
|
||||
return ExtendsNode;
|
||||
}).call(this);
|
||||
@@ -623,7 +620,7 @@
|
||||
this;
|
||||
return this;
|
||||
};
|
||||
__extends(AccessorNode, BaseNode);
|
||||
Coffeescript.extend(AccessorNode, BaseNode);
|
||||
AccessorNode.prototype.type = 'Accessor';
|
||||
AccessorNode.prototype.compile_node = function compile_node(o) {
|
||||
var proto_part;
|
||||
@@ -640,7 +637,7 @@
|
||||
this.soak_node = tag === 'soak';
|
||||
return this;
|
||||
};
|
||||
__extends(IndexNode, BaseNode);
|
||||
Coffeescript.extend(IndexNode, BaseNode);
|
||||
IndexNode.prototype.type = 'Index';
|
||||
IndexNode.prototype.compile_node = function compile_node(o) {
|
||||
var idx;
|
||||
@@ -659,7 +656,7 @@
|
||||
this.exclusive = !!exclusive;
|
||||
return this;
|
||||
};
|
||||
__extends(RangeNode, BaseNode);
|
||||
Coffeescript.extend(RangeNode, BaseNode);
|
||||
RangeNode.prototype.type = 'Range';
|
||||
// Compiles the range's source variables -- where it starts and where it ends.
|
||||
RangeNode.prototype.compile_variables = function compile_variables(o) {
|
||||
@@ -715,7 +712,7 @@
|
||||
this;
|
||||
return this;
|
||||
};
|
||||
__extends(SliceNode, BaseNode);
|
||||
Coffeescript.extend(SliceNode, BaseNode);
|
||||
SliceNode.prototype.type = 'Slice';
|
||||
SliceNode.prototype.compile_node = function compile_node(o) {
|
||||
var from, plus_part, to;
|
||||
@@ -733,7 +730,7 @@
|
||||
this.children = (this.objects = (this.properties = props || []));
|
||||
return this;
|
||||
};
|
||||
__extends(ObjectNode, BaseNode);
|
||||
Coffeescript.extend(ObjectNode, BaseNode);
|
||||
ObjectNode.prototype.type = 'Object';
|
||||
// All the mucking about with commas is to make sure that CommentNodes and
|
||||
// AssignNodes get interleaved correctly, with no trailing commas or
|
||||
@@ -780,14 +777,10 @@
|
||||
exports.ArrayNode = (function() {
|
||||
ArrayNode = function ArrayNode(objects) {
|
||||
this.children = (this.objects = objects || []);
|
||||
this.compile_splat_literal = (function(func, obj, args) {
|
||||
return function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
};
|
||||
}(SplatNode.compile_mixed_array, this, [this.objects]));
|
||||
this.compile_splat_literal = Coffeescript.bind(SplatNode.compile_mixed_array, this, [this.objects]);
|
||||
return this;
|
||||
};
|
||||
__extends(ArrayNode, BaseNode);
|
||||
Coffeescript.extend(ArrayNode, BaseNode);
|
||||
ArrayNode.prototype.type = 'Array';
|
||||
ArrayNode.prototype.compile_node = function compile_node(o) {
|
||||
var _a, _b, code, ending, i, obj, objects;
|
||||
@@ -821,7 +814,7 @@
|
||||
this.returns = false;
|
||||
return this;
|
||||
};
|
||||
__extends(ClassNode, BaseNode);
|
||||
Coffeescript.extend(ClassNode, BaseNode);
|
||||
ClassNode.prototype.type = 'Class';
|
||||
// Initialize a **ClassNode** with its name, an optional superclass, and a
|
||||
// list of prototype property assignments.
|
||||
@@ -881,7 +874,7 @@
|
||||
this.context = context;
|
||||
return this;
|
||||
};
|
||||
__extends(AssignNode, BaseNode);
|
||||
Coffeescript.extend(AssignNode, BaseNode);
|
||||
AssignNode.prototype.type = 'Assign';
|
||||
// Matchers for detecting prototype assignments.
|
||||
AssignNode.prototype.PROTO_ASSIGN = /^(\S+)\.prototype/;
|
||||
@@ -1005,7 +998,7 @@
|
||||
this.bound = tag === 'boundfunc';
|
||||
return this;
|
||||
};
|
||||
__extends(CodeNode, BaseNode);
|
||||
Coffeescript.extend(CodeNode, BaseNode);
|
||||
CodeNode.prototype.type = 'Code';
|
||||
// Compilation creates a new scope unless explicitly asked to share with the
|
||||
// outer scope. Handles splat parameters in the parameter list by peeking at
|
||||
@@ -1013,7 +1006,7 @@
|
||||
// arrow, generates a wrapper that saves the current value of `this` through
|
||||
// a closure.
|
||||
CodeNode.prototype.compile_node = function compile_node(o) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, code, func, i, inner, name_part, param, params, shared_scope, splat, top;
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, code, func, i, name_part, param, params, ref, shared_scope, splat, top;
|
||||
shared_scope = del(o, 'shared_scope');
|
||||
top = del(o, 'top');
|
||||
o.scope = shared_scope || new Scope(o.scope, this.body, this);
|
||||
@@ -1062,8 +1055,8 @@
|
||||
if (!(this.bound)) {
|
||||
return func;
|
||||
}
|
||||
inner = "(function" + name_part + "() {\n" + (this.idt(2)) + "return __func.apply(__this, arguments);\n" + (this.idt(1)) + "});";
|
||||
return "(function(__this) {\n" + (this.idt(1)) + "var __func = " + func + ";\n" + (this.idt(1)) + "return " + inner + "\n" + this.tab + "})(this)";
|
||||
ref = new ValueNode(literal(o.scope.utility('bind')));
|
||||
return (new CallNode(ref, [literal(func), literal('this')])).compile(o);
|
||||
};
|
||||
CodeNode.prototype.top_sensitive = function top_sensitive() {
|
||||
return true;
|
||||
@@ -1110,7 +1103,7 @@
|
||||
this.children = [(this.name = name)];
|
||||
return this;
|
||||
};
|
||||
__extends(SplatNode, BaseNode);
|
||||
Coffeescript.extend(SplatNode, BaseNode);
|
||||
SplatNode.prototype.type = 'Splat';
|
||||
SplatNode.prototype.compile_node = function compile_node(o) {
|
||||
var _a;
|
||||
@@ -1183,7 +1176,7 @@
|
||||
this.filter = opts && opts.filter;
|
||||
return this;
|
||||
};
|
||||
__extends(WhileNode, BaseNode);
|
||||
Coffeescript.extend(WhileNode, BaseNode);
|
||||
WhileNode.prototype.type = 'While';
|
||||
WhileNode.prototype.add_body = function add_body(body) {
|
||||
this.children.push((this.body = body));
|
||||
@@ -1239,7 +1232,7 @@
|
||||
this.flip = !!flip;
|
||||
return this;
|
||||
};
|
||||
__extends(OpNode, BaseNode);
|
||||
Coffeescript.extend(OpNode, BaseNode);
|
||||
OpNode.prototype.type = 'Op';
|
||||
// The map of conversions from CoffeeScript to JavaScript symbols.
|
||||
OpNode.prototype.CONVERSIONS = {
|
||||
@@ -1340,7 +1333,7 @@
|
||||
this;
|
||||
return this;
|
||||
};
|
||||
__extends(TryNode, BaseNode);
|
||||
Coffeescript.extend(TryNode, BaseNode);
|
||||
TryNode.prototype.type = 'Try';
|
||||
TryNode.prototype.make_return = function make_return() {
|
||||
if (this.attempt) {
|
||||
@@ -1373,7 +1366,7 @@
|
||||
this.children = [(this.expression = expression)];
|
||||
return this;
|
||||
};
|
||||
__extends(ThrowNode, BaseNode);
|
||||
Coffeescript.extend(ThrowNode, BaseNode);
|
||||
ThrowNode.prototype.type = 'Throw';
|
||||
// A **ThrowNode** is already a return, of sorts...
|
||||
ThrowNode.prototype.make_return = function make_return() {
|
||||
@@ -1394,7 +1387,7 @@
|
||||
this.children = [(this.expression = expression)];
|
||||
return this;
|
||||
};
|
||||
__extends(ExistenceNode, BaseNode);
|
||||
Coffeescript.extend(ExistenceNode, BaseNode);
|
||||
ExistenceNode.prototype.type = 'Existence';
|
||||
ExistenceNode.prototype.compile_node = function compile_node(o) {
|
||||
return ExistenceNode.compile_test(o, this.expression);
|
||||
@@ -1429,7 +1422,7 @@
|
||||
this.children = [(this.expression = expression)];
|
||||
return this;
|
||||
};
|
||||
__extends(ParentheticalNode, BaseNode);
|
||||
Coffeescript.extend(ParentheticalNode, BaseNode);
|
||||
ParentheticalNode.prototype.type = 'Paren';
|
||||
ParentheticalNode.prototype.is_statement = function is_statement() {
|
||||
return this.expression.is_statement();
|
||||
@@ -1481,7 +1474,7 @@
|
||||
this.returns = false;
|
||||
return this;
|
||||
};
|
||||
__extends(ForNode, BaseNode);
|
||||
Coffeescript.extend(ForNode, BaseNode);
|
||||
ForNode.prototype.type = 'For';
|
||||
ForNode.prototype.top_sensitive = function top_sensitive() {
|
||||
return true;
|
||||
@@ -1555,7 +1548,7 @@
|
||||
this.filter ? (body = Expressions.wrap([new IfNode(this.filter, body)])) : null;
|
||||
if (this.object) {
|
||||
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true);
|
||||
for_part = "" + ivar + " in " + svar + ") { if (__hasProp.call(" + svar + ", " + ivar + ")";
|
||||
for_part = "" + ivar + " in " + svar + ") { if (" + (o.scope.utility('hasProp')) + ".call(" + svar + ", " + ivar + ")";
|
||||
}
|
||||
body = body.compile(merge(o, {
|
||||
indent: body_dent,
|
||||
@@ -1588,7 +1581,7 @@
|
||||
}
|
||||
return this;
|
||||
};
|
||||
__extends(IfNode, BaseNode);
|
||||
Coffeescript.extend(IfNode, BaseNode);
|
||||
IfNode.prototype.type = 'If';
|
||||
// Add a new *else* clause to this **IfNode**, or push it down to the bottom
|
||||
// of the chain recursively.
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
(function(){
|
||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, helpers, include, pair;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, Coffeescript = {
|
||||
bind: function(func, obj, args) {
|
||||
obj = obj || {};
|
||||
return (typeof args !== "undefined" && args !== null) ? function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
} : function() {
|
||||
return func.apply(obj, arguments);
|
||||
};
|
||||
},
|
||||
hasProp: Object.prototype.hasOwnProperty
|
||||
};
|
||||
// The CoffeeScript language has a good deal of optional syntax, implicit syntax,
|
||||
// and shorthand syntax. This can greatly complicate a grammar and bloat
|
||||
// the resulting parse table. Instead of making the parser handle it all, we take
|
||||
@@ -57,8 +67,7 @@
|
||||
// Massage newlines and indentations so that comments don't have to be
|
||||
// correctly indented, or appear on a line of their own.
|
||||
Rewriter.prototype.adjust_comments = function adjust_comments() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var after;
|
||||
if (!(token[0] === 'COMMENT')) {
|
||||
return 1;
|
||||
@@ -74,11 +83,7 @@
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
// Leading newlines would introduce an ambiguity in the grammar, so we
|
||||
// dispatch them here.
|
||||
@@ -93,18 +98,13 @@
|
||||
// Some blocks occur in the middle of expressions -- when we're expecting
|
||||
// this, remove their trailing newlines.
|
||||
Rewriter.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) {
|
||||
return 1;
|
||||
}
|
||||
this.tokens.splice(i, 1);
|
||||
return 0;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
// The lexer has tagged the opening parenthesis of a method call, and the
|
||||
// opening bracket of an indexing operation. Match them with their paired
|
||||
@@ -113,8 +113,7 @@
|
||||
var brackets, parens;
|
||||
parens = [0];
|
||||
brackets = [0];
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var _a;
|
||||
if ((_a = token[0]) === 'CALL_START') {
|
||||
parens.push(0);
|
||||
@@ -140,11 +139,7 @@
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
// Methods may be optionally called without parentheses, for simple cases.
|
||||
// Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
@@ -154,8 +149,7 @@
|
||||
stack = [0];
|
||||
calls = 0;
|
||||
parens = 0;
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var _a, _b, _c, idx, last, open, size, stack_pointer, tag, tmp;
|
||||
tag = token[0];
|
||||
if (tag === 'CALL_START') {
|
||||
@@ -199,19 +193,14 @@
|
||||
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
|
||||
stack[stack.length - 1] += 1;
|
||||
return 2;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
// Because our grammar is LALR(1), it can't handle some single-line
|
||||
// expressions that lack ending delimiters. The **Rewriter** adds the implicit
|
||||
// blocks, so it doesn't need to. ')' can close a single-line block,
|
||||
// but we need to make sure it's balanced.
|
||||
Rewriter.prototype.add_implicit_indentation = function add_implicit_indentation() {
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var idx, insertion, outdent, parens, pre, starter, tok;
|
||||
if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
|
||||
return 1;
|
||||
@@ -243,11 +232,7 @@
|
||||
}
|
||||
this.tokens.splice(i, 1);
|
||||
return 0;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
// Ensure that all listed pairs of tokens are correctly balanced throughout
|
||||
// the course of the token stream.
|
||||
@@ -255,8 +240,7 @@
|
||||
var _a, _b, key, levels, line, open, open_line, unclosed, value;
|
||||
levels = {};
|
||||
open_line = {};
|
||||
this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var _a, _b, _c, _d, close, open, pair;
|
||||
_b = pairs;
|
||||
for (_a = 0, _c = _b.length; _a < _c; _a++) {
|
||||
@@ -279,14 +263,10 @@
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
unclosed = (function() {
|
||||
_a = []; _b = levels;
|
||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
value = _b[key];
|
||||
value > 0 ? _a.push(key) : null;
|
||||
}}
|
||||
@@ -314,12 +294,11 @@
|
||||
stack = [];
|
||||
debt = {};
|
||||
_a = INVERSES;
|
||||
for (key in _a) { if (__hasProp.call(_a, key)) {
|
||||
for (key in _a) { if (Coffeescript.hasProp.call(_a, key)) {
|
||||
val = _a[key];
|
||||
(debt[key] = 0);
|
||||
}}
|
||||
return this.scan_tokens((function(__this) {
|
||||
var __func = function(prev, token, post, i) {
|
||||
return this.scan_tokens(Coffeescript.bind(function(prev, token, post, i) {
|
||||
var inv, match, mtag, tag;
|
||||
tag = token[0];
|
||||
inv = INVERSES[token[0]];
|
||||
@@ -345,11 +324,7 @@
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
return (function() {
|
||||
return __func.apply(__this, arguments);
|
||||
});
|
||||
})(this));
|
||||
}, this));
|
||||
};
|
||||
return Rewriter;
|
||||
}).call(this);
|
||||
|
||||
71
lib/scope.js
71
lib/scope.js
@@ -1,6 +1,8 @@
|
||||
(function(){
|
||||
var Scope;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
var Scope, utilities;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, Coffeescript = {
|
||||
hasProp: Object.prototype.hasOwnProperty
|
||||
};
|
||||
// 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,
|
||||
@@ -11,6 +13,7 @@
|
||||
if (!((typeof process !== "undefined" && process !== null))) {
|
||||
this.exports = this;
|
||||
}
|
||||
utilities = (typeof process !== "undefined" && process !== null) ? require('./utilities').utilities : this.utilities;
|
||||
exports.Scope = (function() {
|
||||
Scope = function Scope(parent, expressions, method) {
|
||||
var _a;
|
||||
@@ -26,6 +29,14 @@
|
||||
// as well as a reference to the **Expressions** node is belongs to, which is
|
||||
// where it should declare its variables, and a reference to the function that
|
||||
// it wraps.
|
||||
// Find the top-most scope object, used for defined global variables
|
||||
Scope.prototype.topmost = function topmost() {
|
||||
if (this.parent) {
|
||||
return this.parent.topmost();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
// Look up a variable name in lexical scope, and declare it if it does not
|
||||
// already exist.
|
||||
Scope.prototype.find = function find(name) {
|
||||
@@ -35,6 +46,18 @@
|
||||
this.variables[name] = 'var';
|
||||
return false;
|
||||
};
|
||||
// Test variables and return true the first time fn(v, k) returns true
|
||||
Scope.prototype.any = function any(fn) {
|
||||
var _a, k, v;
|
||||
_a = this.variables;
|
||||
for (v in _a) { if (Coffeescript.hasProp.call(_a, v)) {
|
||||
k = _a[v];
|
||||
if (fn(v, k)) {
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
return false;
|
||||
};
|
||||
// Reserve a variable name as originating from a function parameter for this
|
||||
// scope. No `var` required for internal references.
|
||||
Scope.prototype.parameter = function parameter(name) {
|
||||
@@ -63,7 +86,7 @@
|
||||
// (or at the top-level scope, if requested).
|
||||
Scope.prototype.assign = function assign(name, value, top_level) {
|
||||
if (top_level && this.parent) {
|
||||
return this.parent.assign(name, value, top_level);
|
||||
return this.topmost().assign(name, value);
|
||||
}
|
||||
this.variables[name] = {
|
||||
value: value,
|
||||
@@ -71,22 +94,52 @@
|
||||
};
|
||||
return this.variables[name];
|
||||
};
|
||||
// Ensure the CoffeeScript utility object is included in the top level
|
||||
// then return a CallNode curried constructor bound to the utility function
|
||||
Scope.prototype.utility = function utility(name) {
|
||||
if (this.parent) {
|
||||
return this.topmost().utility(name);
|
||||
}
|
||||
this.utilities = this.utilities || {};
|
||||
this.utilities[name] = true;
|
||||
return "Coffeescript." + name;
|
||||
};
|
||||
Scope.prototype.included_utilities = function included_utilities(tab) {
|
||||
var _a, _b, _c, _d, key, props;
|
||||
if ((typeof (_d = this.utilities) !== "undefined" && _d !== null)) {
|
||||
props = (function() {
|
||||
_a = []; _b = this.utilities;
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
(typeof (_c = this.utilities[key]) !== "undefined" && _c !== null) ? _a.push(utilities.FORMAT(key, tab)) : null;
|
||||
}}
|
||||
return _a;
|
||||
}).call(this);
|
||||
return ["" + (utilities.KEY) + " = {" + (props.join(', ')) + "\n" + tab + "}"];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
// Does this scope reference any variables that need to be declared in the
|
||||
// given function body?
|
||||
Scope.prototype.has_declarations = function has_declarations(body) {
|
||||
return body === this.expressions && this.declared_variables().length;
|
||||
return body === this.expressions && this.any(function(k, val) {
|
||||
return val === 'var';
|
||||
});
|
||||
};
|
||||
// Does this scope reference any assignments that need to be declared at the
|
||||
// top of the given function body?
|
||||
Scope.prototype.has_assignments = function has_assignments(body) {
|
||||
return body === this.expressions && this.assigned_variables().length;
|
||||
var _a;
|
||||
return body === this.expressions && ((typeof (_a = this.utilities) !== "undefined" && _a !== null) || this.any(function(k, val) {
|
||||
return val.assigned;
|
||||
}));
|
||||
};
|
||||
// Return the list of variables first declared in this scope.
|
||||
Scope.prototype.declared_variables = function declared_variables() {
|
||||
var _a, _b, key, val;
|
||||
return (function() {
|
||||
_a = []; _b = this.variables;
|
||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
val = _b[key];
|
||||
val === 'var' ? _a.push(key) : null;
|
||||
}}
|
||||
@@ -98,7 +151,7 @@
|
||||
Scope.prototype.assigned_variables = function assigned_variables() {
|
||||
var _a, _b, key, val;
|
||||
_a = []; _b = this.variables;
|
||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||
for (key in _b) { if (Coffeescript.hasProp.call(_b, key)) {
|
||||
val = _b[key];
|
||||
val.assigned ? _a.push("" + key + " = " + (val.value)) : null;
|
||||
}}
|
||||
@@ -109,8 +162,8 @@
|
||||
return this.declared_variables().join(', ');
|
||||
};
|
||||
// Compile the JavaScript for all of the variable assignments in this scope.
|
||||
Scope.prototype.compiled_assignments = function compiled_assignments() {
|
||||
return this.assigned_variables().join(', ');
|
||||
Scope.prototype.compiled_assignments = function compiled_assignments(tab) {
|
||||
return this.assigned_variables().concat(this.included_utilities(tab)).join(', ');
|
||||
};
|
||||
return Scope;
|
||||
}).call(this);
|
||||
|
||||
15
lib/utilities.js
Normal file
15
lib/utilities.js
Normal file
@@ -0,0 +1,15 @@
|
||||
(function(){
|
||||
var utilities;
|
||||
if (!((typeof process !== "undefined" && process !== null))) {
|
||||
this.exports = this;
|
||||
}
|
||||
exports.utilities = (utilities = {
|
||||
KEY: "Coffeescript",
|
||||
FORMAT: function FORMAT(key, tab) {
|
||||
return "\n " + tab + key + ": " + (utilities[key].replace(/\n/g, "\n" + tab + " ") || 'undefined');
|
||||
},
|
||||
extend: 'function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.__superClass__ = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n}',
|
||||
bind: 'function(func, obj, args) {\n obj = obj || {};\n return (typeof args !== "undefined" && args !== null) ? function() {\n return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));\n } : function() {\n return func.apply(obj, arguments);\n };\n}',
|
||||
hasProp: "Object.prototype.hasOwnProperty"
|
||||
});
|
||||
})();
|
||||
@@ -188,7 +188,7 @@ exports.Expressions: class Expressions extends BaseNode
|
||||
# declarations of all inner variables pushed up to the top.
|
||||
compile_with_declarations: (o) ->
|
||||
code: @compile_node(o)
|
||||
code: "${@tab}var ${o.scope.compiled_assignments()};\n$code" if o.scope.has_assignments(this)
|
||||
code: "${@tab}var ${o.scope.compiled_assignments(@tab)};\n$code" if o.scope.has_assignments(this)
|
||||
code: "${@tab}var ${o.scope.compiled_declarations()};\n$code" if o.scope.has_declarations(this)
|
||||
code
|
||||
|
||||
@@ -420,10 +420,8 @@ exports.CurryNode: class CurryNode extends CallNode
|
||||
(new ArrayNode(@args)).compile o
|
||||
|
||||
compile_node: (o) ->
|
||||
body: Expressions.wrap([literal @body])
|
||||
curried: new CodeNode([], body)
|
||||
curry: new CodeNode([literal('func'), literal('obj'), literal('args')], Expressions.wrap([curried]))
|
||||
(new ParentheticalNode(new CallNode(curry, [@meth, @context, literal(@arguments(o))]))).compile o
|
||||
ref: new ValueNode literal(o.scope.utility('bind'))
|
||||
(new CallNode(ref, [@meth, @context, literal(@arguments(o))])).compile o
|
||||
|
||||
#### ExtendsNode
|
||||
|
||||
@@ -433,25 +431,13 @@ exports.CurryNode: class CurryNode extends CallNode
|
||||
exports.ExtendsNode: class ExtendsNode extends BaseNode
|
||||
type: 'Extends'
|
||||
|
||||
code: '''
|
||||
function(child, parent) {
|
||||
var ctor = function(){ };
|
||||
ctor.prototype = parent.prototype;
|
||||
child.__superClass__ = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.prototype.constructor = child;
|
||||
}
|
||||
'''
|
||||
|
||||
constructor: (child, parent) ->
|
||||
@children: [@child: child, @parent: parent]
|
||||
|
||||
# Hooks one constructor into another's prototype chain.
|
||||
compile_node: (o) ->
|
||||
o.scope.assign('__extends', @code, true)
|
||||
ref: new ValueNode literal('__extends')
|
||||
call: new CallNode ref, [@child, @parent]
|
||||
call.compile(o)
|
||||
ref: new ValueNode literal(o.scope.utility('extend'))
|
||||
(new CallNode ref, [@child, @parent]).compile o
|
||||
|
||||
#### AccessorNode
|
||||
|
||||
@@ -791,8 +777,8 @@ exports.CodeNode: class CodeNode extends BaseNode
|
||||
func: "function${ if @bound then '' else name_part }(${ params.join(', ') }) {$code${@idt(if @bound then 1 else 0)}}"
|
||||
func: "($func)" if top and not @bound
|
||||
return func unless @bound
|
||||
inner: "(function$name_part() {\n${@idt(2)}return __func.apply(__this, arguments);\n${@idt(1)}});"
|
||||
"(function(__this) {\n${@idt(1)}var __func = $func;\n${@idt(1)}return $inner\n$@tab})(this)"
|
||||
ref: new ValueNode literal(o.scope.utility('bind'))
|
||||
(new CallNode ref, [literal(func), literal('this')]).compile o
|
||||
|
||||
top_sensitive: ->
|
||||
true
|
||||
@@ -1161,7 +1147,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||
body: Expressions.wrap([new IfNode(@filter, body)])
|
||||
if @object
|
||||
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true)
|
||||
for_part: "$ivar in $svar) { if (__hasProp.call($svar, $ivar)"
|
||||
for_part: "$ivar in $svar) { if (${o.scope.utility('hasProp')}.call($svar, $ivar)"
|
||||
body: body.compile(merge(o, {indent: body_dent, top: true}))
|
||||
vars: if range then name else "$name, $ivar"
|
||||
close: if @object then '}}\n' else '}\n'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
# Set up exported variables for both **Node.js** and the browser.
|
||||
this.exports: this unless process?
|
||||
utilities: if process? then require('./utilities').utilities else this.utilities
|
||||
|
||||
exports.Scope: class Scope
|
||||
|
||||
@@ -19,12 +20,22 @@ exports.Scope: class Scope
|
||||
@variables: {}
|
||||
@temp_var: if @parent then @parent.temp_var else '_a'
|
||||
|
||||
# Find the top-most scope object, used for defined global variables
|
||||
topmost: ->
|
||||
if @parent then @parent.topmost() else @
|
||||
|
||||
# Look up a variable name in lexical scope, and declare it if it does not
|
||||
# already exist.
|
||||
find: (name) ->
|
||||
return true if @check name
|
||||
@variables[name]: 'var'
|
||||
false
|
||||
|
||||
# Test variables and return true the first time fn(v, k) returns true
|
||||
any: (fn) ->
|
||||
for v, k of @variables when fn(v, k)
|
||||
return true
|
||||
return false
|
||||
|
||||
# Reserve a variable name as originating from a function parameter for this
|
||||
# scope. No `var` required for internal references.
|
||||
@@ -48,18 +59,33 @@ exports.Scope: class Scope
|
||||
# Ensure that an assignment is made at the top of this scope
|
||||
# (or at the top-level scope, if requested).
|
||||
assign: (name, value, top_level) ->
|
||||
return @parent.assign(name, value, top_level) if top_level and @parent
|
||||
return @topmost().assign(name, value) if top_level and @parent
|
||||
@variables[name]: {value: value, assigned: true}
|
||||
|
||||
# Ensure the CoffeeScript utility object is included in the top level
|
||||
# then return a CallNode curried constructor bound to the utility function
|
||||
utility: (name) ->
|
||||
return @topmost().utility(name) if @parent
|
||||
@utilities: or {}
|
||||
@utilities[name]: true
|
||||
"Coffeescript.$name"
|
||||
|
||||
included_utilities: (tab) ->
|
||||
if @utilities?
|
||||
props: (utilities.FORMAT(key, tab) for key of @utilities when @utilities[key]?)
|
||||
["${utilities.KEY} = {${props.join(', ')}\n$tab}"]
|
||||
else []
|
||||
|
||||
|
||||
# Does this scope reference any variables that need to be declared in the
|
||||
# given function body?
|
||||
has_declarations: (body) ->
|
||||
body is @expressions and @declared_variables().length
|
||||
body is @expressions and @any (k, val) -> val is 'var'
|
||||
|
||||
# Does this scope reference any assignments that need to be declared at the
|
||||
# top of the given function body?
|
||||
has_assignments: (body) ->
|
||||
body is @expressions and @assigned_variables().length
|
||||
body is @expressions and (@utilities? or @any (k, val) -> val.assigned)
|
||||
|
||||
# Return the list of variables first declared in this scope.
|
||||
declared_variables: ->
|
||||
@@ -75,5 +101,5 @@ exports.Scope: class Scope
|
||||
@declared_variables().join ', '
|
||||
|
||||
# Compile the JavaScript for all of the variable assignments in this scope.
|
||||
compiled_assignments: ->
|
||||
@assigned_variables().join ', '
|
||||
compiled_assignments: (tab) ->
|
||||
[@assigned_variables()..., @included_utilities(tab)...].join ', '
|
||||
|
||||
27
src/utilities.coffee
Normal file
27
src/utilities.coffee
Normal file
@@ -0,0 +1,27 @@
|
||||
this.exports: this unless process?
|
||||
exports.utilities: utilities: {
|
||||
KEY: "Coffeescript"
|
||||
FORMAT: (key, tab) ->
|
||||
"\n $tab$key: ${utilities[key].replace(/\n/g, "\n$tab ") or 'undefined'}"
|
||||
|
||||
extend: '''
|
||||
function(child, parent) {
|
||||
var ctor = function(){ };
|
||||
ctor.prototype = parent.prototype;
|
||||
child.__superClass__ = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.prototype.constructor = child;
|
||||
}
|
||||
'''
|
||||
bind: '''
|
||||
function(func, obj, args) {
|
||||
obj = obj || {};
|
||||
return (typeof args !== "undefined" && args !== null) ? function() {
|
||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
||||
} : function() {
|
||||
return func.apply(obj, arguments);
|
||||
};
|
||||
}
|
||||
'''
|
||||
hasProp: "Object.prototype.hasOwnProperty"
|
||||
}
|
||||
Reference in New Issue
Block a user