From 6d7a04228fe06d0537d9ccc020a09d4a09c563c4 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 30 Mar 2010 19:42:09 -0400 Subject: [PATCH] another reshuffle ... removed utilities.coffee entirely. --- lib/nodes.js | 42 ++++++++++++++++++---------- lib/scope.js | 39 ++++---------------------- src/nodes.coffee | 66 ++++++++++++++++++++++++++++++++++++-------- src/scope.coffee | 23 +++------------ src/utilities.coffee | 36 ------------------------ 5 files changed, 92 insertions(+), 114 deletions(-) delete mode 100644 src/utilities.coffee diff --git a/lib/nodes.js b/lib/nodes.js index b7f2ed33..2c01cd92 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1,5 +1,5 @@ (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 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, UTILITIES, ValueNode, WhileNode, compact, del, flatten, helpers, literal, merge, statement; var __extend = function(child, parent) { var ctor = function(){ }; ctor.prototype = parent.prototype; @@ -596,10 +596,14 @@ }; CurryNode.prototype.compile_node = function compile_node(o) { var ref; - o.scope.utility('slice'); - ref = new ValueNode(literal(o.scope.utility('bind'))); + CurryNode.assign_bind(); + ref = new ValueNode(literal('__bind')); return (new CallNode(ref, [this.meth, this.context, literal(this.arguments(o))])).compile(o); }; + CurryNode.assign_bind = function assign_bind() { + Scope.root.assign('__slice', UTILITIES['slice']); + return Scope.root.assign('__bind', UTILITIES['bind']); + }; return CurryNode; }).call(this); //### ExtendsNode @@ -616,7 +620,7 @@ // Hooks one constructor into another's prototype chain. ExtendsNode.prototype.compile_node = function compile_node(o) { var ref; - ref = new ValueNode(literal(o.scope.utility('extend'))); + ref = new ValueNode(literal(Scope.root.assign('__extend', UTILITIES['extend']))); return (new CallNode(ref, [this.child, this.parent])).compile(o); }; return ExtendsNode; @@ -741,7 +745,7 @@ to = (typeof (_b = this.range.to) !== "undefined" && _b !== null) ? this.range.to : literal('null'); exclusive = this.range.exclusive ? 'true' : 'false'; v = o.scope.free_variable(); - rng = new CallNode(new ValueNode(literal(o.scope.utility('range'))), [literal(array), from, to, literal(exclusive)]); + rng = new CallNode(new ValueNode(literal(Scope.root.assign('__range', UTILITIES['range']))), [literal(array), from, to, literal(exclusive)]); args = literal("[(" + v + " = " + (rng.compile(o)) + ")[0], " + v + "[1] - " + v + "[0]].concat(" + (replace.compile(o)) + ")"); call = new CallNode(new ValueNode(literal(array), [literal('.splice.apply')]), [literal(array), args]); return call.compile(o); @@ -752,7 +756,7 @@ from = (typeof (_a = this.range.from) !== "undefined" && _a !== null) ? this.range.from : literal('null'); to = (typeof (_b = this.range.to) !== "undefined" && _b !== null) ? this.range.to : literal('null'); exclusive = this.range.exclusive ? 'true' : 'false'; - rng = new CallNode(new ValueNode(literal(o.scope.utility('range'))), [literal(array), from, to, literal(exclusive)]); + rng = new CallNode(new ValueNode(literal(Scope.root.assign('__range', UTILITIES['range']))), [literal(array), from, to, literal(exclusive)]); call = new CallNode(new ValueNode(literal(array), [literal('.slice.apply')]), [literal(array), rng]); return call.compile(o); }; @@ -1084,8 +1088,8 @@ if (!(this.bound)) { return func; } - o.scope.utility('slice'); - ref = new ValueNode(literal(o.scope.utility('bind'))); + CurryNode.assign_bind(); + ref = new ValueNode(literal('__bind')); return (new CallNode(ref, [literal(func), literal('this')])).compile(o); }; CodeNode.prototype.top_sensitive = function top_sensitive() { @@ -1156,16 +1160,15 @@ o.scope.assign(trailing.compile(o), "arguments[arguments.length - " + this.trailings.length + " + " + i + "]"); i += 1; } - return "" + name + " = " + (o.scope.utility('slice')) + ".call(arguments, " + this.index + ", arguments.length - " + (this.trailings.length) + ")"; + return "" + name + " = " + (Scope.root.assign('__slice', UTILITIES['slice'])) + ".call(arguments, " + this.index + ", arguments.length - " + (this.trailings.length) + ")"; }; // A compiling a splat as a destructuring assignment means slicing arguments // from the right-hand-side's corresponding array. SplatNode.prototype.compile_value = function compile_value(o, name, index, trailings) { - if ((typeof trailings !== "undefined" && trailings !== null)) { - return "" + (o.scope.utility('slice')) + ".call(" + name + ", " + index + ", " + (name) + ".length - " + trailings + ")"; - } else { - return "" + (o.scope.utility('slice')) + ".call(" + name + ", " + index + ")"; - } + var trail; + Scope.root.assign('__slice', UTILITIES['slice']); + trail = trailings ? ", " + (name) + ".length - " + trailings : ''; + return "__slice.call(" + name + ", " + index + trail + ")"; }; // Utility function that converts arbitrary number of elements, mixed with // splats, to a proper array @@ -1576,7 +1579,7 @@ body = PushNode.wrap(rvar, body); } this.filter ? (body = Expressions.wrap([new IfNode(this.filter, body)])) : null; - this.object ? (for_part = "" + ivar + " in " + svar + ") { if (" + (o.scope.utility('hasProp')) + ".call(" + svar + ", " + ivar + ")") : null; + this.object ? (for_part = "" + ivar + " in " + svar + ") { if (" + (Scope.root.assign('__hasProp', UTILITIES['hasProp'])) + ".call(" + svar + ", " + ivar + ")") : null; body = body.compile(merge(o, { indent: body_dent, top: true @@ -1769,6 +1772,15 @@ } } }); + // Utility Functions + // ----------------- + UTILITIES = { + 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 return function() {\n return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);\n };\n }", + range: "function(array, from, to, exclusive) {\n return [\n (from < 0 ? from + array.length : from || 0),\n (to < 0 ? to + array.length : to || array.length) + (exclusive ? 0 : 1)\n ];\n }", + hasProp: 'Object.prototype.hasOwnProperty', + slice: 'Array.prototype.slice' + }; // Constants // --------- // Tabs are two spaces for pretty printing. diff --git a/lib/scope.js b/lib/scope.js index 36249022..0855de51 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -1,5 +1,5 @@ (function(){ - var Scope, utilities; + var Scope; var __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 @@ -11,7 +11,6 @@ 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; @@ -81,38 +80,12 @@ }; // Ensure that an assignment is made at the top of this scope // (or at the top-level scope, if requested). - Scope.prototype.assign = function assign(name, value, top_level) { - if (top_level) { - return Scope.root.assign(name, value); - } + Scope.prototype.assign = function assign(name, value) { this.variables[name] = { value: value, assigned: true }; - 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) { - var _a; - if (this.parent) { - return Scope.root.utility(name); - } - if ((typeof (_a = utilities[name]) !== "undefined" && _a !== null)) { - this.utilities = this.utilities || {}; - this.utilities[name] = utilities[name]; - } - return "__" + name; - }; - // Formats an javascript object containing the utility methods required - // in the scope - Scope.prototype.included_utilities = function included_utilities() { - var _a, _b, key; - _a = []; _b = this.utilities; - for (key in _b) { if (__hasProp.call(_b, key)) { - _a.push("__" + key + " = " + (utilities[key])); - }} - return _a; + return name; }; // Does this scope reference any variables that need to be declared in the // given function body? @@ -124,9 +97,9 @@ // 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.utilities || this.any(function(k, val) { + return body === this.expressions && 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() { @@ -157,7 +130,7 @@ }; // Compile the JavaScript for all of the variable assignments in this scope. Scope.prototype.compiled_assignments = function compiled_assignments(tab) { - return this.assigned_variables().concat(this.included_utilities()).join(', '); + return this.assigned_variables().join(', '); }; return Scope; }).call(this); diff --git a/src/nodes.coffee b/src/nodes.coffee index 99b6be3f..39402f73 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -430,10 +430,15 @@ exports.CurryNode: class CurryNode extends CallNode (new ArrayNode(@args)).compile o compile_node: (o) -> - o.scope.utility('slice') - ref: new ValueNode literal(o.scope.utility('bind')) + CurryNode.assign_bind() + ref: new ValueNode literal '__bind' (new CallNode(ref, [@meth, @context, literal(@arguments(o))])).compile o + @assign_bind: -> + Scope.root.assign '__slice', UTILITIES['slice'] + Scope.root.assign '__bind', UTILITIES['bind'] + + #### ExtendsNode # Node to extend an object's prototype with an ancestor object. @@ -447,7 +452,7 @@ exports.ExtendsNode: class ExtendsNode extends BaseNode # Hooks one constructor into another's prototype chain. compile_node: (o) -> - ref: new ValueNode literal(o.scope.utility('extend')) + ref: new ValueNode literal(Scope.root.assign('__extend', UTILITIES['extend'])) (new CallNode ref, [@child, @parent]).compile o #### AccessorNode @@ -548,7 +553,7 @@ exports.SliceNode: class SliceNode extends BaseNode to: if @range.to? then @range.to else literal('null') exclusive: if @range.exclusive then 'true' else 'false' v: o.scope.free_variable() - rng: new CallNode new ValueNode(literal(o.scope.utility('range'))), [literal(array), from, to, literal(exclusive)] + rng: new CallNode new ValueNode(literal(Scope.root.assign('__range', UTILITIES['range']))), [literal(array), from, to, literal(exclusive)] args: literal "[($v = ${rng.compile(o)})[0], $v[1] - $v[0]].concat(${replace.compile(o)})" call: new CallNode new ValueNode(literal(array), [literal('.splice.apply')]), [literal(array), args] call.compile(o) @@ -558,7 +563,7 @@ exports.SliceNode: class SliceNode extends BaseNode from: if @range.from? then @range.from else literal('null') to: if @range.to? then @range.to else literal('null') exclusive: if @range.exclusive then 'true' else 'false' - rng: new CallNode new ValueNode(literal(o.scope.utility('range'))), [literal(array), from, to, literal(exclusive)] + rng: new CallNode new ValueNode(literal(Scope.root.assign('__range', UTILITIES['range']))), [literal(array), from, to, literal(exclusive)] call: new CallNode new ValueNode(literal(array), [literal('.slice.apply')]), [literal(array), rng] call.compile(o) @@ -802,8 +807,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 - o.scope.utility('slice') - ref: new ValueNode literal(o.scope.utility('bind')) + CurryNode.assign_bind() + ref: new ValueNode literal('__bind') (new CallNode ref, [literal(func), literal('this')]).compile o top_sensitive: -> @@ -847,13 +852,14 @@ exports.SplatNode: class SplatNode extends BaseNode for trailing in @trailings o.scope.assign(trailing.compile(o), "arguments[arguments.length - $@trailings.length + $i]") i: + 1 - "$name = ${o.scope.utility('slice')}.call(arguments, $@index, arguments.length - ${@trailings.length})" + "$name = ${Scope.root.assign('__slice', UTILITIES['slice'])}.call(arguments, $@index, arguments.length - ${@trailings.length})" # A compiling a splat as a destructuring assignment means slicing arguments # from the right-hand-side's corresponding array. compile_value: (o, name, index, trailings) -> - if trailings? then "${o.scope.utility('slice')}.call($name, $index, ${name}.length - $trailings)" \ - else "${o.scope.utility('slice')}.call($name, $index)" + Scope.root.assign '__slice', UTILITIES['slice'] + trail: if trailings then ", ${name}.length - $trailings" else '' + "__slice.call($name, $index$trail)" # Utility function that converts arbitrary number of elements, mixed with # splats, to a proper array @@ -1172,7 +1178,7 @@ exports.ForNode: class ForNode extends BaseNode if @filter body: Expressions.wrap([new IfNode(@filter, body)]) if @object - for_part: "$ivar in $svar) { if (${o.scope.utility('hasProp')}.call($svar, $ivar)" + for_part: "$ivar in $svar) { if (${Scope.root.assign('__hasProp', UTILITIES['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' @@ -1322,6 +1328,44 @@ ClosureNode: exports.ClosureNode: { } +# Utility Functions +# ----------------- + +UTILITIES: { + + 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) { + return function() { + return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments); + }; + } + """ + + range: """ + function(array, from, to, exclusive) { + return [ + (from < 0 ? from + array.length : from || 0), + (to < 0 ? to + array.length : to || array.length) + (exclusive ? 0 : 1) + ]; + } + """ + + hasProp: 'Object.prototype.hasOwnProperty' + + slice: 'Array.prototype.slice' + +} + # Constants # --------- diff --git a/src/scope.coffee b/src/scope.coffee index d9301da4..551eb32c 100644 --- a/src/scope.coffee +++ b/src/scope.coffee @@ -7,7 +7,6 @@ # 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 @@ -61,23 +60,9 @@ 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 Scope.root.assign(name, value) if top_level + assign: (name, value) -> @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 Scope.root.utility(name) if @parent - if utilities[name]? - @utilities: or {} - @utilities[name]: utilities[name] - "__$name" - - # Formats an javascript object containing the utility methods required - # in the scope - included_utilities: -> - "__$key = ${utilities[key]}" for key of @utilities + name # Does this scope reference any variables that need to be declared in the # given function body? @@ -87,7 +72,7 @@ exports.Scope: class Scope # 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 (@utilities or @any (k, val) -> val.assigned) + body is @expressions and @any (k, val) -> val.assigned # Return the list of variables first declared in this scope. declared_variables: -> @@ -104,4 +89,4 @@ exports.Scope: class Scope # Compile the JavaScript for all of the variable assignments in this scope. compiled_assignments: (tab) -> - [@assigned_variables()..., @included_utilities()...].join ', ' + @assigned_variables().join ', ' diff --git a/src/utilities.coffee b/src/utilities.coffee deleted file mode 100644 index abd68d6a..00000000 --- a/src/utilities.coffee +++ /dev/null @@ -1,36 +0,0 @@ -this.exports: this unless process? - -exports.utilities: { - - 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) { - return function() { - return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments); - }; - } - """ - - range: """ - function(array, from, to, exclusive) { - return [ - (from < 0 ? from + array.length : from || 0), - (to < 0 ? to + array.length : to || array.length) + (exclusive ? 0 : 1) - ]; - } - """ - - hasProp: 'Object.prototype.hasOwnProperty' - - slice: 'Array.prototype.slice' - -} \ No newline at end of file