From b2313beaf4a77649506371eb1ece1f4aea1ad23b Mon Sep 17 00:00:00 2001 From: satyr Date: Tue, 28 Sep 2010 21:52:51 +0900 Subject: [PATCH] added `last` to helpers --- lib/helpers.js | 5 +++- lib/lexer.js | 61 +++++++++++++++++----------------------- lib/nodes.js | 58 +++++++++++++++++++------------------- src/helpers.coffee | 5 +++- src/lexer.coffee | 57 ++++++++++++++++--------------------- src/nodes.coffee | 44 ++++++++++++++--------------- test/test_helpers.coffee | 33 ++++++++++++---------- 7 files changed, 128 insertions(+), 135 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index 797d9988..ee3598c3 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -38,7 +38,7 @@ exports.count = function(string, letter) { var num, pos; num = (pos = 0); - while (0 < (pos = 1 + string.indexOf(letter, pos))) { + while (pos = 1 + string.indexOf(letter, pos)) { num++; } return num; @@ -64,4 +64,7 @@ delete obj[key]; return val; }; + exports.last = function(array, back) { + return array[array.length - (back || 0) - 1]; + }; }).call(this); diff --git a/lib/lexer.js b/lib/lexer.js index 85f12665..7b433903 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -1,5 +1,5 @@ (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; _ref = require('./rewriter'); Rewriter = _ref.Rewriter; @@ -8,6 +8,7 @@ count = _ref.count; starts = _ref.starts; compact = _ref.compact; + last = _ref.last; exports.Lexer = (function() { Lexer = function() {}; Lexer.prototype.tokenize = function(code, options) { @@ -212,7 +213,7 @@ indent = match[0]; this.line += count(indent, '\n'); this.i += indent.length; - prev = this.prev(2); + prev = last(this.tokens, 1); size = indent.length - 1 - indent.lastIndexOf('\n'); nextCharacter = NEXT_CHARACTER.exec(this.chunk)[1]; noNewlines = (('.' === nextCharacter || ',' === nextCharacter)) || this.unfinished(); @@ -270,7 +271,7 @@ if (!(match = WHITESPACE.exec(this.chunk))) { return false; } - prev = this.prev(); + prev = last(this.tokens); if (prev) { prev.spaced = true; } @@ -290,7 +291,7 @@ return true; }; 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)) { _ref2 = match; value = _ref2[0]; @@ -302,14 +303,15 @@ value = this.chunk.charAt(0); } this.i += value.length; - spaced = (prev = this.prev()) && prev.spaced; + prev = last(this.tokens); + spaced = prev == null ? undefined : prev.spaced; tag = value; if (value === '=') { - if (include(JS_FORBIDDEN, this.value())) { + if (include(JS_FORBIDDEN, val = this.value())) { this.assignmentError(); } - if (('or' === (_ref2 = this.value()) || 'and' === _ref2)) { - this.tokens.splice(this.tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[this.value()] + '=', prev[2]]); + if (('or' === val || 'and' === val)) { + this.tokens.splice(-1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[val] + '=', prev[2]]); return true; } } @@ -337,10 +339,10 @@ tag = 'INDEX_START'; switch (this.tag()) { case '?': - this.tag(1, 'INDEX_SOAK'); + this.tag(0, 'INDEX_SOAK'); break; case '::': - this.tag(1, 'INDEX_PROTO'); + this.tag(0, 'INDEX_PROTO'); break; } } @@ -350,18 +352,18 @@ }; Lexer.prototype.tagAccessor = function() { var accessor, prev; - if ((!(prev = this.prev())) || (prev && prev.spaced)) { + if (!(prev = last(this.tokens)) || prev.spaced) { return false; } accessor = (function() { if (prev[1] === '::') { - return this.tag(1, 'PROTOTYPE_ACCESS'); - } else if (prev[1] === '.' && this.value(2) !== '.') { - if (this.tag(2) === '?') { - this.tag(1, 'SOAK_ACCESS'); + return this.tag(0, 'PROTOTYPE_ACCESS'); + } else if (prev[1] === '.' && this.value(1) !== '.') { + if (this.tag(1) === '?') { + this.tag(0, 'SOAK_ACCESS'); return this.tokens.splice(-2, 1); } else { - return this.tag(1, 'PROPERTY_ACCESS'); + return this.tag(0, 'PROPERTY_ACCESS'); } } else { return prev[0] === '@'; @@ -402,11 +404,9 @@ if (this.tag() !== ')') { return null; } - i = 0; + i = this.tokens.length; while (true) { - i += 1; - tok = this.prev(i); - if (!tok) { + if (!(tok = this.tokens[--i])) { return null; } switch (tok[0]) { @@ -449,7 +449,7 @@ _ref3 = pair; open = _ref3[0]; 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(); i += close.length - 1; if (!(levels.length)) { @@ -563,30 +563,21 @@ }; Lexer.prototype.tag = function(index, newTag) { var tok; - if (!(tok = this.prev(index))) { + if (!(tok = last(this.tokens, index))) { return null; } - if (typeof newTag !== "undefined" && newTag !== null) { - return (tok[0] = newTag); - } - return tok[0]; + return (tok[0] = (typeof newTag !== "undefined" && newTag !== null) ? newTag : tok[0]); }; Lexer.prototype.value = function(index, val) { var tok; - if (!(tok = this.prev(index))) { + if (!(tok = last(this.tokens, index))) { return null; } - if (typeof val !== "undefined" && val !== null) { - return (tok[1] = val); - } - return tok[1]; - }; - Lexer.prototype.prev = function(index) { - return this.tokens[this.tokens.length - (index || 1)]; + return (tok[1] = (typeof val !== "undefined" && val !== null) ? val : tok[1]); }; Lexer.prototype.unfinished = function() { 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) { return str.replace(MULTILINER, heredoc ? '\\n' : ''); diff --git a/lib/nodes.js b/lib/nodes.js index 5d18b7b9..3a8796ce 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1,5 +1,5 @@ (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 ctor = function(){}; ctor.prototype = parent.prototype; @@ -19,6 +19,7 @@ indexOf = _ref.indexOf; starts = _ref.starts; ends = _ref.ends; + last = _ref.last; YES = function() { return true; }; @@ -191,16 +192,14 @@ return this.expressions.length === 0; }; Expressions.prototype.makeReturn = function() { - var idx, last; - idx = this.expressions.length - 1; - last = this.expressions[idx]; - if (last instanceof CommentNode) { - last = this.expressions[idx -= 1]; + var end, idx; + end = this.expressions[(idx = this.expressions.length - 1)]; + if (end instanceof CommentNode) { + end = this.expressions[idx -= 1]; } - if (!last || last instanceof ReturnNode) { - return this; + if (end && !(end instanceof ReturnNode)) { + this.expressions[idx] = end.makeReturn(); } - this.expressions[idx] = last.makeReturn(); return this; }; Expressions.prototype.compile = function(o) { @@ -335,10 +334,10 @@ return this.base instanceof ObjectNode && !this.hasProperties(); }; 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() { - return this.base.isComplex() || this.properties.length; + return this.base.isComplex() || this.hasProperties(); }; ValueNode.prototype.makeReturn = function() { 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); }; 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'); op = this.tags.operation; props = only ? this.properties.slice(0, -1) : this.properties; @@ -413,7 +412,7 @@ if (prop.soakNode) { if (i === 0 && this.base.isComplex()) { 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 += ' ? undefined : ' + (baseline += prop.compile(o)); @@ -493,7 +492,7 @@ } op = this.tags.operation; 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(); _ref2 = this.variable.compileReference(o); first = _ref2[0]; @@ -771,7 +770,7 @@ } return _result; }).call(this); - lastNoncom = nonComments[nonComments.length - 1]; + lastNoncom = last(nonComments); props = (function() { _result = []; _ref2 = this.properties; for (i = 0, _len = _ref2.length; i < _len; i++) { @@ -949,7 +948,7 @@ return this.isValue() && (this.variable.isArray() || this.variable.isObject()); }; 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'); if (this.isStatement(o)) { return this.compilePatternMatch(o); @@ -959,12 +958,12 @@ } stmt = del(o, 'asStatement'); 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); proto = match && match[1]; if (this.value instanceof CodeNode) { - if (last.match(IDENTIFIER)) { - this.value.name = last; + if (IDENTIFIER.test(end)) { + this.value.name = end; } if (proto) { this.value.proto = proto; @@ -1202,25 +1201,26 @@ return "" + (utility('slice')) + ".call(" + (name) + ", " + (index) + (trail) + ")"; }; SplatNode.compileSplattedArray = function(list, o) { - var _len, _ref2, arg, args, code, i, last, prev; + var _len, _ref2, arg, args, code, end, i, prev; args = []; + end = -1; _ref2 = list; for (i = 0, _len = _ref2.length; i < _len; i++) { arg = _ref2[i]; code = arg.compile(o); - prev = args[(last = args.length - 1)]; + prev = args[end]; if (!(arg instanceof SplatNode)) { if (prev && starts(prev, '[') && ends(prev, ']')) { - args[last] = ("" + (prev.slice(0, -1)) + ", " + (code) + "]"); + args[end] = ("" + (prev.slice(0, -1)) + ", " + (code) + "]"); 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(''); }; @@ -1756,7 +1756,7 @@ code += ("\n" + (this.idt(1)) + "case " + (condition.compile(o)) + ":"); } code += ("\n" + (block.compile(o))); - if (!(exprs[exprs.length - 1] instanceof ReturnNode)) { + if (!(last(exprs) instanceof ReturnNode)) { code += ("\n" + (idt) + "break;"); } } diff --git a/src/helpers.coffee b/src/helpers.coffee index 90786df8..605b3964 100644 --- a/src/helpers.coffee +++ b/src/helpers.coffee @@ -34,7 +34,7 @@ exports.compact = (array) -> # Count the number of occurences of a character in a string. exports.count = (string, letter) -> num = pos = 0 - num++ while 0 < pos = 1 + string.indexOf letter, pos + num++ while pos = 1 + string.indexOf letter, pos num # Merge objects, returning a fresh copy with attributes from both sides. @@ -61,3 +61,6 @@ exports.del = (obj, key) -> val = obj[key] delete obj[key] val + +# Gets the last item of an array(-like) object. +exports.last = (array, back) -> array[array.length - (back or 0) - 1] diff --git a/src/lexer.coffee b/src/lexer.coffee index 010f7644..df83761f 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -10,7 +10,7 @@ {Rewriter} = require './rewriter' # Import the helpers we need. -{include, count, starts, compact} = require './helpers' +{include, count, starts, compact, last} = require './helpers' # The Lexer Class # --------------- @@ -170,7 +170,7 @@ exports.Lexer = class Lexer regexToken: -> return false unless first = @chunk.match REGEX_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 end = @chunk[regex.length..].match REGEX_END flags = end[0] @@ -206,7 +206,7 @@ exports.Lexer = class Lexer indent = match[0] @line += count indent, '\n' @i += indent.length - prev = @prev 2 + prev = last @tokens, 1 size = indent.length - 1 - indent.lastIndexOf '\n' nextCharacter = NEXT_CHARACTER.exec(@chunk)[1] 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. whitespaceToken: -> return false unless match = WHITESPACE.exec @chunk - prev = @prev() + prev = last @tokens prev.spaced = true if prev @i += match[0].length true @@ -282,12 +282,13 @@ exports.Lexer = class Lexer else value = @chunk.charAt 0 @i += value.length - spaced = (prev = @prev()) and prev.spaced + prev = last @tokens + spaced = prev?.spaced tag = value if value is '=' - @assignmentError() if include JS_FORBIDDEN, @value() - if @value() in ['or', 'and'] - @tokens.splice(@tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[@value()] + '=', prev[2]]) + @assignmentError() if include JS_FORBIDDEN, val = @value() + if val in ['or', 'and'] + @tokens.splice(-1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[val] + '=', prev[2]]) return true if value is ';' then tag = 'TERMINATOR' else if include(LOGIC, value) then tag = 'LOGIC' @@ -303,8 +304,8 @@ exports.Lexer = class Lexer else if value is '[' tag = 'INDEX_START' switch @tag() - when '?' then @tag 1, 'INDEX_SOAK' - when '::' then @tag 1, 'INDEX_PROTO' + when '?' then @tag 0, 'INDEX_SOAK' + when '::' then @tag 0, 'INDEX_PROTO' @token tag, value true @@ -315,15 +316,15 @@ exports.Lexer = class Lexer # if it's a special kind of accessor. Return `true` if any type of accessor # is the previous token. 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 '::' - @tag 1, 'PROTOTYPE_ACCESS' - else if prev[1] is '.' and @value(2) isnt '.' - if @tag(2) is '?' - @tag(1, 'SOAK_ACCESS') + @tag 0, 'PROTOTYPE_ACCESS' + else if prev[1] is '.' and @value(1) isnt '.' + if @tag(1) is '?' + @tag(0, 'SOAK_ACCESS') @tokens.splice(-2, 1) else - @tag 1, 'PROPERTY_ACCESS' + @tag 0, 'PROPERTY_ACCESS' else prev[0] is '@' 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. tagParameters: -> return if @tag() isnt ')' - i = 0 + i = @tokens.length loop - i += 1 - tok = @prev i - return if not tok + return unless tok = @tokens[--i] switch tok[0] when 'IDENTIFIER' then tok[0] = 'PARAM' when ')' then tok[0] = 'PARAM_END' @@ -389,7 +388,7 @@ exports.Lexer = class Lexer else for pair in delimited [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() i += close.length - 1 i += 1 unless levels.length @@ -469,23 +468,17 @@ exports.Lexer = class Lexer # Peek at a tag in the current token stream. tag: (index, newTag) -> - return unless tok = @prev index - return tok[0] = newTag if newTag? - tok[0] + return unless tok = last @tokens, index + tok[0] = newTag ? tok[0] # Peek at a value in the current token stream. value: (index, val) -> - return unless tok = @prev index - return tok[1] = val if val? - tok[1] - - # Peek at a previous token, entire. - prev: (index) -> - @tokens[@tokens.length - (index or 1)] + return unless tok = last @tokens, index + tok[1] = val ? tok[1] # Are we in the midst of an unfinished expression? 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 not ASSIGNED.test(@chunk) diff --git a/src/nodes.coffee b/src/nodes.coffee index 863fc41d..e24c578e 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -6,7 +6,7 @@ {Scope} = require './scope' # 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. YES = -> yes @@ -182,11 +182,10 @@ exports.Expressions = class Expressions extends BaseNode # An Expressions node does not return its entire body, rather it # ensures that the final expression is returned. makeReturn: -> - idx = @expressions.length - 1 - last = @expressions[idx] - last = @expressions[idx -= 1] if last instanceof CommentNode - return this if not last or last instanceof ReturnNode - @expressions[idx] = last.makeReturn() + end = @expressions[idx = @expressions.length - 1] + end = @expressions[idx -= 1] if end instanceof CommentNode + if end and end not instanceof ReturnNode + @expressions[idx] = end.makeReturn() this # 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() isSplice: -> - @hasProperties() and @properties[@properties.length - 1] instanceof SliceNode + last(@properties) instanceof SliceNode isComplex: -> - @base.isComplex() or @properties.length + @base.isComplex() or @hasProperties() makeReturn: -> if @hasProperties() then super() else @base.makeReturn() @@ -380,7 +379,7 @@ exports.ValueNode = class ValueNode extends BaseNode if prop.soakNode if i is 0 and @base.isComplex() 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 "(typeof #{complete} === \"undefined\" || #{baseline} === null)" else @@ -457,7 +456,8 @@ exports.CallNode = class CallNode extends BaseNode o.chainRoot = this unless o.chainRoot op = @tags.operation 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() [first, meth] = @variable.compileReference o @first = new ValueNode(first, [methodAccessor]).compile o @@ -683,7 +683,7 @@ exports.ObjectNode = class ObjectNode extends BaseNode top = del o, 'top' o.indent = @idt 1 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 join = ",\n" 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() stmt = del o, 'asStatement' 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) proto = match and match[1] if @value instanceof CodeNode - @value.name = last if last.match(IDENTIFIER) + @value.name = end if IDENTIFIER.test end @value.proto = proto if proto val = @value.compile o return "#{name}: #{val}" if @context is 'object' @@ -1041,20 +1041,20 @@ exports.SplatNode = class SplatNode extends BaseNode # splats, to a proper array @compileSplattedArray: (list, o) -> args = [] + end = -1 for arg, i in list code = arg.compile o - prev = args[last = args.length - 1] + prev = args[end] if arg not instanceof SplatNode if prev and starts(prev, '[') and ends(prev, ']') - args[last] = "#{prev.slice 0, -1}, #{code}]" + args[end] = "#{prev.slice 0, -1}, #{code}]" continue - else if prev and starts(prev, '.concat([') and ends(prev, '])') - args[last] = "#{prev.slice 0, -2}, #{code}])" + if prev and starts(prev, '.concat([') and ends(prev, '])') + args[end] = "#{prev.slice 0, -2}, #{code}])" continue - else - code = "[#{code}]" - args.push(if i is 0 then code else ".concat(#{code})") - args.join('') + code = "[#{code}]" + args[++end] = if i is 0 then code else ".concat(#{code})" + args.join '' #### WhileNode @@ -1470,7 +1470,7 @@ exports.SwitchNode = class SwitchNode extends BaseNode condition = new OpNode '!!', new ParentheticalNode condition if @tags.subjectless code += "\n#{ @idt(1) }case #{ condition.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 code += "\n#{ @idt(1) }default:\n#{ @otherwise.compile o }" code += "\n#{ @tab }}" diff --git a/test/test_helpers.coffee b/test/test_helpers.coffee index 036fcb6c..3124d55f 100644 --- a/test/test_helpers.coffee +++ b/test/test_helpers.coffee @@ -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` -array = [0..4] - -ok indexOf(array, 0) is 0 -ok indexOf(array, 2) is 2 -ok indexOf(array, 4) is 4 -ok indexOf(array, 6) is -1 +eq 0, indexOf array, 0 +eq 2, indexOf array, 2 +eq 4, indexOf array, 4 +eq(-1, indexOf array, 6) # Test `include` ok include array, 0 @@ -15,8 +17,6 @@ ok include array, 4 ok not include array, 6 # Test `starts` -string = array.join '' - ok starts string, '012' ok starts string, '34', 3 ok not starts string, '42' @@ -29,18 +29,21 @@ ok not ends string, '42' ok not ends string, '42', 6 # Test `merge` -object = {} merged = merge object, array - ok merged isnt object -ok merged[3] is 3 +eq merged[3], 3 # Test `extend` ok object is extend object, array -ok object[3] is 3 +eq object[3], 3 # 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 + +# Test `last` +eq 4, last array +eq 2, last array, 2