using granular helper imports, for nicer within.
This commit is contained in:
parent
75ffb9dc84
commit
60b3103314
41
lib/lexer.js
41
lib/lexer.js
|
@ -1,5 +1,5 @@
|
||||||
(function(){
|
(function(){
|
||||||
var ACCESSORS, ASSIGNMENT, BEFORE_WHEN, CALLABLE, CODE, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_ESCAPE, REGEX_FLAGS, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, h, helpers;
|
var ACCESSORS, ASSIGNMENT, BEFORE_WHEN, CALLABLE, CODE, COFFEE_KEYWORDS, COMMENT, COMMENT_CLEANER, HEREDOC, HEREDOC_INDENT, IDENTIFIER, INTERPOLATION, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, KEYWORDS, LAST_DENT, LAST_DENTS, Lexer, MULTILINER, MULTI_DENT, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_ESCAPE, REGEX_FLAGS, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, balanced_string, compact, count, helpers, include, starts;
|
||||||
// The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
|
// The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
|
||||||
// matches against the beginning of the source code. When a match is found,
|
// matches against the beginning of the source code. When a match is found,
|
||||||
// a token is produced, we consume the match, and start again. Tokens are in the
|
// a token is produced, we consume the match, and start again. Tokens are in the
|
||||||
|
@ -14,7 +14,12 @@
|
||||||
this.exports = this;
|
this.exports = this;
|
||||||
Rewriter = this.Rewriter;
|
Rewriter = this.Rewriter;
|
||||||
}
|
}
|
||||||
h = helpers;
|
// Import the helpers we need.
|
||||||
|
include = helpers.include;
|
||||||
|
count = helpers.count;
|
||||||
|
starts = helpers.starts;
|
||||||
|
compact = helpers.compact;
|
||||||
|
balanced_string = helpers.balanced_string;
|
||||||
// The Lexer Class
|
// The Lexer Class
|
||||||
// ---------------
|
// ---------------
|
||||||
// The Lexer class reads a stream of CoffeeScript and divvys it up into tagged
|
// The Lexer class reads a stream of CoffeeScript and divvys it up into tagged
|
||||||
|
@ -122,13 +127,13 @@
|
||||||
}
|
}
|
||||||
this.name_access_type();
|
this.name_access_type();
|
||||||
tag = 'IDENTIFIER';
|
tag = 'IDENTIFIER';
|
||||||
if (h.include(KEYWORDS, id) && !(h.include(ACCESSORS, this.tag(0)) && !this.prev().spaced)) {
|
if (include(KEYWORDS, id) && !(include(ACCESSORS, this.tag(0)) && !this.prev().spaced)) {
|
||||||
tag = id.toUpperCase();
|
tag = id.toUpperCase();
|
||||||
}
|
}
|
||||||
if (h.include(RESERVED, id)) {
|
if (include(RESERVED, id)) {
|
||||||
this.identifier_error(id);
|
this.identifier_error(id);
|
||||||
}
|
}
|
||||||
if (tag === 'WHEN' && h.include(BEFORE_WHEN, this.tag())) {
|
if (tag === 'WHEN' && include(BEFORE_WHEN, this.tag())) {
|
||||||
tag = 'LEADING_WHEN';
|
tag = 'LEADING_WHEN';
|
||||||
}
|
}
|
||||||
this.token(tag, id);
|
this.token(tag, id);
|
||||||
|
@ -149,14 +154,14 @@
|
||||||
// are balanced within the string's contents, and within nested interpolations.
|
// are balanced within the string's contents, and within nested interpolations.
|
||||||
Lexer.prototype.string_token = function string_token() {
|
Lexer.prototype.string_token = function string_token() {
|
||||||
var string;
|
var string;
|
||||||
if (!(h.starts(this.chunk, '"') || h.starts(this.chunk, "'"))) {
|
if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!((string = this.balanced_token(['"', '"'], ['${', '}']) || this.balanced_token(["'", "'"])))) {
|
if (!((string = this.balanced_token(['"', '"'], ['${', '}']) || this.balanced_token(["'", "'"])))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.interpolate_string(string.replace(STRING_NEWLINES, " \\\n"));
|
this.interpolate_string(string.replace(STRING_NEWLINES, " \\\n"));
|
||||||
this.line += h.count(string, "\n");
|
this.line += count(string, "\n");
|
||||||
this.i += string.length;
|
this.i += string.length;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -169,14 +174,14 @@
|
||||||
}
|
}
|
||||||
doc = this.sanitize_heredoc(match[2] || match[4]);
|
doc = this.sanitize_heredoc(match[2] || match[4]);
|
||||||
this.token('STRING', "\"" + doc + "\"");
|
this.token('STRING', "\"" + doc + "\"");
|
||||||
this.line += h.count(match[1], "\n");
|
this.line += count(match[1], "\n");
|
||||||
this.i += match[1].length;
|
this.i += match[1].length;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Matches JavaScript interpolated directly into the source via backticks.
|
// Matches JavaScript interpolated directly into the source via backticks.
|
||||||
Lexer.prototype.js_token = function js_token() {
|
Lexer.prototype.js_token = function js_token() {
|
||||||
var script;
|
var script;
|
||||||
if (!(h.starts(this.chunk, '`'))) {
|
if (!(starts(this.chunk, '`'))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!((script = this.balanced_token(['`', '`'])))) {
|
if (!((script = this.balanced_token(['`', '`'])))) {
|
||||||
|
@ -195,7 +200,7 @@
|
||||||
if (!(this.chunk.match(REGEX_START))) {
|
if (!(this.chunk.match(REGEX_START))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (h.include(NOT_REGEX, this.tag())) {
|
if (include(NOT_REGEX, this.tag())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!((regex = this.balanced_token(['/', '/'])))) {
|
if (!((regex = this.balanced_token(['/', '/'])))) {
|
||||||
|
@ -221,7 +226,7 @@
|
||||||
Lexer.prototype.balanced_token = function balanced_token() {
|
Lexer.prototype.balanced_token = function balanced_token() {
|
||||||
var delimited;
|
var delimited;
|
||||||
delimited = Array.prototype.slice.call(arguments, 0);
|
delimited = Array.prototype.slice.call(arguments, 0);
|
||||||
return h.balanced_string(this.chunk, delimited);
|
return balanced_string(this.chunk, delimited);
|
||||||
};
|
};
|
||||||
// Matches and conumes comments. We pass through comments into JavaScript,
|
// Matches and conumes comments. We pass through comments into JavaScript,
|
||||||
// so they're treated as real tokens, like any other part of the language.
|
// so they're treated as real tokens, like any other part of the language.
|
||||||
|
@ -232,7 +237,7 @@
|
||||||
}
|
}
|
||||||
this.line += (comment.match(MULTILINER) || []).length;
|
this.line += (comment.match(MULTILINER) || []).length;
|
||||||
lines = comment.replace(COMMENT_CLEANER, '').split(MULTILINER);
|
lines = comment.replace(COMMENT_CLEANER, '').split(MULTILINER);
|
||||||
this.token('COMMENT', h.compact(lines));
|
this.token('COMMENT', compact(lines));
|
||||||
this.token('TERMINATOR', "\n");
|
this.token('TERMINATOR', "\n");
|
||||||
this.i += comment.length;
|
this.i += comment.length;
|
||||||
return true;
|
return true;
|
||||||
|
@ -334,7 +339,7 @@
|
||||||
tag = value;
|
tag = value;
|
||||||
if (value.match(ASSIGNMENT)) {
|
if (value.match(ASSIGNMENT)) {
|
||||||
tag = 'ASSIGN';
|
tag = 'ASSIGN';
|
||||||
if (h.include(JS_FORBIDDEN, this.value)) {
|
if (include(JS_FORBIDDEN, this.value)) {
|
||||||
this.assignment_error();
|
this.assignment_error();
|
||||||
}
|
}
|
||||||
} else if (value === ';') {
|
} else if (value === ';') {
|
||||||
|
@ -346,7 +351,7 @@
|
||||||
} else if (value === ']' && this.soaked_index) {
|
} else if (value === ']' && this.soaked_index) {
|
||||||
tag = 'SOAKED_INDEX_END';
|
tag = 'SOAKED_INDEX_END';
|
||||||
this.soaked_index = false;
|
this.soaked_index = false;
|
||||||
} else if (h.include(CALLABLE, this.tag()) && not_spaced) {
|
} else if (include(CALLABLE, this.tag()) && not_spaced) {
|
||||||
if (value === '(') {
|
if (value === '(') {
|
||||||
tag = 'CALL_START';
|
tag = 'CALL_START';
|
||||||
}
|
}
|
||||||
|
@ -431,7 +436,7 @@
|
||||||
// token stream.
|
// token stream.
|
||||||
Lexer.prototype.interpolate_string = function interpolate_string(str, escape_quotes) {
|
Lexer.prototype.interpolate_string = function interpolate_string(str, escape_quotes) {
|
||||||
var _a, _b, _c, _d, _e, escaped, expr, group, i, inner, interp, lexer, match, nested, pi, quote, tag, token, tokens, value;
|
var _a, _b, _c, _d, _e, escaped, expr, group, i, inner, interp, lexer, match, nested, pi, quote, tag, token, tokens, value;
|
||||||
if (str.length < 3 || !h.starts(str, '"')) {
|
if (str.length < 3 || !starts(str, '"')) {
|
||||||
return this.token('STRING', str);
|
return this.token('STRING', str);
|
||||||
} else {
|
} else {
|
||||||
lexer = new Lexer();
|
lexer = new Lexer();
|
||||||
|
@ -441,13 +446,13 @@
|
||||||
i = _a[0];
|
i = _a[0];
|
||||||
pi = _a[1];
|
pi = _a[1];
|
||||||
while (i < str.length - 1) {
|
while (i < str.length - 1) {
|
||||||
if (h.starts(str, '\\', i)) {
|
if (starts(str, '\\', i)) {
|
||||||
i += 1;
|
i += 1;
|
||||||
} else if ((match = str.substring(i).match(INTERPOLATION))) {
|
} else if ((match = str.substring(i).match(INTERPOLATION))) {
|
||||||
_b = match;
|
_b = match;
|
||||||
group = _b[0];
|
group = _b[0];
|
||||||
interp = _b[1];
|
interp = _b[1];
|
||||||
if (h.starts(interp, '@')) {
|
if (starts(interp, '@')) {
|
||||||
interp = "this." + (interp.substring(1));
|
interp = "this." + (interp.substring(1));
|
||||||
}
|
}
|
||||||
if (pi < i) {
|
if (pi < i) {
|
||||||
|
@ -456,7 +461,7 @@
|
||||||
tokens.push(['IDENTIFIER', interp]);
|
tokens.push(['IDENTIFIER', interp]);
|
||||||
i += group.length - 1;
|
i += group.length - 1;
|
||||||
pi = i + 1;
|
pi = i + 1;
|
||||||
} else if (((expr = h.balanced_string(str.substring(i), [['${', '}']])))) {
|
} else if (((expr = balanced_string(str.substring(i), [['${', '}']])))) {
|
||||||
if (pi < i) {
|
if (pi < i) {
|
||||||
tokens.push(['STRING', '' + quote + (str.substring(pi, i)) + quote]);
|
tokens.push(['STRING', '' + quote + (str.substring(pi, i)) + quote]);
|
||||||
}
|
}
|
||||||
|
|
90
lib/nodes.js
90
lib/nodes.js
|
@ -1,5 +1,5 @@
|
||||||
(function(){
|
(function(){
|
||||||
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, ValueNode, WhileNode, h, helpers, literal, statement;
|
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, ValueNode, WhileNode, compact, del, flatten, helpers, literal, merge, statement;
|
||||||
var __extends = function(child, parent) {
|
var __extends = function(child, parent) {
|
||||||
var ctor = function(){ };
|
var ctor = function(){ };
|
||||||
ctor.prototype = parent.prototype;
|
ctor.prototype = parent.prototype;
|
||||||
|
@ -19,7 +19,11 @@
|
||||||
} else {
|
} else {
|
||||||
this.exports = this;
|
this.exports = this;
|
||||||
}
|
}
|
||||||
h = helpers;
|
// Import the helpers we need.
|
||||||
|
compact = helpers.compact;
|
||||||
|
flatten = helpers.flatten;
|
||||||
|
merge = helpers.merge;
|
||||||
|
del = helpers.del;
|
||||||
// Helper function that marks a node as a JavaScript *statement*, or as a
|
// Helper function that marks a node as a JavaScript *statement*, or as a
|
||||||
// *pure_statement*. Statements must be wrapped in a closure when used as an
|
// *pure_statement*. Statements must be wrapped in a closure when used as an
|
||||||
// expression, and nodes tagged as *pure_statement* cannot be closure-wrapped
|
// expression, and nodes tagged as *pure_statement* cannot be closure-wrapped
|
||||||
|
@ -57,12 +61,12 @@
|
||||||
// top-level statement within the function body.
|
// top-level statement within the function body.
|
||||||
BaseNode.prototype.compile = function compile(o) {
|
BaseNode.prototype.compile = function compile(o) {
|
||||||
var closure, top;
|
var closure, top;
|
||||||
this.options = h.merge(o || {});
|
this.options = merge(o || {});
|
||||||
this.tab = o.indent;
|
this.tab = o.indent;
|
||||||
if (!(this instanceof ValueNode)) {
|
if (!(this instanceof ValueNode)) {
|
||||||
h.del(this.options, 'operation');
|
del(this.options, 'operation');
|
||||||
}
|
}
|
||||||
top = this.top_sensitive() ? this.options.top : h.del(this.options, 'top');
|
top = this.top_sensitive() ? this.options.top : del(this.options, 'top');
|
||||||
closure = this.is_statement() && !this.is_pure_statement() && !top && !this.options.returns && !(this instanceof CommentNode) && !this.contains(function(node) {
|
closure = this.is_statement() && !this.is_pure_statement() && !top && !this.options.returns && !(this instanceof CommentNode) && !this.contains(function(node) {
|
||||||
return node.is_pure_statement();
|
return node.is_pure_statement();
|
||||||
});
|
});
|
||||||
|
@ -164,7 +168,7 @@
|
||||||
// `if`, `switch`, or `try`, and so on...
|
// `if`, `switch`, or `try`, and so on...
|
||||||
exports.Expressions = (function() {
|
exports.Expressions = (function() {
|
||||||
Expressions = function Expressions(nodes) {
|
Expressions = function Expressions(nodes) {
|
||||||
this.children = (this.expressions = h.compact(h.flatten(nodes || [])));
|
this.children = (this.expressions = compact(flatten(nodes || [])));
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(Expressions, BaseNode);
|
__extends(Expressions, BaseNode);
|
||||||
|
@ -206,7 +210,7 @@
|
||||||
_a = []; _b = this.expressions;
|
_a = []; _b = this.expressions;
|
||||||
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
||||||
node = _b[_c];
|
node = _b[_c];
|
||||||
_a.push(this.compile_expression(node, h.merge(o)));
|
_a.push(this.compile_expression(node, merge(o)));
|
||||||
}
|
}
|
||||||
return _a;
|
return _a;
|
||||||
}).call(this).join("\n");
|
}).call(this).join("\n");
|
||||||
|
@ -247,14 +251,14 @@
|
||||||
var returns, stmt;
|
var returns, stmt;
|
||||||
this.tab = o.indent;
|
this.tab = o.indent;
|
||||||
stmt = node.is_statement();
|
stmt = node.is_statement();
|
||||||
returns = h.del(o, 'returns') && this.is_last(node) && !node.is_pure_statement();
|
returns = del(o, 'returns') && this.is_last(node) && !node.is_pure_statement();
|
||||||
if (!(returns)) {
|
if (!(returns)) {
|
||||||
return (stmt ? '' : this.idt()) + node.compile(h.merge(o, {
|
return (stmt ? '' : this.idt()) + node.compile(merge(o, {
|
||||||
top: true
|
top: true
|
||||||
})) + (stmt ? '' : ';');
|
})) + (stmt ? '' : ';');
|
||||||
}
|
}
|
||||||
if (node.is_statement()) {
|
if (node.is_statement()) {
|
||||||
return node.compile(h.merge(o, {
|
return node.compile(merge(o, {
|
||||||
returns: true
|
returns: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -311,7 +315,7 @@
|
||||||
ReturnNode.prototype.type = 'Return';
|
ReturnNode.prototype.type = 'Return';
|
||||||
ReturnNode.prototype.compile_node = function compile_node(o) {
|
ReturnNode.prototype.compile_node = function compile_node(o) {
|
||||||
if (this.expression.is_statement()) {
|
if (this.expression.is_statement()) {
|
||||||
return this.expression.compile(h.merge(o, {
|
return this.expression.compile(merge(o, {
|
||||||
returns: true
|
returns: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -325,7 +329,7 @@
|
||||||
// or vanilla.
|
// or vanilla.
|
||||||
exports.ValueNode = (function() {
|
exports.ValueNode = (function() {
|
||||||
ValueNode = function ValueNode(base, properties) {
|
ValueNode = function ValueNode(base, properties) {
|
||||||
this.children = h.flatten([(this.base = base), (this.properties = (properties || []))]);
|
this.children = flatten([(this.base = base), (this.properties = (properties || []))]);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(ValueNode, BaseNode);
|
__extends(ValueNode, BaseNode);
|
||||||
|
@ -370,8 +374,8 @@
|
||||||
ValueNode.prototype.compile_node = function compile_node(o) {
|
ValueNode.prototype.compile_node = function compile_node(o) {
|
||||||
var _a, _b, _c, baseline, complete, only, op, part, prop, props, soaked, temp;
|
var _a, _b, _c, baseline, complete, only, op, part, prop, props, soaked, temp;
|
||||||
soaked = false;
|
soaked = false;
|
||||||
only = h.del(o, 'only_first');
|
only = del(o, 'only_first');
|
||||||
op = h.del(o, 'operation');
|
op = del(o, 'operation');
|
||||||
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
|
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
|
||||||
baseline = this.base.compile(o);
|
baseline = this.base.compile(o);
|
||||||
if (this.base instanceof ObjectNode && this.has_properties()) {
|
if (this.base instanceof ObjectNode && this.has_properties()) {
|
||||||
|
@ -423,7 +427,7 @@
|
||||||
// calls against the prototype's function of the same name.
|
// calls against the prototype's function of the same name.
|
||||||
exports.CallNode = (function() {
|
exports.CallNode = (function() {
|
||||||
CallNode = function CallNode(variable, args) {
|
CallNode = function CallNode(variable, args) {
|
||||||
this.children = h.flatten([(this.variable = variable), (this.args = (args || []))]);
|
this.children = flatten([(this.variable = variable), (this.args = (args || []))]);
|
||||||
this.prefix = '';
|
this.prefix = '';
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -578,8 +582,8 @@
|
||||||
if (!(o.index)) {
|
if (!(o.index)) {
|
||||||
return this.compile_array(o);
|
return this.compile_array(o);
|
||||||
}
|
}
|
||||||
idx = h.del(o, 'index');
|
idx = del(o, 'index');
|
||||||
step = h.del(o, 'step');
|
step = del(o, 'step');
|
||||||
vars = '' + idx + " = " + this.from_var;
|
vars = '' + idx + " = " + this.from_var;
|
||||||
step = step ? step.compile(o) : '1';
|
step = step ? step.compile(o) : '1';
|
||||||
equals = this.exclusive ? '' : '=';
|
equals = this.exclusive ? '' : '=';
|
||||||
|
@ -714,7 +718,7 @@
|
||||||
// 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 = h.compact(h.flatten([(this.variable = variable), (this.parent = parent), (this.properties = props || [])]));
|
this.children = compact(flatten([(this.variable = variable), (this.parent = parent), (this.properties = props || [])]));
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(ClassNode, BaseNode);
|
__extends(ClassNode, BaseNode);
|
||||||
|
@ -730,7 +734,7 @@
|
||||||
constructor = null;
|
constructor = null;
|
||||||
props = new Expressions();
|
props = new Expressions();
|
||||||
o.top = true;
|
o.top = true;
|
||||||
ret = h.del(o, 'returns');
|
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];
|
||||||
|
@ -792,14 +796,14 @@
|
||||||
// has not been seen yet within the current scope, declare it.
|
// has not been seen yet within the current scope, declare it.
|
||||||
AssignNode.prototype.compile_node = function compile_node(o) {
|
AssignNode.prototype.compile_node = function compile_node(o) {
|
||||||
var last, match, name, proto, stmt, top, val;
|
var last, match, name, proto, stmt, top, val;
|
||||||
top = h.del(o, 'top');
|
top = del(o, 'top');
|
||||||
if (this.is_statement()) {
|
if (this.is_statement()) {
|
||||||
return this.compile_pattern_match(o);
|
return this.compile_pattern_match(o);
|
||||||
}
|
}
|
||||||
if (this.is_value() && this.variable.is_splice()) {
|
if (this.is_value() && this.variable.is_splice()) {
|
||||||
return this.compile_splice(o);
|
return this.compile_splice(o);
|
||||||
}
|
}
|
||||||
stmt = h.del(o, 'as_statement');
|
stmt = del(o, 'as_statement');
|
||||||
name = this.variable.compile(o);
|
name = this.variable.compile(o);
|
||||||
last = this.is_value() ? this.variable.last.replace(this.LEADING_DOT, '') : name;
|
last = this.is_value() ? this.variable.last.replace(this.LEADING_DOT, '') : name;
|
||||||
match = name.match(this.PROTO_ASSIGN);
|
match = name.match(this.PROTO_ASSIGN);
|
||||||
|
@ -872,7 +876,7 @@
|
||||||
// `Array#splice` method.
|
// `Array#splice` method.
|
||||||
AssignNode.prototype.compile_splice = function compile_splice(o) {
|
AssignNode.prototype.compile_splice = function compile_splice(o) {
|
||||||
var from, l, name, plus, range, to, val;
|
var from, l, name, plus, range, to, val;
|
||||||
name = this.variable.compile(h.merge(o, {
|
name = this.variable.compile(merge(o, {
|
||||||
only_first: true
|
only_first: true
|
||||||
}));
|
}));
|
||||||
l = this.variable.properties.length;
|
l = this.variable.properties.length;
|
||||||
|
@ -905,14 +909,14 @@
|
||||||
// a closure.
|
// a closure.
|
||||||
CodeNode.prototype.compile_node = function compile_node(o) {
|
CodeNode.prototype.compile_node = function compile_node(o) {
|
||||||
var _a, _b, _c, _d, _e, _f, _g, code, func, inner, name_part, param, params, shared_scope, splat, top;
|
var _a, _b, _c, _d, _e, _f, _g, code, func, inner, name_part, param, params, shared_scope, splat, top;
|
||||||
shared_scope = h.del(o, 'shared_scope');
|
shared_scope = del(o, 'shared_scope');
|
||||||
top = h.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.returns = true;
|
||||||
o.top = true;
|
o.top = true;
|
||||||
o.indent = this.idt(this.bound ? 2 : 1);
|
o.indent = this.idt(this.bound ? 2 : 1);
|
||||||
h.del(o, 'no_wrap');
|
del(o, 'no_wrap');
|
||||||
h.del(o, 'globals');
|
del(o, 'globals');
|
||||||
if (this.params[this.params.length - 1] instanceof SplatNode) {
|
if (this.params[this.params.length - 1] instanceof SplatNode) {
|
||||||
splat = this.params.pop();
|
splat = this.params.pop();
|
||||||
splat.index = this.params.length;
|
splat.index = this.params.length;
|
||||||
|
@ -949,7 +953,7 @@
|
||||||
// When traversing (for printing or inspecting), return the real children of
|
// When traversing (for printing or inspecting), return the real children of
|
||||||
// the function -- the parameters and body of expressions.
|
// the function -- the parameters and body of expressions.
|
||||||
CodeNode.prototype.real_children = function real_children() {
|
CodeNode.prototype.real_children = function real_children() {
|
||||||
return h.flatten([this.params, this.body.expressions]);
|
return flatten([this.params, this.body.expressions]);
|
||||||
};
|
};
|
||||||
// Custom `traverse` implementation that uses the `real_children`.
|
// Custom `traverse` implementation that uses the `real_children`.
|
||||||
CodeNode.prototype.traverse = function traverse(block) {
|
CodeNode.prototype.traverse = function traverse(block) {
|
||||||
|
@ -1033,8 +1037,8 @@
|
||||||
// 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, returns, rvar, set, top;
|
||||||
returns = h.del(o, 'returns');
|
returns = del(o, 'returns');
|
||||||
top = h.del(o, 'top') && !returns;
|
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);
|
||||||
|
@ -1065,7 +1069,7 @@
|
||||||
exports.OpNode = (function() {
|
exports.OpNode = (function() {
|
||||||
OpNode = function OpNode(operator, first, second, flip) {
|
OpNode = function OpNode(operator, first, second, flip) {
|
||||||
this.type += ' ' + operator;
|
this.type += ' ' + operator;
|
||||||
this.children = h.compact([(this.first = first), (this.second = second)]);
|
this.children = compact([(this.first = first), (this.second = second)]);
|
||||||
this.operator = this.CONVERSIONS[operator] || operator;
|
this.operator = this.CONVERSIONS[operator] || operator;
|
||||||
this.flip = !!flip;
|
this.flip = !!flip;
|
||||||
return this;
|
return this;
|
||||||
|
@ -1171,7 +1175,7 @@
|
||||||
// A classic *try/catch/finally* block.
|
// A classic *try/catch/finally* block.
|
||||||
exports.TryNode = (function() {
|
exports.TryNode = (function() {
|
||||||
TryNode = function TryNode(attempt, error, recovery, ensure) {
|
TryNode = function TryNode(attempt, error, recovery, ensure) {
|
||||||
this.children = h.compact([(this.attempt = attempt), (this.recovery = recovery), (this.ensure = ensure)]);
|
this.children = compact([(this.attempt = attempt), (this.recovery = recovery), (this.ensure = ensure)]);
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this;
|
this;
|
||||||
return this;
|
return this;
|
||||||
|
@ -1187,7 +1191,7 @@
|
||||||
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(h.merge(o, {
|
finally_part = (this.ensure || '') && ' finally {\n' + this.ensure.compile(merge(o, {
|
||||||
returns: null
|
returns: null
|
||||||
})) + "\n" + this.tab + "}";
|
})) + "\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;
|
||||||
|
@ -1295,7 +1299,7 @@
|
||||||
this.name = _a[0];
|
this.name = _a[0];
|
||||||
this.index = _a[1];
|
this.index = _a[1];
|
||||||
}
|
}
|
||||||
this.children = h.compact([this.body, this.source, this.filter]);
|
this.children = compact([this.body, this.source, this.filter]);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(ForNode, BaseNode);
|
__extends(ForNode, BaseNode);
|
||||||
|
@ -1309,7 +1313,7 @@
|
||||||
// some cannot.
|
// some cannot.
|
||||||
ForNode.prototype.compile_node = function compile_node(o) {
|
ForNode.prototype.compile_node = function compile_node(o) {
|
||||||
var body, body_dent, close, for_part, index, index_var, ivar, lvar, name, range, return_result, rvar, scope, set_result, source, source_part, step_part, svar, top_level, var_part, vars;
|
var body, body_dent, close, for_part, index, index_var, ivar, lvar, name, range, return_result, rvar, scope, set_result, source, source_part, step_part, svar, top_level, var_part, vars;
|
||||||
top_level = h.del(o, 'top') && !o.returns;
|
top_level = del(o, 'top') && !o.returns;
|
||||||
range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length;
|
range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length;
|
||||||
source = range ? this.source.base : this.source;
|
source = range ? this.source.base : this.source;
|
||||||
scope = o.scope;
|
scope = o.scope;
|
||||||
|
@ -1332,7 +1336,7 @@
|
||||||
if (range) {
|
if (range) {
|
||||||
index_var = scope.free_variable();
|
index_var = scope.free_variable();
|
||||||
source_part = source.compile_variables(o);
|
source_part = source.compile_variables(o);
|
||||||
for_part = source.compile(h.merge(o, {
|
for_part = source.compile(merge(o, {
|
||||||
index: ivar,
|
index: ivar,
|
||||||
step: this.step
|
step: this.step
|
||||||
}));
|
}));
|
||||||
|
@ -1361,7 +1365,7 @@
|
||||||
}
|
}
|
||||||
if (o.returns) {
|
if (o.returns) {
|
||||||
return_result = 'return ' + return_result;
|
return_result = 'return ' + return_result;
|
||||||
h.del(o, 'returns');
|
del(o, 'returns');
|
||||||
if (this.filter) {
|
if (this.filter) {
|
||||||
body = new IfNode(this.filter, body, null, {
|
body = new IfNode(this.filter, body, null, {
|
||||||
statement: true
|
statement: true
|
||||||
|
@ -1377,7 +1381,7 @@
|
||||||
if (!(top_level)) {
|
if (!(top_level)) {
|
||||||
return_result = "\n" + this.tab + return_result + ";";
|
return_result = "\n" + this.tab + return_result + ";";
|
||||||
}
|
}
|
||||||
body = body.compile(h.merge(o, {
|
body = body.compile(merge(o, {
|
||||||
indent: body_dent,
|
indent: body_dent,
|
||||||
top: true
|
top: true
|
||||||
}));
|
}));
|
||||||
|
@ -1398,7 +1402,7 @@
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body && body.unwrap();
|
this.body = body && body.unwrap();
|
||||||
this.else_body = else_body && else_body.unwrap();
|
this.else_body = else_body && else_body.unwrap();
|
||||||
this.children = h.compact([this.condition, this.body, this.else_body]);
|
this.children = compact([this.condition, this.body, this.else_body]);
|
||||||
this.tags = tags || {};
|
this.tags = tags || {};
|
||||||
if (this.condition instanceof Array) {
|
if (this.condition instanceof Array) {
|
||||||
this.multiple = true;
|
this.multiple = true;
|
||||||
|
@ -1479,7 +1483,7 @@
|
||||||
IfNode.prototype.compile_condition = function compile_condition(o) {
|
IfNode.prototype.compile_condition = function compile_condition(o) {
|
||||||
var _a, _b, _c, _d, cond;
|
var _a, _b, _c, _d, cond;
|
||||||
return (function() {
|
return (function() {
|
||||||
_a = []; _b = h.flatten([this.condition]);
|
_a = []; _b = flatten([this.condition]);
|
||||||
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
for (_c = 0, _d = _b.length; _c < _d; _c++) {
|
||||||
cond = _b[_c];
|
cond = _b[_c];
|
||||||
_a.push(cond.compile(o));
|
_a.push(cond.compile(o));
|
||||||
|
@ -1497,9 +1501,9 @@
|
||||||
if (this.switcher) {
|
if (this.switcher) {
|
||||||
this.rewrite_switch(o);
|
this.rewrite_switch(o);
|
||||||
}
|
}
|
||||||
child = h.del(o, 'chain_child');
|
child = del(o, 'chain_child');
|
||||||
cond_o = h.merge(o);
|
cond_o = merge(o);
|
||||||
h.del(cond_o, 'returns');
|
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();
|
||||||
|
@ -1510,7 +1514,7 @@
|
||||||
if (!(this.else_body)) {
|
if (!(this.else_body)) {
|
||||||
return if_part;
|
return if_part;
|
||||||
}
|
}
|
||||||
else_part = this.is_chain() ? ' else ' + this.else_body.compile(h.merge(o, {
|
else_part = this.is_chain() ? ' else ' + this.else_body.compile(merge(o, {
|
||||||
indent: this.idt(),
|
indent: this.idt(),
|
||||||
chain_child: true
|
chain_child: true
|
||||||
})) : " else {\n" + (Expressions.wrap([this.else_body]).compile(o)) + "\n" + this.tab + "}";
|
})) : " else {\n" + (Expressions.wrap([this.else_body]).compile(o)) + "\n" + this.tab + "}";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(function(){
|
(function(){
|
||||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, h, helpers, pair;
|
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, helpers, include, pair;
|
||||||
var __hasProp = Object.prototype.hasOwnProperty;
|
var __hasProp = Object.prototype.hasOwnProperty;
|
||||||
// The CoffeeScript language has a decent amount of optional syntax,
|
// The CoffeeScript language has a decent amount of optional syntax,
|
||||||
// implicit syntax, and shorthand syntax. These things can greatly complicate a
|
// implicit syntax, and shorthand syntax. These things can greatly complicate a
|
||||||
|
@ -10,7 +10,8 @@
|
||||||
// generally clean things up.
|
// generally clean things up.
|
||||||
// Set up exported variables for both Node.js and the browser.
|
// Set up exported variables for both Node.js and the browser.
|
||||||
(typeof process !== "undefined" && process !== null) ? (helpers = require('helpers').helpers) : (this.exports = this);
|
(typeof process !== "undefined" && process !== null) ? (helpers = require('helpers').helpers) : (this.exports = this);
|
||||||
h = helpers;
|
// Import the helpers we need.
|
||||||
|
include = helpers.include;
|
||||||
// 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() {
|
||||||
|
@ -90,7 +91,7 @@
|
||||||
Rewriter.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
|
Rewriter.prototype.remove_mid_expression_newlines = function remove_mid_expression_newlines() {
|
||||||
return this.scan_tokens((function(__this) {
|
return this.scan_tokens((function(__this) {
|
||||||
var __func = function(prev, token, post, i) {
|
var __func = function(prev, token, post, i) {
|
||||||
if (!(post && h.include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) {
|
if (!(post && include(EXPRESSION_CLOSE, post[0]) && token[0] === 'TERMINATOR')) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
this.tokens.splice(i, 1);
|
this.tokens.splice(i, 1);
|
||||||
|
@ -168,8 +169,8 @@
|
||||||
this.tokens.splice(i, 0, ['CALL_END', ')', token[2]]);
|
this.tokens.splice(i, 0, ['CALL_END', ')', token[2]]);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (!(typeof post !== "undefined" && post !== null) || h.include(IMPLICIT_END, tag)) {
|
if (!(typeof post !== "undefined" && post !== null) || include(IMPLICIT_END, tag)) {
|
||||||
if (tag === 'INDENT' && prev && h.include(IMPLICIT_BLOCK, prev[0])) {
|
if (tag === 'INDENT' && prev && include(IMPLICIT_BLOCK, prev[0])) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (open || tag === 'INDENT') {
|
if (open || tag === 'INDENT') {
|
||||||
|
@ -184,7 +185,7 @@
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(prev && h.include(IMPLICIT_FUNC, prev[0]) && h.include(IMPLICIT_CALL, tag))) {
|
if (!(prev && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
calls = 0;
|
calls = 0;
|
||||||
|
@ -205,7 +206,7 @@
|
||||||
return this.scan_tokens((function(__this) {
|
return this.scan_tokens((function(__this) {
|
||||||
var __func = function(prev, token, post, i) {
|
var __func = function(prev, token, post, i) {
|
||||||
var idx, insertion, parens, pre, starter, tok;
|
var idx, insertion, parens, pre, starter, tok;
|
||||||
if (!(h.include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
|
if (!(include(SINGLE_LINERS, token[0]) && post[0] !== 'INDENT' && !(token[0] === 'ELSE' && post[0] === 'IF'))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
starter = token[0];
|
starter = token[0];
|
||||||
|
@ -216,7 +217,7 @@
|
||||||
idx += 1;
|
idx += 1;
|
||||||
tok = this.tokens[idx];
|
tok = this.tokens[idx];
|
||||||
pre = this.tokens[idx - 1];
|
pre = this.tokens[idx - 1];
|
||||||
if ((!tok || (h.include(SINGLE_CLOSERS, tok[0]) && tok[1] !== ';') || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
|
if ((!tok || (include(SINGLE_CLOSERS, tok[0]) && tok[1] !== ';') || (tok[0] === ')' && parens === 0)) && !(starter === 'ELSE' && tok[0] === 'ELSE')) {
|
||||||
insertion = pre[0] === "," ? idx - 1 : idx;
|
insertion = pre[0] === "," ? idx - 1 : idx;
|
||||||
this.tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]]);
|
this.tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]]);
|
||||||
break;
|
break;
|
||||||
|
@ -309,10 +310,10 @@
|
||||||
var inv, match, mtag, tag;
|
var inv, match, mtag, tag;
|
||||||
tag = token[0];
|
tag = token[0];
|
||||||
inv = INVERSES[token[0]];
|
inv = INVERSES[token[0]];
|
||||||
if (h.include(EXPRESSION_START, tag)) {
|
if (include(EXPRESSION_START, tag)) {
|
||||||
stack.push(token);
|
stack.push(token);
|
||||||
return 1;
|
return 1;
|
||||||
} else if (h.include(EXPRESSION_END, tag)) {
|
} else if (include(EXPRESSION_END, tag)) {
|
||||||
if (debt[inv] > 0) {
|
if (debt[inv] > 0) {
|
||||||
debt[inv] -= 1;
|
debt[inv] -= 1;
|
||||||
this.tokens.splice(i, 1);
|
this.tokens.splice(i, 1);
|
||||||
|
|
|
@ -15,7 +15,12 @@ else
|
||||||
this.exports: this
|
this.exports: this
|
||||||
Rewriter: this.Rewriter
|
Rewriter: this.Rewriter
|
||||||
|
|
||||||
h: helpers
|
# Import the helpers we need.
|
||||||
|
include: helpers.include
|
||||||
|
count: helpers.count
|
||||||
|
starts: helpers.starts
|
||||||
|
compact: helpers.compact
|
||||||
|
balanced_string: helpers.balanced_string
|
||||||
|
|
||||||
# The Lexer Class
|
# The Lexer Class
|
||||||
# ---------------
|
# ---------------
|
||||||
|
@ -89,10 +94,10 @@ exports.Lexer: class Lexer
|
||||||
return false unless id: @match IDENTIFIER, 1
|
return false unless id: @match IDENTIFIER, 1
|
||||||
@name_access_type()
|
@name_access_type()
|
||||||
tag: 'IDENTIFIER'
|
tag: 'IDENTIFIER'
|
||||||
tag: id.toUpperCase() if h.include(KEYWORDS, id) and
|
tag: id.toUpperCase() if include(KEYWORDS, id) and
|
||||||
not (h.include(ACCESSORS, @tag(0)) and not @prev().spaced)
|
not (include(ACCESSORS, @tag(0)) and not @prev().spaced)
|
||||||
@identifier_error id if h.include RESERVED, id
|
@identifier_error id if include RESERVED, id
|
||||||
tag: 'LEADING_WHEN' if tag is 'WHEN' and h.include BEFORE_WHEN, @tag()
|
tag: 'LEADING_WHEN' if tag is 'WHEN' and include BEFORE_WHEN, @tag()
|
||||||
@token(tag, id)
|
@token(tag, id)
|
||||||
@i += id.length
|
@i += id.length
|
||||||
true
|
true
|
||||||
|
@ -107,12 +112,12 @@ exports.Lexer: class Lexer
|
||||||
# Matches strings, including multi-line strings. Ensures that quotation marks
|
# Matches strings, including multi-line strings. Ensures that quotation marks
|
||||||
# are balanced within the string's contents, and within nested interpolations.
|
# are balanced within the string's contents, and within nested interpolations.
|
||||||
string_token: ->
|
string_token: ->
|
||||||
return false unless h.starts(@chunk, '"') or h.starts(@chunk, "'")
|
return false unless starts(@chunk, '"') or starts(@chunk, "'")
|
||||||
return false unless string:
|
return false unless string:
|
||||||
@balanced_token(['"', '"'], ['${', '}']) or
|
@balanced_token(['"', '"'], ['${', '}']) or
|
||||||
@balanced_token ["'", "'"]
|
@balanced_token ["'", "'"]
|
||||||
@interpolate_string string.replace(STRING_NEWLINES, " \\\n")
|
@interpolate_string string.replace(STRING_NEWLINES, " \\\n")
|
||||||
@line += h.count string, "\n"
|
@line += count string, "\n"
|
||||||
@i += string.length
|
@i += string.length
|
||||||
true
|
true
|
||||||
|
|
||||||
|
@ -122,13 +127,13 @@ exports.Lexer: class Lexer
|
||||||
return false unless match = @chunk.match(HEREDOC)
|
return false unless match = @chunk.match(HEREDOC)
|
||||||
doc: @sanitize_heredoc match[2] or match[4]
|
doc: @sanitize_heredoc match[2] or match[4]
|
||||||
@token 'STRING', "\"$doc\""
|
@token 'STRING', "\"$doc\""
|
||||||
@line += h.count match[1], "\n"
|
@line += count match[1], "\n"
|
||||||
@i += match[1].length
|
@i += match[1].length
|
||||||
true
|
true
|
||||||
|
|
||||||
# Matches JavaScript interpolated directly into the source via backticks.
|
# Matches JavaScript interpolated directly into the source via backticks.
|
||||||
js_token: ->
|
js_token: ->
|
||||||
return false unless h.starts @chunk, '`'
|
return false unless starts @chunk, '`'
|
||||||
return false unless script: @balanced_token ['`', '`']
|
return false unless script: @balanced_token ['`', '`']
|
||||||
@token 'JS', script.replace(JS_CLEANER, '')
|
@token 'JS', script.replace(JS_CLEANER, '')
|
||||||
@i += script.length
|
@i += script.length
|
||||||
|
@ -140,7 +145,7 @@ exports.Lexer: class Lexer
|
||||||
# borrow interpolation from `@interpolate_string`.
|
# borrow interpolation from `@interpolate_string`.
|
||||||
regex_token: ->
|
regex_token: ->
|
||||||
return false unless @chunk.match REGEX_START
|
return false unless @chunk.match REGEX_START
|
||||||
return false if h.include NOT_REGEX, @tag()
|
return false if include NOT_REGEX, @tag()
|
||||||
return false unless regex: @balanced_token ['/', '/']
|
return false unless regex: @balanced_token ['/', '/']
|
||||||
regex += (flags: @chunk.substr(regex.length).match(REGEX_FLAGS))
|
regex += (flags: @chunk.substr(regex.length).match(REGEX_FLAGS))
|
||||||
if regex.match REGEX_INTERPOLATION
|
if regex.match REGEX_INTERPOLATION
|
||||||
|
@ -157,7 +162,7 @@ exports.Lexer: class Lexer
|
||||||
# Matches a token in which which the passed delimiter pairs must be correctly
|
# Matches a token in which which the passed delimiter pairs must be correctly
|
||||||
# balanced (ie. strings, JS literals).
|
# balanced (ie. strings, JS literals).
|
||||||
balanced_token: (delimited...) ->
|
balanced_token: (delimited...) ->
|
||||||
h.balanced_string @chunk, delimited
|
balanced_string @chunk, delimited
|
||||||
|
|
||||||
# Matches and conumes comments. We pass through comments into JavaScript,
|
# Matches and conumes comments. We pass through comments into JavaScript,
|
||||||
# so they're treated as real tokens, like any other part of the language.
|
# so they're treated as real tokens, like any other part of the language.
|
||||||
|
@ -165,7 +170,7 @@ exports.Lexer: class Lexer
|
||||||
return false unless comment: @match COMMENT, 1
|
return false unless comment: @match COMMENT, 1
|
||||||
@line += (comment.match(MULTILINER) or []).length
|
@line += (comment.match(MULTILINER) or []).length
|
||||||
lines: comment.replace(COMMENT_CLEANER, '').split(MULTILINER)
|
lines: comment.replace(COMMENT_CLEANER, '').split(MULTILINER)
|
||||||
@token 'COMMENT', h.compact lines
|
@token 'COMMENT', compact lines
|
||||||
@token 'TERMINATOR', "\n"
|
@token 'TERMINATOR', "\n"
|
||||||
@i += comment.length
|
@i += comment.length
|
||||||
true
|
true
|
||||||
|
@ -246,7 +251,7 @@ exports.Lexer: class Lexer
|
||||||
tag: value
|
tag: value
|
||||||
if value.match(ASSIGNMENT)
|
if value.match(ASSIGNMENT)
|
||||||
tag: 'ASSIGN'
|
tag: 'ASSIGN'
|
||||||
@assignment_error() if h.include JS_FORBIDDEN, @value
|
@assignment_error() if include JS_FORBIDDEN, @value
|
||||||
else if value is ';'
|
else if value is ';'
|
||||||
tag: 'TERMINATOR'
|
tag: 'TERMINATOR'
|
||||||
else if value is '[' and @tag() is '?' and not_spaced
|
else if value is '[' and @tag() is '?' and not_spaced
|
||||||
|
@ -256,7 +261,7 @@ exports.Lexer: class Lexer
|
||||||
else if value is ']' and @soaked_index
|
else if value is ']' and @soaked_index
|
||||||
tag: 'SOAKED_INDEX_END'
|
tag: 'SOAKED_INDEX_END'
|
||||||
@soaked_index: false
|
@soaked_index: false
|
||||||
else if h.include(CALLABLE, @tag()) and not_spaced
|
else if include(CALLABLE, @tag()) and not_spaced
|
||||||
tag: 'CALL_START' if value is '('
|
tag: 'CALL_START' if value is '('
|
||||||
tag: 'INDEX_START' if value is '['
|
tag: 'INDEX_START' if value is '['
|
||||||
@token tag, value
|
@token tag, value
|
||||||
|
@ -326,7 +331,7 @@ exports.Lexer: class Lexer
|
||||||
# new Lexer, tokenize the interpolated contents, and merge them into the
|
# new Lexer, tokenize the interpolated contents, and merge them into the
|
||||||
# token stream.
|
# token stream.
|
||||||
interpolate_string: (str, escape_quotes) ->
|
interpolate_string: (str, escape_quotes) ->
|
||||||
if str.length < 3 or not h.starts str, '"'
|
if str.length < 3 or not starts str, '"'
|
||||||
@token 'STRING', str
|
@token 'STRING', str
|
||||||
else
|
else
|
||||||
lexer: new Lexer()
|
lexer: new Lexer()
|
||||||
|
@ -334,16 +339,16 @@ exports.Lexer: class Lexer
|
||||||
quote: str.substring(0, 1)
|
quote: str.substring(0, 1)
|
||||||
[i, pi]: [1, 1]
|
[i, pi]: [1, 1]
|
||||||
while i < str.length - 1
|
while i < str.length - 1
|
||||||
if h.starts str, '\\', i
|
if starts str, '\\', i
|
||||||
i += 1
|
i += 1
|
||||||
else if match: str.substring(i).match INTERPOLATION
|
else if match: str.substring(i).match INTERPOLATION
|
||||||
[group, interp]: match
|
[group, interp]: match
|
||||||
interp: "this.${ interp.substring(1) }" if h.starts interp, '@'
|
interp: "this.${ interp.substring(1) }" if starts interp, '@'
|
||||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
||||||
tokens.push ['IDENTIFIER', interp]
|
tokens.push ['IDENTIFIER', interp]
|
||||||
i += group.length - 1
|
i += group.length - 1
|
||||||
pi: i + 1
|
pi: i + 1
|
||||||
else if (expr: h.balanced_string str.substring(i), [['${', '}']])
|
else if (expr: balanced_string str.substring(i), [['${', '}']])
|
||||||
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i
|
||||||
inner: expr.substring(2, expr.length - 1)
|
inner: expr.substring(2, expr.length - 1)
|
||||||
if inner.length
|
if inner.length
|
||||||
|
|
|
@ -11,7 +11,11 @@ if process?
|
||||||
else
|
else
|
||||||
this.exports: this
|
this.exports: this
|
||||||
|
|
||||||
h: helpers
|
# Import the helpers we need.
|
||||||
|
compact: helpers.compact
|
||||||
|
flatten: helpers.flatten
|
||||||
|
merge: helpers.merge
|
||||||
|
del: helpers.del
|
||||||
|
|
||||||
# Helper function that marks a node as a JavaScript *statement*, or as a
|
# Helper function that marks a node as a JavaScript *statement*, or as a
|
||||||
# *pure_statement*. Statements must be wrapped in a closure when used as an
|
# *pure_statement*. Statements must be wrapped in a closure when used as an
|
||||||
|
@ -45,10 +49,10 @@ exports.BaseNode: class BaseNode
|
||||||
# depending on whether it's being used as part of a larger expression, or is a
|
# depending on whether it's being used as part of a larger expression, or is a
|
||||||
# top-level statement within the function body.
|
# top-level statement within the function body.
|
||||||
compile: (o) ->
|
compile: (o) ->
|
||||||
@options: h.merge o or {}
|
@options: merge o or {}
|
||||||
@tab: o.indent
|
@tab: o.indent
|
||||||
h.del @options, 'operation' unless this instanceof ValueNode
|
del @options, 'operation' unless this instanceof ValueNode
|
||||||
top: if @top_sensitive() then @options.top else h.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.returns and not (this instanceof CommentNode) and
|
||||||
not @contains (node) -> node.is_pure_statement()
|
not @contains (node) -> node.is_pure_statement()
|
||||||
|
@ -115,7 +119,7 @@ exports.Expressions: class Expressions extends BaseNode
|
||||||
type: 'Expressions'
|
type: 'Expressions'
|
||||||
|
|
||||||
constructor: (nodes) ->
|
constructor: (nodes) ->
|
||||||
@children: @expressions: h.compact h.flatten nodes or []
|
@children: @expressions: compact flatten nodes or []
|
||||||
|
|
||||||
# Tack an expression on to the end of this expression list.
|
# Tack an expression on to the end of this expression list.
|
||||||
push: (node) ->
|
push: (node) ->
|
||||||
|
@ -148,7 +152,7 @@ exports.Expressions: class Expressions extends BaseNode
|
||||||
if o.scope then super(o) else @compile_root(o)
|
if o.scope then super(o) else @compile_root(o)
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
(@compile_expression(node, h.merge(o)) for node in @expressions).join("\n")
|
(@compile_expression(node, merge(o)) for node in @expressions).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
|
||||||
# a safety closure, unless requested not to.
|
# a safety closure, unless requested not to.
|
||||||
|
@ -175,9 +179,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: h.del(o, 'returns') and @is_last(node) and not node.is_pure_statement()
|
returns: del(o, 'returns') and @is_last(node) and not node.is_pure_statement()
|
||||||
return (if stmt then '' else @idt()) + node.compile(h.merge(o, {top: true})) + (if stmt then '' else ';') unless returns
|
return (if stmt then '' else @idt()) + node.compile(merge(o, {top: true})) + (if stmt then '' else ';') unless returns
|
||||||
return node.compile(h.merge(o, {returns: true})) if node.is_statement()
|
return node.compile(merge(o, {returns: true})) if node.is_statement()
|
||||||
"${@tab}return ${node.compile(o)};"
|
"${@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
|
||||||
|
@ -224,7 +228,7 @@ exports.ReturnNode: class ReturnNode extends BaseNode
|
||||||
@children: [@expression: expression]
|
@children: [@expression: expression]
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
return @expression.compile(h.merge(o, {returns: true})) if @expression.is_statement()
|
return @expression.compile(merge(o, {returns: true})) if @expression.is_statement()
|
||||||
"${@tab}return ${@expression.compile(o)};"
|
"${@tab}return ${@expression.compile(o)};"
|
||||||
|
|
||||||
statement ReturnNode, true
|
statement ReturnNode, true
|
||||||
|
@ -240,7 +244,7 @@ exports.ValueNode: class ValueNode extends BaseNode
|
||||||
|
|
||||||
# A **ValueNode** has a base and a list of property accesses.
|
# A **ValueNode** has a base and a list of property accesses.
|
||||||
constructor: (base, properties) ->
|
constructor: (base, properties) ->
|
||||||
@children: h.flatten [@base: base, @properties: (properties or [])]
|
@children: flatten [@base: base, @properties: (properties or [])]
|
||||||
|
|
||||||
# Add a property access to the list.
|
# Add a property access to the list.
|
||||||
push: (prop) ->
|
push: (prop) ->
|
||||||
|
@ -280,8 +284,8 @@ exports.ValueNode: class ValueNode extends BaseNode
|
||||||
# evaluate a anything twice when building the soak chain.
|
# evaluate a anything twice when building the soak chain.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
soaked: false
|
soaked: false
|
||||||
only: h.del(o, 'only_first')
|
only: del(o, 'only_first')
|
||||||
op: h.del(o, 'operation')
|
op: del(o, 'operation')
|
||||||
props: if only then @properties[0...@properties.length - 1] else @properties
|
props: if only then @properties[0...@properties.length - 1] else @properties
|
||||||
baseline: @base.compile o
|
baseline: @base.compile o
|
||||||
baseline: "($baseline)" if @base instanceof ObjectNode and @has_properties()
|
baseline: "($baseline)" if @base instanceof ObjectNode and @has_properties()
|
||||||
|
@ -328,7 +332,7 @@ exports.CallNode: class CallNode extends BaseNode
|
||||||
type: 'Call'
|
type: 'Call'
|
||||||
|
|
||||||
constructor: (variable, args) ->
|
constructor: (variable, args) ->
|
||||||
@children: h.flatten [@variable: variable, @args: (args or [])]
|
@children: flatten [@variable: variable, @args: (args or [])]
|
||||||
@prefix: ''
|
@prefix: ''
|
||||||
|
|
||||||
# Tag this invocation as creating a new instance.
|
# Tag this invocation as creating a new instance.
|
||||||
|
@ -450,8 +454,8 @@ exports.RangeNode: class RangeNode extends BaseNode
|
||||||
# needed to iterate over the values in the range. Used by comprehensions.
|
# needed to iterate over the values in the range. Used by comprehensions.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
return @compile_array(o) unless o.index
|
return @compile_array(o) unless o.index
|
||||||
idx: h.del o, 'index'
|
idx: del o, 'index'
|
||||||
step: h.del o, 'step'
|
step: del o, 'step'
|
||||||
vars: "$idx = $@from_var"
|
vars: "$idx = $@from_var"
|
||||||
step: if step then step.compile(o) else '1'
|
step: if step then step.compile(o) else '1'
|
||||||
equals: if @exclusive then '' else '='
|
equals: if @exclusive then '' else '='
|
||||||
|
@ -547,7 +551,7 @@ exports.ClassNode: class ClassNode extends BaseNode
|
||||||
# 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.
|
||||||
constructor: (variable, parent, props) ->
|
constructor: (variable, parent, props) ->
|
||||||
@children: h.compact h.flatten [@variable: variable, @parent: parent, @properties: props or []]
|
@children: compact flatten [@variable: variable, @parent: parent, @properties: props or []]
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -557,7 +561,7 @@ exports.ClassNode: class ClassNode extends BaseNode
|
||||||
constructor: null
|
constructor: null
|
||||||
props: new Expressions()
|
props: new Expressions()
|
||||||
o.top: true
|
o.top: true
|
||||||
ret: h.del o, 'returns'
|
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'
|
||||||
|
@ -616,10 +620,10 @@ exports.AssignNode: class AssignNode extends BaseNode
|
||||||
# we've been assigned to, for correct internal references. If the variable
|
# we've been assigned to, for correct internal references. If the variable
|
||||||
# has not been seen yet within the current scope, declare it.
|
# has not been seen yet within the current scope, declare it.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
top: h.del o, 'top'
|
top: del o, 'top'
|
||||||
return @compile_pattern_match(o) if @is_statement()
|
return @compile_pattern_match(o) if @is_statement()
|
||||||
return @compile_splice(o) if @is_value() and @variable.is_splice()
|
return @compile_splice(o) if @is_value() and @variable.is_splice()
|
||||||
stmt: h.del o, 'as_statement'
|
stmt: del o, 'as_statement'
|
||||||
name: @variable.compile(o)
|
name: @variable.compile(o)
|
||||||
last: if @is_value() then @variable.last.replace(@LEADING_DOT, '') else name
|
last: if @is_value() then @variable.last.replace(@LEADING_DOT, '') else name
|
||||||
match: name.match(@PROTO_ASSIGN)
|
match: name.match(@PROTO_ASSIGN)
|
||||||
|
@ -663,7 +667,7 @@ exports.AssignNode: class AssignNode extends BaseNode
|
||||||
# Compile the assignment from an array splice literal, using JavaScript's
|
# Compile the assignment from an array splice literal, using JavaScript's
|
||||||
# `Array#splice` method.
|
# `Array#splice` method.
|
||||||
compile_splice: (o) ->
|
compile_splice: (o) ->
|
||||||
name: @variable.compile(h.merge(o, {only_first: true}))
|
name: @variable.compile(merge(o, {only_first: true}))
|
||||||
l: @variable.properties.length
|
l: @variable.properties.length
|
||||||
range: @variable.properties[l - 1].range
|
range: @variable.properties[l - 1].range
|
||||||
plus: if range.exclusive then '' else ' + 1'
|
plus: if range.exclusive then '' else ' + 1'
|
||||||
|
@ -691,14 +695,14 @@ exports.CodeNode: class CodeNode extends BaseNode
|
||||||
# arrow, generates a wrapper that saves the current value of `this` through
|
# arrow, generates a wrapper that saves the current value of `this` through
|
||||||
# a closure.
|
# a closure.
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
shared_scope: h.del o, 'shared_scope'
|
shared_scope: del o, 'shared_scope'
|
||||||
top: h.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.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)
|
||||||
h.del o, 'no_wrap'
|
del o, 'no_wrap'
|
||||||
h.del o, 'globals'
|
del o, 'globals'
|
||||||
if @params[@params.length - 1] instanceof SplatNode
|
if @params[@params.length - 1] instanceof SplatNode
|
||||||
splat: @params.pop()
|
splat: @params.pop()
|
||||||
splat.index: @params.length
|
splat.index: @params.length
|
||||||
|
@ -719,7 +723,7 @@ exports.CodeNode: class CodeNode extends BaseNode
|
||||||
# When traversing (for printing or inspecting), return the real children of
|
# When traversing (for printing or inspecting), return the real children of
|
||||||
# the function -- the parameters and body of expressions.
|
# the function -- the parameters and body of expressions.
|
||||||
real_children: ->
|
real_children: ->
|
||||||
h.flatten [@params, @body.expressions]
|
flatten [@params, @body.expressions]
|
||||||
|
|
||||||
# Custom `traverse` implementation that uses the `real_children`.
|
# Custom `traverse` implementation that uses the `real_children`.
|
||||||
traverse: (block) ->
|
traverse: (block) ->
|
||||||
|
@ -780,8 +784,8 @@ 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: h.del(o, 'returns')
|
returns: del(o, 'returns')
|
||||||
top: h.del(o, 'top') and not returns
|
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)
|
||||||
|
@ -828,7 +832,7 @@ exports.OpNode: class OpNode extends BaseNode
|
||||||
|
|
||||||
constructor: (operator, first, second, flip) ->
|
constructor: (operator, first, second, flip) ->
|
||||||
@type += ' ' + operator
|
@type += ' ' + operator
|
||||||
@children: h.compact [@first: first, @second: second]
|
@children: compact [@first: first, @second: second]
|
||||||
@operator: @CONVERSIONS[operator] or operator
|
@operator: @CONVERSIONS[operator] or operator
|
||||||
@flip: !!flip
|
@flip: !!flip
|
||||||
|
|
||||||
|
@ -887,7 +891,7 @@ exports.TryNode: class TryNode extends BaseNode
|
||||||
type: 'Try'
|
type: 'Try'
|
||||||
|
|
||||||
constructor: (attempt, error, recovery, ensure) ->
|
constructor: (attempt, error, recovery, ensure) ->
|
||||||
@children: h.compact [@attempt: attempt, @recovery: recovery, @ensure: ensure]
|
@children: compact [@attempt: attempt, @recovery: recovery, @ensure: ensure]
|
||||||
@error: error
|
@error: error
|
||||||
this
|
this
|
||||||
|
|
||||||
|
@ -899,7 +903,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(h.merge(o, {returns: null})) + "\n$@tab}"
|
finally_part: (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o, {returns: null})) + "\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
|
||||||
|
@ -986,7 +990,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||||
@step: source.step
|
@step: source.step
|
||||||
@object: !!source.object
|
@object: !!source.object
|
||||||
[@name, @index]: [@index, @name] if @object
|
[@name, @index]: [@index, @name] if @object
|
||||||
@children: h.compact [@body, @source, @filter]
|
@children: compact [@body, @source, @filter]
|
||||||
|
|
||||||
top_sensitive: ->
|
top_sensitive: ->
|
||||||
true
|
true
|
||||||
|
@ -996,7 +1000,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||||
# 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: h.del(o, 'top') and not o.returns
|
top_level: del(o, 'top') and not o.returns
|
||||||
range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
|
range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
|
||||||
source: if range then @source.base else @source
|
source: if range then @source.base else @source
|
||||||
scope: o.scope
|
scope: o.scope
|
||||||
|
@ -1013,7 +1017,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||||
if range
|
if range
|
||||||
index_var: scope.free_variable()
|
index_var: scope.free_variable()
|
||||||
source_part: source.compile_variables o
|
source_part: source.compile_variables o
|
||||||
for_part: source.compile h.merge o, {index: ivar, step: @step}
|
for_part: source.compile merge o, {index: ivar, step: @step}
|
||||||
for_part: "$index_var = 0, $for_part, $index_var++"
|
for_part: "$index_var = 0, $for_part, $index_var++"
|
||||||
else
|
else
|
||||||
index_var: null
|
index_var: null
|
||||||
|
@ -1029,7 +1033,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||||
body: PushNode.wrap(rvar, body) unless top_level
|
body: PushNode.wrap(rvar, body) unless top_level
|
||||||
if o.returns
|
if o.returns
|
||||||
return_result: 'return ' + return_result
|
return_result: 'return ' + return_result
|
||||||
h.del o, 'returns'
|
del o, 'returns'
|
||||||
body: new IfNode(@filter, body, null, {statement: true}) if @filter
|
body: new IfNode(@filter, body, null, {statement: true}) if @filter
|
||||||
else if @filter
|
else if @filter
|
||||||
body: Expressions.wrap([new IfNode(@filter, body)])
|
body: Expressions.wrap([new IfNode(@filter, body)])
|
||||||
|
@ -1037,7 +1041,7 @@ exports.ForNode: class ForNode extends BaseNode
|
||||||
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
|
return_result: "\n$@tab$return_result;" unless top_level
|
||||||
body: body.compile(h.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$@tab$return_result"
|
||||||
|
@ -1058,7 +1062,7 @@ exports.IfNode: class IfNode extends BaseNode
|
||||||
@condition: condition
|
@condition: condition
|
||||||
@body: body and body.unwrap()
|
@body: body and body.unwrap()
|
||||||
@else_body: else_body and else_body.unwrap()
|
@else_body: else_body and else_body.unwrap()
|
||||||
@children: h.compact [@condition, @body, @else_body]
|
@children: compact [@condition, @body, @else_body]
|
||||||
@tags: tags or {}
|
@tags: tags or {}
|
||||||
@multiple: true if @condition instanceof Array
|
@multiple: true if @condition instanceof Array
|
||||||
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
|
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
|
||||||
|
@ -1115,7 +1119,7 @@ exports.IfNode: class IfNode extends BaseNode
|
||||||
@statement ||= !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
|
@statement ||= !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
|
||||||
|
|
||||||
compile_condition: (o) ->
|
compile_condition: (o) ->
|
||||||
(cond.compile(o) for cond in h.flatten([@condition])).join(' || ')
|
(cond.compile(o) for cond in flatten([@condition])).join(' || ')
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1124,9 +1128,9 @@ exports.IfNode: class IfNode extends BaseNode
|
||||||
# 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: h.del o, 'chain_child'
|
child: del o, 'chain_child'
|
||||||
cond_o: h.merge o
|
cond_o: merge o
|
||||||
h.del cond_o, 'returns'
|
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()
|
||||||
|
@ -1136,7 +1140,7 @@ exports.IfNode: class IfNode extends BaseNode
|
||||||
if_part: "$prefix${if_dent}if (${ @compile_condition(cond_o) }) {\n$body\n$@tab}"
|
if_part: "$prefix${if_dent}if (${ @compile_condition(cond_o) }) {\n$body\n$@tab}"
|
||||||
return if_part unless @else_body
|
return if_part unless @else_body
|
||||||
else_part: if @is_chain()
|
else_part: if @is_chain()
|
||||||
' else ' + @else_body.compile(h.merge(o, {indent: @idt(), chain_child: true}))
|
' else ' + @else_body.compile(merge(o, {indent: @idt(), chain_child: true}))
|
||||||
else
|
else
|
||||||
" else {\n${ Expressions.wrap([@else_body]).compile(o) }\n$@tab}"
|
" else {\n${ Expressions.wrap([@else_body]).compile(o) }\n$@tab}"
|
||||||
"$if_part$else_part"
|
"$if_part$else_part"
|
||||||
|
|
|
@ -12,7 +12,8 @@ if process?
|
||||||
else
|
else
|
||||||
this.exports: this
|
this.exports: this
|
||||||
|
|
||||||
h: helpers
|
# Import the helpers we need.
|
||||||
|
include: helpers.include
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -73,7 +74,7 @@ exports.Rewriter: class Rewriter
|
||||||
# this, remove their trailing newlines.
|
# this, remove their trailing newlines.
|
||||||
remove_mid_expression_newlines: ->
|
remove_mid_expression_newlines: ->
|
||||||
@scan_tokens (prev, token, post, i) =>
|
@scan_tokens (prev, token, post, i) =>
|
||||||
return 1 unless post and h.include(EXPRESSION_CLOSE, post[0]) and token[0] is 'TERMINATOR'
|
return 1 unless post and include(EXPRESSION_CLOSE, post[0]) and token[0] is 'TERMINATOR'
|
||||||
@tokens.splice(i, 1)
|
@tokens.splice(i, 1)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -123,8 +124,8 @@ exports.Rewriter: class Rewriter
|
||||||
stack[stack.length - 1] -= 1
|
stack[stack.length - 1] -= 1
|
||||||
@tokens.splice(i, 0, ['CALL_END', ')', token[2]])
|
@tokens.splice(i, 0, ['CALL_END', ')', token[2]])
|
||||||
return 2
|
return 2
|
||||||
if !post? or h.include IMPLICIT_END, tag
|
if !post? or include IMPLICIT_END, tag
|
||||||
return 1 if tag is 'INDENT' and prev and h.include IMPLICIT_BLOCK, prev[0]
|
return 1 if tag is 'INDENT' and prev and include IMPLICIT_BLOCK, prev[0]
|
||||||
if open or tag is 'INDENT'
|
if open or tag is 'INDENT'
|
||||||
idx: if tag is 'OUTDENT' then i + 1 else i
|
idx: if tag is 'OUTDENT' then i + 1 else i
|
||||||
stack_pointer: if tag is 'INDENT' then 2 else 1
|
stack_pointer: if tag is 'INDENT' then 2 else 1
|
||||||
|
@ -133,7 +134,7 @@ exports.Rewriter: class Rewriter
|
||||||
size: stack[stack.length - stack_pointer] + 1
|
size: stack[stack.length - stack_pointer] + 1
|
||||||
stack[stack.length - stack_pointer]: 0
|
stack[stack.length - stack_pointer]: 0
|
||||||
return size
|
return size
|
||||||
return 1 unless prev and h.include(IMPLICIT_FUNC, prev[0]) and h.include IMPLICIT_CALL, tag
|
return 1 unless prev and include(IMPLICIT_FUNC, prev[0]) and include IMPLICIT_CALL, tag
|
||||||
calls: 0
|
calls: 0
|
||||||
@tokens.splice(i, 0, ['CALL_START', '(', token[2]])
|
@tokens.splice(i, 0, ['CALL_START', '(', token[2]])
|
||||||
stack[stack.length - 1] += 1
|
stack[stack.length - 1] += 1
|
||||||
|
@ -145,7 +146,7 @@ exports.Rewriter: class Rewriter
|
||||||
# but we need to make sure it's balanced.
|
# but we need to make sure it's balanced.
|
||||||
add_implicit_indentation: ->
|
add_implicit_indentation: ->
|
||||||
@scan_tokens (prev, token, post, i) =>
|
@scan_tokens (prev, token, post, i) =>
|
||||||
return 1 unless h.include(SINGLE_LINERS, token[0]) and
|
return 1 unless include(SINGLE_LINERS, token[0]) and
|
||||||
post[0] isnt 'INDENT' and
|
post[0] isnt 'INDENT' and
|
||||||
not (token[0] is 'ELSE' and post[0] is 'IF')
|
not (token[0] is 'ELSE' and post[0] is 'IF')
|
||||||
starter: token[0]
|
starter: token[0]
|
||||||
|
@ -157,7 +158,7 @@ exports.Rewriter: class Rewriter
|
||||||
tok: @tokens[idx]
|
tok: @tokens[idx]
|
||||||
pre: @tokens[idx - 1]
|
pre: @tokens[idx - 1]
|
||||||
if (not tok or
|
if (not tok or
|
||||||
(h.include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';') or
|
(include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';') or
|
||||||
(tok[0] is ')' && parens is 0)) and
|
(tok[0] is ')' && parens is 0)) and
|
||||||
not (starter is 'ELSE' and tok[0] is 'ELSE')
|
not (starter is 'ELSE' and tok[0] is 'ELSE')
|
||||||
insertion: if pre[0] is "," then idx - 1 else idx
|
insertion: if pre[0] is "," then idx - 1 else idx
|
||||||
|
@ -206,10 +207,10 @@ exports.Rewriter: class Rewriter
|
||||||
@scan_tokens (prev, token, post, i) =>
|
@scan_tokens (prev, token, post, i) =>
|
||||||
tag: token[0]
|
tag: token[0]
|
||||||
inv: INVERSES[token[0]]
|
inv: INVERSES[token[0]]
|
||||||
if h.include EXPRESSION_START, tag
|
if include EXPRESSION_START, tag
|
||||||
stack.push token
|
stack.push token
|
||||||
return 1
|
return 1
|
||||||
else if h.include EXPRESSION_END, tag
|
else if include EXPRESSION_END, tag
|
||||||
if debt[inv] > 0
|
if debt[inv] > 0
|
||||||
debt[inv] -= 1
|
debt[inv] -= 1
|
||||||
@tokens.splice i, 1
|
@tokens.splice i, 1
|
||||||
|
|
Loading…
Reference in New Issue