mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-19 03:44:23 -05:00
merging gfxmonk's cleanups
This commit is contained in:
36
Cakefile
36
Cakefile
@@ -71,23 +71,37 @@ 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'
|
||||||
test_count: 0
|
passed_test_count: 0
|
||||||
|
failed_test_count: 0
|
||||||
start_time: new Date()
|
start_time: new Date()
|
||||||
[original_ok, original_throws]: [ok, throws]
|
[original_ok, original_throws]: [ok, throws]
|
||||||
helpers.extend global, {
|
helpers.extend global, {
|
||||||
ok: (args...) -> test_count += 1; original_ok(args...)
|
ok: (args...) -> passed_test_count += 1; original_ok(args...)
|
||||||
throws: (args...) -> test_count += 1; original_throws(args...)
|
|
||||||
CoffeeScript: CoffeeScript
|
CoffeeScript: CoffeeScript
|
||||||
}
|
}
|
||||||
process.addListener 'exit', ->
|
red: '\033[0;31m'
|
||||||
|
green: '\033[0;32m'
|
||||||
|
reset: '\033[0m'
|
||||||
|
on_exit: ->
|
||||||
time: ((new Date() - start_time) / 1000).toFixed(2)
|
time: ((new Date() - start_time) / 1000).toFixed(2)
|
||||||
puts '\033[0;32mpassed ' + test_count + ' tests in ' + time + ' seconds\033[0m'
|
if failed_test_count > 0
|
||||||
|
puts "${red}FAILED " + failed_test_count + " and passed " + passed_test_count + " tests in " + time + " seconds${reset}"
|
||||||
|
else
|
||||||
|
puts "${green}passed " + passed_test_count + " tests in " + time + " seconds${reset}"
|
||||||
|
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)
|
||||||
source: path.join 'test', file
|
source: path.join 'test', file
|
||||||
fs.readFile source, (err, code) ->
|
print " " + source + " ... "
|
||||||
try
|
code = fs.readFileSync source
|
||||||
CoffeeScript.run code, {source: source}
|
try
|
||||||
catch err
|
CoffeeScript.run code, {source: source}
|
||||||
puts "Failed test: $source"
|
puts "${green}ok${reset}"
|
||||||
throw err
|
catch err
|
||||||
|
failed_test_count += 1
|
||||||
|
puts "${red}FAILED!${reset}\n"
|
||||||
|
puts "Failed test: $source"
|
||||||
|
puts err
|
||||||
|
puts ''
|
||||||
|
process.exit(if failed_test_count == 0 then 0 else 1)
|
||||||
|
|||||||
@@ -23,11 +23,12 @@
|
|||||||
// Define a Cake task with a short name, a sentence description,
|
// Define a Cake task with a short name, a sentence description,
|
||||||
// and the function to run as the action itself.
|
// and the function to run as the action itself.
|
||||||
task: function task(name, description, action) {
|
task: function task(name, description, action) {
|
||||||
return tasks[name] = {
|
tasks[name] = {
|
||||||
name: name,
|
name: name,
|
||||||
description: description,
|
description: description,
|
||||||
action: action
|
action: action
|
||||||
};
|
};
|
||||||
|
return tasks[name];
|
||||||
},
|
},
|
||||||
// Define an option that the Cakefile accepts. The parsed options hash,
|
// Define an option that the Cakefile accepts. The parsed options hash,
|
||||||
// containing all of the command-line options passed, will be made available
|
// containing all of the command-line options passed, will be made available
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
_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`
|
||||||
@@ -83,12 +85,14 @@
|
|||||||
_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) {
|
||||||
|
|||||||
@@ -37,7 +37,9 @@
|
|||||||
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) {
|
||||||
@@ -78,7 +80,8 @@
|
|||||||
},
|
},
|
||||||
setInput: function setInput(tokens) {
|
setInput: function setInput(tokens) {
|
||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
return this.pos = 0;
|
this.pos = 0;
|
||||||
|
return this.pos;
|
||||||
},
|
},
|
||||||
upcomingInput: function upcomingInput() {
|
upcomingInput: function upcomingInput() {
|
||||||
return "";
|
return "";
|
||||||
@@ -97,11 +100,10 @@
|
|||||||
_a = []; _b = document.getElementsByTagName('script');
|
_a = []; _b = document.getElementsByTagName('script');
|
||||||
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
||||||
tag = _b[_c];
|
tag = _b[_c];
|
||||||
if (tag.type === 'text/coffeescript') {
|
tag.type === 'text/coffeescript' ? _a.push(eval(exports.compile(tag.innerHTML))) : null;
|
||||||
_a.push(eval(exports.compile(tag.innerHTML)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return _a;
|
return _a;
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
if (window.addEventListener) {
|
if (window.addEventListener) {
|
||||||
window.addEventListener('load', process_scripts, false);
|
window.addEventListener('load', process_scripts, false);
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
_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
|
||||||
@@ -99,14 +100,18 @@
|
|||||||
} 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**.
|
||||||
@@ -118,6 +123,7 @@
|
|||||||
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);
|
||||||
@@ -147,6 +153,7 @@
|
|||||||
_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
|
||||||
@@ -167,11 +174,13 @@
|
|||||||
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();
|
||||||
@@ -191,6 +200,7 @@
|
|||||||
}).call(this));
|
}).call(this));
|
||||||
}
|
}
|
||||||
return _a;
|
return _a;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
return puts(strings.join(' '));
|
return puts(strings.join(' '));
|
||||||
};
|
};
|
||||||
@@ -202,7 +212,8 @@
|
|||||||
o = (options = option_parser.parse(process.argv));
|
o = (options = option_parser.parse(process.argv));
|
||||||
options.run = !(o.compile || o.print || o.lint);
|
options.run = !(o.compile || o.print || o.lint);
|
||||||
options.print = !!(o.print || (o.eval || o.stdio && o.compile));
|
options.print = !!(o.print || (o.eval || o.stdio && o.compile));
|
||||||
return sources = options.arguments.slice(2, options.arguments.length);
|
sources = options.arguments.slice(2, options.arguments.length);
|
||||||
|
return sources;
|
||||||
};
|
};
|
||||||
// The compile-time options to pass to the CoffeeScript compiler.
|
// The compile-time options to pass to the CoffeeScript compiler.
|
||||||
compile_options = function compile_options(source) {
|
compile_options = function compile_options(source) {
|
||||||
|
|||||||
@@ -348,7 +348,11 @@
|
|||||||
// this to be separate from the **ArgList** for use in **Switch** blocks, where
|
// this to be separate from the **ArgList** for use in **Switch** blocks, where
|
||||||
// having the newlines wouldn't make sense.
|
// having the newlines wouldn't make sense.
|
||||||
SimpleArgs: [o("Expression"), o("SimpleArgs , Expression", function() {
|
SimpleArgs: [o("Expression"), o("SimpleArgs , Expression", function() {
|
||||||
return $1 instanceof Array ? $1.concat([$3]) : [$1].concat([$3]);
|
if ($1 instanceof Array) {
|
||||||
|
return $1.concat([$3]);
|
||||||
|
} else {
|
||||||
|
return [$1].concat([$3]);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
// The variants of *try/catch/finally* exception handling blocks.
|
// The variants of *try/catch/finally* exception handling blocks.
|
||||||
@@ -641,6 +645,7 @@
|
|||||||
}).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**
|
||||||
|
|||||||
@@ -23,11 +23,10 @@
|
|||||||
_a = []; _b = array;
|
_a = []; _b = array;
|
||||||
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
||||||
item = _b[_c];
|
item = _b[_c];
|
||||||
if (item) {
|
item ? _a.push(item) : null;
|
||||||
_a.push(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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) {
|
||||||
@@ -70,6 +69,7 @@
|
|||||||
_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.
|
||||||
@@ -136,6 +136,10 @@
|
|||||||
}
|
}
|
||||||
throw new Error("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1));
|
throw new Error("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1));
|
||||||
}
|
}
|
||||||
return !i ? false : str.substring(0, i);
|
if (!i) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return str.substring(0, i);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
15
lib/lexer.js
15
lib/lexer.js
@@ -27,7 +27,9 @@
|
|||||||
// 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
|
||||||
@@ -386,6 +388,7 @@
|
|||||||
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.
|
||||||
@@ -427,11 +430,13 @@
|
|||||||
// 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)
|
||||||
@@ -514,6 +519,7 @@
|
|||||||
}
|
}
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
// Helpers
|
// Helpers
|
||||||
// -------
|
// -------
|
||||||
@@ -554,7 +560,11 @@
|
|||||||
if (!((m = this.chunk.match(regex)))) {
|
if (!((m = this.chunk.match(regex)))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return m ? m[index] : false;
|
if (m) {
|
||||||
|
return m[index];
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Are we in the midst of an unfinished expression?
|
// Are we in the midst of an unfinished expression?
|
||||||
Lexer.prototype.unfinished = function unfinished() {
|
Lexer.prototype.unfinished = function unfinished() {
|
||||||
@@ -563,6 +573,7 @@
|
|||||||
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 = [];
|
||||||
|
|||||||
360
lib/nodes.js
360
lib/nodes.js
@@ -35,10 +35,12 @@
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (only) {
|
if (only) {
|
||||||
return ((klass.prototype.is_pure_statement = function is_pure_statement() {
|
klass.prototype.is_pure_statement = function is_pure_statement() {
|
||||||
return true;
|
return true;
|
||||||
}));
|
};
|
||||||
|
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.
|
||||||
@@ -51,7 +53,9 @@
|
|||||||
// 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
|
||||||
@@ -100,6 +104,15 @@
|
|||||||
}
|
}
|
||||||
return idt;
|
return idt;
|
||||||
};
|
};
|
||||||
|
// construct a node that returns the current node's result
|
||||||
|
// note that this is overridden for smarter behaviour for
|
||||||
|
// many statement nodes (eg IfNode, ForNode)
|
||||||
|
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);
|
||||||
|
};
|
||||||
// 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
|
||||||
// and returning true when the block finds a match. `contains` does not cross
|
// and returning true when the block finds a match. `contains` does not cross
|
||||||
@@ -129,9 +142,11 @@
|
|||||||
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.
|
||||||
@@ -145,6 +160,7 @@
|
|||||||
_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
|
||||||
@@ -163,6 +179,7 @@
|
|||||||
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
|
||||||
@@ -188,23 +205,62 @@
|
|||||||
// If this Expressions consists of just a single node, unwrap it by pulling
|
// If this Expressions consists of just a single node, unwrap it by pulling
|
||||||
// it back out.
|
// it back out.
|
||||||
Expressions.prototype.unwrap = function unwrap() {
|
Expressions.prototype.unwrap = function unwrap() {
|
||||||
return this.expressions.length === 1 ? this.expressions[0] : this;
|
if (this.expressions.length === 1) {
|
||||||
|
return this.expressions[0];
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Is this an empty block of code?
|
// Is this an empty block of code?
|
||||||
Expressions.prototype.empty = function empty() {
|
Expressions.prototype.empty = function empty() {
|
||||||
return this.expressions.length === 0;
|
return this.expressions.length === 0;
|
||||||
};
|
};
|
||||||
// Is the given node the last one in this block of expressions?
|
// make a copy of this node
|
||||||
Expressions.prototype.is_last = function is_last(node) {
|
Expressions.prototype.copy = function copy() {
|
||||||
var l, last_index;
|
return new Expressions(this.children.slice());
|
||||||
l = this.expressions.length;
|
};
|
||||||
last_index = this.expressions[l - 1] instanceof CommentNode ? 2 : 1;
|
// an Expressions node does not return its entire body, rather it
|
||||||
return node === this.expressions[l - last_index];
|
// ensures that the final expression is returned
|
||||||
|
Expressions.prototype.make_return = function make_return() {
|
||||||
|
var _a, _b, _c, _d, converted, i, last_expr, last_expr_idx;
|
||||||
|
last_expr_idx = -1;
|
||||||
|
_c = this.expressions.length - 1; _d = 0;
|
||||||
|
for (_b = 0, i = _c; (_c <= _d ? i <= _d : i >= _d); (_c <= _d ? i += 1 : i -= 1), _b++) {
|
||||||
|
if (!(this.expressions[i] instanceof CommentNode)) {
|
||||||
|
last_expr_idx = i;
|
||||||
|
last_expr = this.expressions[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (last_expr_idx < 0) {
|
||||||
|
// just add a return null to ensure return is always called
|
||||||
|
this.push(new ReturnNode(literal(null)));
|
||||||
|
} else {
|
||||||
|
if ((last_expr instanceof ReturnNode)) {
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
// An **Expressions** is the only node that can serve as the root.
|
// An **Expressions** is the only node that can serve as the root.
|
||||||
Expressions.prototype.compile = function compile(o) {
|
Expressions.prototype.compile = function compile(o) {
|
||||||
o = o || {};
|
o = o || {};
|
||||||
return o.scope ? Expressions.__superClass__.compile.call(this, o) : this.compile_root(o);
|
if (o.scope) {
|
||||||
|
return Expressions.__superClass__.compile.call(this, o);
|
||||||
|
} else {
|
||||||
|
return this.compile_root(o);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Expressions.prototype.compile_node = function compile_node(o) {
|
Expressions.prototype.compile_node = function compile_node(o) {
|
||||||
var _a, _b, _c, _d, node;
|
var _a, _b, _c, _d, node;
|
||||||
@@ -215,6 +271,7 @@
|
|||||||
_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
|
||||||
@@ -225,7 +282,11 @@
|
|||||||
o.scope = new Scope(null, this, null);
|
o.scope = new Scope(null, this, null);
|
||||||
code = o.globals ? this.compile_node(o) : this.compile_with_declarations(o);
|
code = o.globals ? this.compile_node(o) : this.compile_with_declarations(o);
|
||||||
code = code.replace(TRAILING_WHITESPACE, '');
|
code = code.replace(TRAILING_WHITESPACE, '');
|
||||||
return o.no_wrap ? code : "(function(){\n" + code + "\n})();\n";
|
if (o.no_wrap) {
|
||||||
|
return code;
|
||||||
|
} else {
|
||||||
|
return "(function(){\n" + code + "\n})();\n";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Compile the expressions body for the contents of a function, with
|
// Compile the expressions body for the contents of a function, with
|
||||||
// declarations of all inner variables pushed up to the top.
|
// declarations of all inner variables pushed up to the top.
|
||||||
@@ -244,23 +305,19 @@
|
|||||||
// 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 returns, stmt;
|
var compiled_node, stmt;
|
||||||
this.tab = o.indent;
|
this.tab = o.indent;
|
||||||
stmt = node.is_statement();
|
stmt = node.is_statement();
|
||||||
returns = del(o, 'returns') && this.is_last(node) && !node.is_pure_statement();
|
compiled_node = node.compile(merge(o, {
|
||||||
if (!(returns)) {
|
top: true
|
||||||
return (stmt ? '' : this.idt()) + node.compile(merge(o, {
|
}));
|
||||||
top: true
|
if (stmt) {
|
||||||
})) + (stmt ? '' : ';');
|
return compiled_node;
|
||||||
}
|
}
|
||||||
if (node.is_statement()) {
|
return '' + (this.idt()) + (compiled_node) + ";";
|
||||||
return node.compile(merge(o, {
|
|
||||||
returns: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return '' + (this.tab) + "return " + (node.compile(o)) + ";";
|
|
||||||
};
|
};
|
||||||
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.
|
||||||
@@ -298,6 +355,7 @@
|
|||||||
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
|
||||||
@@ -310,14 +368,28 @@
|
|||||||
__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.expression.compile(merge(o, {
|
return this.compile_statement(o);
|
||||||
returns: true
|
} else {
|
||||||
}));
|
compiled_expr = this.expression.compile(o);
|
||||||
|
return '' + (this.tab) + "return " + (compiled_expr) + ";";
|
||||||
}
|
}
|
||||||
return '' + (this.tab) + "return " + (this.expression.compile(o)) + ";";
|
return null;
|
||||||
|
};
|
||||||
|
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
|
||||||
@@ -351,10 +423,22 @@
|
|||||||
ValueNode.prototype.is_splice = function is_splice() {
|
ValueNode.prototype.is_splice = function is_splice() {
|
||||||
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() {
|
||||||
|
if (!this.has_properties()) {
|
||||||
|
return this.base.make_return();
|
||||||
|
} else {
|
||||||
|
return ValueNode.__superClass__.make_return.call(this);
|
||||||
|
}
|
||||||
|
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.
|
||||||
ValueNode.prototype.unwrap = function unwrap() {
|
ValueNode.prototype.unwrap = function unwrap() {
|
||||||
return this.properties.length ? this : this.base;
|
if (this.properties.length) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return this.base;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Values are considered to be statements if their base is a statement.
|
// Values are considered to be statements if their base is a statement.
|
||||||
ValueNode.prototype.is_statement = function is_statement() {
|
ValueNode.prototype.is_statement = function is_statement() {
|
||||||
@@ -394,9 +478,14 @@
|
|||||||
this.last = part;
|
this.last = part;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return op && soaked ? "(" + complete + ")" : complete;
|
if (op && soaked) {
|
||||||
|
return "(" + complete + ")";
|
||||||
|
} else {
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
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
|
||||||
@@ -413,6 +502,7 @@
|
|||||||
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
|
||||||
@@ -421,21 +511,23 @@
|
|||||||
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.children = flatten([(this.variable = variable), (this.args = (args || []))]);
|
||||||
this.compile_splat_arguments = (function(func, obj, args) {
|
this.is_new = false;
|
||||||
return (function() {
|
|
||||||
return func.apply(obj, args.concat(Array.prototype.slice.call(arguments, 0)));
|
|
||||||
});
|
|
||||||
}(SplatNode.compile_mixed_array, this, [this.args]));
|
|
||||||
this.prefix = '';
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(CallNode, BaseNode);
|
__extends(CallNode, BaseNode);
|
||||||
CallNode.prototype.type = 'Call';
|
CallNode.prototype.type = 'Call';
|
||||||
// Tag this invocation as creating a new instance.
|
// Tag this invocation as creating a new instance.
|
||||||
CallNode.prototype.new_instance = function new_instance() {
|
CallNode.prototype.new_instance = function new_instance() {
|
||||||
this.prefix = 'new ';
|
this.is_new = true;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
CallNode.prototype.prefix = function prefix() {
|
||||||
|
if (this.is_new) {
|
||||||
|
return 'new ';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
// Compile a vanilla function call.
|
// Compile a vanilla function call.
|
||||||
CallNode.prototype.compile_node = function compile_node(o) {
|
CallNode.prototype.compile_node = function compile_node(o) {
|
||||||
var _a, _b, _c, _d, _e, _f, _g, arg, args;
|
var _a, _b, _c, _d, _e, _f, _g, arg, args;
|
||||||
@@ -453,11 +545,12 @@
|
|||||||
_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.variable === '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 + ")";
|
||||||
};
|
};
|
||||||
// `super()` is converted into a call against the superclass's implementation
|
// `super()` is converted into a call against the superclass's implementation
|
||||||
// of the current function.
|
// of the current function.
|
||||||
@@ -478,9 +571,10 @@
|
|||||||
obj = temp;
|
obj = temp;
|
||||||
meth = "(" + temp + " = " + (this.variable.source) + ")" + (this.variable.last);
|
meth = "(" + temp + " = " + (this.variable.source) + ")" + (this.variable.last);
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -517,6 +611,7 @@
|
|||||||
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.
|
||||||
@@ -539,6 +634,7 @@
|
|||||||
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
|
||||||
@@ -559,6 +655,7 @@
|
|||||||
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.
|
||||||
@@ -576,6 +673,7 @@
|
|||||||
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,
|
||||||
@@ -629,9 +727,10 @@
|
|||||||
source: (new ValueNode(this))
|
source: (new ValueNode(this))
|
||||||
}, literal(name))
|
}, literal(name))
|
||||||
]);
|
]);
|
||||||
return (new ParentheticalNode(new CallNode(new CodeNode([], arr)))).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
|
||||||
@@ -653,6 +752,7 @@
|
|||||||
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.
|
||||||
@@ -674,11 +774,10 @@
|
|||||||
_a = []; _b = this.properties;
|
_a = []; _b = this.properties;
|
||||||
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
||||||
prop = _b[_c];
|
prop = _b[_c];
|
||||||
if (!(prop instanceof CommentNode)) {
|
!(prop instanceof CommentNode) ? _a.push(prop) : null;
|
||||||
_a.push(prop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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() {
|
||||||
@@ -698,12 +797,14 @@
|
|||||||
}).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.
|
||||||
@@ -722,48 +823,57 @@
|
|||||||
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 = [];
|
objects = (function() {
|
||||||
_a = this.objects;
|
_a = []; _b = this.objects;
|
||||||
for (i = 0, _b = _a.length; i < _b; i++) {
|
for (i = 0, _c = _b.length; i < _c; i++) {
|
||||||
obj = _a[i];
|
obj = _b[i];
|
||||||
code = obj.compile(o);
|
_a.push((function() {
|
||||||
if (obj instanceof SplatNode) {
|
code = obj.compile(o);
|
||||||
return this.compile_splat_literal(this.objects, o);
|
if (obj instanceof CommentNode) {
|
||||||
} else if (obj instanceof CommentNode) {
|
return "\n" + code + "\n" + o.indent;
|
||||||
objects.push("\n" + code + "\n" + o.indent);
|
} else if (i === this.objects.length - 1) {
|
||||||
} else if (i === this.objects.length - 1) {
|
return code;
|
||||||
objects.push(code);
|
} else {
|
||||||
} else {
|
return '' + code + ", ";
|
||||||
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;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(ClassNode, BaseNode);
|
__extends(ClassNode, BaseNode);
|
||||||
ClassNode.prototype.type = 'Class';
|
ClassNode.prototype.type = 'Class';
|
||||||
// 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() {
|
||||||
|
this.do_return = true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
// Instead of generating the JavaScript string directly, we build up the
|
// Instead of generating the JavaScript string directly, we build up the
|
||||||
// equivalent syntax tree and compile that, in pieces. You can see the
|
// equivalent syntax tree and compile that, in pieces. You can see the
|
||||||
// constructor, property assignments, and inheritance getting built out below.
|
// constructor, property assignments, and inheritance getting built out below.
|
||||||
ClassNode.prototype.compile_node = function compile_node(o) {
|
ClassNode.prototype.compile_node = function compile_node(o) {
|
||||||
var _a, _b, _c, applied, construct, extension, func, prop, props, ret, returns, val;
|
var _a, _b, _c, applied, construct, extension, func, prop, props, returns, val;
|
||||||
extension = this.parent && new ExtendsNode(this.variable, this.parent);
|
extension = this.parent && new ExtendsNode(this.variable, this.parent);
|
||||||
constructor = null;
|
constructor = null;
|
||||||
props = new Expressions();
|
props = new Expressions();
|
||||||
o.top = true;
|
o.top = true;
|
||||||
ret = del(o, 'returns');
|
|
||||||
_a = this.properties;
|
_a = this.properties;
|
||||||
for (_b = 0, _c = _a.length; _b < _c; _b++) {
|
for (_b = 0, _c = _a.length; _b < _c; _b++) {
|
||||||
prop = _a[_b];
|
prop = _a[_b];
|
||||||
@@ -787,13 +897,14 @@
|
|||||||
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 = ret ? '\n' + this.idt() + 'return ' + 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
|
||||||
@@ -816,6 +927,9 @@
|
|||||||
AssignNode.prototype.is_value = function is_value() {
|
AssignNode.prototype.is_value = function is_value() {
|
||||||
return this.variable instanceof ValueNode;
|
return this.variable instanceof ValueNode;
|
||||||
};
|
};
|
||||||
|
AssignNode.prototype.make_return = function make_return() {
|
||||||
|
return new Expressions([this, new ReturnNode(this.variable)]);
|
||||||
|
};
|
||||||
AssignNode.prototype.is_statement = function is_statement() {
|
AssignNode.prototype.is_statement = function is_statement() {
|
||||||
return this.is_value() && (this.variable.is_array() || this.variable.is_object());
|
return this.is_value() && (this.variable.is_array() || this.variable.is_object());
|
||||||
};
|
};
|
||||||
@@ -856,12 +970,9 @@
|
|||||||
if (stmt) {
|
if (stmt) {
|
||||||
return '' + this.tab + val + ";";
|
return '' + this.tab + val + ";";
|
||||||
}
|
}
|
||||||
if (!top || o.returns) {
|
if (!top) {
|
||||||
val = "(" + val + ")";
|
val = "(" + val + ")";
|
||||||
}
|
}
|
||||||
if (o.returns) {
|
|
||||||
val = '' + (this.tab) + "return " + val;
|
|
||||||
}
|
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
// Brief implementation of recursive pattern matching, when assigning array or
|
// Brief implementation of recursive pattern matching, when assigning array or
|
||||||
@@ -896,9 +1007,6 @@
|
|||||||
assigns.push(new AssignNode(obj, val).compile(o));
|
assigns.push(new AssignNode(obj, val).compile(o));
|
||||||
}
|
}
|
||||||
code = assigns.join("\n");
|
code = assigns.join("\n");
|
||||||
if (o.returns) {
|
|
||||||
code += "\n" + (this.tab) + "return " + (this.variable.compile(o)) + ";";
|
|
||||||
}
|
|
||||||
return code;
|
return code;
|
||||||
};
|
};
|
||||||
// Compile the assignment from an array splice literal, using JavaScript's
|
// Compile the assignment from an array splice literal, using JavaScript's
|
||||||
@@ -917,6 +1025,7 @@
|
|||||||
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.
|
||||||
@@ -941,7 +1050,6 @@
|
|||||||
shared_scope = del(o, 'shared_scope');
|
shared_scope = del(o, 'shared_scope');
|
||||||
top = del(o, 'top');
|
top = del(o, 'top');
|
||||||
o.scope = shared_scope || new Scope(o.scope, this.body, this);
|
o.scope = shared_scope || new Scope(o.scope, this.body, this);
|
||||||
o.returns = true;
|
|
||||||
o.top = true;
|
o.top = true;
|
||||||
o.indent = this.idt(this.bound ? 2 : 1);
|
o.indent = this.idt(this.bound ? 2 : 1);
|
||||||
del(o, 'no_wrap');
|
del(o, 'no_wrap');
|
||||||
@@ -971,7 +1079,9 @@
|
|||||||
_d.push(param.compile(o));
|
_d.push(param.compile(o));
|
||||||
}
|
}
|
||||||
return _d;
|
return _d;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
this.body.make_return();
|
||||||
_h = params;
|
_h = params;
|
||||||
for (_i = 0, _j = _h.length; _i < _j; _i++) {
|
for (_i = 0, _j = _h.length; _i < _j; _i++) {
|
||||||
param = _h[_i];
|
param = _h[_i];
|
||||||
@@ -1007,6 +1117,7 @@
|
|||||||
_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;
|
||||||
@@ -1018,10 +1129,12 @@
|
|||||||
_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,
|
||||||
@@ -1038,7 +1151,11 @@
|
|||||||
SplatNode.prototype.type = 'Splat';
|
SplatNode.prototype.type = 'Splat';
|
||||||
SplatNode.prototype.compile_node = function compile_node(o) {
|
SplatNode.prototype.compile_node = function compile_node(o) {
|
||||||
var _a;
|
var _a;
|
||||||
return (typeof (_a = this.index) !== "undefined" && _a !== null) ? this.compile_param(o) : this.name.compile(o);
|
if ((typeof (_a = this.index) !== "undefined" && _a !== null)) {
|
||||||
|
return this.compile_param(o);
|
||||||
|
} else {
|
||||||
|
return this.name.compile(o);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Compiling a parameter splat means recovering the parameters that succeed
|
// Compiling a parameter splat means recovering the parameters that succeed
|
||||||
// the splat in the parameter list, by slicing the arguments object.
|
// the splat in the parameter list, by slicing the arguments object.
|
||||||
@@ -1061,6 +1178,7 @@
|
|||||||
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
|
||||||
@@ -1105,6 +1223,10 @@
|
|||||||
this.children.push((this.body = body));
|
this.children.push((this.body = body));
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
WhileNode.prototype.make_return = function make_return() {
|
||||||
|
this.do_return = true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
WhileNode.prototype.top_sensitive = function top_sensitive() {
|
WhileNode.prototype.top_sensitive = function top_sensitive() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -1112,9 +1234,8 @@
|
|||||||
// *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.
|
||||||
WhileNode.prototype.compile_node = function compile_node(o) {
|
WhileNode.prototype.compile_node = function compile_node(o) {
|
||||||
var cond, post, pre, returns, rvar, set, top;
|
var cond, post, pre, rvar, set, top;
|
||||||
returns = del(o, 'returns');
|
top = del(o, 'top') && !this.do_return;
|
||||||
top = del(o, 'top') && !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);
|
||||||
@@ -1126,7 +1247,6 @@
|
|||||||
this.body = PushNode.wrap(rvar, this.body);
|
this.body = PushNode.wrap(rvar, this.body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post = returns ? "\n" + (this.tab) + "return " + rvar + ";" : '';
|
|
||||||
pre = '' + set + (this.tab) + "while (" + cond + ")";
|
pre = '' + set + (this.tab) + "while (" + cond + ")";
|
||||||
if (!this.body) {
|
if (!this.body) {
|
||||||
return '' + pre + " null;" + post;
|
return '' + pre + " null;" + post;
|
||||||
@@ -1134,9 +1254,13 @@
|
|||||||
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)]);
|
||||||
}
|
}
|
||||||
return '' + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}" + post;
|
this.do_return ? (post = new ReturnNode(literal(rvar)).compile(merge(o, {
|
||||||
|
indent: this.idt()
|
||||||
|
}))) : (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
|
||||||
@@ -1246,6 +1370,7 @@
|
|||||||
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.
|
||||||
@@ -1258,6 +1383,15 @@
|
|||||||
};
|
};
|
||||||
__extends(TryNode, BaseNode);
|
__extends(TryNode, BaseNode);
|
||||||
TryNode.prototype.type = 'Try';
|
TryNode.prototype.type = 'Try';
|
||||||
|
TryNode.prototype.make_return = function make_return() {
|
||||||
|
if (this.attempt) {
|
||||||
|
this.attempt = this.attempt.make_return();
|
||||||
|
}
|
||||||
|
if (this.recovery) {
|
||||||
|
this.recovery = this.recovery.make_return();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
// Compilation is more or less as you would expect -- the *finally* clause
|
// Compilation is more or less as you would expect -- the *finally* clause
|
||||||
// is optional, the *catch* is not.
|
// is optional, the *catch* is not.
|
||||||
TryNode.prototype.compile_node = function compile_node(o) {
|
TryNode.prototype.compile_node = function compile_node(o) {
|
||||||
@@ -1267,12 +1401,11 @@
|
|||||||
attempt_part = this.attempt.compile(o);
|
attempt_part = this.attempt.compile(o);
|
||||||
error_part = this.error ? " (" + (this.error.compile(o)) + ") " : ' ';
|
error_part = this.error ? " (" + (this.error.compile(o)) + ") " : ' ';
|
||||||
catch_part = this.recovery ? " catch" + error_part + "{\n" + (this.recovery.compile(o)) + "\n" + this.tab + "}" : '';
|
catch_part = this.recovery ? " catch" + error_part + "{\n" + (this.recovery.compile(o)) + "\n" + this.tab + "}" : '';
|
||||||
finally_part = (this.ensure || '') && ' finally {\n' + this.ensure.compile(merge(o, {
|
finally_part = (this.ensure || '') && ' finally {\n' + this.ensure.compile(merge(o)) + "\n" + this.tab + "}";
|
||||||
returns: null
|
|
||||||
})) + "\n" + this.tab + "}";
|
|
||||||
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
|
||||||
@@ -1284,10 +1417,15 @@
|
|||||||
};
|
};
|
||||||
__extends(ThrowNode, BaseNode);
|
__extends(ThrowNode, BaseNode);
|
||||||
ThrowNode.prototype.type = 'Throw';
|
ThrowNode.prototype.type = 'Throw';
|
||||||
|
ThrowNode.prototype.make_return = function make_return() {
|
||||||
|
// a throw is already a return...
|
||||||
|
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
|
||||||
@@ -1305,6 +1443,7 @@
|
|||||||
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.
|
||||||
@@ -1339,6 +1478,9 @@
|
|||||||
ParentheticalNode.prototype.is_statement = function is_statement() {
|
ParentheticalNode.prototype.is_statement = function is_statement() {
|
||||||
return this.expression.is_statement();
|
return this.expression.is_statement();
|
||||||
};
|
};
|
||||||
|
ParentheticalNode.prototype.make_return = function make_return() {
|
||||||
|
return this.expression.make_return();
|
||||||
|
};
|
||||||
ParentheticalNode.prototype.compile_node = function compile_node(o) {
|
ParentheticalNode.prototype.compile_node = function compile_node(o) {
|
||||||
var code, l;
|
var code, l;
|
||||||
code = this.expression.compile(o);
|
code = this.expression.compile(o);
|
||||||
@@ -1352,6 +1494,7 @@
|
|||||||
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
|
||||||
@@ -1376,6 +1519,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;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(ForNode, BaseNode);
|
__extends(ForNode, BaseNode);
|
||||||
@@ -1383,13 +1527,25 @@
|
|||||||
ForNode.prototype.top_sensitive = function top_sensitive() {
|
ForNode.prototype.top_sensitive = function top_sensitive() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
ForNode.prototype.make_return = function make_return() {
|
||||||
|
this.do_return = true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
ForNode.prototype.compile_return_value = function compile_return_value(retvar, o) {
|
||||||
|
if (this.do_return) {
|
||||||
|
return new ReturnNode(literal(retvar)).compile(o);
|
||||||
|
} else {
|
||||||
|
return retvar || '';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
// 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.
|
||||||
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') && !o.returns;
|
top_level = del(o, 'top') && !this.do_return;
|
||||||
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;
|
||||||
@@ -1430,7 +1586,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
set_result = rvar ? this.idt() + rvar + ' = []; ' : this.idt();
|
set_result = rvar ? this.idt() + rvar + ' = []; ' : this.idt();
|
||||||
return_result = rvar || '';
|
return_result = this.compile_return_value(rvar, o);
|
||||||
if (top_level && body.contains(function(n) {
|
if (top_level && body.contains(function(n) {
|
||||||
return n instanceof CodeNode;
|
return n instanceof CodeNode;
|
||||||
})) {
|
})) {
|
||||||
@@ -1439,33 +1595,21 @@
|
|||||||
if (!(top_level)) {
|
if (!(top_level)) {
|
||||||
body = PushNode.wrap(rvar, body);
|
body = PushNode.wrap(rvar, body);
|
||||||
}
|
}
|
||||||
if (o.returns) {
|
this.filter ? (body = Expressions.wrap([new IfNode(this.filter, body)])) : null;
|
||||||
return_result = 'return ' + return_result;
|
|
||||||
del(o, 'returns');
|
|
||||||
if (this.filter) {
|
|
||||||
body = new IfNode(this.filter, body, null, {
|
|
||||||
statement: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (this.filter) {
|
|
||||||
body = Expressions.wrap([new IfNode(this.filter, body)]);
|
|
||||||
}
|
|
||||||
if (this.object) {
|
if (this.object) {
|
||||||
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true);
|
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true);
|
||||||
for_part = '' + ivar + " in " + svar + ") { if (__hasProp.call(" + svar + ", " + ivar + ")";
|
for_part = '' + ivar + " in " + svar + ") { if (__hasProp.call(" + svar + ", " + ivar + ")";
|
||||||
}
|
}
|
||||||
if (!(top_level)) {
|
|
||||||
return_result = "\n" + this.tab + return_result + ";";
|
|
||||||
}
|
|
||||||
body = body.compile(merge(o, {
|
body = body.compile(merge(o, {
|
||||||
indent: body_dent,
|
indent: body_dent,
|
||||||
top: true
|
top: true
|
||||||
}));
|
}));
|
||||||
vars = range ? name : '' + name + ", " + ivar;
|
vars = range ? name : '' + name + ", " + ivar;
|
||||||
close = this.object ? '}}\n' : '}\n';
|
close = this.object ? '}}\n' : '}\n';
|
||||||
return '' + set_result + (source_part) + "for (" + for_part + ") {\n" + var_part + body + "\n" + this.tab + close + this.tab + 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
|
||||||
@@ -1529,6 +1673,7 @@
|
|||||||
} 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);
|
||||||
@@ -1565,10 +1710,27 @@
|
|||||||
_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) {
|
||||||
return this.is_statement() ? this.compile_statement(o) : this.compile_ternary(o);
|
if (this.is_statement()) {
|
||||||
|
return this.compile_statement(o);
|
||||||
|
} else {
|
||||||
|
return this.compile_ternary(o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IfNode.prototype.make_return = function make_return() {
|
||||||
|
try {
|
||||||
|
if (this.body) {
|
||||||
|
this.body = this.body.make_return();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (this.else_body) {
|
||||||
|
this.else_body = this.else_body.make_return();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.
|
||||||
@@ -1579,7 +1741,6 @@
|
|||||||
}
|
}
|
||||||
child = del(o, 'chain_child');
|
child = del(o, 'chain_child');
|
||||||
cond_o = merge(o);
|
cond_o = merge(o);
|
||||||
del(cond_o, 'returns');
|
|
||||||
o.indent = this.idt(1);
|
o.indent = this.idt(1);
|
||||||
o.top = true;
|
o.top = true;
|
||||||
if_dent = child ? '' : this.idt();
|
if_dent = child ? '' : this.idt();
|
||||||
@@ -1604,6 +1765,7 @@
|
|||||||
return '' + if_part + " : " + else_part;
|
return '' + if_part + " : " + else_part;
|
||||||
};
|
};
|
||||||
return IfNode;
|
return IfNode;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
// Faux-Nodes
|
// Faux-Nodes
|
||||||
// ----------
|
// ----------
|
||||||
@@ -1638,7 +1800,11 @@
|
|||||||
}
|
}
|
||||||
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')]);
|
||||||
return statement ? Expressions.wrap([call]) : call;
|
if (statement) {
|
||||||
|
return Expressions.wrap([call]);
|
||||||
|
} else {
|
||||||
|
return call;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Constants
|
// Constants
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
_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));
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
return "\n" + (lines.join('\n')) + "\n";
|
return "\n" + (lines.join('\n')) + "\n";
|
||||||
};
|
};
|
||||||
return OptionParser;
|
return OptionParser;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
// Helpers
|
// Helpers
|
||||||
// -------
|
// -------
|
||||||
@@ -93,6 +95,7 @@
|
|||||||
}).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.
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
// 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
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
return (function() {
|
return (function() {
|
||||||
return __func.apply(__this, arguments);
|
return __func.apply(__this, arguments);
|
||||||
@@ -89,6 +92,7 @@
|
|||||||
_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.
|
||||||
@@ -283,17 +287,18 @@
|
|||||||
_a = []; _b = levels;
|
_a = []; _b = levels;
|
||||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||||
value = _b[key];
|
value = _b[key];
|
||||||
if (value > 0) {
|
value > 0 ? _a.push(key) : null;
|
||||||
_a.push(key);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
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) ->
|
||||||
@@ -342,6 +347,7 @@
|
|||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
return (function() {
|
return (function() {
|
||||||
return __func.apply(__this, arguments);
|
return __func.apply(__this, arguments);
|
||||||
@@ -349,6 +355,7 @@
|
|||||||
})(this));
|
})(this));
|
||||||
};
|
};
|
||||||
return Rewriter;
|
return Rewriter;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
// Constants
|
// Constants
|
||||||
// ---------
|
// ---------
|
||||||
@@ -371,6 +378,7 @@
|
|||||||
_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() {
|
||||||
@@ -380,6 +388,7 @@
|
|||||||
_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);
|
||||||
|
|||||||
17
lib/scope.js
17
lib/scope.js
@@ -38,7 +38,8 @@
|
|||||||
// Reserve a variable name as originating from a function parameter for this
|
// Reserve a variable name as originating from a function parameter for this
|
||||||
// scope. No `var` required for internal references.
|
// scope. No `var` required for internal references.
|
||||||
Scope.prototype.parameter = function parameter(name) {
|
Scope.prototype.parameter = function parameter(name) {
|
||||||
return this.variables[name] = 'param';
|
this.variables[name] = 'param';
|
||||||
|
return this.variables[name];
|
||||||
};
|
};
|
||||||
// Just check to see if a variable has already been declared, without reserving.
|
// Just check to see if a variable has already been declared, without reserving.
|
||||||
Scope.prototype.check = function check(name) {
|
Scope.prototype.check = function check(name) {
|
||||||
@@ -64,10 +65,11 @@
|
|||||||
if (top_level && this.parent) {
|
if (top_level && this.parent) {
|
||||||
return this.parent.assign(name, value, top_level);
|
return this.parent.assign(name, value, top_level);
|
||||||
}
|
}
|
||||||
return this.variables[name] = {
|
this.variables[name] = {
|
||||||
value: value,
|
value: value,
|
||||||
assigned: true
|
assigned: true
|
||||||
};
|
};
|
||||||
|
return this.variables[name];
|
||||||
};
|
};
|
||||||
// Does this scope reference any variables that need to be declared in the
|
// Does this scope reference any variables that need to be declared in the
|
||||||
// given function body?
|
// given function body?
|
||||||
@@ -86,11 +88,10 @@
|
|||||||
_a = []; _b = this.variables;
|
_a = []; _b = this.variables;
|
||||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||||
val = _b[key];
|
val = _b[key];
|
||||||
if (val === 'var') {
|
val === 'var' ? _a.push(key) : null;
|
||||||
_a.push(key);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
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
|
||||||
@@ -100,11 +101,10 @@
|
|||||||
_a = []; _b = this.variables;
|
_a = []; _b = this.variables;
|
||||||
for (key in _b) { if (__hasProp.call(_b, key)) {
|
for (key in _b) { if (__hasProp.call(_b, key)) {
|
||||||
val = _b[key];
|
val = _b[key];
|
||||||
if (val.assigned) {
|
val.assigned ? _a.push('' + key + " = " + (val.value)) : null;
|
||||||
_a.push('' + key + " = " + (val.value));
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
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,5 +115,6 @@
|
|||||||
return this.assigned_variables().join(', ');
|
return this.assigned_variables().join(', ');
|
||||||
};
|
};
|
||||||
return Scope;
|
return Scope;
|
||||||
|
return null;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
})();
|
})();
|
||||||
|
|||||||
172
src/nodes.coffee
172
src/nodes.coffee
@@ -56,7 +56,7 @@ exports.BaseNode: class BaseNode
|
|||||||
del @options, 'operation' unless this instanceof ValueNode
|
del @options, 'operation' unless this instanceof ValueNode
|
||||||
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.returns 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 (n) -> n.is_pure_statement())
|
||||||
if closure then @compile_closure(@options) else @compile_node(@options)
|
if closure then @compile_closure(@options) else @compile_node(@options)
|
||||||
|
|
||||||
@@ -82,6 +82,14 @@ 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
|
||||||
|
# note that this is overridden for smarter behaviour for
|
||||||
|
# many statement nodes (eg IfNode, ForNode)
|
||||||
|
make_return: ->
|
||||||
|
if @is_statement()
|
||||||
|
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
|
||||||
# and returning true when the block finds a match. `contains` does not cross
|
# and returning true when the block finds a match. `contains` does not cross
|
||||||
@@ -142,11 +150,36 @@ exports.Expressions: class Expressions extends BaseNode
|
|||||||
empty: ->
|
empty: ->
|
||||||
@expressions.length is 0
|
@expressions.length is 0
|
||||||
|
|
||||||
# Is the given node the last one in this block of expressions?
|
# make a copy of this node
|
||||||
is_last: (node) ->
|
copy: ->
|
||||||
l: @expressions.length
|
new Expressions(@children.slice())
|
||||||
last_index: if @expressions[l - 1] instanceof CommentNode then 2 else 1
|
|
||||||
node is @expressions[l - last_index]
|
# an Expressions node does not return its entire body, rather it
|
||||||
|
# ensures that the final expression is returned
|
||||||
|
make_return: ->
|
||||||
|
last_expr_idx: -1
|
||||||
|
for i in [@expressions.length-1 .. 0]
|
||||||
|
if not (@expressions[i] instanceof CommentNode)
|
||||||
|
last_expr_idx: i
|
||||||
|
last_expr: @expressions[i]
|
||||||
|
break
|
||||||
|
|
||||||
|
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) ->
|
||||||
@@ -179,10 +212,9 @@ exports.Expressions: class Expressions extends BaseNode
|
|||||||
compile_expression: (node, o) ->
|
compile_expression: (node, o) ->
|
||||||
@tab: o.indent
|
@tab: o.indent
|
||||||
stmt: node.is_statement()
|
stmt: node.is_statement()
|
||||||
returns: del(o, 'returns') and @is_last(node) and not node.is_pure_statement()
|
compiled_node = node.compile(merge(o, {top:true}))
|
||||||
return (if stmt then '' else @idt()) + node.compile(merge(o, {top: true})) + (if stmt then '' else ';') unless returns
|
return compiled_node if stmt
|
||||||
return node.compile(merge(o, {returns: true})) if node.is_statement()
|
return "${@idt()}${compiled_node};"
|
||||||
"${@tab}return ${node.compile(o)};"
|
|
||||||
|
|
||||||
# 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.
|
||||||
@@ -228,8 +260,18 @@ exports.ReturnNode: class ReturnNode extends BaseNode
|
|||||||
@children: [@expression: expression]
|
@children: [@expression: expression]
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
return @expression.compile(merge(o, {returns: true})) if @expression.is_statement()
|
if @expression.is_statement()
|
||||||
"${@tab}return ${@expression.compile(o)};"
|
return @compile_statement(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
|
||||||
|
|
||||||
@@ -266,6 +308,12 @@ exports.ValueNode: class ValueNode extends BaseNode
|
|||||||
is_splice: ->
|
is_splice: ->
|
||||||
@has_properties() and @properties[@properties.length - 1] instanceof SliceNode
|
@has_properties() and @properties[@properties.length - 1] instanceof SliceNode
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
if not @has_properties()
|
||||||
|
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.
|
||||||
unwrap: ->
|
unwrap: ->
|
||||||
@@ -329,22 +377,25 @@ exports.CallNode: class CallNode extends BaseNode
|
|||||||
type: 'Call'
|
type: 'Call'
|
||||||
|
|
||||||
constructor: (variable, args) ->
|
constructor: (variable, args) ->
|
||||||
@children: flatten [@variable: variable, @args: (args or [])]
|
@children: flatten [@variable: variable, @args: (args or [])]
|
||||||
@compile_splat_arguments: SplatNode.compile_mixed_array <- @, @args
|
@compile_splat_arguments: SplatNode.compile_mixed_array <- @, @args
|
||||||
@prefix: ''
|
@is_new: false
|
||||||
|
|
||||||
# Tag this invocation as creating a new instance.
|
# Tag this invocation as creating a new instance.
|
||||||
new_instance: ->
|
new_instance: ->
|
||||||
@prefix: 'new '
|
@is_new: true
|
||||||
this
|
this
|
||||||
|
|
||||||
|
prefix: ->
|
||||||
|
if @is_new then 'new ' else ''
|
||||||
|
|
||||||
# Compile a vanilla function call.
|
# Compile a vanilla function call.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
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 @variable 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
|
||||||
# of the current function.
|
# of the current function.
|
||||||
@@ -365,7 +416,7 @@ exports.CallNode: class CallNode extends BaseNode
|
|||||||
temp: o.scope.free_variable()
|
temp: o.scope.free_variable()
|
||||||
obj: temp
|
obj: temp
|
||||||
meth: "($temp = ${ @variable.source })${ @variable.last }"
|
meth: "($temp = ${ @variable.source })${ @variable.last }"
|
||||||
"$@prefix${meth}.apply($obj, ${ @compile_splat_arguments(o) })"
|
"${@prefix()}${meth}.apply($obj, ${ @compile_splat_arguments(o) })"
|
||||||
|
|
||||||
|
|
||||||
#### CurryNode
|
#### CurryNode
|
||||||
@@ -493,7 +544,7 @@ exports.RangeNode: class RangeNode extends BaseNode
|
|||||||
name: o.scope.free_variable()
|
name: o.scope.free_variable()
|
||||||
body: Expressions.wrap([literal(name)])
|
body: Expressions.wrap([literal(name)])
|
||||||
arr: Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, literal(name))])
|
arr: Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, literal(name))])
|
||||||
(new ParentheticalNode(new CallNode(new CodeNode([], arr)))).compile(o)
|
(new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o)
|
||||||
|
|
||||||
#### SliceNode
|
#### SliceNode
|
||||||
|
|
||||||
@@ -578,6 +629,11 @@ 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
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
@do_return: true
|
||||||
|
this
|
||||||
|
|
||||||
# Instead of generating the JavaScript string directly, we build up the
|
# Instead of generating the JavaScript string directly, we build up the
|
||||||
# equivalent syntax tree and compile that, in pieces. You can see the
|
# equivalent syntax tree and compile that, in pieces. You can see the
|
||||||
@@ -587,7 +643,6 @@ exports.ClassNode: class ClassNode extends BaseNode
|
|||||||
constructor: null
|
constructor: null
|
||||||
props: new Expressions()
|
props: new Expressions()
|
||||||
o.top: true
|
o.top: true
|
||||||
ret: del o, 'returns'
|
|
||||||
|
|
||||||
for prop in @properties
|
for prop in @properties
|
||||||
if prop.variable and prop.variable.base.value is 'constructor'
|
if prop.variable and prop.variable.base.value is 'constructor'
|
||||||
@@ -609,10 +664,14 @@ 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 ret then '\n' + @idt() + 'return ' + @variable.compile(o) + ';' else ''
|
|
||||||
"$construct$extension$props$returns"
|
"$construct$extension$props$returns"
|
||||||
|
|
||||||
statement ClassNode
|
statement ClassNode
|
||||||
@@ -638,6 +697,9 @@ exports.AssignNode: class AssignNode extends BaseNode
|
|||||||
is_value: ->
|
is_value: ->
|
||||||
@variable instanceof ValueNode
|
@variable instanceof ValueNode
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
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())
|
||||||
|
|
||||||
@@ -662,9 +724,8 @@ 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 or o.returns
|
val: "($val)" if not top
|
||||||
val: "${@tab}return $val" if o.returns
|
return val
|
||||||
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.
|
||||||
@@ -687,7 +748,6 @@ exports.AssignNode: class AssignNode extends BaseNode
|
|||||||
val: new ValueNode(literal(val_var), [new access_class(idx)])
|
val: new ValueNode(literal(val_var), [new access_class(idx)])
|
||||||
assigns.push(new AssignNode(obj, val).compile(o))
|
assigns.push(new AssignNode(obj, val).compile(o))
|
||||||
code: assigns.join("\n")
|
code: assigns.join("\n")
|
||||||
code += "\n${@tab}return ${ @variable.compile(o) };" if o.returns
|
|
||||||
code
|
code
|
||||||
|
|
||||||
# Compile the assignment from an array splice literal, using JavaScript's
|
# Compile the assignment from an array splice literal, using JavaScript's
|
||||||
@@ -724,7 +784,6 @@ exports.CodeNode: class CodeNode extends BaseNode
|
|||||||
shared_scope: del o, 'shared_scope'
|
shared_scope: del o, 'shared_scope'
|
||||||
top: del o, 'top'
|
top: del o, 'top'
|
||||||
o.scope: shared_scope or new Scope(o.scope, @body, this)
|
o.scope: shared_scope or new Scope(o.scope, @body, this)
|
||||||
o.returns: true
|
|
||||||
o.top: true
|
o.top: true
|
||||||
o.indent: @idt(if @bound then 2 else 1)
|
o.indent: @idt(if @bound then 2 else 1)
|
||||||
del o, 'no_wrap'
|
del o, 'no_wrap'
|
||||||
@@ -744,6 +803,7 @@ exports.CodeNode: class CodeNode extends BaseNode
|
|||||||
params.push(param)
|
params.push(param)
|
||||||
i += 1
|
i += 1
|
||||||
params: (param.compile(o) for param in params)
|
params: (param.compile(o) for param in params)
|
||||||
|
@body.make_return()
|
||||||
(o.scope.parameter(param)) for param in params
|
(o.scope.parameter(param)) for param in params
|
||||||
code: if @body.expressions.length then "\n${ @body.compile_with_declarations(o) }\n" else ''
|
code: if @body.expressions.length then "\n${ @body.compile_with_declarations(o) }\n" else ''
|
||||||
name_part: if @name then ' ' + @name else ''
|
name_part: if @name then ' ' + @name else ''
|
||||||
@@ -838,6 +898,10 @@ exports.WhileNode: class WhileNode extends BaseNode
|
|||||||
@children.push @body: body
|
@children.push @body: body
|
||||||
this
|
this
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
@do_return: true
|
||||||
|
this
|
||||||
|
|
||||||
top_sensitive: ->
|
top_sensitive: ->
|
||||||
true
|
true
|
||||||
|
|
||||||
@@ -845,8 +909,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) ->
|
||||||
returns: del(o, 'returns')
|
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)
|
||||||
@@ -855,11 +918,14 @@ exports.WhileNode: class WhileNode extends BaseNode
|
|||||||
rvar: o.scope.free_variable()
|
rvar: o.scope.free_variable()
|
||||||
set: "$@tab$rvar = [];\n"
|
set: "$@tab$rvar = [];\n"
|
||||||
@body: PushNode.wrap(rvar, @body) if @body
|
@body: PushNode.wrap(rvar, @body) if @body
|
||||||
post: if returns then "\n${@tab}return $rvar;" else ''
|
|
||||||
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
|
||||||
"$pre {\n${ @body.compile(o) }\n$@tab}$post"
|
if @do_return
|
||||||
|
post: new ReturnNode(literal(rvar)).compile(merge(o, {indent: @idt()}))
|
||||||
|
else
|
||||||
|
post: ''
|
||||||
|
"$pre {\n${ @body.compile(o) }\n$@tab}\n$post"
|
||||||
|
|
||||||
statement WhileNode
|
statement WhileNode
|
||||||
|
|
||||||
@@ -956,6 +1022,11 @@ exports.TryNode: class TryNode extends BaseNode
|
|||||||
@error: error
|
@error: error
|
||||||
this
|
this
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
@attempt: @attempt.make_return() if @attempt
|
||||||
|
@recovery: @recovery.make_return() if @recovery
|
||||||
|
this
|
||||||
|
|
||||||
# Compilation is more or less as you would expect -- the *finally* clause
|
# Compilation is more or less as you would expect -- the *finally* clause
|
||||||
# is optional, the *catch* is not.
|
# is optional, the *catch* is not.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
@@ -964,7 +1035,7 @@ exports.TryNode: class TryNode extends BaseNode
|
|||||||
attempt_part: @attempt.compile(o)
|
attempt_part: @attempt.compile(o)
|
||||||
error_part: if @error then " (${ @error.compile(o) }) " else ' '
|
error_part: if @error then " (${ @error.compile(o) }) " else ' '
|
||||||
catch_part: if @recovery then " catch$error_part{\n${ @recovery.compile(o) }\n$@tab}" else ''
|
catch_part: if @recovery then " catch$error_part{\n${ @recovery.compile(o) }\n$@tab}" else ''
|
||||||
finally_part: (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o, {returns: null})) + "\n$@tab}"
|
finally_part: (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n$@tab}"
|
||||||
"${@tab}try {\n$attempt_part\n$@tab}$catch_part$finally_part"
|
"${@tab}try {\n$attempt_part\n$@tab}$catch_part$finally_part"
|
||||||
|
|
||||||
statement TryNode
|
statement TryNode
|
||||||
@@ -978,6 +1049,10 @@ exports.ThrowNode: class ThrowNode extends BaseNode
|
|||||||
constructor: (expression) ->
|
constructor: (expression) ->
|
||||||
@children: [@expression: expression]
|
@children: [@expression: expression]
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
# a throw is already a return...
|
||||||
|
return this
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
"${@tab}throw ${@expression.compile(o)};"
|
"${@tab}throw ${@expression.compile(o)};"
|
||||||
|
|
||||||
@@ -1023,6 +1098,8 @@ exports.ParentheticalNode: class ParentheticalNode extends BaseNode
|
|||||||
is_statement: ->
|
is_statement: ->
|
||||||
@expression.is_statement()
|
@expression.is_statement()
|
||||||
|
|
||||||
|
make_return: -> @expression.make_return()
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
code: @expression.compile(o)
|
code: @expression.compile(o)
|
||||||
return code if @is_statement()
|
return code if @is_statement()
|
||||||
@@ -1052,16 +1129,27 @@ 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
|
||||||
|
|
||||||
top_sensitive: ->
|
top_sensitive: ->
|
||||||
true
|
true
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
@do_return: true
|
||||||
|
this
|
||||||
|
|
||||||
|
compile_return_value: (retvar, o) ->
|
||||||
|
if @do_return
|
||||||
|
return new ReturnNode(literal(retvar)).compile(o)
|
||||||
|
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 o.returns
|
top_level: del(o, 'top') and not @do_return
|
||||||
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
|
||||||
@@ -1089,23 +1177,19 @@ exports.ForNode: class ForNode extends BaseNode
|
|||||||
step_part: if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
|
step_part: if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
|
||||||
for_part: "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $step_part"
|
for_part: "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $step_part"
|
||||||
set_result: if rvar then @idt() + rvar + ' = []; ' else @idt()
|
set_result: if rvar then @idt() + rvar + ' = []; ' else @idt()
|
||||||
return_result: rvar or ''
|
return_result: @compile_return_value(rvar, o)
|
||||||
|
|
||||||
body: ClosureNode.wrap(body, true) if top_level and body.contains (n) -> n instanceof CodeNode
|
body: ClosureNode.wrap(body, true) if top_level and body.contains (n) -> n instanceof CodeNode
|
||||||
body: PushNode.wrap(rvar, body) unless top_level
|
body: PushNode.wrap(rvar, body) unless top_level
|
||||||
if o.returns
|
if @filter
|
||||||
return_result: 'return ' + return_result
|
|
||||||
del o, 'returns'
|
|
||||||
body: new IfNode(@filter, body, null, {statement: true}) if @filter
|
|
||||||
else if @filter
|
|
||||||
body: Expressions.wrap([new IfNode(@filter, body)])
|
body: Expressions.wrap([new IfNode(@filter, body)])
|
||||||
if @object
|
if @object
|
||||||
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true)
|
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true)
|
||||||
for_part: "$ivar in $svar) { if (__hasProp.call($svar, $ivar)"
|
for_part: "$ivar in $svar) { if (__hasProp.call($svar, $ivar)"
|
||||||
return_result: "\n$@tab$return_result;" unless top_level
|
|
||||||
body: body.compile(merge(o, {indent: body_dent, top: true}))
|
body: body.compile(merge(o, {indent: body_dent, top: true}))
|
||||||
vars: if range then name else "$name, $ivar"
|
vars: if range then name else "$name, $ivar"
|
||||||
close: if @object then '}}\n' else '}\n'
|
close: if @object then '}}\n' else '}\n'
|
||||||
"$set_result${source_part}for ($for_part) {\n$var_part$body\n$@tab$close$@tab$return_result"
|
"$set_result${source_part}for ($for_part) {\n$var_part$body\n$@tab$close$return_result"
|
||||||
|
|
||||||
statement ForNode
|
statement ForNode
|
||||||
|
|
||||||
@@ -1185,13 +1269,19 @@ exports.IfNode: class IfNode extends BaseNode
|
|||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
|
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
|
||||||
|
|
||||||
|
make_return: ->
|
||||||
|
try
|
||||||
|
@body: @body.make_return() if @body
|
||||||
|
finally
|
||||||
|
@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.
|
||||||
compile_statement: (o) ->
|
compile_statement: (o) ->
|
||||||
@rewrite_switch(o) if @switcher
|
@rewrite_switch(o) if @switcher
|
||||||
child: del o, 'chain_child'
|
child: del o, 'chain_child'
|
||||||
cond_o: merge o
|
cond_o: merge o
|
||||||
del cond_o, 'returns'
|
|
||||||
o.indent: @idt(1)
|
o.indent: @idt(1)
|
||||||
o.top: true
|
o.top: true
|
||||||
if_dent: if child then '' else @idt()
|
if_dent: if child then '' else @idt()
|
||||||
|
|||||||
Reference in New Issue
Block a user