Merge pull request #3841 from lydell/last

Replace `last array` helper with `[..., last] = array`
This commit is contained in:
Michael Ficarra 2015-02-08 10:22:09 -08:00
commit 53c7891599
9 changed files with 119 additions and 125 deletions

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.9.0
(function() {
var buildLocationData, extend, flatten, last, ref, repeat, syntaxErrorToString;
var buildLocationData, extend, flatten, ref, repeat, syntaxErrorToString;
exports.starts = function(string, literal, start) {
return literal === string.substr(start, literal.length);
@ -83,10 +83,6 @@
return val;
};
exports.last = last = function(array, back) {
return array[array.length - (back || 0) - 1];
};
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
var e, i, len1;
for (i = 0, len1 = this.length; i < len1; i++) {

View File

@ -1,11 +1,11 @@
// Generated by CoffeeScript 1.9.0
(function() {
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
ref = require('./rewriter'), Rewriter = ref.Rewriter, INVERSES = ref.INVERSES;
ref1 = require('./helpers'), count = ref1.count, starts = ref1.starts, compact = ref1.compact, last = ref1.last, repeat = ref1.repeat, invertLiterate = ref1.invertLiterate, locationDataToString = ref1.locationDataToString, throwSyntaxError = ref1.throwSyntaxError;
ref1 = require('./helpers'), count = ref1.count, starts = ref1.starts, compact = ref1.compact, repeat = ref1.repeat, invertLiterate = ref1.invertLiterate, locationDataToString = ref1.locationDataToString, throwSyntaxError = ref1.throwSyntaxError;
exports.Lexer = Lexer = (function() {
function Lexer() {}
@ -64,7 +64,7 @@
};
Lexer.prototype.identifierToken = function() {
var colon, colonOffset, forcedIdentifier, id, idLength, input, match, poppedToken, prev, ref2, ref3, ref4, tag, tagToken;
var colon, colonOffset, forcedIdentifier, id, idLength, input, match, poppedToken, prev, ref2, ref3, ref4, ref5, tag, tagToken;
if (!(match = IDENTIFIER.exec(this.chunk))) {
return 0;
}
@ -79,11 +79,12 @@
this.token('FROM', id);
return id.length;
}
forcedIdentifier = colon || (prev = last(this.tokens)) && (((ref2 = prev[0]) === '.' || ref2 === '?.' || ref2 === '::' || ref2 === '?::') || !prev.spaced && prev[0] === '@');
ref2 = this.tokens, prev = ref2[ref2.length - 1];
forcedIdentifier = colon || (prev != null) && (((ref3 = prev[0]) === '.' || ref3 === '?.' || ref3 === '::' || ref3 === '?::') || !prev.spaced && prev[0] === '@');
tag = 'IDENTIFIER';
if (!forcedIdentifier && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
tag = id.toUpperCase();
if (tag === 'WHEN' && (ref3 = this.tag(), indexOf.call(LINE_BREAK, ref3) >= 0)) {
if (tag === 'WHEN' && (ref4 = this.tag(), indexOf.call(LINE_BREAK, ref4) >= 0)) {
tag = 'LEADING_WHEN';
} else if (tag === 'FOR') {
this.seenFor = true;
@ -143,7 +144,7 @@
tagToken = this.token(tag, id, 0, idLength);
tagToken.variable = !forcedIdentifier;
if (poppedToken) {
ref4 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = ref4[0], tagToken[2].first_column = ref4[1];
ref5 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = ref5[0], tagToken[2].first_column = ref5[1];
}
if (colon) {
colonOffset = input.lastIndexOf(':');
@ -296,7 +297,7 @@
};
Lexer.prototype.regexToken = function() {
var body, closed, end, errorToken, flags, index, match, prev, ref2, ref3, regex, rparen, tokens;
var body, closed, end, errorToken, flags, index, match, prev, ref2, ref3, ref4, regex, rparen, tokens;
switch (false) {
case !(match = REGEX_ILLEGAL.exec(this.chunk)):
this.error("regular expressions cannot begin with " + match[2], {
@ -312,13 +313,13 @@
isRegex: true
});
index = regex.length;
prev = last(this.tokens);
ref2 = this.tokens, prev = ref2[ref2.length - 1];
if (prev) {
if (prev.spaced && (ref2 = prev[0], indexOf.call(CALLABLE, ref2) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (prev.spaced && (ref3 = prev[0], indexOf.call(CALLABLE, ref3) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (!closed || POSSIBLY_DIVISION.test(regex)) {
return 0;
}
} else if (ref3 = prev[0], indexOf.call(NOT_REGEX, ref3) >= 0) {
} else if (ref4 = prev[0], indexOf.call(NOT_REGEX, ref4) >= 0) {
return 0;
}
}
@ -449,11 +450,11 @@
};
Lexer.prototype.whitespaceToken = function() {
var match, nline, prev;
var match, nline, prev, ref2;
if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) {
return 0;
}
prev = last(this.tokens);
ref2 = this.tokens, prev = ref2[ref2.length - 1];
if (prev) {
prev[match ? 'spaced' : 'newLine'] = true;
}
@ -482,7 +483,7 @@
};
Lexer.prototype.literalToken = function() {
var match, prev, ref2, ref3, ref4, ref5, tag, token, value;
var match, prev, ref2, ref3, ref4, ref5, ref6, tag, token, value;
if (match = OPERATOR.exec(this.chunk)) {
value = match[0];
if (CODE.test(value)) {
@ -492,12 +493,12 @@
value = this.chunk.charAt(0);
}
tag = value;
prev = last(this.tokens);
ref2 = this.tokens, prev = ref2[ref2.length - 1];
if (value === '=' && prev) {
if (!prev[1].reserved && (ref2 = prev[1], indexOf.call(JS_FORBIDDEN, ref2) >= 0)) {
if (!prev[1].reserved && (ref3 = prev[1], indexOf.call(JS_FORBIDDEN, ref3) >= 0)) {
this.error("reserved word '" + prev[1] + "' can't be assigned", prev[2]);
}
if ((ref3 = prev[1]) === '||' || ref3 === '&&') {
if ((ref4 = prev[1]) === '||' || ref4 === '&&') {
prev[0] = 'COMPOUND_ASSIGN';
prev[1] += '=';
return value.length;
@ -521,12 +522,12 @@
} else if (indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
tag = 'LOGIC';
} else if (prev && !prev.spaced) {
if (value === '(' && (ref4 = prev[0], indexOf.call(CALLABLE, ref4) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (value === '(' && (ref5 = prev[0], indexOf.call(CALLABLE, ref5) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (prev[0] === '?') {
prev[0] = 'FUNC_EXIST';
}
tag = 'CALL_START';
} else if (value === '[' && (ref5 = prev[0], indexOf.call(INDEXABLE, ref5) >= 0)) {
} else if (value === '[' && (ref6 = prev[0], indexOf.call(INDEXABLE, ref6) >= 0)) {
tag = 'INDEX_START';
switch (prev[0]) {
case '?':
@ -706,19 +707,21 @@
};
Lexer.prototype.pair = function(tag) {
var ref2, wanted;
if (tag !== (wanted = (ref2 = last(this.ends)) != null ? ref2.tag : void 0)) {
var lastIndent, prev, ref2, ref3, wanted;
ref2 = this.ends, prev = ref2[ref2.length - 1];
if (tag !== (wanted = prev != null ? prev.tag : void 0)) {
if ('OUTDENT' !== wanted) {
this.error("unmatched " + tag);
}
this.outdentToken(last(this.indents), true);
ref3 = this.indents, lastIndent = ref3[ref3.length - 1];
this.outdentToken(lastIndent, true);
return this.pair(tag);
}
return this.ends.pop();
};
Lexer.prototype.getLineAndColumnFromChunk = function(offset) {
var column, lineCount, lines, string;
var column, lastLine, lineCount, ref2, string;
if (offset === 0) {
return [this.chunkLine, this.chunkColumn];
}
@ -730,8 +733,8 @@
lineCount = count(string, '\n');
column = this.chunkColumn;
if (lineCount > 0) {
lines = string.split('\n');
column = last(lines).length;
ref2 = string.split('\n'), lastLine = ref2[ref2.length - 1];
column = lastLine.length;
} else {
column += string.length;
}
@ -764,14 +767,16 @@
return token;
};
Lexer.prototype.tag = function(index, tag) {
var tok;
return (tok = last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]);
Lexer.prototype.tag = function() {
var ref2, token;
ref2 = this.tokens, token = ref2[ref2.length - 1];
return token != null ? token[0] : void 0;
};
Lexer.prototype.value = function(index, val) {
var tok;
return (tok = last(this.tokens, index)) && (val ? tok[1] = val : tok[1]);
Lexer.prototype.value = function() {
var ref2, token;
ref2 = this.tokens, token = ref2[ref2.length - 1];
return token != null ? token[1] : void 0;
};
Lexer.prototype.unfinished = function() {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.9.0
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, locationDataToString, merge, multident, parseNum, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
@ -12,7 +12,7 @@
ref1 = require('./lexer'), RESERVED = ref1.RESERVED, STRICT_PROSCRIBED = ref1.STRICT_PROSCRIBED;
ref2 = require('./helpers'), compact = ref2.compact, flatten = ref2.flatten, extend = ref2.extend, merge = ref2.merge, del = ref2.del, starts = ref2.starts, ends = ref2.ends, last = ref2.last, some = ref2.some, addLocationDataFn = ref2.addLocationDataFn, locationDataToString = ref2.locationDataToString, throwSyntaxError = ref2.throwSyntaxError;
ref2 = require('./helpers'), compact = ref2.compact, flatten = ref2.flatten, extend = ref2.extend, merge = ref2.merge, del = ref2.del, starts = ref2.starts, ends = ref2.ends, some = ref2.some, addLocationDataFn = ref2.addLocationDataFn, locationDataToString = ref2.locationDataToString, throwSyntaxError = ref2.throwSyntaxError;
exports.extend = extend;
@ -760,7 +760,9 @@
};
Value.prototype.isSplice = function() {
return last(this.properties) instanceof Slice;
var lastProp, ref3;
ref3 = this.properties, lastProp = ref3[ref3.length - 1];
return lastProp instanceof Slice;
};
Value.prototype.looksStatic = function(className) {
@ -777,8 +779,8 @@
};
Value.prototype.cacheReference = function(o) {
var base, bref, name, nref;
name = last(this.properties);
var base, bref, name, nref, ref3;
ref3 = this.properties, name = ref3[ref3.length - 1];
if (this.properties.length < 2 && !this.base.isComplex() && !(name != null ? name.isComplex() : void 0)) {
return [this, this];
}
@ -1407,11 +1409,12 @@
Class.prototype.children = ['variable', 'parent', 'body'];
Class.prototype.determineName = function() {
var decl, tail;
var decl, ref3, tail;
if (!this.variable) {
return null;
}
decl = (tail = last(this.variable.properties)) ? tail instanceof Access && tail.name.value : this.variable.base.value;
ref3 = this.variable.properties, tail = ref3[ref3.length - 1];
decl = tail ? tail instanceof Access && tail.name.value : this.variable.base.value;
if (indexOf.call(STRICT_PROSCRIBED, decl) >= 0) {
this.variable.error("class variable name may not be " + decl);
}
@ -2127,7 +2130,7 @@
};
Splat.compileSplattedArray = function(o, list, apply) {
var args, base, compiledNode, concatPart, fragments, i, index, j, len1, node;
var args, base, compiledNode, concatPart, fragments, i, index, j, last, len1, node;
index = -1;
while ((node = list[++index]) && !(node instanceof Splat)) {
continue;
@ -2166,7 +2169,8 @@
})();
base = list[0].joinFragmentArrays(base, ', ');
concatPart = list[index].joinFragmentArrays(args, ', ');
return [].concat(list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last(list)).makeCode(")"));
last = list[list.length - 1];
return [].concat(list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, last.makeCode(")"));
};
return Splat;
@ -2767,10 +2771,10 @@
For.prototype.children = ['body', 'source', 'guard', 'step'];
For.prototype.compileNode = function(o) {
var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, lastJumps, lvar, name, namePart, ref, ref3, ref4, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart;
var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, last, lvar, name, namePart, ref, ref3, ref4, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart;
body = Block.wrap([this.body]);
lastJumps = (ref3 = last(body.expressions)) != null ? ref3.jumps() : void 0;
if (lastJumps && lastJumps instanceof Return) {
ref3 = body.expressions, last = ref3[ref3.length - 1];
if ((last != null ? last.jumps() : void 0) instanceof Return) {
this.returns = false;
}
source = this.range ? this.source.base : this.source;

View File

@ -1,13 +1,11 @@
// Generated by CoffeeScript 1.9.0
(function() {
var Scope, extend, last, ref,
var Scope,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
ref = require('./helpers'), extend = ref.extend, last = ref.last;
exports.Scope = Scope = (function() {
function Scope(parent, expressions, method, referencedVars) {
var ref1, ref2;
var ref, ref1;
this.parent = parent;
this.expressions = expressions;
this.method = method;
@ -22,7 +20,7 @@
if (!this.parent) {
this.utilities = {};
}
this.root = (ref1 = (ref2 = this.parent) != null ? ref2.root : void 0) != null ? ref1 : this;
this.root = (ref = (ref1 = this.parent) != null ? ref1.root : void 0) != null ? ref : this;
}
Scope.prototype.add = function(name, type, immediate) {
@ -40,8 +38,8 @@
};
Scope.prototype.namedMethod = function() {
var ref1;
if (((ref1 = this.method) != null ? ref1.name : void 0) || !this.parent) {
var ref;
if (((ref = this.method) != null ? ref.name : void 0) || !this.parent) {
return this.method;
}
return this.parent.namedMethod();
@ -63,8 +61,8 @@
};
Scope.prototype.check = function(name) {
var ref1;
return !!(this.type(name) || ((ref1 = this.parent) != null ? ref1.check(name) : void 0));
var ref;
return !!(this.type(name) || ((ref = this.parent) != null ? ref.check(name) : void 0));
};
Scope.prototype.temporary = function(name, index, single) {
@ -79,10 +77,10 @@
};
Scope.prototype.type = function(name) {
var i, len, ref1, v;
ref1 = this.variables;
for (i = 0, len = ref1.length; i < len; i++) {
v = ref1[i];
var i, len, ref, v;
ref = this.variables;
for (i = 0, len = ref.length; i < len; i++) {
v = ref[i];
if (v.name === name) {
return v.type;
}
@ -91,7 +89,7 @@
};
Scope.prototype.freeVariable = function(name, options) {
var index, ref1, temp;
var index, ref, temp;
if (options == null) {
options = {};
}
@ -103,7 +101,7 @@
}
index++;
}
if ((ref1 = options.reserve) != null ? ref1 : true) {
if ((ref = options.reserve) != null ? ref : true) {
this.add(temp, 'var', true);
}
return temp;
@ -124,11 +122,11 @@
Scope.prototype.declaredVariables = function() {
var v;
return ((function() {
var i, len, ref1, results;
ref1 = this.variables;
var i, len, ref, results;
ref = this.variables;
results = [];
for (i = 0, len = ref1.length; i < len; i++) {
v = ref1[i];
for (i = 0, len = ref.length; i < len; i++) {
v = ref[i];
if (v.type === 'var') {
results.push(v.name);
}
@ -138,11 +136,11 @@
};
Scope.prototype.assignedVariables = function() {
var i, len, ref1, results, v;
ref1 = this.variables;
var i, len, ref, results, v;
ref = this.variables;
results = [];
for (i = 0, len = ref1.length; i < len; i++) {
v = ref1[i];
for (i = 0, len = ref.length; i < len; i++) {
v = ref[i];
if (v.type.assigned) {
results.push(v.name + " = " + v.type.value);
}

View File

@ -62,9 +62,6 @@ exports.del = (obj, key) ->
delete obj[key]
val
# Gets the last item of an array(-like) object.
exports.last = last = (array, back) -> array[array.length - (back or 0) - 1]
# Typical Array::some
exports.some = Array::some ? (fn) ->
return true for e in this when fn e

View File

@ -12,7 +12,7 @@
{Rewriter, INVERSES} = require './rewriter'
# Import the helpers we need.
{count, starts, compact, last, repeat, invertLiterate,
{count, starts, compact, repeat, invertLiterate,
locationDataToString, throwSyntaxError} = require './helpers'
# The Lexer Class
@ -112,8 +112,9 @@ exports.Lexer = class Lexer
if id is 'from' and @tag() is 'YIELD'
@token 'FROM', id
return id.length
forcedIdentifier = colon or
(prev = last @tokens) and (prev[0] in ['.', '?.', '::', '?::'] or
[..., prev] = @tokens
forcedIdentifier = colon or prev? and
(prev[0] in ['.', '?.', '::', '?::'] or
not prev.spaced and prev[0] is '@')
tag = 'IDENTIFIER'
@ -264,7 +265,7 @@ exports.Lexer = class Lexer
[regex, body, closed] = match
@validateEscapes regex, isRegex: yes
index = regex.length
prev = last @tokens
[..., prev] = @tokens
if prev
if prev.spaced and prev[0] in CALLABLE and not prev.stringEnd and not prev.regexEnd
return 0 if not closed or POSSIBLY_DIVISION.test regex
@ -372,7 +373,7 @@ exports.Lexer = class Lexer
whitespaceToken: ->
return 0 unless (match = WHITESPACE.exec @chunk) or
(nline = @chunk.charAt(0) is '\n')
prev = last @tokens
[..., prev] = @tokens
prev[if match then 'spaced' else 'newLine'] = true if prev
if match then match[0].length else 0
@ -400,7 +401,7 @@ exports.Lexer = class Lexer
else
value = @chunk.charAt 0
tag = value
prev = last @tokens
[..., prev] = @tokens
if value is '=' and prev
if not prev[1].reserved and prev[1] in JS_FORBIDDEN
@error "reserved word '#{prev[1]}' can't be assigned", prev[2]
@ -596,14 +597,16 @@ exports.Lexer = class Lexer
# Pairs up a closing token, ensuring that all listed pairs of tokens are
# correctly balanced throughout the course of the token stream.
pair: (tag) ->
unless tag is wanted = last(@ends)?.tag
[..., prev] = @ends
unless tag is wanted = prev?.tag
@error "unmatched #{tag}" unless 'OUTDENT' is wanted
# Auto-close INDENT to support syntax like this:
#
# el.click((event) ->
# el.hide())
#
@outdentToken last(@indents), true
[..., lastIndent] = @indents
@outdentToken lastIndent, true
return @pair tag
@ends.pop()
@ -626,8 +629,8 @@ exports.Lexer = class Lexer
column = @chunkColumn
if lineCount > 0
lines = string.split '\n'
column = last(lines).length
[..., lastLine] = string.split '\n'
column = lastLine.length
else
column += string.length
@ -662,13 +665,15 @@ exports.Lexer = class Lexer
@tokens.push token
token
# Peek at a tag in the current token stream.
tag: (index, tag) ->
(tok = last @tokens, index) and if tag then tok[0] = tag else tok[0]
# Peek at the last tag in the token stream.
tag: ->
[..., token] = @tokens
token?[0]
# Peek at a value in the current token stream.
value: (index, val) ->
(tok = last @tokens, index) and if val then tok[1] = val else tok[1]
# Peek at the last value in the token stream.
value: ->
[..., token] = @tokens
token?[1]
# Are we in the midst of an unfinished expression?
unfinished: ->

View File

@ -9,7 +9,7 @@ Error.stackTraceLimit = Infinity
{RESERVED, STRICT_PROSCRIBED} = require './lexer'
# Import the helpers we plan to use.
{compact, flatten, extend, merge, del, starts, ends, last, some,
{compact, flatten, extend, merge, del, starts, ends, some,
addLocationDataFn, locationDataToString, throwSyntaxError} = require './helpers'
# Functions required by parser
@ -510,7 +510,8 @@ exports.Value = class Value extends Base
(@base instanceof Obj) and (not onlyGenerated or @base.generated)
isSplice: ->
last(@properties) instanceof Slice
[..., lastProp] = @properties
lastProp instanceof Slice
looksStatic: (className) ->
@base.value is className and @properties.length is 1 and
@ -525,7 +526,7 @@ exports.Value = class Value extends Base
# We cache them separately for compiling complex expressions.
# `a()[b()] ?= c` -> `(_base = a())[_name = b()] ? _base[_name] = c`
cacheReference: (o) ->
name = last @properties
[..., name] = @properties
if @properties.length < 2 and not @base.isComplex() and not name?.isComplex()
return [this, this] # `a` `a.b`
base = new Value @base, @properties[...-1]
@ -1006,7 +1007,8 @@ exports.Class = class Class extends Base
# Figure out the appropriate name for the constructor function of this class.
determineName: ->
return null unless @variable
decl = if tail = last @variable.properties
[..., tail] = @variable.properties
decl = if tail
tail instanceof Access and tail.name.value
else
@variable.base.value
@ -1540,7 +1542,8 @@ exports.Splat = class Splat extends Base
base = (node.compileToFragments o, LEVEL_LIST for node in list[...index])
base = list[0].joinFragmentArrays base, ', '
concatPart = list[index].joinFragmentArrays args, ', '
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")
[..., last] = list
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, last.makeCode(")")
#### Expansion
@ -1976,27 +1979,27 @@ exports.For = class For extends While
# comprehensions. Some of the generated code can be shared in common, and
# some cannot.
compileNode: (o) ->
body = Block.wrap [@body]
lastJumps = last(body.expressions)?.jumps()
@returns = no if lastJumps and lastJumps instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
index = @index and (@index.compile o, LEVEL_LIST)
body = Block.wrap [@body]
[..., last] = body.expressions
@returns = no if last?.jumps() instanceof Return
source = if @range then @source.base else @source
scope = o.scope
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
index = @index and (@index.compile o, LEVEL_LIST)
scope.find(name) if name and not @pattern
scope.find(index) if index
rvar = scope.freeVariable 'results' if @returns
ivar = (@object and index) or scope.freeVariable 'i', single: true
kvar = (@range and name) or index or ivar
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
rvar = scope.freeVariable 'results' if @returns
ivar = (@object and index) or scope.freeVariable 'i', single: true
kvar = (@range and name) or index or ivar
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
if @step and not @range
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, isComplexOrAssignable
stepNum = stepVar.match NUMBER
name = ivar if @pattern
varPart = ''
guardPart = ''
defPart = ''
idt1 = @tab + TAB
name = ivar if @pattern
varPart = ''
guardPart = ''
defPart = ''
idt1 = @tab + TAB
if @range
forPartFragments = source.compileToFragments merge o,
{index: ivar, name, @step, isComplex: isComplexOrAssignable}

View File

@ -5,10 +5,6 @@ and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with `var`, and which are shared
with external scopes.
Import the helpers we plan to use.
{extend, last} = require './helpers'
exports.Scope = class Scope
Initialize a scope with its parent, for lookups up the chain,

View File

@ -2,7 +2,7 @@
# -------
# pull the helpers from `CoffeeScript.helpers` into local variables
{starts, ends, repeat, compact, count, merge, extend, flatten, del, last, baseFileName} = CoffeeScript.helpers
{starts, ends, repeat, compact, count, merge, extend, flatten, del, baseFileName} = CoffeeScript.helpers
# `starts`
@ -94,16 +94,6 @@ test "the `del` helper deletes a property from an object and returns the deleted
ok 1 not of obj
# `last`
test "the `last` helper returns the last item of an array-like object", ->
ary = [0, 1, 2, 3, 4]
eq 4, last(ary)
test "the `last` helper allows one to specify an optional offset", ->
ary = [0, 1, 2, 3, 4]
eq 2, last(ary, 2)
# `baseFileName`
test "the `baseFileName` helper returns the file name to write to", ->