merging in gfxmonk's major refactor to the way that returns are pushed down into the interior of expressions

This commit is contained in:
Jeremy Ashkenas
2010-03-21 11:28:05 -04:00
parent ce7c0d176b
commit 80230414a2
12 changed files with 162 additions and 320 deletions

View File

@@ -71,12 +71,11 @@ task 'doc:underscore', 'rebuild the Underscore.coffee documentation page', ->
task 'test', 'run the CoffeeScript language test suite', -> task 'test', 'run the CoffeeScript language test suite', ->
helpers.extend global, require 'assert' helpers.extend global, require 'assert'
passed_test_count: 0 passed_tests: failed_tests: 0
failed_test_count: 0
start_time: new Date() start_time: new Date()
[original_ok, original_throws]: [ok, throws] original_ok: ok
helpers.extend global, { helpers.extend global, {
ok: (args...) -> passed_test_count += 1; original_ok(args...) ok: (args...) -> passed_tests += 1; original_ok(args...)
CoffeeScript: CoffeeScript CoffeeScript: CoffeeScript
} }
red: '\033[0;31m' red: '\033[0;31m'
@@ -84,24 +83,17 @@ task 'test', 'run the CoffeeScript language test suite', ->
reset: '\033[0m' reset: '\033[0m'
on_exit: -> on_exit: ->
time: ((new Date() - start_time) / 1000).toFixed(2) time: ((new Date() - start_time) / 1000).toFixed(2)
if failed_test_count > 0 message: "passed $passed_tests tests in $time seconds$reset"
puts "${red}FAILED " + failed_test_count + " and passed " + passed_test_count + " tests in " + time + " seconds${reset}" puts(if failed_tests then "${red}failed $failed_tests and $message" else "$green$message")
else
puts "${green}passed " + passed_test_count + " tests in " + time + " seconds${reset}"
process.addListener 'exit', on_exit process.addListener 'exit', on_exit
fs.readdir 'test', (err, files) -> fs.readdir 'test', (err, files) ->
files.forEach (file) -> files.forEach (file) ->
return unless file.match(/\.coffee$/i) return unless file.match(/\.coffee$/i)
source: path.join 'test', file source: path.join 'test', file
print " $file " fs.readFile source, (err, code) ->
code = fs.readFileSync source try
try CoffeeScript.run code, {source: source}
CoffeeScript.run code, {source: source} catch err
puts "${green}ok${reset}" failed_tests += 1
catch err puts "${red}failed:${reset} $source"
failed_test_count += 1 puts err.stack
puts "${red}FAILED!${reset}\n"
puts "Failed test: $source"
puts err
puts ''
process.exit(if failed_test_count == 0 then 0 else 1)

View File

@@ -68,7 +68,6 @@
_a.push(invoke(arg)); _a.push(invoke(arg));
} }
return _a; return _a;
return null;
}); });
}; };
// Display the list of Cake tasks in a format similar to `rake -T` // Display the list of Cake tasks in a format similar to `rake -T`
@@ -85,14 +84,12 @@
_b.push(' '); _b.push(' ');
} }
return _b; return _b;
return null;
}).call(this).join('') : ''; }).call(this).join('') : '';
puts("cake " + name + spaces + " # " + (task.description)); puts("cake " + name + spaces + " # " + (task.description));
}} }}
if (switches.length) { if (switches.length) {
return puts(oparse.help()); return puts(oparse.help());
} }
return null;
}; };
// Print an error and exit when attempting to all an undefined task. // Print an error and exit when attempting to all an undefined task.
no_such_task = function no_such_task(task) { no_such_task = function no_such_task(task) {

View File

@@ -37,9 +37,7 @@
err.message = "In " + (options.source) + ", " + (err.message); err.message = "In " + (options.source) + ", " + (err.message);
} }
throw err; throw err;
return null;
} }
return null;
}); });
// Tokenize a string of CoffeeScript code, and return the array of tokens. // Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = function tokens(code) { exports.tokens = function tokens(code) {
@@ -103,7 +101,6 @@
tag.type === 'text/coffeescript' ? _a.push(eval(exports.compile(tag.innerHTML))) : null; tag.type === 'text/coffeescript' ? _a.push(eval(exports.compile(tag.innerHTML))) : null;
} }
return _a; return _a;
return null;
}; };
if (window.addEventListener) { if (window.addEventListener) {
window.addEventListener('load', process_scripts, false); window.addEventListener('load', process_scripts, false);

View File

@@ -74,7 +74,6 @@
_a.push(compile(source)); _a.push(compile(source));
} }
return _a; return _a;
return null;
}; };
// Compile a single source script, containing the given code, according to the // Compile a single source script, containing the given code, according to the
// requested options. Both compile_scripts and watch_scripts share this method // requested options. Both compile_scripts and watch_scripts share this method
@@ -100,18 +99,14 @@
} else if (o.lint) { } else if (o.lint) {
return lint(js); return lint(js);
} }
return null;
} }
return null;
} catch (err) { } catch (err) {
if (o.watch) { if (o.watch) {
return puts(err.message); return puts(err.message);
} else { } else {
throw err; throw err;
} }
return null;
} }
return null;
}; };
// Attach the appropriate listeners to compile scripts incoming over **stdin**, // Attach the appropriate listeners to compile scripts incoming over **stdin**,
// and write them back to **stdout**. // and write them back to **stdout**.
@@ -123,7 +118,6 @@
if (string) { if (string) {
return code += string; return code += string;
} }
return null;
}); });
return process.stdio.addListener('close', function() { return process.stdio.addListener('close', function() {
return compile_script('stdio', code); return compile_script('stdio', code);
@@ -153,7 +147,6 @@
_a.push(watch(source)); _a.push(watch(source));
} }
return _a; return _a;
return null;
}; };
// Write out a JavaScript source file with the compiled code. By default, files // Write out a JavaScript source file with the compiled code. By default, files
// are written out in `cwd` as `.js` files with the same name, but the output // are written out in `cwd` as `.js` files with the same name, but the output
@@ -174,13 +167,11 @@
if (result) { if (result) {
return puts(result.replace(/\n/g, '')); return puts(result.replace(/\n/g, ''));
} }
return null;
}); });
jsl.addListener('error', function(result) { jsl.addListener('error', function(result) {
if (result) { if (result) {
return puts(result); return puts(result);
} }
return null;
}); });
jsl.write(js); jsl.write(js);
return jsl.close(); return jsl.close();
@@ -200,7 +191,6 @@
}).call(this)); }).call(this));
} }
return _a; return _a;
return null;
}).call(this); }).call(this);
return puts(strings.join(' ')); return puts(strings.join(' '));
}; };

View File

@@ -645,7 +645,6 @@
}).call(this)); }).call(this));
} }
return _b; return _b;
return null;
}).call(this); }).call(this);
}} }}
// Initialize the **Parser** with our list of terminal **tokens**, our **grammar** // Initialize the **Parser** with our list of terminal **tokens**, our **grammar**

View File

@@ -26,7 +26,6 @@
item ? _a.push(item) : null; item ? _a.push(item) : null;
} }
return _a; return _a;
return null;
}); });
// Count the number of occurences of a character in a string. // Count the number of occurences of a character in a string.
helpers.count = (count = function count(string, letter) { helpers.count = (count = function count(string, letter) {
@@ -69,7 +68,6 @@
_a.push(((object[key] = val))); _a.push(((object[key] = val)));
}} }}
return _a; return _a;
return null;
}); });
// Return a completely flattened version of an array. Handy for getting a // Return a completely flattened version of an array. Handy for getting a
// list of `children` from the nodes. // list of `children` from the nodes.

View File

@@ -27,9 +27,7 @@
// tokens. Some potential ambiguity in the grammar has been avoided by // tokens. Some potential ambiguity in the grammar has been avoided by
// pushing some extra smarts into the Lexer. // pushing some extra smarts into the Lexer.
exports.Lexer = (function() { exports.Lexer = (function() {
Lexer = function Lexer() { Lexer = function Lexer() { };
return null;
};
// **tokenize** is the Lexer's main method. Scan by attempting to match tokens // **tokenize** is the Lexer's main method. Scan by attempting to match tokens
// one at a time, using a regular expression anchored at the start of the // one at a time, using a regular expression anchored at the start of the
// remaining code, or a custom recursive token-matching method // remaining code, or a custom recursive token-matching method
@@ -388,7 +386,6 @@
return this.tag(1, 'PROPERTY_ACCESS'); return this.tag(1, 'PROPERTY_ACCESS');
} }
} }
return null;
}; };
// Sanitize a heredoc by escaping internal double quotes and erasing all // Sanitize a heredoc by escaping internal double quotes and erasing all
// external indentation on the left-hand side. // external indentation on the left-hand side.
@@ -430,13 +427,11 @@
// an identifier. // an identifier.
Lexer.prototype.identifier_error = function identifier_error(word) { Lexer.prototype.identifier_error = function identifier_error(word) {
throw new Error("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1)); throw new Error("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1));
return null;
}; };
// The error for when you try to assign to a reserved word in JavaScript, // The error for when you try to assign to a reserved word in JavaScript,
// like "function" or "default". // like "function" or "default".
Lexer.prototype.assignment_error = function assignment_error() { Lexer.prototype.assignment_error = function assignment_error() {
throw new Error("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); throw new Error("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
return null;
}; };
// Expand variables and expressions inside double-quoted strings using // Expand variables and expressions inside double-quoted strings using
// [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation) // [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation)
@@ -519,7 +514,6 @@
} }
return tokens; return tokens;
} }
return null;
}; };
// Helpers // Helpers
// ------- // -------
@@ -573,7 +567,6 @@
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE); return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);
}; };
return Lexer; return Lexer;
return null;
}).call(this); }).call(this);
// There are no exensions to the core lexer by default. // There are no exensions to the core lexer by default.
Lexer.extensions = []; Lexer.extensions = [];

View File

@@ -40,7 +40,6 @@
}; };
return klass.prototype.is_pure_statement; return klass.prototype.is_pure_statement;
} }
return null;
}; };
//### BaseNode //### BaseNode
// The **BaseNode** is the abstract base class for all nodes in the syntax tree. // The **BaseNode** is the abstract base class for all nodes in the syntax tree.
@@ -53,9 +52,7 @@
// being requested by the surrounding function), information about the current // being requested by the surrounding function), information about the current
// scope, and indentation level. // scope, and indentation level.
exports.BaseNode = (function() { exports.BaseNode = (function() {
BaseNode = function BaseNode() { BaseNode = function BaseNode() { };
return null;
};
// Common logic for determining whether to wrap this node in a closure before // Common logic for determining whether to wrap this node in a closure before
// compiling it, or to compile directly. We need to wrap if this node is a // compiling it, or to compile directly. We need to wrap if this node is a
// *statement*, and it's not a *pure_statement*, and we're not at // *statement*, and it's not a *pure_statement*, and we're not at
@@ -73,10 +70,12 @@
del(this.options, 'operation'); del(this.options, 'operation');
} }
top = this.top_sensitive() ? this.options.top : del(this.options, 'top'); top = this.top_sensitive() ? this.options.top : del(this.options, 'top');
closure = this.is_statement() && !this.is_pure_statement() && !top && !this.options.returns && !(this instanceof CommentNode) && !(this.contains(function(n) { closure = this.is_statement() && !this.is_pure_statement() && !top && !this.options.as_statement && !(this instanceof CommentNode) && !this.contains_pure_statement();
return n.is_pure_statement(); if (closure) {
})); return this.compile_closure(this.options);
return closure ? this.compile_closure(this.options) : this.compile_node(this.options); } else {
return this.compile_node(this.options);
}
}; };
// 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.
@@ -104,13 +103,10 @@
} }
return idt; return idt;
}; };
// construct a node that returns the current node's result // Construct a node that returns the current node's result.
// note that this is overridden for smarter behaviour for // Note that this is overridden for smarter behavior for
// many statement nodes (eg IfNode, ForNode) // many statement nodes (eg IfNode, ForNode)...
BaseNode.prototype.make_return = function make_return() { BaseNode.prototype.make_return = function make_return() {
if (this.is_statement()) {
throw new Error("Can't convert statement " + (this) + " into a return value!");
}
return new ReturnNode(this); return new ReturnNode(this);
}; };
// Does this node, or any of its children, contain a node of a certain kind? // Does this node, or any of its children, contain a node of a certain kind?
@@ -131,6 +127,13 @@
} }
return false; return false;
}; };
// Convenience for the most common use of contains. Does the node contain
// a pure statement?
BaseNode.prototype.contains_pure_statement = function contains_pure_statement() {
return this.is_pure_statement() || this.contains(function(n) {
return n.is_pure_statement();
});
};
// Perform an in-order traversal of the AST. Crosses scope boundaries. // Perform an in-order traversal of the AST. Crosses scope boundaries.
BaseNode.prototype.traverse = function traverse(block) { BaseNode.prototype.traverse = function traverse(block) {
var _a, _b, _c, _d, node; var _a, _b, _c, _d, node;
@@ -142,11 +145,9 @@
if (node.traverse) { if (node.traverse) {
return node.traverse(block); return node.traverse(block);
} }
return null;
}).call(this)); }).call(this));
} }
return _a; return _a;
return null;
}; };
// `toString` representation of the node, for inspecting the parse tree. // `toString` representation of the node, for inspecting the parse tree.
// This is what `coffee --nodes` prints out. // This is what `coffee --nodes` prints out.
@@ -160,7 +161,6 @@
_a.push(child.toString(idt + TAB)); _a.push(child.toString(idt + TAB));
} }
return _a; return _a;
return null;
}).call(this).join(''); }).call(this).join('');
}; };
// Default implementations of the common node identification methods. Nodes // Default implementations of the common node identification methods. Nodes
@@ -179,7 +179,6 @@
return false; return false;
}; };
return BaseNode; return BaseNode;
return null;
}).call(this); }).call(this);
//### Expressions //### Expressions
// The expressions body is the list of expressions that forms the body of an // The expressions body is the list of expressions that forms the body of an
@@ -215,41 +214,24 @@
Expressions.prototype.empty = function empty() { Expressions.prototype.empty = function empty() {
return this.expressions.length === 0; return this.expressions.length === 0;
}; };
// make a copy of this node // Make a copy of this node.
Expressions.prototype.copy = function copy() { Expressions.prototype.copy = function copy() {
return new Expressions(this.children.slice()); return new Expressions(this.children.slice());
}; };
// an Expressions node does not return its entire body, rather it // An Expressions node does not return its entire body, rather it
// ensures that the final expression is returned // ensures that the final expression is returned.
Expressions.prototype.make_return = function make_return() { Expressions.prototype.make_return = function make_return() {
var _a, _b, _c, _d, converted, i, last_expr, last_expr_idx; var idx, last;
last_expr_idx = -1; idx = this.expressions.length - 1;
_c = this.expressions.length - 1; _d = 0; last = this.expressions[idx];
for (_b = 0, i = _c; (_c <= _d ? i <= _d : i >= _d); (_c <= _d ? i += 1 : i -= 1), _b++) { if (last instanceof CommentNode) {
if (!(this.expressions[i] instanceof CommentNode)) { last = this.expressions[idx -= 1];
last_expr_idx = i;
last_expr = this.expressions[i];
break;
}
} }
if (last_expr_idx < 0) { if (!last || last instanceof ReturnNode) {
// just add a return null to ensure return is always called return this;
this.push(new ReturnNode(literal(null))); }
} else { if (!(last.contains_pure_statement())) {
if ((last_expr instanceof ReturnNode)) { this.expressions[idx] = last.make_return();
return this;
}
if (last_expr.is_statement()) {
this.push(new ReturnNode(literal(null)));
}
// we still make an attempt at converting statements,
// since many are able to be returned in some fashion
try {
converted = last_expr.make_return();
this.expressions[last_expr_idx] = converted;
} catch (e) {
// ignore
}
} }
return this; return this;
}; };
@@ -271,7 +253,6 @@
_a.push(this.compile_expression(node, merge(o))); _a.push(this.compile_expression(node, merge(o)));
} }
return _a; return _a;
return null;
}).call(this).join("\n"); }).call(this).join("\n");
}; };
// If we happen to be the top-level **Expressions**, wrap everything in // If we happen to be the top-level **Expressions**, wrap everything in
@@ -305,19 +286,18 @@
// return the result, and it's an expression, simply return it. If it's a // return the result, and it's an expression, simply return it. If it's a
// statement, ask the statement to do so. // statement, ask the statement to do so.
Expressions.prototype.compile_expression = function compile_expression(node, o) { Expressions.prototype.compile_expression = function compile_expression(node, o) {
var compiled_node, stmt; var compiled_node;
this.tab = o.indent; this.tab = o.indent;
stmt = node.is_statement();
compiled_node = node.compile(merge(o, { compiled_node = node.compile(merge(o, {
top: true top: true
})); }));
if (stmt) { if (node.is_statement()) {
return compiled_node; return compiled_node;
} else {
return '' + (this.idt()) + compiled_node + ";";
} }
return '' + (this.idt()) + (compiled_node) + ";";
}; };
return Expressions; return Expressions;
return null;
}).call(this); }).call(this);
// Wrap up the given nodes as an **Expressions**, unless it already happens // Wrap up the given nodes as an **Expressions**, unless it already happens
// to be one. // to be one.
@@ -355,7 +335,6 @@
return " \"" + this.value + "\""; return " \"" + this.value + "\"";
}; };
return LiteralNode; return LiteralNode;
return null;
}).call(this); }).call(this);
//### ReturnNode //### ReturnNode
// A `return` is a *pure_statement* -- wrapping it in a closure wouldn't // A `return` is a *pure_statement* -- wrapping it in a closure wouldn't
@@ -368,28 +347,12 @@
__extends(ReturnNode, BaseNode); __extends(ReturnNode, BaseNode);
ReturnNode.prototype.type = 'Return'; ReturnNode.prototype.type = 'Return';
ReturnNode.prototype.compile_node = function compile_node(o) { ReturnNode.prototype.compile_node = function compile_node(o) {
var compiled_expr;
if (this.expression.is_statement()) { if (this.expression.is_statement()) {
return this.compile_statement(o); o.as_statement = true;
} else {
compiled_expr = this.expression.compile(o);
return '' + (this.tab) + "return " + (compiled_expr) + ";";
} }
return null; return '' + (this.tab) + "return " + (this.expression.compile(o)) + ";";
};
ReturnNode.prototype.compile_statement = function compile_statement(o) {
var assign, ret, temp_var;
// split statement into computation and return, via a temporary variable
temp_var = literal(o.scope.free_variable());
assign = new AssignNode(temp_var, this.expression);
ret = new ReturnNode(temp_var);
return [assign.compile(merge(o, {
as_statement: true
})), ret.compile(o)
].join("\n");
}; };
return ReturnNode; return ReturnNode;
return null;
}).call(this); }).call(this);
statement(ReturnNode, true); statement(ReturnNode, true);
//### ValueNode //### ValueNode
@@ -424,12 +387,11 @@
return this.has_properties() && this.properties[this.properties.length - 1] instanceof SliceNode; return this.has_properties() && this.properties[this.properties.length - 1] instanceof SliceNode;
}; };
ValueNode.prototype.make_return = function make_return() { ValueNode.prototype.make_return = function make_return() {
if (!this.has_properties()) { if (this.has_properties()) {
return this.base.make_return();
} else {
return ValueNode.__superClass__.make_return.call(this); return ValueNode.__superClass__.make_return.call(this);
} else {
return this.base.make_return();
} }
return null;
}; };
// The value can be unwrapped as its inner node, if there are no attached // The value can be unwrapped as its inner node, if there are no attached
// properties. // properties.
@@ -485,7 +447,6 @@
} }
}; };
return ValueNode; return ValueNode;
return null;
}).call(this); }).call(this);
//### CommentNode //### CommentNode
// CoffeeScript passes through comments as JavaScript comments at the // CoffeeScript passes through comments as JavaScript comments at the
@@ -498,11 +459,13 @@
}; };
__extends(CommentNode, BaseNode); __extends(CommentNode, BaseNode);
CommentNode.prototype.type = 'Comment'; CommentNode.prototype.type = 'Comment';
CommentNode.prototype.make_return = function make_return() {
return this;
};
CommentNode.prototype.compile_node = function compile_node(o) { CommentNode.prototype.compile_node = function compile_node(o) {
return '' + this.tab + "//" + this.lines.join("\n" + this.tab + "//"); return '' + this.tab + "//" + this.lines.join("\n" + this.tab + "//");
}; };
return CommentNode; return CommentNode;
return null;
}).call(this); }).call(this);
statement(CommentNode); statement(CommentNode);
//### CallNode //### CallNode
@@ -510,8 +473,15 @@
// calls against the prototype's function of the same name. // calls against the prototype's function of the same name.
exports.CallNode = (function() { exports.CallNode = (function() {
CallNode = function CallNode(variable, args) { CallNode = function CallNode(variable, args) {
this.children = flatten([(this.variable = variable), (this.args = (args || []))]);
this.is_new = false; this.is_new = false;
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]));
return this; return this;
}; };
__extends(CallNode, BaseNode); __extends(CallNode, BaseNode);
@@ -545,9 +515,8 @@
_d.push(arg.compile(o)); _d.push(arg.compile(o));
} }
return _d; return _d;
return null;
}).call(this).join(', '); }).call(this).join(', ');
if (this.variable === 'super') { if (this.is_super) {
return this.compile_super(args, o); return this.compile_super(args, o);
} }
return '' + (this.prefix()) + (this.variable.compile(o)) + "(" + args + ")"; return '' + (this.prefix()) + (this.variable.compile(o)) + "(" + args + ")";
@@ -574,7 +543,6 @@
return '' + (this.prefix()) + (meth) + ".apply(" + obj + ", " + (this.compile_splat_arguments(o)) + ")"; return '' + (this.prefix()) + (meth) + ".apply(" + obj + ", " + (this.compile_splat_arguments(o)) + ")";
}; };
return CallNode; return CallNode;
return null;
}).call(this); }).call(this);
//### CurryNode //### CurryNode
// Node to bind an context and/or some arguments to a function, returning a new function // Node to bind an context and/or some arguments to a function, returning a new function
@@ -583,9 +551,9 @@
CurryNode = function CurryNode(meth, args) { CurryNode = function CurryNode(meth, args) {
this.children = flatten([(this.meth = meth), (this.context = args[0]), (this.args = (args.slice(1) || []))]); this.children = flatten([(this.meth = meth), (this.context = args[0]), (this.args = (args.slice(1) || []))]);
this.compile_splat_arguments = (function(func, obj, args) { this.compile_splat_arguments = (function(func, obj, args) {
return (function() { return function() {
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0))); return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
}); };
}(SplatNode.compile_mixed_array, this, [this.args])); }(SplatNode.compile_mixed_array, this, [this.args]));
return this; return this;
}; };
@@ -611,7 +579,6 @@
return (new ParentheticalNode(new CallNode(curry, [this.meth, this.context, literal(this.arguments(o))]))).compile(o); return (new ParentheticalNode(new CallNode(curry, [this.meth, this.context, literal(this.arguments(o))]))).compile(o);
}; };
return CurryNode; return CurryNode;
return null;
}).call(this); }).call(this);
//### ExtendsNode //### ExtendsNode
// Node to extend an object's prototype with an ancestor object. // Node to extend an object's prototype with an ancestor object.
@@ -634,7 +601,6 @@
return call.compile(o); return call.compile(o);
}; };
return ExtendsNode; return ExtendsNode;
return null;
}).call(this); }).call(this);
//### AccessorNode //### AccessorNode
// A `.` accessor into a property of a value, or the `::` shorthand for // A `.` accessor into a property of a value, or the `::` shorthand for
@@ -655,7 +621,6 @@
return "." + proto_part + (this.name.compile(o)); return "." + proto_part + (this.name.compile(o));
}; };
return AccessorNode; return AccessorNode;
return null;
}).call(this); }).call(this);
//### IndexNode //### IndexNode
// A `[ ... ]` indexed accessor into an array or object. // A `[ ... ]` indexed accessor into an array or object.
@@ -673,7 +638,6 @@
return "[" + idx + "]"; return "[" + idx + "]";
}; };
return IndexNode; return IndexNode;
return null;
}).call(this); }).call(this);
//### RangeNode //### RangeNode
// A range literal. Ranges can be used to extract portions (slices) of arrays, // A range literal. Ranges can be used to extract portions (slices) of arrays,
@@ -730,7 +694,6 @@
return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o); return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o);
}; };
return RangeNode; return RangeNode;
return null;
}).call(this); }).call(this);
//### SliceNode //### SliceNode
// An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter // An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter
@@ -752,7 +715,6 @@
return ".slice(" + from + ", " + to + plus_part + ")"; return ".slice(" + from + ", " + to + plus_part + ")";
}; };
return SliceNode; return SliceNode;
return null;
}).call(this); }).call(this);
//### ObjectNode //### ObjectNode
// An object literal, nothing fancy. // An object literal, nothing fancy.
@@ -777,7 +739,6 @@
!(prop instanceof CommentNode) ? _a.push(prop) : null; !(prop instanceof CommentNode) ? _a.push(prop) : null;
} }
return _a; return _a;
return null;
}).call(this); }).call(this);
last_noncom = non_comments[non_comments.length - 1]; last_noncom = non_comments[non_comments.length - 1];
props = (function() { props = (function() {
@@ -797,14 +758,12 @@
}).call(this)); }).call(this));
} }
return _e; return _e;
return null;
}).call(this); }).call(this);
props = props.join(''); props = props.join('');
inner = props ? '\n' + props + '\n' + this.idt() : ''; inner = props ? '\n' + props + '\n' + this.idt() : '';
return "{" + inner + "}"; return "{" + inner + "}";
}; };
return ObjectNode; return ObjectNode;
return null;
}).call(this); }).call(this);
//### ArrayNode //### ArrayNode
// An array literal. // An array literal.
@@ -812,9 +771,9 @@
ArrayNode = function ArrayNode(objects) { ArrayNode = function ArrayNode(objects) {
this.children = (this.objects = objects || []); this.children = (this.objects = objects || []);
this.compile_splat_literal = (function(func, obj, args) { this.compile_splat_literal = (function(func, obj, args) {
return (function() { return function() {
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0))); return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
}); };
}(SplatNode.compile_mixed_array, this, [this.objects])); }(SplatNode.compile_mixed_array, this, [this.objects]));
return this; return this;
}; };
@@ -823,38 +782,33 @@
ArrayNode.prototype.compile_node = function compile_node(o) { ArrayNode.prototype.compile_node = function compile_node(o) {
var _a, _b, code, ending, i, obj, objects; var _a, _b, code, ending, i, obj, objects;
o.indent = this.idt(1); o.indent = this.idt(1);
objects = (function() { objects = [];
_a = []; _b = this.objects; _a = this.objects;
for (i = 0, _c = _b.length; i < _c; i++) { for (i = 0, _b = _a.length; i < _b; i++) {
obj = _b[i]; obj = _a[i];
_a.push((function() { code = obj.compile(o);
code = obj.compile(o); if (obj instanceof SplatNode) {
if (obj instanceof CommentNode) { return this.compile_splat_literal(this.objects, o);
return "\n" + code + "\n" + o.indent; } else if (obj instanceof CommentNode) {
} else if (i === this.objects.length - 1) { objects.push("\n" + code + "\n" + o.indent);
return code; } else if (i === this.objects.length - 1) {
} else { objects.push(code);
return '' + code + ", "; } else {
} objects.push('' + code + ", ");
return null;
}).call(this));
} }
return _a; }
return null;
}).call(this);
objects = objects.join(''); objects = objects.join('');
ending = objects.indexOf('\n') >= 0 ? "\n" + this.tab + "]" : ']'; ending = objects.indexOf('\n') >= 0 ? "\n" + this.tab + "]" : ']';
return "[" + objects + ending; return "[" + objects + ending;
}; };
return ArrayNode; return ArrayNode;
return null;
}).call(this); }).call(this);
//### ClassNode //### ClassNode
// The CoffeeScript class definition. // The CoffeeScript class definition.
exports.ClassNode = (function() { exports.ClassNode = (function() {
ClassNode = function ClassNode(variable, parent, props) { ClassNode = function ClassNode(variable, parent, props) {
this.children = compact(flatten([(this.variable = variable), (this.parent = parent), (this.properties = props || [])])); this.children = compact(flatten([(this.variable = variable), (this.parent = parent), (this.properties = props || [])]));
this.do_return = false; this.returns = false;
return this; return this;
}; };
__extends(ClassNode, BaseNode); __extends(ClassNode, BaseNode);
@@ -862,7 +816,7 @@
// Initialize a **ClassNode** with its name, an optional superclass, and a // Initialize a **ClassNode** with its name, an optional superclass, and a
// list of prototype property assignments. // list of prototype property assignments.
ClassNode.prototype.make_return = function make_return() { ClassNode.prototype.make_return = function make_return() {
this.do_return = true; this.returns = true;
return this; return this;
}; };
// Instead of generating the JavaScript string directly, we build up the // Instead of generating the JavaScript string directly, we build up the
@@ -897,14 +851,13 @@
constructor = new AssignNode(this.variable, new CodeNode()); constructor = new AssignNode(this.variable, new CodeNode());
} }
} }
this.do_return ? (returns = new ReturnNode(this.variable).compile(o)) : (returns = '');
construct = this.idt() + constructor.compile(o) + ';\n'; construct = this.idt() + constructor.compile(o) + ';\n';
props = props.empty() ? '' : props.compile(o) + '\n'; props = props.empty() ? '' : props.compile(o) + '\n';
extension = extension ? this.idt() + extension.compile(o) + ';\n' : ''; extension = extension ? this.idt() + extension.compile(o) + ';\n' : '';
returns = this.returns ? new ReturnNode(this.variable).compile(o) : '';
return '' + construct + extension + props + returns; return '' + construct + extension + props + returns;
}; };
return ClassNode; return ClassNode;
return null;
}).call(this); }).call(this);
statement(ClassNode); statement(ClassNode);
//### AssignNode //### AssignNode
@@ -970,10 +923,11 @@
if (stmt) { if (stmt) {
return '' + this.tab + val + ";"; return '' + this.tab + val + ";";
} }
if (!top) { if (top) {
val = "(" + val + ")"; return val;
} else {
return "(" + val + ")";
} }
return val;
}; };
// Brief implementation of recursive pattern matching, when assigning array or // Brief implementation of recursive pattern matching, when assigning array or
// object literals to a value. Peeks at their properties to assign inner names. // object literals to a value. Peeks at their properties to assign inner names.
@@ -1025,7 +979,6 @@
return '' + (name) + ".splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + val + "))"; return '' + (name) + ".splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + val + "))";
}; };
return AssignNode; return AssignNode;
return null;
}).call(this); }).call(this);
//### CodeNode //### CodeNode
// A function definition. This is the only node that creates a new Scope. // A function definition. This is the only node that creates a new Scope.
@@ -1079,7 +1032,6 @@
_d.push(param.compile(o)); _d.push(param.compile(o));
} }
return _d; return _d;
return null;
}).call(this); }).call(this);
this.body.make_return(); this.body.make_return();
_h = params; _h = params;
@@ -1117,7 +1069,6 @@
_a.push(child.traverse(block)); _a.push(child.traverse(block));
} }
return _a; return _a;
return null;
}; };
CodeNode.prototype.toString = function toString(idt) { CodeNode.prototype.toString = function toString(idt) {
var _a, _b, _c, _d, child, children; var _a, _b, _c, _d, child, children;
@@ -1129,12 +1080,10 @@
_a.push(child.toString(idt + TAB)); _a.push(child.toString(idt + TAB));
} }
return _a; return _a;
return null;
}).call(this).join(''); }).call(this).join('');
return "\n" + idt + children; return "\n" + idt + children;
}; };
return CodeNode; return CodeNode;
return null;
}).call(this); }).call(this);
//### SplatNode //### SplatNode
// A splat, either as a parameter to a function, an argument to a call, // A splat, either as a parameter to a function, an argument to a call,
@@ -1178,7 +1127,6 @@
return "Array.prototype.slice.call(" + name + ", " + index + ")"; return "Array.prototype.slice.call(" + name + ", " + index + ")";
}; };
return SplatNode; return SplatNode;
return null;
}).call(this); }).call(this);
// Utility function that converts arbitrary number of elements, mixed with // Utility function that converts arbitrary number of elements, mixed with
// splats, to a proper array // splats, to a proper array
@@ -1224,7 +1172,7 @@
return this; return this;
}; };
WhileNode.prototype.make_return = function make_return() { WhileNode.prototype.make_return = function make_return() {
this.do_return = true; this.returns = true;
return this; return this;
}; };
WhileNode.prototype.top_sensitive = function top_sensitive() { WhileNode.prototype.top_sensitive = function top_sensitive() {
@@ -1235,7 +1183,7 @@
// return an array containing the computed result of each iteration. // return an array containing the computed result of each iteration.
WhileNode.prototype.compile_node = function compile_node(o) { WhileNode.prototype.compile_node = function compile_node(o) {
var cond, post, pre, rvar, set, top; var cond, post, pre, rvar, set, top;
top = del(o, 'top') && !this.do_return; top = del(o, 'top') && !this.returns;
o.indent = this.idt(1); o.indent = this.idt(1);
o.top = true; o.top = true;
cond = this.condition.compile(o); cond = this.condition.compile(o);
@@ -1254,13 +1202,12 @@
if (this.filter) { if (this.filter) {
this.body = Expressions.wrap([new IfNode(this.filter, this.body)]); this.body = Expressions.wrap([new IfNode(this.filter, this.body)]);
} }
this.do_return ? (post = new ReturnNode(literal(rvar)).compile(merge(o, { this.returns ? (post = new ReturnNode(literal(rvar)).compile(merge(o, {
indent: this.idt() indent: this.idt()
}))) : (post = ''); }))) : (post = '');
return '' + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}\n" + post; return '' + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}\n" + post;
}; };
return WhileNode; return WhileNode;
return null;
}).call(this); }).call(this);
statement(WhileNode); statement(WhileNode);
//### OpNode //### OpNode
@@ -1370,7 +1317,6 @@
return parts.join(''); return parts.join('');
}; };
return OpNode; return OpNode;
return null;
}).call(this); }).call(this);
//### TryNode //### TryNode
// A classic *try/catch/finally* block. // A classic *try/catch/finally* block.
@@ -1405,7 +1351,6 @@
return '' + (this.tab) + "try {\n" + attempt_part + "\n" + this.tab + "}" + catch_part + finally_part; return '' + (this.tab) + "try {\n" + attempt_part + "\n" + this.tab + "}" + catch_part + finally_part;
}; };
return TryNode; return TryNode;
return null;
}).call(this); }).call(this);
statement(TryNode); statement(TryNode);
//### ThrowNode //### ThrowNode
@@ -1417,15 +1362,14 @@
}; };
__extends(ThrowNode, BaseNode); __extends(ThrowNode, BaseNode);
ThrowNode.prototype.type = 'Throw'; ThrowNode.prototype.type = 'Throw';
// A **ThrowNode** is already a return, of sorts...
ThrowNode.prototype.make_return = function make_return() { ThrowNode.prototype.make_return = function make_return() {
// a throw is already a return...
return this; return this;
}; };
ThrowNode.prototype.compile_node = function compile_node(o) { ThrowNode.prototype.compile_node = function compile_node(o) {
return '' + (this.tab) + "throw " + (this.expression.compile(o)) + ";"; return '' + (this.tab) + "throw " + (this.expression.compile(o)) + ";";
}; };
return ThrowNode; return ThrowNode;
return null;
}).call(this); }).call(this);
statement(ThrowNode); statement(ThrowNode);
//### ExistenceNode //### ExistenceNode
@@ -1443,7 +1387,6 @@
return ExistenceNode.compile_test(o, this.expression); return ExistenceNode.compile_test(o, this.expression);
}; };
return ExistenceNode; return ExistenceNode;
return null;
}).call(this); }).call(this);
// The meat of the **ExistenceNode** is in this static `compile_test` method // The meat of the **ExistenceNode** is in this static `compile_test` method
// because other nodes like to check the existence of their variables as well. // because other nodes like to check the existence of their variables as well.
@@ -1494,7 +1437,6 @@
return "(" + code + ")"; return "(" + code + ")";
}; };
return ParentheticalNode; return ParentheticalNode;
return null;
}).call(this); }).call(this);
//### ForNode //### ForNode
// CoffeeScript's replacement for the *for* loop is our array and object // CoffeeScript's replacement for the *for* loop is our array and object
@@ -1519,7 +1461,7 @@
this.index = _a[1]; this.index = _a[1];
} }
this.children = compact([this.body, this.source, this.filter]); this.children = compact([this.body, this.source, this.filter]);
this.do_return = false; this.returns = false;
return this; return this;
}; };
__extends(ForNode, BaseNode); __extends(ForNode, BaseNode);
@@ -1528,16 +1470,14 @@
return true; return true;
}; };
ForNode.prototype.make_return = function make_return() { ForNode.prototype.make_return = function make_return() {
this.do_return = true; this.returns = true;
return this; return this;
}; };
ForNode.prototype.compile_return_value = function compile_return_value(retvar, o) { ForNode.prototype.compile_return_value = function compile_return_value(val, o) {
if (this.do_return) { if (this.returns) {
return new ReturnNode(literal(retvar)).compile(o); return new ReturnNode(literal(val)).compile(o);
} else {
return retvar || '';
} }
return null; return val || '';
}; };
// Welcome to the hairiest method in all of CoffeeScript. Handles the inner // Welcome to the hairiest method in all of CoffeeScript. Handles the inner
// loop, filtering, stepping, and result saving for array, object, and range // loop, filtering, stepping, and result saving for array, object, and range
@@ -1545,7 +1485,7 @@
// some cannot. // some cannot.
ForNode.prototype.compile_node = function compile_node(o) { ForNode.prototype.compile_node = function compile_node(o) {
var body, body_dent, close, for_part, index, index_var, ivar, lvar, name, range, return_result, rvar, scope, set_result, source, source_part, step_part, svar, top_level, var_part, vars; var body, body_dent, close, for_part, index, index_var, ivar, lvar, name, range, return_result, rvar, scope, set_result, source, source_part, step_part, svar, top_level, var_part, vars;
top_level = del(o, 'top') && !this.do_return; top_level = del(o, 'top') && !this.returns;
range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length; range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length;
source = range ? this.source.base : this.source; source = range ? this.source.base : this.source;
scope = o.scope; scope = o.scope;
@@ -1609,7 +1549,6 @@
return '' + set_result + (source_part) + "for (" + for_part + ") {\n" + var_part + body + "\n" + this.tab + close + return_result; return '' + set_result + (source_part) + "for (" + for_part + ") {\n" + var_part + body + "\n" + this.tab + close + return_result;
}; };
return ForNode; return ForNode;
return null;
}).call(this); }).call(this);
statement(ForNode); statement(ForNode);
//### IfNode //### IfNode
@@ -1622,7 +1561,7 @@
this.condition = condition; this.condition = condition;
this.body = body && body.unwrap(); this.body = body && body.unwrap();
this.else_body = else_body && else_body.unwrap(); this.else_body = else_body && else_body.unwrap();
this.children = compact([this.condition, this.body, this.else_body]); this.children = compact(flatten([this.condition, this.body, this.else_body]));
this.tags = tags || {}; this.tags = tags || {};
if (this.condition instanceof Array) { if (this.condition instanceof Array) {
this.multiple = true; this.multiple = true;
@@ -1673,7 +1612,6 @@
} else { } else {
return new OpNode('is', assigner, this.condition); return new OpNode('is', assigner, this.condition);
} }
return null;
}).call(this); }).call(this);
if (this.is_chain()) { if (this.is_chain()) {
this.else_body.rewrite_condition(this.switcher); this.else_body.rewrite_condition(this.switcher);
@@ -1710,7 +1648,6 @@
_a.push(cond.compile(o)); _a.push(cond.compile(o));
} }
return _a; return _a;
return null;
}).call(this).join(' || '); }).call(this).join(' || ');
}; };
IfNode.prototype.compile_node = function compile_node(o) { IfNode.prototype.compile_node = function compile_node(o) {
@@ -1721,15 +1658,8 @@
} }
}; };
IfNode.prototype.make_return = function make_return() { IfNode.prototype.make_return = function make_return() {
try { this.body = this.body && this.body.make_return();
if (this.body) { this.else_body = this.else_body && this.else_body.make_return();
this.body = this.body.make_return();
}
} finally {
if (this.else_body) {
this.else_body = this.else_body.make_return();
}
}
return this; return this;
}; };
// Compile the **IfNode** as a regular *if-else* statement. Flattened chains // Compile the **IfNode** as a regular *if-else* statement. Flattened chains
@@ -1765,7 +1695,6 @@
return '' + if_part + " : " + else_part; return '' + if_part + " : " + else_part;
}; };
return IfNode; return IfNode;
return null;
}).call(this); }).call(this);
// Faux-Nodes // Faux-Nodes
// ---------- // ----------
@@ -1778,9 +1707,7 @@
wrap: function wrap(array, expressions) { wrap: function wrap(array, expressions) {
var expr; var expr;
expr = expressions.unwrap(); expr = expressions.unwrap();
if (expr.is_pure_statement() || expr.contains(function(n) { if (expr.is_pure_statement() || expr.contains_pure_statement()) {
return n.is_pure_statement();
})) {
return expressions; return expressions;
} }
return Expressions.wrap([new CallNode(new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr])]); return Expressions.wrap([new CallNode(new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr])]);
@@ -1793,9 +1720,7 @@
// in which case, no dice. // in which case, no dice.
wrap: function wrap(expressions, statement) { wrap: function wrap(expressions, statement) {
var call, func; var call, func;
if (expressions.contains(function(n) { if (expressions.contains_pure_statement()) {
return n.is_pure_statement();
})) {
return expressions; return expressions;
} }
func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))); func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])));

View File

@@ -63,7 +63,6 @@
_d.push(' '); _d.push(' ');
} }
return _d; return _d;
return null;
}).call(this).join('') : ''; }).call(this).join('') : '';
let_part = rule.short_flag ? rule.short_flag + ', ' : ' '; let_part = rule.short_flag ? rule.short_flag + ', ' : ' ';
lines.push(" " + let_part + (rule.long_flag) + spaces + (rule.description)); lines.push(" " + let_part + (rule.long_flag) + spaces + (rule.description));
@@ -71,7 +70,6 @@
return "\n" + (lines.join('\n')) + "\n"; return "\n" + (lines.join('\n')) + "\n";
}; };
return OptionParser; return OptionParser;
return null;
}).call(this); }).call(this);
// Helpers // Helpers
// ------- // -------
@@ -95,7 +93,6 @@
}).call(this)); }).call(this));
} }
return _a; return _a;
return null;
}; };
// Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the // Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
// description of what the option does. // description of what the option does.

View File

@@ -19,9 +19,7 @@
// The **Rewriter** class is used by the [Lexer](lexer.html), directly against // The **Rewriter** class is used by the [Lexer](lexer.html), directly against
// its internal array of tokens. // its internal array of tokens.
exports.Rewriter = (function() { exports.Rewriter = (function() {
Rewriter = function Rewriter() { Rewriter = function Rewriter() { };
return null;
};
// Rewrite the token stream in multiple passes, one logical filter at // Rewrite the token stream in multiple passes, one logical filter at
// a time. This could certainly be changed into a single pass through the // a time. This could certainly be changed into a single pass through the
// stream, with a big ol' efficient switch, but it's much nicer to work with // stream, with a big ol' efficient switch, but it's much nicer to work with
@@ -76,7 +74,6 @@
} else { } else {
return 1; return 1;
} }
return null;
}; };
return (function() { return (function() {
return __func.apply(__this, arguments); return __func.apply(__this, arguments);
@@ -92,7 +89,6 @@
_a.push(this.tokens.shift()); _a.push(this.tokens.shift());
} }
return _a; return _a;
return null;
}; };
// Some blocks occur in the middle of expressions -- when we're expecting // Some blocks occur in the middle of expressions -- when we're expecting
// this, remove their trailing newlines. // this, remove their trailing newlines.
@@ -290,15 +286,12 @@
value > 0 ? _a.push(key) : null; value > 0 ? _a.push(key) : null;
}} }}
return _a; return _a;
return null;
}).call(this); }).call(this);
if (unclosed.length) { if (unclosed.length) {
open = unclosed[0]; open = unclosed[0];
line = open_line[open] + 1; line = open_line[open] + 1;
throw new Error("unclosed " + open + " on line " + line); throw new Error("unclosed " + open + " on line " + line);
return null;
} }
return null;
}; };
// We'd like to support syntax like this: // We'd like to support syntax like this:
// el.click((event) -> // el.click((event) ->
@@ -347,7 +340,6 @@
} else { } else {
return 1; return 1;
} }
return null;
}; };
return (function() { return (function() {
return __func.apply(__this, arguments); return __func.apply(__this, arguments);
@@ -355,7 +347,6 @@
})(this)); })(this));
}; };
return Rewriter; return Rewriter;
return null;
}).call(this); }).call(this);
// Constants // Constants
// --------- // ---------
@@ -378,7 +369,6 @@
_d.push(pair[0]); _d.push(pair[0]);
} }
return _d; return _d;
return null;
}).call(this); }).call(this);
// The tokens that signal the end of a balanced pair. // The tokens that signal the end of a balanced pair.
EXPRESSION_END = (function() { EXPRESSION_END = (function() {
@@ -388,7 +378,6 @@
_h.push(pair[1]); _h.push(pair[1]);
} }
return _h; return _h;
return null;
}).call(this); }).call(this);
// Tokens that indicate the close of a clause of an expression. // Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END); EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);

View File

@@ -91,7 +91,6 @@
val === 'var' ? _a.push(key) : null; val === 'var' ? _a.push(key) : null;
}} }}
return _a; return _a;
return null;
}).call(this).sort(); }).call(this).sort();
}; };
// Return the list of assignments that are supposed to be made at the top // Return the list of assignments that are supposed to be made at the top
@@ -104,7 +103,6 @@
val.assigned ? _a.push('' + key + " = " + (val.value)) : null; val.assigned ? _a.push('' + key + " = " + (val.value)) : null;
}} }}
return _a; return _a;
return null;
}; };
// Compile the JavaScript for all of the variable declarations in this scope. // Compile the JavaScript for all of the variable declarations in this scope.
Scope.prototype.compiled_declarations = function compiled_declarations() { Scope.prototype.compiled_declarations = function compiled_declarations() {
@@ -115,6 +113,5 @@
return this.assigned_variables().join(', '); return this.assigned_variables().join(', ');
}; };
return Scope; return Scope;
return null;
}).call(this); }).call(this);
})(); })();

View File

@@ -57,7 +57,7 @@ exports.BaseNode: class BaseNode
top: if @top_sensitive() then @options.top else del @options, 'top' top: if @top_sensitive() then @options.top else del @options, 'top'
closure: @is_statement() and not @is_pure_statement() and not top and closure: @is_statement() and not @is_pure_statement() and not top and
not @options.as_statement and not (this instanceof CommentNode) and not @options.as_statement and not (this instanceof CommentNode) and
not (@contains (n) -> n.is_pure_statement()) not @contains_pure_statement()
if closure then @compile_closure(@options) else @compile_node(@options) if closure then @compile_closure(@options) else @compile_node(@options)
# Statements converted into expressions via closure-wrapping share a scope # Statements converted into expressions via closure-wrapping share a scope
@@ -82,13 +82,11 @@ exports.BaseNode: class BaseNode
idt += TAB while num -= 1 idt += TAB while num -= 1
idt idt
# construct a node that returns the current node's result # Construct a node that returns the current node's result.
# note that this is overridden for smarter behaviour for # Note that this is overridden for smarter behavior for
# many statement nodes (eg IfNode, ForNode) # many statement nodes (eg IfNode, ForNode)...
make_return: -> make_return: ->
if @is_statement() new ReturnNode this
throw new Error("Can't convert statement ${this} into a return value!")
return new ReturnNode(this)
# Does this node, or any of its children, contain a node of a certain kind? # Does this node, or any of its children, contain a node of a certain kind?
# Recursively traverses down the *children* of the nodes, yielding to a block # Recursively traverses down the *children* of the nodes, yielding to a block
@@ -100,6 +98,11 @@ exports.BaseNode: class BaseNode
return true if node.contains and node.contains block return true if node.contains and node.contains block
false false
# Convenience for the most common use of contains. Does the node contain
# a pure statement?
contains_pure_statement: ->
@is_pure_statement() or @contains (n) -> n.is_pure_statement()
# Perform an in-order traversal of the AST. Crosses scope boundaries. # Perform an in-order traversal of the AST. Crosses scope boundaries.
traverse: (block) -> traverse: (block) ->
for node in @children for node in @children
@@ -150,36 +153,19 @@ exports.Expressions: class Expressions extends BaseNode
empty: -> empty: ->
@expressions.length is 0 @expressions.length is 0
# make a copy of this node # Make a copy of this node.
copy: -> copy: ->
new Expressions(@children.slice()) new Expressions @children.slice()
# an Expressions node does not return its entire body, rather it # An Expressions node does not return its entire body, rather it
# ensures that the final expression is returned # ensures that the final expression is returned.
make_return: -> make_return: ->
last_expr_idx: -1 idx: @expressions.length - 1
for i in [@expressions.length-1 .. 0] last: @expressions[idx]
if not (@expressions[i] instanceof CommentNode) last: @expressions[idx -= 1] if last instanceof CommentNode
last_expr_idx: i return this if not last or last instanceof ReturnNode
last_expr: @expressions[i] @expressions[idx]: last.make_return() unless last.contains_pure_statement()
break this
if last_expr_idx < 0
# just add a return null to ensure return is always called
@push(new ReturnNode(literal(null)))
else
return this if (last_expr instanceof ReturnNode)
@push(new ReturnNode(literal(null))) if last_expr.is_statement()
# we still make an attempt at converting statements,
# since many are able to be returned in some fashion
try
converted = last_expr.make_return()
@expressions[last_expr_idx] = converted
catch e
# ignore
return this
# An **Expressions** is the only node that can serve as the root. # An **Expressions** is the only node that can serve as the root.
compile: (o) -> compile: (o) ->
@@ -211,10 +197,8 @@ exports.Expressions: class Expressions extends BaseNode
# statement, ask the statement to do so. # statement, ask the statement to do so.
compile_expression: (node, o) -> compile_expression: (node, o) ->
@tab: o.indent @tab: o.indent
stmt: node.is_statement() compiled_node: node.compile merge o, {top: true}
compiled_node = node.compile(merge(o, {top:true})) if node.is_statement() then compiled_node else "${@idt()}$compiled_node;"
return compiled_node if stmt
return "${@idt()}${compiled_node};"
# Wrap up the given nodes as an **Expressions**, unless it already happens # Wrap up the given nodes as an **Expressions**, unless it already happens
# to be one. # to be one.
@@ -260,18 +244,8 @@ exports.ReturnNode: class ReturnNode extends BaseNode
@children: [@expression: expression] @children: [@expression: expression]
compile_node: (o) -> compile_node: (o) ->
if @expression.is_statement() o.as_statement: true if @expression.is_statement()
return @compile_statement(o) "${@tab}return ${@expression.compile(o)};"
else
compiled_expr: @expression.compile(o)
return "${@tab}return ${compiled_expr};"
compile_statement: (o) ->
# split statement into computation and return, via a temporary variable
temp_var: literal(o.scope.free_variable())
assign: new AssignNode(temp_var, @expression)
ret: new ReturnNode(temp_var)
[assign.compile(merge(o, {as_statement:true})), ret.compile(o)].join("\n")
statement ReturnNode, true statement ReturnNode, true
@@ -309,10 +283,7 @@ exports.ValueNode: class ValueNode extends BaseNode
@has_properties() and @properties[@properties.length - 1] instanceof SliceNode @has_properties() and @properties[@properties.length - 1] instanceof SliceNode
make_return: -> make_return: ->
if not @has_properties() if @has_properties() then super() else @base.make_return()
return @base.make_return()
else
return super()
# The value can be unwrapped as its inner node, if there are no attached # The value can be unwrapped as its inner node, if there are no attached
# properties. # properties.
@@ -364,6 +335,9 @@ exports.CommentNode: class CommentNode extends BaseNode
@lines: lines @lines: lines
this this
make_return: ->
this
compile_node: (o) -> compile_node: (o) ->
"$@tab//" + @lines.join("\n$@tab//") "$@tab//" + @lines.join("\n$@tab//")
@@ -377,9 +351,11 @@ exports.CallNode: class CallNode extends BaseNode
type: 'Call' type: 'Call'
constructor: (variable, args) -> constructor: (variable, args) ->
@children: flatten [@variable: variable, @args: (args or [])] @is_new: false
@is_super: variable is 'super'
@variable: if @is_super then null else variable
@children: compact flatten [@variable, @args: (args or [])]
@compile_splat_arguments: SplatNode.compile_mixed_array <- @, @args @compile_splat_arguments: SplatNode.compile_mixed_array <- @, @args
@is_new: false
# Tag this invocation as creating a new instance. # Tag this invocation as creating a new instance.
new_instance: -> new_instance: ->
@@ -394,7 +370,7 @@ exports.CallNode: class CallNode extends BaseNode
for arg in @args for arg in @args
return @compile_splat(o) if arg instanceof SplatNode return @compile_splat(o) if arg instanceof SplatNode
args: (arg.compile(o) for arg in @args).join(', ') args: (arg.compile(o) for arg in @args).join(', ')
return @compile_super(args, o) if @variable is 'super' return @compile_super(args, o) if @is_super
"${@prefix()}${@variable.compile(o)}($args)" "${@prefix()}${@variable.compile(o)}($args)"
# `super()` is converted into a call against the superclass's implementation # `super()` is converted into a call against the superclass's implementation
@@ -629,10 +605,10 @@ exports.ClassNode: class ClassNode extends BaseNode
# list of prototype property assignments. # list of prototype property assignments.
constructor: (variable, parent, props) -> constructor: (variable, parent, props) ->
@children: compact flatten [@variable: variable, @parent: parent, @properties: props or []] @children: compact flatten [@variable: variable, @parent: parent, @properties: props or []]
@do_return: false @returns: false
make_return: -> make_return: ->
@do_return: true @returns: true
this this
# Instead of generating the JavaScript string directly, we build up the # Instead of generating the JavaScript string directly, we build up the
@@ -664,14 +640,10 @@ exports.ClassNode: class ClassNode extends BaseNode
else else
constructor: new AssignNode(@variable, new CodeNode()) constructor: new AssignNode(@variable, new CodeNode())
if @do_return
returns: new ReturnNode(@variable).compile(o)
else
returns: ''
construct: @idt() + constructor.compile(o) + ';\n' construct: @idt() + constructor.compile(o) + ';\n'
props: if props.empty() then '' else props.compile(o) + '\n' props: if props.empty() then '' else props.compile(o) + '\n'
extension: if extension then @idt() + extension.compile(o) + ';\n' else '' extension: if extension then @idt() + extension.compile(o) + ';\n' else ''
returns: if @returns then new ReturnNode(@variable).compile(o) else ''
"$construct$extension$props$returns" "$construct$extension$props$returns"
statement ClassNode statement ClassNode
@@ -698,7 +670,7 @@ exports.AssignNode: class AssignNode extends BaseNode
@variable instanceof ValueNode @variable instanceof ValueNode
make_return: -> make_return: ->
return new Expressions([this, new ReturnNode(@variable)]) return new Expressions [this, new ReturnNode(@variable)]
is_statement: -> is_statement: ->
@is_value() and (@variable.is_array() or @variable.is_object()) @is_value() and (@variable.is_array() or @variable.is_object())
@@ -724,8 +696,7 @@ exports.AssignNode: class AssignNode extends BaseNode
o.scope.find name unless @is_value() and @variable.has_properties() o.scope.find name unless @is_value() and @variable.has_properties()
val: "$name = $val" val: "$name = $val"
return "$@tab$val;" if stmt return "$@tab$val;" if stmt
val: "($val)" if not top if top then val else "($val)"
return val
# Brief implementation of recursive pattern matching, when assigning array or # Brief implementation of recursive pattern matching, when assigning array or
# object literals to a value. Peeks at their properties to assign inner names. # object literals to a value. Peeks at their properties to assign inner names.
@@ -899,7 +870,7 @@ exports.WhileNode: class WhileNode extends BaseNode
this this
make_return: -> make_return: ->
@do_return: true @returns: true
this this
top_sensitive: -> top_sensitive: ->
@@ -909,7 +880,7 @@ exports.WhileNode: class WhileNode extends BaseNode
# *while* can be used as a part of a larger expression -- while loops may # *while* can be used as a part of a larger expression -- while loops may
# return an array containing the computed result of each iteration. # return an array containing the computed result of each iteration.
compile_node: (o) -> compile_node: (o) ->
top: del(o, 'top') and not @do_return top: del(o, 'top') and not @returns
o.indent: @idt(1) o.indent: @idt(1)
o.top: true o.top: true
cond: @condition.compile(o) cond: @condition.compile(o)
@@ -921,7 +892,7 @@ exports.WhileNode: class WhileNode extends BaseNode
pre: "$set${@tab}while ($cond)" pre: "$set${@tab}while ($cond)"
return "$pre null;$post" if not @body return "$pre null;$post" if not @body
@body: Expressions.wrap([new IfNode(@filter, @body)]) if @filter @body: Expressions.wrap([new IfNode(@filter, @body)]) if @filter
if @do_return if @returns
post: new ReturnNode(literal(rvar)).compile(merge(o, {indent: @idt()})) post: new ReturnNode(literal(rvar)).compile(merge(o, {indent: @idt()}))
else else
post: '' post: ''
@@ -1049,8 +1020,8 @@ exports.ThrowNode: class ThrowNode extends BaseNode
constructor: (expression) -> constructor: (expression) ->
@children: [@expression: expression] @children: [@expression: expression]
# A **ThrowNode** is already a return, of sorts...
make_return: -> make_return: ->
# a throw is already a return...
return this return this
compile_node: (o) -> compile_node: (o) ->
@@ -1098,7 +1069,8 @@ exports.ParentheticalNode: class ParentheticalNode extends BaseNode
is_statement: -> is_statement: ->
@expression.is_statement() @expression.is_statement()
make_return: -> @expression.make_return() make_return: ->
@expression.make_return()
compile_node: (o) -> compile_node: (o) ->
code: @expression.compile(o) code: @expression.compile(o)
@@ -1129,27 +1101,25 @@ exports.ForNode: class ForNode extends BaseNode
@object: !!source.object @object: !!source.object
[@name, @index]: [@index, @name] if @object [@name, @index]: [@index, @name] if @object
@children: compact [@body, @source, @filter] @children: compact [@body, @source, @filter]
@do_return: false @returns: false
top_sensitive: -> top_sensitive: ->
true true
make_return: -> make_return: ->
@do_return: true @returns: true
this this
compile_return_value: (retvar, o) -> compile_return_value: (val, o) ->
if @do_return return new ReturnNode(literal(val)).compile(o) if @returns
return new ReturnNode(literal(retvar)).compile(o) val or ''
else
return retvar or ''
# Welcome to the hairiest method in all of CoffeeScript. Handles the inner # Welcome to the hairiest method in all of CoffeeScript. Handles the inner
# loop, filtering, stepping, and result saving for array, object, and range # loop, filtering, stepping, and result saving for array, object, and range
# comprehensions. Some of the generated code can be shared in common, and # comprehensions. Some of the generated code can be shared in common, and
# some cannot. # some cannot.
compile_node: (o) -> compile_node: (o) ->
top_level: del(o, 'top') and not @do_return top_level: del(o, 'top') and not @returns
range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
source: if range then @source.base else @source source: if range then @source.base else @source
scope: o.scope scope: o.scope
@@ -1207,7 +1177,7 @@ exports.IfNode: class IfNode extends BaseNode
@condition: condition @condition: condition
@body: body and body.unwrap() @body: body and body.unwrap()
@else_body: else_body and else_body.unwrap() @else_body: else_body and else_body.unwrap()
@children: compact [@condition, @body, @else_body] @children: compact flatten [@condition, @body, @else_body]
@tags: tags or {} @tags: tags or {}
@multiple: true if @condition instanceof Array @multiple: true if @condition instanceof Array
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert @condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
@@ -1270,11 +1240,9 @@ exports.IfNode: class IfNode extends BaseNode
if @is_statement() then @compile_statement(o) else @compile_ternary(o) if @is_statement() then @compile_statement(o) else @compile_ternary(o)
make_return: -> make_return: ->
try @body &&= @body.make_return()
@body: @body.make_return() if @body @else_body &&= @else_body.make_return()
finally this
@else_body: @else_body.make_return() if @else_body
return this
# Compile the **IfNode** as a regular *if-else* statement. Flattened chains # Compile the **IfNode** as a regular *if-else* statement. Flattened chains
# force inner *else* bodies into statement form. # force inner *else* bodies into statement form.
@@ -1315,7 +1283,7 @@ PushNode: exports.PushNode: {
wrap: (array, expressions) -> wrap: (array, expressions) ->
expr: expressions.unwrap() expr: expressions.unwrap()
return expressions if expr.is_pure_statement() or expr.contains (n) -> n.is_pure_statement() return expressions if expr.is_pure_statement() or expr.contains_pure_statement()
Expressions.wrap([new CallNode( Expressions.wrap([new CallNode(
new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr] new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr]
)]) )])
@@ -1330,7 +1298,7 @@ ClosureNode: exports.ClosureNode: {
# Wrap the expressions body, unless it contains a pure statement, # Wrap the expressions body, unless it contains a pure statement,
# in which case, no dice. # in which case, no dice.
wrap: (expressions, statement) -> wrap: (expressions, statement) ->
return expressions if expressions.contains (n) -> n.is_pure_statement() return expressions if expressions.contains_pure_statement()
func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))) func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
call: new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')]) call: new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')])
if statement then Expressions.wrap([call]) else call if statement then Expressions.wrap([call]) else call