mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
added last
to helpers
This commit is contained in:
parent
c064c90ee9
commit
b2313beaf4
7 changed files with 128 additions and 135 deletions
|
@ -38,7 +38,7 @@
|
||||||
exports.count = function(string, letter) {
|
exports.count = function(string, letter) {
|
||||||
var num, pos;
|
var num, pos;
|
||||||
num = (pos = 0);
|
num = (pos = 0);
|
||||||
while (0 < (pos = 1 + string.indexOf(letter, pos))) {
|
while (pos = 1 + string.indexOf(letter, pos)) {
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
return num;
|
return num;
|
||||||
|
@ -64,4 +64,7 @@
|
||||||
delete obj[key];
|
delete obj[key];
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
exports.last = function(array, back) {
|
||||||
|
return array[array.length - (back || 0) - 1];
|
||||||
|
};
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
61
lib/lexer.js
61
lib/lexer.js
|
@ -1,5 +1,5 @@
|
||||||
(function() {
|
(function() {
|
||||||
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, SHIFT, SIMPLESTR, UNARY, WHITESPACE, _ref, compact, count, include, starts;
|
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, SHIFT, SIMPLESTR, UNARY, WHITESPACE, _ref, compact, count, include, last, starts;
|
||||||
var __slice = Array.prototype.slice;
|
var __slice = Array.prototype.slice;
|
||||||
_ref = require('./rewriter');
|
_ref = require('./rewriter');
|
||||||
Rewriter = _ref.Rewriter;
|
Rewriter = _ref.Rewriter;
|
||||||
|
@ -8,6 +8,7 @@
|
||||||
count = _ref.count;
|
count = _ref.count;
|
||||||
starts = _ref.starts;
|
starts = _ref.starts;
|
||||||
compact = _ref.compact;
|
compact = _ref.compact;
|
||||||
|
last = _ref.last;
|
||||||
exports.Lexer = (function() {
|
exports.Lexer = (function() {
|
||||||
Lexer = function() {};
|
Lexer = function() {};
|
||||||
Lexer.prototype.tokenize = function(code, options) {
|
Lexer.prototype.tokenize = function(code, options) {
|
||||||
|
@ -212,7 +213,7 @@
|
||||||
indent = match[0];
|
indent = match[0];
|
||||||
this.line += count(indent, '\n');
|
this.line += count(indent, '\n');
|
||||||
this.i += indent.length;
|
this.i += indent.length;
|
||||||
prev = this.prev(2);
|
prev = last(this.tokens, 1);
|
||||||
size = indent.length - 1 - indent.lastIndexOf('\n');
|
size = indent.length - 1 - indent.lastIndexOf('\n');
|
||||||
nextCharacter = NEXT_CHARACTER.exec(this.chunk)[1];
|
nextCharacter = NEXT_CHARACTER.exec(this.chunk)[1];
|
||||||
noNewlines = (('.' === nextCharacter || ',' === nextCharacter)) || this.unfinished();
|
noNewlines = (('.' === nextCharacter || ',' === nextCharacter)) || this.unfinished();
|
||||||
|
@ -270,7 +271,7 @@
|
||||||
if (!(match = WHITESPACE.exec(this.chunk))) {
|
if (!(match = WHITESPACE.exec(this.chunk))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
prev = this.prev();
|
prev = last(this.tokens);
|
||||||
if (prev) {
|
if (prev) {
|
||||||
prev.spaced = true;
|
prev.spaced = true;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +291,7 @@
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
Lexer.prototype.literalToken = function() {
|
Lexer.prototype.literalToken = function() {
|
||||||
var _ref2, match, prev, space, spaced, tag, value;
|
var _ref2, match, prev, space, spaced, tag, val, value;
|
||||||
if (match = this.chunk.match(OPERATOR)) {
|
if (match = this.chunk.match(OPERATOR)) {
|
||||||
_ref2 = match;
|
_ref2 = match;
|
||||||
value = _ref2[0];
|
value = _ref2[0];
|
||||||
|
@ -302,14 +303,15 @@
|
||||||
value = this.chunk.charAt(0);
|
value = this.chunk.charAt(0);
|
||||||
}
|
}
|
||||||
this.i += value.length;
|
this.i += value.length;
|
||||||
spaced = (prev = this.prev()) && prev.spaced;
|
prev = last(this.tokens);
|
||||||
|
spaced = prev == null ? undefined : prev.spaced;
|
||||||
tag = value;
|
tag = value;
|
||||||
if (value === '=') {
|
if (value === '=') {
|
||||||
if (include(JS_FORBIDDEN, this.value())) {
|
if (include(JS_FORBIDDEN, val = this.value())) {
|
||||||
this.assignmentError();
|
this.assignmentError();
|
||||||
}
|
}
|
||||||
if (('or' === (_ref2 = this.value()) || 'and' === _ref2)) {
|
if (('or' === val || 'and' === val)) {
|
||||||
this.tokens.splice(this.tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[this.value()] + '=', prev[2]]);
|
this.tokens.splice(-1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[val] + '=', prev[2]]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,10 +339,10 @@
|
||||||
tag = 'INDEX_START';
|
tag = 'INDEX_START';
|
||||||
switch (this.tag()) {
|
switch (this.tag()) {
|
||||||
case '?':
|
case '?':
|
||||||
this.tag(1, 'INDEX_SOAK');
|
this.tag(0, 'INDEX_SOAK');
|
||||||
break;
|
break;
|
||||||
case '::':
|
case '::':
|
||||||
this.tag(1, 'INDEX_PROTO');
|
this.tag(0, 'INDEX_PROTO');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,18 +352,18 @@
|
||||||
};
|
};
|
||||||
Lexer.prototype.tagAccessor = function() {
|
Lexer.prototype.tagAccessor = function() {
|
||||||
var accessor, prev;
|
var accessor, prev;
|
||||||
if ((!(prev = this.prev())) || (prev && prev.spaced)) {
|
if (!(prev = last(this.tokens)) || prev.spaced) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
accessor = (function() {
|
accessor = (function() {
|
||||||
if (prev[1] === '::') {
|
if (prev[1] === '::') {
|
||||||
return this.tag(1, 'PROTOTYPE_ACCESS');
|
return this.tag(0, 'PROTOTYPE_ACCESS');
|
||||||
} else if (prev[1] === '.' && this.value(2) !== '.') {
|
} else if (prev[1] === '.' && this.value(1) !== '.') {
|
||||||
if (this.tag(2) === '?') {
|
if (this.tag(1) === '?') {
|
||||||
this.tag(1, 'SOAK_ACCESS');
|
this.tag(0, 'SOAK_ACCESS');
|
||||||
return this.tokens.splice(-2, 1);
|
return this.tokens.splice(-2, 1);
|
||||||
} else {
|
} else {
|
||||||
return this.tag(1, 'PROPERTY_ACCESS');
|
return this.tag(0, 'PROPERTY_ACCESS');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return prev[0] === '@';
|
return prev[0] === '@';
|
||||||
|
@ -402,11 +404,9 @@
|
||||||
if (this.tag() !== ')') {
|
if (this.tag() !== ')') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
i = 0;
|
i = this.tokens.length;
|
||||||
while (true) {
|
while (true) {
|
||||||
i += 1;
|
if (!(tok = this.tokens[--i])) {
|
||||||
tok = this.prev(i);
|
|
||||||
if (!tok) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch (tok[0]) {
|
switch (tok[0]) {
|
||||||
|
@ -449,7 +449,7 @@
|
||||||
_ref3 = pair;
|
_ref3 = pair;
|
||||||
open = _ref3[0];
|
open = _ref3[0];
|
||||||
close = _ref3[1];
|
close = _ref3[1];
|
||||||
if (levels.length && starts(str, close, i) && levels[levels.length - 1] === pair) {
|
if (levels.length && starts(str, close, i) && last(levels) === pair) {
|
||||||
levels.pop();
|
levels.pop();
|
||||||
i += close.length - 1;
|
i += close.length - 1;
|
||||||
if (!(levels.length)) {
|
if (!(levels.length)) {
|
||||||
|
@ -563,30 +563,21 @@
|
||||||
};
|
};
|
||||||
Lexer.prototype.tag = function(index, newTag) {
|
Lexer.prototype.tag = function(index, newTag) {
|
||||||
var tok;
|
var tok;
|
||||||
if (!(tok = this.prev(index))) {
|
if (!(tok = last(this.tokens, index))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (typeof newTag !== "undefined" && newTag !== null) {
|
return (tok[0] = (typeof newTag !== "undefined" && newTag !== null) ? newTag : tok[0]);
|
||||||
return (tok[0] = newTag);
|
|
||||||
}
|
|
||||||
return tok[0];
|
|
||||||
};
|
};
|
||||||
Lexer.prototype.value = function(index, val) {
|
Lexer.prototype.value = function(index, val) {
|
||||||
var tok;
|
var tok;
|
||||||
if (!(tok = this.prev(index))) {
|
if (!(tok = last(this.tokens, index))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (typeof val !== "undefined" && val !== null) {
|
return (tok[1] = (typeof val !== "undefined" && val !== null) ? val : tok[1]);
|
||||||
return (tok[1] = val);
|
|
||||||
}
|
|
||||||
return tok[1];
|
|
||||||
};
|
|
||||||
Lexer.prototype.prev = function(index) {
|
|
||||||
return this.tokens[this.tokens.length - (index || 1)];
|
|
||||||
};
|
};
|
||||||
Lexer.prototype.unfinished = function() {
|
Lexer.prototype.unfinished = function() {
|
||||||
var prev, value;
|
var prev, value;
|
||||||
return (prev = this.prev(2)) && prev[0] !== '.' && (value = this.value()) && NO_NEWLINE.test(value) && !CODE.test(value) && !ASSIGNED.test(this.chunk);
|
return (prev = last(this.tokens, 1)) && prev[0] !== '.' && (value = this.value()) && NO_NEWLINE.test(value) && !CODE.test(value) && !ASSIGNED.test(this.chunk);
|
||||||
};
|
};
|
||||||
Lexer.prototype.escapeLines = function(str, heredoc) {
|
Lexer.prototype.escapeLines = function(str, heredoc) {
|
||||||
return str.replace(MULTILINER, heredoc ? '\\n' : '');
|
return str.replace(MULTILINER, heredoc ? '\\n' : '');
|
||||||
|
|
58
lib/nodes.js
58
lib/nodes.js
|
@ -1,5 +1,5 @@
|
||||||
(function() {
|
(function() {
|
||||||
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NO, NUMBER, ObjectNode, OpNode, ParamNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SIMPLENUM, Scope, SliceNode, SplatNode, SwitchNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, YES, _ref, compact, del, ends, flatten, include, indexOf, literal, merge, starts, utility;
|
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NO, NUMBER, ObjectNode, OpNode, ParamNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SIMPLENUM, Scope, SliceNode, SplatNode, SwitchNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, YES, _ref, compact, del, ends, flatten, include, indexOf, last, literal, merge, starts, utility;
|
||||||
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,6 +19,7 @@
|
||||||
indexOf = _ref.indexOf;
|
indexOf = _ref.indexOf;
|
||||||
starts = _ref.starts;
|
starts = _ref.starts;
|
||||||
ends = _ref.ends;
|
ends = _ref.ends;
|
||||||
|
last = _ref.last;
|
||||||
YES = function() {
|
YES = function() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -191,16 +192,14 @@
|
||||||
return this.expressions.length === 0;
|
return this.expressions.length === 0;
|
||||||
};
|
};
|
||||||
Expressions.prototype.makeReturn = function() {
|
Expressions.prototype.makeReturn = function() {
|
||||||
var idx, last;
|
var end, idx;
|
||||||
idx = this.expressions.length - 1;
|
end = this.expressions[(idx = this.expressions.length - 1)];
|
||||||
last = this.expressions[idx];
|
if (end instanceof CommentNode) {
|
||||||
if (last instanceof CommentNode) {
|
end = this.expressions[idx -= 1];
|
||||||
last = this.expressions[idx -= 1];
|
|
||||||
}
|
}
|
||||||
if (!last || last instanceof ReturnNode) {
|
if (end && !(end instanceof ReturnNode)) {
|
||||||
return this;
|
this.expressions[idx] = end.makeReturn();
|
||||||
}
|
}
|
||||||
this.expressions[idx] = last.makeReturn();
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
Expressions.prototype.compile = function(o) {
|
Expressions.prototype.compile = function(o) {
|
||||||
|
@ -335,10 +334,10 @@
|
||||||
return this.base instanceof ObjectNode && !this.hasProperties();
|
return this.base instanceof ObjectNode && !this.hasProperties();
|
||||||
};
|
};
|
||||||
ValueNode.prototype.isSplice = function() {
|
ValueNode.prototype.isSplice = function() {
|
||||||
return this.hasProperties() && this.properties[this.properties.length - 1] instanceof SliceNode;
|
return last(this.properties) instanceof SliceNode;
|
||||||
};
|
};
|
||||||
ValueNode.prototype.isComplex = function() {
|
ValueNode.prototype.isComplex = function() {
|
||||||
return this.base.isComplex() || this.properties.length;
|
return this.base.isComplex() || this.hasProperties();
|
||||||
};
|
};
|
||||||
ValueNode.prototype.makeReturn = function() {
|
ValueNode.prototype.makeReturn = function() {
|
||||||
return this.hasProperties() ? ValueNode.__super__.makeReturn.call(this) : this.base.makeReturn();
|
return this.hasProperties() ? ValueNode.__super__.makeReturn.call(this) : this.base.makeReturn();
|
||||||
|
@ -380,7 +379,7 @@
|
||||||
return !o.top || this.properties.length ? ValueNode.__super__.compile.call(this, o) : this.base.compile(o);
|
return !o.top || this.properties.length ? ValueNode.__super__.compile.call(this, o) : this.base.compile(o);
|
||||||
};
|
};
|
||||||
ValueNode.prototype.compileNode = function(o) {
|
ValueNode.prototype.compileNode = function(o) {
|
||||||
var _i, _len, _ref2, baseline, complete, copy, hasSoak, i, me, only, op, part, prevcomp, prop, props, temp;
|
var _i, _len, _ref2, baseline, complete, copy, hasSoak, i, me, only, op, part, prop, props, temp;
|
||||||
only = del(o, 'onlyFirst');
|
only = del(o, 'onlyFirst');
|
||||||
op = this.tags.operation;
|
op = this.tags.operation;
|
||||||
props = only ? this.properties.slice(0, -1) : this.properties;
|
props = only ? this.properties.slice(0, -1) : this.properties;
|
||||||
|
@ -413,7 +412,7 @@
|
||||||
if (prop.soakNode) {
|
if (prop.soakNode) {
|
||||||
if (i === 0 && this.base.isComplex()) {
|
if (i === 0 && this.base.isComplex()) {
|
||||||
temp = o.scope.freeVariable('ref');
|
temp = o.scope.freeVariable('ref');
|
||||||
complete = ("(" + (baseline = temp) + " = (" + (prevcomp = complete) + "))");
|
complete = ("(" + (baseline = temp) + " = (" + (complete) + "))");
|
||||||
}
|
}
|
||||||
complete = i === 0 && !o.scope.check(complete) ? ("(typeof " + (complete) + " === \"undefined\" || " + (baseline) + " === null)") : ("" + (complete) + " == null");
|
complete = i === 0 && !o.scope.check(complete) ? ("(typeof " + (complete) + " === \"undefined\" || " + (baseline) + " === null)") : ("" + (complete) + " == null");
|
||||||
complete += ' ? undefined : ' + (baseline += prop.compile(o));
|
complete += ' ? undefined : ' + (baseline += prop.compile(o));
|
||||||
|
@ -493,7 +492,7 @@
|
||||||
}
|
}
|
||||||
op = this.tags.operation;
|
op = this.tags.operation;
|
||||||
if (this.exist) {
|
if (this.exist) {
|
||||||
if (this.variable instanceof ValueNode && this.variable.properties[this.variable.properties.length - 1] instanceof AccessorNode) {
|
if (this.variable instanceof ValueNode && last(this.variable.properties) instanceof AccessorNode) {
|
||||||
methodAccessor = this.variable.properties.pop();
|
methodAccessor = this.variable.properties.pop();
|
||||||
_ref2 = this.variable.compileReference(o);
|
_ref2 = this.variable.compileReference(o);
|
||||||
first = _ref2[0];
|
first = _ref2[0];
|
||||||
|
@ -771,7 +770,7 @@
|
||||||
}
|
}
|
||||||
return _result;
|
return _result;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
lastNoncom = nonComments[nonComments.length - 1];
|
lastNoncom = last(nonComments);
|
||||||
props = (function() {
|
props = (function() {
|
||||||
_result = []; _ref2 = this.properties;
|
_result = []; _ref2 = this.properties;
|
||||||
for (i = 0, _len = _ref2.length; i < _len; i++) {
|
for (i = 0, _len = _ref2.length; i < _len; i++) {
|
||||||
|
@ -949,7 +948,7 @@
|
||||||
return this.isValue() && (this.variable.isArray() || this.variable.isObject());
|
return this.isValue() && (this.variable.isArray() || this.variable.isObject());
|
||||||
};
|
};
|
||||||
AssignNode.prototype.compileNode = function(o) {
|
AssignNode.prototype.compileNode = function(o) {
|
||||||
var last, match, name, proto, stmt, top, val;
|
var end, match, name, proto, stmt, top, val;
|
||||||
top = del(o, 'top');
|
top = del(o, 'top');
|
||||||
if (this.isStatement(o)) {
|
if (this.isStatement(o)) {
|
||||||
return this.compilePatternMatch(o);
|
return this.compilePatternMatch(o);
|
||||||
|
@ -959,12 +958,12 @@
|
||||||
}
|
}
|
||||||
stmt = del(o, 'asStatement');
|
stmt = del(o, 'asStatement');
|
||||||
name = this.variable.compile(o);
|
name = this.variable.compile(o);
|
||||||
last = this.isValue() ? this.variable.last.replace(this.LEADING_DOT, '') : name;
|
end = this.isValue() ? this.variable.last.replace(this.LEADING_DOT, '') : name;
|
||||||
match = name.match(this.PROTO_ASSIGN);
|
match = name.match(this.PROTO_ASSIGN);
|
||||||
proto = match && match[1];
|
proto = match && match[1];
|
||||||
if (this.value instanceof CodeNode) {
|
if (this.value instanceof CodeNode) {
|
||||||
if (last.match(IDENTIFIER)) {
|
if (IDENTIFIER.test(end)) {
|
||||||
this.value.name = last;
|
this.value.name = end;
|
||||||
}
|
}
|
||||||
if (proto) {
|
if (proto) {
|
||||||
this.value.proto = proto;
|
this.value.proto = proto;
|
||||||
|
@ -1202,25 +1201,26 @@
|
||||||
return "" + (utility('slice')) + ".call(" + (name) + ", " + (index) + (trail) + ")";
|
return "" + (utility('slice')) + ".call(" + (name) + ", " + (index) + (trail) + ")";
|
||||||
};
|
};
|
||||||
SplatNode.compileSplattedArray = function(list, o) {
|
SplatNode.compileSplattedArray = function(list, o) {
|
||||||
var _len, _ref2, arg, args, code, i, last, prev;
|
var _len, _ref2, arg, args, code, end, i, prev;
|
||||||
args = [];
|
args = [];
|
||||||
|
end = -1;
|
||||||
_ref2 = list;
|
_ref2 = list;
|
||||||
for (i = 0, _len = _ref2.length; i < _len; i++) {
|
for (i = 0, _len = _ref2.length; i < _len; i++) {
|
||||||
arg = _ref2[i];
|
arg = _ref2[i];
|
||||||
code = arg.compile(o);
|
code = arg.compile(o);
|
||||||
prev = args[(last = args.length - 1)];
|
prev = args[end];
|
||||||
if (!(arg instanceof SplatNode)) {
|
if (!(arg instanceof SplatNode)) {
|
||||||
if (prev && starts(prev, '[') && ends(prev, ']')) {
|
if (prev && starts(prev, '[') && ends(prev, ']')) {
|
||||||
args[last] = ("" + (prev.slice(0, -1)) + ", " + (code) + "]");
|
args[end] = ("" + (prev.slice(0, -1)) + ", " + (code) + "]");
|
||||||
continue;
|
continue;
|
||||||
} else if (prev && starts(prev, '.concat([') && ends(prev, '])')) {
|
|
||||||
args[last] = ("" + (prev.slice(0, -2)) + ", " + (code) + "])");
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
code = ("[" + (code) + "]");
|
|
||||||
}
|
}
|
||||||
|
if (prev && starts(prev, '.concat([') && ends(prev, '])')) {
|
||||||
|
args[end] = ("" + (prev.slice(0, -2)) + ", " + (code) + "])");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
code = ("[" + (code) + "]");
|
||||||
}
|
}
|
||||||
args.push(i === 0 ? code : (".concat(" + (code) + ")"));
|
args[++end] = i === 0 ? code : (".concat(" + (code) + ")");
|
||||||
}
|
}
|
||||||
return args.join('');
|
return args.join('');
|
||||||
};
|
};
|
||||||
|
@ -1756,7 +1756,7 @@
|
||||||
code += ("\n" + (this.idt(1)) + "case " + (condition.compile(o)) + ":");
|
code += ("\n" + (this.idt(1)) + "case " + (condition.compile(o)) + ":");
|
||||||
}
|
}
|
||||||
code += ("\n" + (block.compile(o)));
|
code += ("\n" + (block.compile(o)));
|
||||||
if (!(exprs[exprs.length - 1] instanceof ReturnNode)) {
|
if (!(last(exprs) instanceof ReturnNode)) {
|
||||||
code += ("\n" + (idt) + "break;");
|
code += ("\n" + (idt) + "break;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ exports.compact = (array) ->
|
||||||
# Count the number of occurences of a character in a string.
|
# Count the number of occurences of a character in a string.
|
||||||
exports.count = (string, letter) ->
|
exports.count = (string, letter) ->
|
||||||
num = pos = 0
|
num = pos = 0
|
||||||
num++ while 0 < pos = 1 + string.indexOf letter, pos
|
num++ while pos = 1 + string.indexOf letter, pos
|
||||||
num
|
num
|
||||||
|
|
||||||
# Merge objects, returning a fresh copy with attributes from both sides.
|
# Merge objects, returning a fresh copy with attributes from both sides.
|
||||||
|
@ -61,3 +61,6 @@ exports.del = (obj, key) ->
|
||||||
val = obj[key]
|
val = obj[key]
|
||||||
delete obj[key]
|
delete obj[key]
|
||||||
val
|
val
|
||||||
|
|
||||||
|
# Gets the last item of an array(-like) object.
|
||||||
|
exports.last = (array, back) -> array[array.length - (back or 0) - 1]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
{Rewriter} = require './rewriter'
|
{Rewriter} = require './rewriter'
|
||||||
|
|
||||||
# Import the helpers we need.
|
# Import the helpers we need.
|
||||||
{include, count, starts, compact} = require './helpers'
|
{include, count, starts, compact, last} = require './helpers'
|
||||||
|
|
||||||
# The Lexer Class
|
# The Lexer Class
|
||||||
# ---------------
|
# ---------------
|
||||||
|
@ -170,7 +170,7 @@ exports.Lexer = class Lexer
|
||||||
regexToken: ->
|
regexToken: ->
|
||||||
return false unless first = @chunk.match REGEX_START
|
return false unless first = @chunk.match REGEX_START
|
||||||
return false if first[1] is ' ' and @tag() not in ['CALL_START', '=']
|
return false if first[1] is ' ' and @tag() not in ['CALL_START', '=']
|
||||||
return false if include NOT_REGEX, @tag()
|
return false if include NOT_REGEX, @tag()
|
||||||
return false unless regex = @balancedToken ['/', '/']
|
return false unless regex = @balancedToken ['/', '/']
|
||||||
return false unless end = @chunk[regex.length..].match REGEX_END
|
return false unless end = @chunk[regex.length..].match REGEX_END
|
||||||
flags = end[0]
|
flags = end[0]
|
||||||
|
@ -206,7 +206,7 @@ exports.Lexer = class Lexer
|
||||||
indent = match[0]
|
indent = match[0]
|
||||||
@line += count indent, '\n'
|
@line += count indent, '\n'
|
||||||
@i += indent.length
|
@i += indent.length
|
||||||
prev = @prev 2
|
prev = last @tokens, 1
|
||||||
size = indent.length - 1 - indent.lastIndexOf '\n'
|
size = indent.length - 1 - indent.lastIndexOf '\n'
|
||||||
nextCharacter = NEXT_CHARACTER.exec(@chunk)[1]
|
nextCharacter = NEXT_CHARACTER.exec(@chunk)[1]
|
||||||
noNewlines = (nextCharacter in ['.', ',']) or @unfinished()
|
noNewlines = (nextCharacter in ['.', ',']) or @unfinished()
|
||||||
|
@ -254,7 +254,7 @@ exports.Lexer = class Lexer
|
||||||
# as being "spaced", because there are some cases where it makes a difference.
|
# as being "spaced", because there are some cases where it makes a difference.
|
||||||
whitespaceToken: ->
|
whitespaceToken: ->
|
||||||
return false unless match = WHITESPACE.exec @chunk
|
return false unless match = WHITESPACE.exec @chunk
|
||||||
prev = @prev()
|
prev = last @tokens
|
||||||
prev.spaced = true if prev
|
prev.spaced = true if prev
|
||||||
@i += match[0].length
|
@i += match[0].length
|
||||||
true
|
true
|
||||||
|
@ -282,12 +282,13 @@ exports.Lexer = class Lexer
|
||||||
else
|
else
|
||||||
value = @chunk.charAt 0
|
value = @chunk.charAt 0
|
||||||
@i += value.length
|
@i += value.length
|
||||||
spaced = (prev = @prev()) and prev.spaced
|
prev = last @tokens
|
||||||
|
spaced = prev?.spaced
|
||||||
tag = value
|
tag = value
|
||||||
if value is '='
|
if value is '='
|
||||||
@assignmentError() if include JS_FORBIDDEN, @value()
|
@assignmentError() if include JS_FORBIDDEN, val = @value()
|
||||||
if @value() in ['or', 'and']
|
if val in ['or', 'and']
|
||||||
@tokens.splice(@tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[@value()] + '=', prev[2]])
|
@tokens.splice(-1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[val] + '=', prev[2]])
|
||||||
return true
|
return true
|
||||||
if value is ';' then tag = 'TERMINATOR'
|
if value is ';' then tag = 'TERMINATOR'
|
||||||
else if include(LOGIC, value) then tag = 'LOGIC'
|
else if include(LOGIC, value) then tag = 'LOGIC'
|
||||||
|
@ -303,8 +304,8 @@ exports.Lexer = class Lexer
|
||||||
else if value is '['
|
else if value is '['
|
||||||
tag = 'INDEX_START'
|
tag = 'INDEX_START'
|
||||||
switch @tag()
|
switch @tag()
|
||||||
when '?' then @tag 1, 'INDEX_SOAK'
|
when '?' then @tag 0, 'INDEX_SOAK'
|
||||||
when '::' then @tag 1, 'INDEX_PROTO'
|
when '::' then @tag 0, 'INDEX_PROTO'
|
||||||
@token tag, value
|
@token tag, value
|
||||||
true
|
true
|
||||||
|
|
||||||
|
@ -315,15 +316,15 @@ exports.Lexer = class Lexer
|
||||||
# if it's a special kind of accessor. Return `true` if any type of accessor
|
# if it's a special kind of accessor. Return `true` if any type of accessor
|
||||||
# is the previous token.
|
# is the previous token.
|
||||||
tagAccessor: ->
|
tagAccessor: ->
|
||||||
return false if (not prev = @prev()) or (prev and prev.spaced)
|
return false if not (prev = last @tokens) or prev.spaced
|
||||||
accessor = if prev[1] is '::'
|
accessor = if prev[1] is '::'
|
||||||
@tag 1, 'PROTOTYPE_ACCESS'
|
@tag 0, 'PROTOTYPE_ACCESS'
|
||||||
else if prev[1] is '.' and @value(2) isnt '.'
|
else if prev[1] is '.' and @value(1) isnt '.'
|
||||||
if @tag(2) is '?'
|
if @tag(1) is '?'
|
||||||
@tag(1, 'SOAK_ACCESS')
|
@tag(0, 'SOAK_ACCESS')
|
||||||
@tokens.splice(-2, 1)
|
@tokens.splice(-2, 1)
|
||||||
else
|
else
|
||||||
@tag 1, 'PROPERTY_ACCESS'
|
@tag 0, 'PROPERTY_ACCESS'
|
||||||
else
|
else
|
||||||
prev[0] is '@'
|
prev[0] is '@'
|
||||||
if accessor then 'accessor' else false
|
if accessor then 'accessor' else false
|
||||||
|
@ -348,11 +349,9 @@ exports.Lexer = class Lexer
|
||||||
# parameters specially in order to make things easier for the parser.
|
# parameters specially in order to make things easier for the parser.
|
||||||
tagParameters: ->
|
tagParameters: ->
|
||||||
return if @tag() isnt ')'
|
return if @tag() isnt ')'
|
||||||
i = 0
|
i = @tokens.length
|
||||||
loop
|
loop
|
||||||
i += 1
|
return unless tok = @tokens[--i]
|
||||||
tok = @prev i
|
|
||||||
return if not tok
|
|
||||||
switch tok[0]
|
switch tok[0]
|
||||||
when 'IDENTIFIER' then tok[0] = 'PARAM'
|
when 'IDENTIFIER' then tok[0] = 'PARAM'
|
||||||
when ')' then tok[0] = 'PARAM_END'
|
when ')' then tok[0] = 'PARAM_END'
|
||||||
|
@ -389,7 +388,7 @@ exports.Lexer = class Lexer
|
||||||
else
|
else
|
||||||
for pair in delimited
|
for pair in delimited
|
||||||
[open, close] = pair
|
[open, close] = pair
|
||||||
if levels.length and starts(str, close, i) and levels[levels.length - 1] is pair
|
if levels.length and starts(str, close, i) and last(levels) is pair
|
||||||
levels.pop()
|
levels.pop()
|
||||||
i += close.length - 1
|
i += close.length - 1
|
||||||
i += 1 unless levels.length
|
i += 1 unless levels.length
|
||||||
|
@ -469,23 +468,17 @@ exports.Lexer = class Lexer
|
||||||
|
|
||||||
# Peek at a tag in the current token stream.
|
# Peek at a tag in the current token stream.
|
||||||
tag: (index, newTag) ->
|
tag: (index, newTag) ->
|
||||||
return unless tok = @prev index
|
return unless tok = last @tokens, index
|
||||||
return tok[0] = newTag if newTag?
|
tok[0] = newTag ? tok[0]
|
||||||
tok[0]
|
|
||||||
|
|
||||||
# Peek at a value in the current token stream.
|
# Peek at a value in the current token stream.
|
||||||
value: (index, val) ->
|
value: (index, val) ->
|
||||||
return unless tok = @prev index
|
return unless tok = last @tokens, index
|
||||||
return tok[1] = val if val?
|
tok[1] = val ? tok[1]
|
||||||
tok[1]
|
|
||||||
|
|
||||||
# Peek at a previous token, entire.
|
|
||||||
prev: (index) ->
|
|
||||||
@tokens[@tokens.length - (index or 1)]
|
|
||||||
|
|
||||||
# Are we in the midst of an unfinished expression?
|
# Are we in the midst of an unfinished expression?
|
||||||
unfinished: ->
|
unfinished: ->
|
||||||
(prev = @prev 2 ) and prev[0] isnt '.' and
|
(prev = last @tokens, 1) and prev[0] isnt '.' and
|
||||||
(value = @value()) and NO_NEWLINE.test(value) and not CODE.test(value) and
|
(value = @value()) and NO_NEWLINE.test(value) and not CODE.test(value) and
|
||||||
not ASSIGNED.test(@chunk)
|
not ASSIGNED.test(@chunk)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{Scope} = require './scope'
|
{Scope} = require './scope'
|
||||||
|
|
||||||
# Import the helpers we plan to use.
|
# Import the helpers we plan to use.
|
||||||
{compact, flatten, merge, del, include, indexOf, starts, ends} = require './helpers'
|
{compact, flatten, merge, del, include, indexOf, starts, ends, last} = require './helpers'
|
||||||
|
|
||||||
# Constant functions for nodes that don't need customization.
|
# Constant functions for nodes that don't need customization.
|
||||||
YES = -> yes
|
YES = -> yes
|
||||||
|
@ -182,11 +182,10 @@ exports.Expressions = class Expressions extends BaseNode
|
||||||
# An Expressions node does not return its entire body, rather it
|
# An Expressions node does not return its entire body, rather it
|
||||||
# ensures that the final expression is returned.
|
# ensures that the final expression is returned.
|
||||||
makeReturn: ->
|
makeReturn: ->
|
||||||
idx = @expressions.length - 1
|
end = @expressions[idx = @expressions.length - 1]
|
||||||
last = @expressions[idx]
|
end = @expressions[idx -= 1] if end instanceof CommentNode
|
||||||
last = @expressions[idx -= 1] if last instanceof CommentNode
|
if end and end not instanceof ReturnNode
|
||||||
return this if not last or last instanceof ReturnNode
|
@expressions[idx] = end.makeReturn()
|
||||||
@expressions[idx] = last.makeReturn()
|
|
||||||
this
|
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.
|
||||||
|
@ -317,10 +316,10 @@ exports.ValueNode = class ValueNode extends BaseNode
|
||||||
@base instanceof ObjectNode and not @hasProperties()
|
@base instanceof ObjectNode and not @hasProperties()
|
||||||
|
|
||||||
isSplice: ->
|
isSplice: ->
|
||||||
@hasProperties() and @properties[@properties.length - 1] instanceof SliceNode
|
last(@properties) instanceof SliceNode
|
||||||
|
|
||||||
isComplex: ->
|
isComplex: ->
|
||||||
@base.isComplex() or @properties.length
|
@base.isComplex() or @hasProperties()
|
||||||
|
|
||||||
makeReturn: ->
|
makeReturn: ->
|
||||||
if @hasProperties() then super() else @base.makeReturn()
|
if @hasProperties() then super() else @base.makeReturn()
|
||||||
|
@ -380,7 +379,7 @@ exports.ValueNode = class ValueNode extends BaseNode
|
||||||
if prop.soakNode
|
if prop.soakNode
|
||||||
if i is 0 and @base.isComplex()
|
if i is 0 and @base.isComplex()
|
||||||
temp = o.scope.freeVariable 'ref'
|
temp = o.scope.freeVariable 'ref'
|
||||||
complete = "(#{ baseline = temp } = (#{prevcomp = complete}))"
|
complete = "(#{ baseline = temp } = (#{complete}))"
|
||||||
complete = if i is 0 and not o.scope.check complete
|
complete = if i is 0 and not o.scope.check complete
|
||||||
"(typeof #{complete} === \"undefined\" || #{baseline} === null)"
|
"(typeof #{complete} === \"undefined\" || #{baseline} === null)"
|
||||||
else
|
else
|
||||||
|
@ -457,7 +456,8 @@ exports.CallNode = class CallNode extends BaseNode
|
||||||
o.chainRoot = this unless o.chainRoot
|
o.chainRoot = this unless o.chainRoot
|
||||||
op = @tags.operation
|
op = @tags.operation
|
||||||
if @exist
|
if @exist
|
||||||
if @variable instanceof ValueNode and @variable.properties[@variable.properties.length - 1] instanceof AccessorNode
|
if @variable instanceof ValueNode and
|
||||||
|
last(@variable.properties) instanceof AccessorNode
|
||||||
methodAccessor = @variable.properties.pop()
|
methodAccessor = @variable.properties.pop()
|
||||||
[first, meth] = @variable.compileReference o
|
[first, meth] = @variable.compileReference o
|
||||||
@first = new ValueNode(first, [methodAccessor]).compile o
|
@first = new ValueNode(first, [methodAccessor]).compile o
|
||||||
|
@ -683,7 +683,7 @@ exports.ObjectNode = class ObjectNode extends BaseNode
|
||||||
top = del o, 'top'
|
top = del o, 'top'
|
||||||
o.indent = @idt 1
|
o.indent = @idt 1
|
||||||
nonComments = prop for prop in @properties when (prop not instanceof CommentNode)
|
nonComments = prop for prop in @properties when (prop not instanceof CommentNode)
|
||||||
lastNoncom = nonComments[nonComments.length - 1]
|
lastNoncom = last nonComments
|
||||||
props = for prop, i in @properties
|
props = for prop, i in @properties
|
||||||
join = ",\n"
|
join = ",\n"
|
||||||
join = "\n" if (prop is lastNoncom) or (prop instanceof CommentNode)
|
join = "\n" if (prop is lastNoncom) or (prop instanceof CommentNode)
|
||||||
|
@ -841,11 +841,11 @@ exports.AssignNode = class AssignNode extends BaseNode
|
||||||
return @compileSplice(o) if @isValue() and @variable.isSplice()
|
return @compileSplice(o) if @isValue() and @variable.isSplice()
|
||||||
stmt = del o, 'asStatement'
|
stmt = del o, 'asStatement'
|
||||||
name = @variable.compile(o)
|
name = @variable.compile(o)
|
||||||
last = if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
|
end = if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
|
||||||
match = name.match(@PROTO_ASSIGN)
|
match = name.match(@PROTO_ASSIGN)
|
||||||
proto = match and match[1]
|
proto = match and match[1]
|
||||||
if @value instanceof CodeNode
|
if @value instanceof CodeNode
|
||||||
@value.name = last if last.match(IDENTIFIER)
|
@value.name = end if IDENTIFIER.test end
|
||||||
@value.proto = proto if proto
|
@value.proto = proto if proto
|
||||||
val = @value.compile o
|
val = @value.compile o
|
||||||
return "#{name}: #{val}" if @context is 'object'
|
return "#{name}: #{val}" if @context is 'object'
|
||||||
|
@ -1041,20 +1041,20 @@ exports.SplatNode = class SplatNode extends BaseNode
|
||||||
# splats, to a proper array
|
# splats, to a proper array
|
||||||
@compileSplattedArray: (list, o) ->
|
@compileSplattedArray: (list, o) ->
|
||||||
args = []
|
args = []
|
||||||
|
end = -1
|
||||||
for arg, i in list
|
for arg, i in list
|
||||||
code = arg.compile o
|
code = arg.compile o
|
||||||
prev = args[last = args.length - 1]
|
prev = args[end]
|
||||||
if arg not instanceof SplatNode
|
if arg not instanceof SplatNode
|
||||||
if prev and starts(prev, '[') and ends(prev, ']')
|
if prev and starts(prev, '[') and ends(prev, ']')
|
||||||
args[last] = "#{prev.slice 0, -1}, #{code}]"
|
args[end] = "#{prev.slice 0, -1}, #{code}]"
|
||||||
continue
|
continue
|
||||||
else if prev and starts(prev, '.concat([') and ends(prev, '])')
|
if prev and starts(prev, '.concat([') and ends(prev, '])')
|
||||||
args[last] = "#{prev.slice 0, -2}, #{code}])"
|
args[end] = "#{prev.slice 0, -2}, #{code}])"
|
||||||
continue
|
continue
|
||||||
else
|
code = "[#{code}]"
|
||||||
code = "[#{code}]"
|
args[++end] = if i is 0 then code else ".concat(#{code})"
|
||||||
args.push(if i is 0 then code else ".concat(#{code})")
|
args.join ''
|
||||||
args.join('')
|
|
||||||
|
|
||||||
#### WhileNode
|
#### WhileNode
|
||||||
|
|
||||||
|
@ -1470,7 +1470,7 @@ exports.SwitchNode = class SwitchNode extends BaseNode
|
||||||
condition = new OpNode '!!', new ParentheticalNode condition if @tags.subjectless
|
condition = new OpNode '!!', new ParentheticalNode condition if @tags.subjectless
|
||||||
code += "\n#{ @idt(1) }case #{ condition.compile o }:"
|
code += "\n#{ @idt(1) }case #{ condition.compile o }:"
|
||||||
code += "\n#{ block.compile o }"
|
code += "\n#{ block.compile o }"
|
||||||
code += "\n#{ idt }break;" unless exprs[exprs.length - 1] instanceof ReturnNode
|
code += "\n#{ idt }break;" unless last(exprs) instanceof ReturnNode
|
||||||
if @otherwise
|
if @otherwise
|
||||||
code += "\n#{ @idt(1) }default:\n#{ @otherwise.compile o }"
|
code += "\n#{ @idt(1) }default:\n#{ @otherwise.compile o }"
|
||||||
code += "\n#{ @tab }}"
|
code += "\n#{ @tab }}"
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
{indexOf, include, starts, ends, compact, count, merge, extend, flatten, del} = require '../lib/helpers'
|
{indexOf, include, starts, ends, compact, count, merge, extend, flatten, del, last} = require '../lib/helpers'
|
||||||
|
|
||||||
|
array = [0..4]
|
||||||
|
string = array.join ''
|
||||||
|
object = {}
|
||||||
|
|
||||||
# Test `indexOf`
|
# Test `indexOf`
|
||||||
array = [0..4]
|
eq 0, indexOf array, 0
|
||||||
|
eq 2, indexOf array, 2
|
||||||
ok indexOf(array, 0) is 0
|
eq 4, indexOf array, 4
|
||||||
ok indexOf(array, 2) is 2
|
eq(-1, indexOf array, 6)
|
||||||
ok indexOf(array, 4) is 4
|
|
||||||
ok indexOf(array, 6) is -1
|
|
||||||
|
|
||||||
# Test `include`
|
# Test `include`
|
||||||
ok include array, 0
|
ok include array, 0
|
||||||
|
@ -15,8 +17,6 @@ ok include array, 4
|
||||||
ok not include array, 6
|
ok not include array, 6
|
||||||
|
|
||||||
# Test `starts`
|
# Test `starts`
|
||||||
string = array.join ''
|
|
||||||
|
|
||||||
ok starts string, '012'
|
ok starts string, '012'
|
||||||
ok starts string, '34', 3
|
ok starts string, '34', 3
|
||||||
ok not starts string, '42'
|
ok not starts string, '42'
|
||||||
|
@ -29,18 +29,21 @@ ok not ends string, '42'
|
||||||
ok not ends string, '42', 6
|
ok not ends string, '42', 6
|
||||||
|
|
||||||
# Test `merge`
|
# Test `merge`
|
||||||
object = {}
|
|
||||||
merged = merge object, array
|
merged = merge object, array
|
||||||
|
|
||||||
ok merged isnt object
|
ok merged isnt object
|
||||||
ok merged[3] is 3
|
eq merged[3], 3
|
||||||
|
|
||||||
# Test `extend`
|
# Test `extend`
|
||||||
ok object is extend object, array
|
ok object is extend object, array
|
||||||
ok object[3] is 3
|
eq object[3], 3
|
||||||
|
|
||||||
# Test `flatten`
|
# Test `flatten`
|
||||||
ok "#{ flatten [0, [1, 2], 3, [4]] }" is "#{ array }"
|
eq "#{ flatten [0, [1, 2], 3, [4]] }", "#{ array }"
|
||||||
|
|
||||||
ok 1 is del object, 1
|
# Test `del`
|
||||||
|
eq 1, del object, 1
|
||||||
ok 1 not of object
|
ok 1 not of object
|
||||||
|
|
||||||
|
# Test `last`
|
||||||
|
eq 4, last array
|
||||||
|
eq 2, last array, 2
|
||||||
|
|
Loading…
Add table
Reference in a new issue