diff --git a/lib/cake.js b/lib/cake.js index e8ad2b9b..5b851875 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -63,12 +63,12 @@ spaces = 20 - name.length; spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; desc = task.description ? ("# " + (task.description)) : ''; - puts("cake " + (name) + (spaces) + " " + (desc)); + puts("cake " + name + spaces + " " + desc); } return switches.length ? puts(oparse.help()) : undefined; }; missingTask = function(task) { - puts("No such task: \"" + (task) + "\""); + puts("No such task: \"" + task + "\""); return process.exit(1); }; }).call(this); diff --git a/lib/command.js b/lib/command.js index fcc90317..761df2bf 100644 --- a/lib/command.js +++ b/lib/command.js @@ -60,7 +60,7 @@ compile = function(source, topLevel) { return path.exists(source, function(exists) { if (!(exists)) { - throw new Error("File not found: " + (source)); + throw new Error("File not found: " + source); } return fs.stat(source, function(err, stats) { if (stats.isDirectory()) { @@ -168,11 +168,11 @@ js = ' '; } return fs.writeFile(jsPath, js, function(err) { - return opts.compile && opts.watch ? puts("Compiled " + (source)) : undefined; + return opts.compile && opts.watch ? puts("Compiled " + source) : undefined; }); }; return path.exists(dir, function(exists) { - return exists ? compile() : exec("mkdir -p " + (dir), compile); + return exists ? compile() : exec("mkdir -p " + dir, compile); }); }; lint = function(js) { @@ -195,7 +195,7 @@ token = tokens[_i]; _result.push((function() { _ref2 = [token[0], token[1].toString().replace(/\n/, '\\n')], tag = _ref2[0], value = _ref2[1]; - return "[" + (tag) + " " + (value) + "]"; + return "[" + tag + " " + value + "]"; })()); } return _result; diff --git a/lib/grammar.js b/lib/grammar.js index c8042670..9c0b3f76 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -8,9 +8,9 @@ if (!(action)) { return [patternString, '$$ = $1;', options]; } - action = (match = (action + '').match(unwrap)) ? match[1] : ("(" + (action) + "())"); + action = (match = (action + '').match(unwrap)) ? match[1] : ("(" + action + "())"); action = action.replace(/\b(?:[A-Z][a-z]+Node|Expressions)\b/g, 'yy.$&'); - return [patternString, ("$$ = " + (action) + ";"), options]; + return [patternString, ("$$ = " + action + ";"), options]; }; grammar = { Root: [ diff --git a/lib/lexer.js b/lib/lexer.js index edc545b0..5d23d077 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -1,6 +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, last, starts; - var __slice = Array.prototype.slice; + var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_SPACES, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, _ref, compact, count, include, last, starts; Rewriter = require('./rewriter').Rewriter; _ref = require('./helpers'), include = _ref.include, count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last; exports.Lexer = (function() { @@ -9,7 +8,7 @@ })(); Lexer.prototype.tokenize = function(code, options) { var o; - code = code.replace(/\r/g, '').replace(/\s+$/, ''); + code = code.replace(/\r/g, '').replace(TRAILING_SPACES, ''); o = options || {}; this.code = code; this.i = 0; @@ -52,7 +51,7 @@ if (include(JS_FORBIDDEN, id)) { if (forcedIdentifier) { tag = 'STRING'; - id = ("\"" + (id) + "\""); + id = ("\"" + id + "\""); if (forcedIdentifier === 'accessor') { closeIndex = true; if (this.tag() !== '@') { @@ -103,10 +102,14 @@ this.token('STRING', (string = match[0]).replace(MULTILINER, '\\\n')); break; case '"': - if (!(string = this.balancedToken(['"', '"'], ['#{', '}']))) { + if (!(string = this.balancedString(this.chunk, [['"', '"'], ['#{', '}']]))) { return false; } - this.interpolateString(string); + if (0 < string.indexOf('#{', 1)) { + this.interpolateString(string.slice(1, -1)); + } else { + this.token('STRING', this.escapeLines(string)); + } break; default: return false; @@ -117,7 +120,7 @@ }; Lexer.prototype.heredocToken = function() { var doc, heredoc, match, quote; - if (!(match = this.chunk.match(HEREDOC))) { + if (!(match = HEREDOC.exec(this.chunk))) { return false; } heredoc = match[0]; @@ -126,12 +129,12 @@ quote: quote, indent: null }); - if (quote === '"') { - this.interpolateString(quote + doc + quote, { + if (quote === '"' && (0 <= doc.indexOf('#{'))) { + this.interpolateString(doc, { heredoc: true }); } else { - this.token('STRING', quote + doc + quote); + this.token('STRING', this.makeString(doc, quote, true)); } this.line += count(heredoc, '\n'); this.i += heredoc.length; @@ -164,44 +167,61 @@ return true; }; Lexer.prototype.regexToken = function() { - var _ref2, end, first, flags, regex, str; - if (!(first = this.chunk.match(REGEX_START))) { + var match; + if (this.chunk.charAt(0) !== '/') { return false; } - if (first[1] === ' ' && !('CALL_START' === (_ref2 = this.tag()) || '=' === _ref2)) { - return false; + if (match = HEREGEX.exec(this.chunk)) { + return this.heregexToken(match); } if (include(NOT_REGEX, this.tag())) { return false; } - if (!(regex = this.balancedToken(['/', '/']))) { + if (!(match = REGEX.exec(this.chunk))) { return false; } - if (!(end = this.chunk.slice(regex.length).match(REGEX_END))) { - return false; - } - flags = end[0]; - if (REGEX_INTERPOLATION.test(regex)) { - str = regex.slice(1, -1); - str = str.replace(REGEX_ESCAPE, '\\$&'); - this.tokens.push(['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']); - this.interpolateString("\"" + (str) + "\"", { - escapeQuotes: true - }); - if (flags) { - this.tokens.push([',', ','], ['STRING', ("\"" + (flags) + "\"")]); - } - this.tokens.push([')', ')'], [')', ')']); - } else { - this.token('REGEX', regex + flags); - } - this.i += regex.length + flags.length; + this.token('REGEX', match[0]); + this.i += match[0].length; return true; }; - Lexer.prototype.balancedToken = function() { - var delimited; - delimited = __slice.call(arguments, 0); - return this.balancedString(this.chunk, delimited); + Lexer.prototype.heregexToken = function(match) { + var _i, _len, _ref2, _ref3, _this, body, flags, heregex, re, tag, tokens, value; + _ref2 = match, heregex = _ref2[0], body = _ref2[1], flags = _ref2[2]; + this.i += heregex.length; + if (0 > body.indexOf('#{')) { + re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/'); + this.token('REGEX', "/" + (re || '(?:)') + "/" + flags); + return true; + } + this.token('IDENTIFIER', 'RegExp'); + this.tokens.push(['CALL_START', '(']); + tokens = []; + _ref2 = this.interpolateString(body, { + regex: true + }); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + _ref3 = _ref2[_i], tag = _ref3[0], value = _ref3[1]; + if (tag === 'TOKENS') { + tokens.push.apply(tokens, value); + } else { + if (!(value = value.replace(HEREGEX_OMIT, ''))) { + continue; + } + value = value.replace(/\\/g, '\\\\'); + tokens.push(['STRING', this.makeString(value, '"', true)]); + } + tokens.push(['+', '+']); + } + tokens.pop(); + if ((((_ref2 = tokens[0]) != null) ? _ref2[0] !== 'STRING' : undefined)) { + this.tokens.push(['STRING', '""'], ['+', '+']); + } + (_this = this.tokens).push.apply(_this, tokens); + if (flags) { + this.tokens.push([',', ','], ['STRING', '"' + flags + '"']); + } + this.tokens.push(['CALL_END', ')']); + return true; }; Lexer.prototype.lineToken = function() { var diff, indent, match, nextCharacter, noNewlines, prev, size; @@ -368,13 +388,13 @@ return accessor ? 'accessor' : false; }; Lexer.prototype.sanitizeHeredoc = function(doc, options) { - var _ref2, attempt, herecomment, indent, match, quote; + var _ref2, attempt, herecomment, indent, match; _ref2 = options, indent = _ref2.indent, herecomment = _ref2.herecomment; - if (herecomment && !include(doc, '\n')) { + if (herecomment && 0 > doc.indexOf('\n')) { return doc; } if (!(herecomment)) { - while ((match = HEREDOC_INDENT.exec(doc))) { + while (match = HEREDOC_INDENT.exec(doc)) { attempt = match[1]; if (indent === null || (0 < (_ref2 = attempt.length)) && (_ref2 < indent.length)) { indent = attempt; @@ -382,19 +402,10 @@ } } if (indent) { - doc = doc.replace(RegExp("\\n" + (indent), "g"), '\n'); + doc = doc.replace(RegExp("\\n" + indent, "g"), '\n'); } - if (herecomment) { - return doc; - } - quote = options.quote; - doc = doc.replace(/^\n/, ''); - doc = doc.replace(/\\([\s\S])/g, function(m, c) { - return ('\n' === c || quote === c) ? c : m; - }); - doc = doc.replace(RegExp("" + (quote), "g"), '\\$&'); - if (quote === "'") { - doc = this.escapeLines(doc, true); + if (!(herecomment)) { + doc = doc.replace(/^\n/, ''); } return doc; }; @@ -426,15 +437,14 @@ return this.outdentToken(this.indent); }; Lexer.prototype.identifierError = function(word) { - throw new Error("SyntaxError: Reserved word \"" + (word) + "\" on line " + (this.line + 1)); + throw new Error("SyntaxError: Reserved word \"" + word + "\" on line " + (this.line + 1)); }; Lexer.prototype.assignmentError = function() { throw new Error("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); }; Lexer.prototype.balancedString = function(str, delimited, options) { - var _i, _len, _ref2, close, i, levels, open, pair, slash, slen; + var _i, _len, _ref2, close, i, levels, open, pair, slen; options || (options = {}); - slash = delimited[0][0] === '/'; levels = []; i = 0; slen = str.length; @@ -452,95 +462,82 @@ i += 1; } break; - } else if (starts(str, open, i)) { + } + if (starts(str, open, i)) { levels.push(pair); i += open.length - 1; break; } } } - if (!levels.length || slash && str.charAt(i) === '\n') { + if (!levels.length) { break; } i += 1; } if (levels.length) { - if (slash) { - return false; - } throw new Error("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1)); } return !i ? false : str.slice(0, i); }; Lexer.prototype.interpolateString = function(str, options) { - var _len, _ref2, end, escapeQuotes, escaped, expr, heredoc, i, idx, inner, interpolated, lexer, nested, pi, push, quote, s, tag, tok, token, tokens, value; - _ref2 = options || {}, heredoc = _ref2.heredoc, escapeQuotes = _ref2.escapeQuotes; - quote = str.charAt(0); - if (quote !== '"' || str.length < 3) { - return this.token('STRING', str); - } - lexer = new Lexer; + var _len, _ref2, _this, expr, heredoc, i, inner, interpolated, letter, nested, pi, regex, tag, tokens, value; + _ref2 = options || (options = {}), heredoc = _ref2.heredoc, regex = _ref2.regex; tokens = []; - i = (pi = 1); - end = str.length - 1; - while (i < end) { - if (str.charAt(i) === '\\') { + pi = 0; + i = -1; + while (letter = str.charAt(i += 1)) { + if (letter === '\\') { i += 1; - } else if (expr = this.balancedString(str.slice(i), [['#{', '}']])) { - if (pi < i) { - s = quote + this.escapeLines(str.slice(pi, i), heredoc) + quote; - tokens.push(['STRING', s]); - } - inner = expr.slice(2, -1).replace(/^[ \t]*\n/, ''); - if (inner.length) { - if (heredoc) { - inner = inner.replace(RegExp('\\\\' + quote, 'g'), quote); - } - nested = lexer.tokenize("(" + (inner) + ")", { - line: this.line - }); - for (idx = 0, _len = nested.length; idx < _len; idx++) { - tok = nested[idx]; - if (tok[0] === 'CALL_END') { - (tok[0] = ')'); - } - } - nested.pop(); - tokens.push(['TOKENS', nested]); - } else { - tokens.push(['STRING', quote + quote]); - } - i += expr.length - 1; - pi = i + 1; + continue; } - i += 1; + if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), [['{', '}']])))) { + continue; + } + if (pi < i) { + tokens.push(['TO_BE_STRING', str.slice(pi, i)]); + } + inner = expr.slice(1, -1).replace(LEADING_SPACES, '').replace(TRAILING_SPACES, ''); + if (inner.length) { + nested = new Lexer().tokenize(inner, { + line: this.line, + rewrite: false + }); + nested.pop(); + if (nested.length > 1) { + nested.unshift(['(', '(']); + nested.push([')', ')']); + } + tokens.push(['TOKENS', nested]); + } + i += expr.length; + pi = i + 1; } - if ((i > pi) && (pi < str.length - 1)) { - s = str.slice(pi, i).replace(MULTILINER, heredoc ? '\\n' : ''); - tokens.push(['STRING', quote + s + quote]); + if ((i > pi) && (pi < str.length)) { + tokens.push(['TO_BE_STRING', str.slice(pi)]); } - if (tokens[0][0] !== 'STRING') { - tokens.unshift(['STRING', '""']); + if (regex) { + return tokens; } - interpolated = tokens.length > 1; - if (interpolated) { + if (!(tokens.length)) { + return this.token('STRING', '""'); + } + if (interpolated = tokens.length > 1) { this.token('(', '('); } - push = tokens.push; + if (tokens[0][0] !== 'TO_BE_STRING') { + this.tokens.push(['STRING', '""'], ['+', '+']); + } for (i = 0, _len = tokens.length; i < _len; i++) { - token = tokens[i]; - _ref2 = token, tag = _ref2[0], value = _ref2[1]; - if (tag === 'TOKENS') { - push.apply(this.tokens, value); - } else if (tag === 'STRING' && escapeQuotes) { - escaped = value.slice(1, -1).replace(/"/g, '\\"'); - this.token(tag, "\"" + (escaped) + "\""); - } else { - this.token(tag, value); - } - if (i < tokens.length - 1) { + _ref2 = tokens[i], tag = _ref2[0], value = _ref2[1]; + if (i) { this.token('+', '+'); } + if (tag === 'TOKENS') { + (_this = this.tokens).push.apply(_this, value); + } else { + this.token('STRING', this.makeString(value, '"', heredoc)); + } } if (interpolated) { this.token(')', ')'); @@ -571,6 +568,13 @@ Lexer.prototype.escapeLines = function(str, heredoc) { return str.replace(MULTILINER, heredoc ? '\\n' : ''); }; + Lexer.prototype.makeString = function(body, quote, heredoc) { + body = body.replace(/\\([\s\S])/g, function($amp, $1) { + return ('\n' === $1 || quote === $1) ? $1 : $amp; + }); + body = body.replace(RegExp("" + quote, "g"), '\\$&'); + return quote + this.escapeLines(body, heredoc) + quote; + }; return Lexer; })(); JS_KEYWORDS = ['if', 'else', 'true', 'false', 'new', 'return', 'try', 'catch', 'finally', 'throw', 'break', 'continue', 'for', 'in', 'while', 'delete', 'instanceof', 'typeof', 'switch', 'super', 'extends', 'class', 'this', 'null', 'debugger']; @@ -580,7 +584,7 @@ JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED); IDENTIFIER = /^[a-zA-Z_$][\w$]*/; NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i; - HEREDOC = /^("""|''')([\s\S]*?)\n?[ \t]*\1/; + HEREDOC = /^("""|''')([\s\S]*?)(?:\n[ \t]*)?\1/; OPERATOR = /^(?:-[-=>]?|\+[+=]?|[*&|\/%=<>^:!?]+)(?=([ \t]*))/; WHITESPACE = /^[ \t]+/; COMMENT = /^###([^#][\s\S]*?)(?:###[ \t]*\n|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/; @@ -588,15 +592,16 @@ MULTI_DENT = /^(?:\n[ \t]*)+/; SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/; JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/; - REGEX_START = /^\/([^\/])/; - REGEX_INTERPOLATION = /[^\\]#\{.*[^\\]\}/; - REGEX_END = /^[imgy]{0,4}(?![a-zA-Z])/; - REGEX_ESCAPE = /\\[^#]/g; + REGEX = /^\/(?!\s)(?:[^[\/\n\\]+|\\.|\[([^\\\]]+|\\.)*])+\/[imgy]{0,4}(?![A-Za-z])/; + HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/; + HEREGEX_OMIT = /\s+(?:#.*)?/g; MULTILINER = /\n/g; - NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/; HEREDOC_INDENT = /\n+([ \t]*)/g; ASSIGNED = /^\s*@?[$A-Za-z_][$\w]*[ \t]*?[:=][^:=>]/; NEXT_CHARACTER = /^\s*(\S?)/; + LEADING_SPACES = /^\s+/; + TRAILING_SPACES = /\s+$/; + NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/; COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']; UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'NEW', 'TYPEOF', 'DELETE']; LOGIC = ['&', '|', '^', '&&', '||']; diff --git a/lib/nodes.js b/lib/nodes.js index 2dbc5de3..5e38a31d 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -216,16 +216,16 @@ o.scope = new Scope(null, this, null); code = this.compileWithDeclarations(o); code = code.replace(TRAILING_WHITESPACE, ''); - return o.noWrap ? code : ("(function() {\n" + (code) + "\n}).call(this);\n"); + return o.noWrap ? code : ("(function() {\n" + code + "\n}).call(this);\n"); }; Expressions.prototype.compileWithDeclarations = function(o) { var code; code = this.compileNode(o); if (o.scope.hasAssignments(this)) { - code = ("" + (this.tab) + "var " + (o.scope.compiledAssignments()) + ";\n" + (code)); + code = ("" + (this.tab) + "var " + (o.scope.compiledAssignments()) + ";\n" + code); } if (!o.globals && o.scope.hasDeclarations(this)) { - code = ("" + (this.tab) + "var " + (o.scope.compiledDeclarations()) + ";\n" + (code)); + code = ("" + (this.tab) + "var " + (o.scope.compiledDeclarations()) + ";\n" + code); } return code; }; @@ -235,7 +235,7 @@ compiledNode = node.compile(merge(o, { top: true })); - return node.isStatement(o) ? compiledNode : ("" + (this.idt()) + (compiledNode) + ";"); + return node.isStatement(o) ? compiledNode : ("" + (this.idt()) + compiledNode + ";"); }; return Expressions; })(); @@ -384,7 +384,7 @@ } code = this.base.compile(o); if (props[0] instanceof AccessorNode && this.isNumber() || o.top && this.base instanceof ObjectNode) { - code = ("(" + (code) + ")"); + code = ("(" + code + ")"); } for (_i = 0, _len = props.length; _i < _len; _i++) { prop = props[_i]; @@ -487,7 +487,7 @@ if (!(name)) { throw Error("cannot call super on an anonymous function."); } - return method.klass ? ("" + (method.klass) + ".__super__." + (name)) : ("" + (name) + ".__super__.constructor"); + return method.klass ? ("" + (method.klass) + ".__super__." + name) : ("" + name + ".__super__.constructor"); }; CallNode.prototype.unfoldSoak = function(o) { var _i, _len, _ref2, call, list, node; @@ -540,7 +540,7 @@ } left = ("typeof " + (left.compile(o)) + " !== \"function\""); rite = rite.compile(o); - return ("(" + (left) + " ? undefined : " + (rite) + ")"); + return ("(" + left + " ? undefined : " + rite + ")"); } _ref2 = this.args; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { @@ -557,16 +557,16 @@ } return _result; }).call(this).join(', '); - return this.isSuper ? this.compileSuper(args, o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + (args) + ")"); + return this.isSuper ? this.compileSuper(args, o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + args + ")"); }; CallNode.prototype.compileSuper = function(args, o) { - return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + (args) + ")"; + return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + args + ")"; }; CallNode.prototype.compileSplat = function(o) { var _i, _len, _ref2, a, arg, argvar, b, base, c, call, fun, idt, name, ref, splatargs; splatargs = this.compileSplatArguments(o); if (this.isSuper) { - return ("" + (this.superReference(o)) + ".apply(this, " + (splatargs) + ")"); + return ("" + (this.superReference(o)) + ".apply(this, " + splatargs + ")"); } if (!(this.isNew)) { if (!((base = this.variable) instanceof ValueNode)) { @@ -574,14 +574,14 @@ } if ((name = base.properties.pop()) && base.isComplex()) { ref = o.scope.freeVariable('this'); - fun = ("(" + (ref) + " = " + (base.compile(o)) + ")" + (name.compile(o))); + fun = ("(" + ref + " = " + (base.compile(o)) + ")" + (name.compile(o))); } else { fun = (ref = base.compile(o)); if (name) { fun += name.compile(o); } } - return ("" + (fun) + ".apply(" + (ref) + ", " + (splatargs) + ")"); + return ("" + fun + ".apply(" + ref + ", " + splatargs + ")"); } call = 'call(this)'; argvar = function(n) { @@ -598,7 +598,7 @@ a = o.scope.freeVariable('ctor'); b = o.scope.freeVariable('ref'); c = o.scope.freeVariable('result'); - return "(function() {\n" + (idt = this.idt(1)) + "var ctor = function() {};\n" + (idt) + (utility('extends')) + "(ctor, " + (a) + " = " + (this.variable.compile(o)) + ");\n" + (idt) + "return typeof (" + (c) + " = " + (a) + ".apply(" + (b) + " = new ctor, " + (splatargs) + ")) === \"object\" ? " + (c) + " : " + (b) + ";\n" + (this.tab) + "})." + (call); + return "(function() {\n" + (idt = this.idt(1)) + "var ctor = function() {};\n" + idt + (utility('extends')) + "(ctor, " + a + " = " + (this.variable.compile(o)) + ");\n" + idt + "return typeof (" + c + " = " + a + ".apply(" + b + " = new ctor, " + splatargs + ")) === \"object\" ? " + c + " : " + b + ";\n" + (this.tab) + "})." + call; }; return CallNode; })(); @@ -635,7 +635,7 @@ AccessorNode.prototype.compileNode = function(o) { var name, namePart; name = this.name.compile(o); - namePart = name.match(IS_STRING) ? ("[" + (name) + "]") : ("." + (name)); + namePart = name.match(IS_STRING) ? ("[" + name + "]") : ("." + name); return this.prototype + namePart; }; AccessorNode.prototype.isComplex = NO; @@ -655,7 +655,7 @@ var idx, prefix; idx = this.index.compile(o); prefix = this.proto ? '.prototype' : ''; - return "" + (prefix) + "[" + (idx) + "]"; + return "" + prefix + "[" + idx + "]"; }; IndexNode.prototype.isComplex = function() { return this.index.isComplex(); @@ -706,20 +706,20 @@ } idx = del(o, 'index'); step = del(o, 'step'); - vars = ("" + (idx) + " = " + (this.fromVar)); - intro = ("(" + (this.fromVar) + " <= " + (this.toVar) + " ? " + (idx)); - compare = ("" + (intro) + " <" + (this.equals) + " " + (this.toVar) + " : " + (idx) + " >" + (this.equals) + " " + (this.toVar) + ")"); + vars = ("" + idx + " = " + (this.fromVar)); + intro = ("(" + (this.fromVar) + " <= " + (this.toVar) + " ? " + idx); + compare = ("" + intro + " <" + (this.equals) + " " + (this.toVar) + " : " + idx + " >" + (this.equals) + " " + (this.toVar) + ")"); stepPart = step ? step.compile(o) : '1'; - incr = step ? ("" + (idx) + " += " + (stepPart)) : ("" + (intro) + " += " + (stepPart) + " : " + (idx) + " -= " + (stepPart) + ")"); - return "" + (vars) + "; " + (compare) + "; " + (incr); + incr = step ? ("" + idx + " += " + stepPart) : ("" + intro + " += " + stepPart + " : " + idx + " -= " + stepPart + ")"); + return "" + vars + "; " + compare + "; " + incr; }; RangeNode.prototype.compileSimple = function(o) { var _ref2, from, idx, step, to; _ref2 = [+this.fromNum, +this.toNum], from = _ref2[0], to = _ref2[1]; idx = del(o, 'index'); step = del(o, 'step'); - step && (step = ("" + (idx) + " += " + (step.compile(o)))); - return from <= to ? ("" + (idx) + " = " + (from) + "; " + (idx) + " <" + (this.equals) + " " + (to) + "; " + (step || ("" + (idx) + "++"))) : ("" + (idx) + " = " + (from) + "; " + (idx) + " >" + (this.equals) + " " + (to) + "; " + (step || ("" + (idx) + "--"))); + step && (step = ("" + idx + " += " + (step.compile(o)))); + return from <= to ? ("" + idx + " = " + from + "; " + idx + " <" + (this.equals) + " " + to + "; " + (step || ("" + idx + "++"))) : ("" + idx + " = " + from + "; " + idx + " >" + (this.equals) + " " + to + "; " + (step || ("" + idx + "--"))); }; RangeNode.prototype.compileArray = function(o) { var _i, _ref2, _ref3, _result, body, clause, i, idt, post, pre, range, result, vars; @@ -740,16 +740,16 @@ } i = o.scope.freeVariable('i'); result = o.scope.freeVariable('result'); - pre = ("\n" + (idt) + (result) + " = []; " + (vars)); + pre = ("\n" + idt + result + " = []; " + vars); if (this.fromNum && this.toNum) { o.index = i; body = this.compileSimple(o); } else { clause = ("" + (this.fromVar) + " <= " + (this.toVar) + " ?"); - body = ("var " + (i) + " = " + (this.fromVar) + "; " + (clause) + " " + (i) + " <" + (this.equals) + " " + (this.toVar) + " : " + (i) + " >" + (this.equals) + " " + (this.toVar) + "; " + (clause) + " " + (i) + " += 1 : " + (i) + " -= 1"); + body = ("var " + i + " = " + (this.fromVar) + "; " + clause + " " + i + " <" + (this.equals) + " " + (this.toVar) + " : " + i + " >" + (this.equals) + " " + (this.toVar) + "; " + clause + " " + i + " += 1 : " + i + " -= 1"); } - post = ("{ " + (result) + ".push(" + (i) + "); }\n" + (idt) + "return " + (result) + ";\n" + (o.indent)); - return "(function() {" + (pre) + "\n" + (idt) + "for (" + (body) + ")" + (post) + "}).call(this)"; + post = ("{ " + result + ".push(" + i + "); }\n" + idt + "return " + result + ";\n" + (o.indent)); + return "(function() {" + pre + "\n" + idt + "for (" + body + ")" + post + "}).call(this)"; }; return RangeNode; })(); @@ -771,7 +771,7 @@ if (to) { to = ', ' + to; } - return ".slice(" + (from) + (to) + ")"; + return ".slice(" + from + to + ")"; }; return SliceNode; })(); @@ -826,7 +826,7 @@ }).call(this); props = props.join(''); obj = '{' + (props ? '\n' + props + '\n' + this.idt() : '') + '}'; - return top ? ("(" + (obj) + ")") : obj; + return top ? ("(" + obj + ")") : obj; }; return ObjectNode; })(); @@ -855,15 +855,15 @@ if (obj instanceof SplatNode) { return this.compileSplatLiteral(o); } else if (obj instanceof CommentNode) { - objects.push("\n" + (code) + "\n" + (o.indent)); + objects.push("\n" + code + "\n" + (o.indent)); } else if (i === this.objects.length - 1) { objects.push(code); } else { - objects.push("" + (code) + ", "); + objects.push("" + code + ", "); } } objects = objects.join(''); - return indexOf(objects, '\n') >= 0 ? ("[\n" + (this.idt(1)) + (objects) + "\n" + (this.tab) + "]") : ("[" + (objects) + "]"); + return indexOf(objects, '\n') >= 0 ? ("[\n" + (this.idt(1)) + objects + "\n" + (this.tab) + "]") : ("[" + objects + "]"); }; return ArrayNode; })(); @@ -929,7 +929,7 @@ if (constructor.body.empty()) { constructor.body.push(new ReturnNode(literal('this'))); } - constructor.body.unshift(literal("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + (me) + ", arguments); }")); + constructor.body.unshift(literal("this." + pname + " = function(){ return " + className + ".prototype." + pname + ".apply(" + me + ", arguments); }")); } } if (pvar) { @@ -941,7 +941,7 @@ } constructor.className = className.match(/[\w\d\$_]+$/); if (me) { - constructor.body.unshift(literal("" + (me) + " = this")); + constructor.body.unshift(literal("" + me + " = this")); } construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(merge(o, { sharedScope: constScope @@ -992,16 +992,16 @@ } val = this.value.compile(o); if (this.context === 'object') { - return ("" + (name) + ": " + (val)); + return ("" + name + ": " + val); } if (!(isValue && (this.variable.hasProperties() || this.variable.namespaced))) { o.scope.find(name); } - val = ("" + (name) + " = " + (val)); + val = ("" + name + " = " + val); if (stmt) { - return ("" + (this.tab) + (val) + ";"); + return ("" + (this.tab) + val + ";"); } - return top || this.parenthetical ? val : ("(" + (val) + ")"); + return top || this.parenthetical ? val : ("(" + val + ")"); }; AssignNode.prototype.compilePatternMatch = function(o) { var _len, _ref2, accessClass, assigns, code, i, idx, isObject, obj, objects, olength, otop, splat, top, val, valVar, value; @@ -1031,7 +1031,7 @@ top: true }); valVar = o.scope.freeVariable('ref'); - assigns = [("" + (valVar) + " = " + (value.compile(o)))]; + assigns = [("" + valVar + " = " + (value.compile(o)))]; splat = false; for (i = 0, _len = objects.length; i < _len; i++) { obj = objects[i]; @@ -1052,7 +1052,7 @@ splat = true; } else { if (typeof idx !== 'object') { - idx = literal(splat ? ("" + (valVar) + ".length - " + (olength - idx)) : idx); + idx = literal(splat ? ("" + valVar + ".length - " + (olength - idx)) : idx); } val = new ValueNode(literal(valVar), [new accessClass(idx)]); } @@ -1062,7 +1062,7 @@ assigns.push(valVar); } code = assigns.join(', '); - return top || this.parenthetical ? code : ("(" + (code) + ")"); + return top || this.parenthetical ? code : ("(" + code + ")"); }; AssignNode.prototype.compileSplice = function(o) { var from, name, plus, range, ref, to, val; @@ -1070,10 +1070,10 @@ name = this.variable.compile(o); plus = range.exclusive ? '' : ' + 1'; from = range.from ? range.from.compile(o) : '0'; - to = range.to ? range.to.compile(o) + ' - ' + from + plus : ("" + (name) + ".length"); + to = range.to ? range.to.compile(o) + ' - ' + from + plus : ("" + name + ".length"); ref = o.scope.freeVariable('ref'); val = this.value.compile(o); - return "([].splice.apply(" + (name) + ", [" + (from) + ", " + (to) + "].concat(" + (ref) + " = " + (val) + ")), " + (ref) + ")"; + return "([].splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + ref + " = " + val + ")), " + ref + ")"; }; return AssignNode; })(); @@ -1153,11 +1153,11 @@ code = this.body.expressions.length ? ("\n" + (this.body.compileWithDeclarations(o)) + "\n") : ''; open = this.className ? ("(function() {\n" + (this.idt(1)) + "return function " + (this.className) + "(") : "function("; close = this.className ? ("" + (code && this.idt(1)) + "};\n" + (this.tab) + "})()") : ("" + (code && this.tab) + "}"); - func = ("" + (open) + (params.join(', ')) + ") {" + (code) + (close)); + func = ("" + open + (params.join(', ')) + ") {" + code + close); if (this.bound) { - return ("" + (utility('bind')) + "(" + (func) + ", " + (this.context) + ")"); + return ("" + (utility('bind')) + "(" + func + ", " + (this.context) + ")"); } - return top ? ("(" + (func) + ")") : func; + return top ? ("(" + func + ")") : func; }; CodeNode.prototype.topSensitive = YES; CodeNode.prototype.traverseChildren = function(crossScope, func) { @@ -1220,7 +1220,7 @@ o.scope.assign(len, "arguments.length"); variadic = o.scope.freeVariable('result'); o.scope.assign(variadic, len + ' >= ' + this.arglength); - end = this.trailings.length ? (", " + (len) + " - " + (this.trailings.length)) : undefined; + end = this.trailings.length ? (", " + len + " - " + (this.trailings.length)) : undefined; _ref2 = this.trailings; for (idx = 0, _len = _ref2.length; idx < _len; idx++) { trailing = _ref2[idx]; @@ -1230,15 +1230,15 @@ assign.value = trailing; } pos = this.trailings.length - idx; - o.scope.assign(trailing.compile(o), "arguments[" + (variadic) + " ? " + (len) + " - " + (pos) + " : " + (this.index + idx) + "]"); + o.scope.assign(trailing.compile(o), "arguments[" + variadic + " ? " + len + " - " + pos + " : " + (this.index + idx) + "]"); } } - return "" + (name) + " = " + (utility('slice')) + ".call(arguments, " + (this.index) + (end) + ")"; + return "" + name + " = " + (utility('slice')) + ".call(arguments, " + (this.index) + end + ")"; }; SplatNode.prototype.compileValue = function(o, name, index, trailings) { var trail; - trail = trailings ? (", " + (name) + ".length - " + (trailings)) : ''; - return "" + (utility('slice')) + ".call(" + (name) + ", " + (index) + (trail) + ")"; + trail = trailings ? (", " + name + ".length - " + trailings) : ''; + return "" + (utility('slice')) + ".call(" + name + ", " + index + trail + ")"; }; SplatNode.compileSplattedArray = function(list, o) { var _len, arg, args, code, end, i, prev; @@ -1250,16 +1250,16 @@ prev = args[end]; if (!(arg instanceof SplatNode)) { if (prev && starts(prev, '[') && ends(prev, ']')) { - args[end] = ("" + (prev.slice(0, -1)) + ", " + (code) + "]"); + args[end] = ("" + (prev.slice(0, -1)) + ", " + code + "]"); continue; } if (prev && starts(prev, '.concat([') && ends(prev, '])')) { - args[end] = ("" + (prev.slice(0, -2)) + ", " + (code) + "])"); + args[end] = ("" + (prev.slice(0, -2)) + ", " + code + "])"); continue; } - code = ("[" + (code) + "]"); + code = ("[" + code + "]"); } - args[++end] = i === 0 ? code : (".concat(" + (code) + ")"); + args[++end] = i === 0 ? code : (".concat(" + code + ")"); } return args.join(''); }; @@ -1302,12 +1302,12 @@ set = ''; if (!(top)) { rvar = o.scope.freeVariable('result'); - set = ("" + (this.tab) + (rvar) + " = [];\n"); + set = ("" + (this.tab) + rvar + " = [];\n"); if (this.body) { this.body = PushNode.wrap(rvar, this.body); } } - pre = ("" + (set) + (this.tab) + "while (" + (cond) + ")"); + pre = ("" + set + (this.tab) + "while (" + cond + ")"); if (this.guard) { this.body = Expressions.wrap([new IfNode(this.guard, this.body)]); } @@ -1318,7 +1318,7 @@ } else { post = ''; } - return "" + (pre) + " {\n" + (this.body.compile(o)) + "\n" + (this.tab) + "}" + (post); + return "" + pre + " {\n" + (this.body.compile(o)) + "\n" + (this.tab) + "}" + post; }; return WhileNode; })(); @@ -1409,7 +1409,7 @@ shared = this.first.unwrap().second; _ref2 = shared.compileReference(o), this.first.second = _ref2[0], shared = _ref2[1]; _ref2 = [this.first.compile(o), this.second.compile(o), shared.compile(o)], first = _ref2[0], second = _ref2[1], shared = _ref2[2]; - return "(" + (first) + ") && (" + (shared) + " " + (this.operator) + " " + (second) + ")"; + return "(" + first + ") && (" + shared + " " + (this.operator) + " " + second + ")"; }; OpNode.prototype.compileAssignment = function(o) { var _ref2, left, rite; @@ -1426,7 +1426,7 @@ fst = this.first; ref = fst.compile(o); } - return new ExistenceNode(fst).compile(o) + (" ? " + (ref) + " : " + (this.second.compile(o))); + return new ExistenceNode(fst).compile(o) + (" ? " + ref + " : " + (this.second.compile(o))); }; OpNode.prototype.compileUnary = function(o) { var parts, space; @@ -1479,7 +1479,7 @@ }), this.arr1 = _ref2[0], this.arr2 = _ref2[1]; _ref2 = [o.scope.freeVariable('i'), o.scope.freeVariable('len')], i = _ref2[0], l = _ref2[1]; prefix = this.obj1 !== this.obj2 ? this.obj1 + '; ' : ''; - return "(function(){ " + (prefix) + "for (var " + (i) + "=0, " + (l) + "=" + (this.arr1) + ".length; " + (i) + "<" + (l) + "; " + (i) + "++) { if (" + (this.arr2) + "[" + (i) + "] === " + (this.obj2) + ") return true; } return false; }).call(this)"; + return "(function(){ " + prefix + "for (var " + i + "=0, " + l + "=" + (this.arr1) + ".length; " + i + "<" + l + "; " + i + "++) { if (" + (this.arr2) + "[" + i + "] === " + (this.obj2) + ") return true; } return false; }).call(this)"; }; return InNode; })(); @@ -1512,9 +1512,9 @@ o.top = true; attemptPart = this.attempt.compile(o); errorPart = this.error ? (" (" + (this.error.compile(o)) + ") ") : ' '; - catchPart = this.recovery ? (" catch" + (errorPart) + "{\n" + (this.recovery.compile(o)) + "\n" + (this.tab) + "}") : (!(this.ensure || this.recovery) ? ' catch (_e) {}' : ''); + catchPart = this.recovery ? (" catch" + errorPart + "{\n" + (this.recovery.compile(o)) + "\n" + (this.tab) + "}") : (!(this.ensure || this.recovery) ? ' catch (_e) {}' : ''); finallyPart = (this.ensure || '') && ' finally {\n' + this.ensure.compile(merge(o)) + ("\n" + (this.tab) + "}"); - return "" + (this.tab) + "try {\n" + (attemptPart) + "\n" + (this.tab) + "}" + (catchPart) + (finallyPart); + return "" + (this.tab) + "try {\n" + attemptPart + "\n" + (this.tab) + "}" + catchPart + finallyPart; }; return TryNode; })(); @@ -1548,8 +1548,8 @@ ExistenceNode.prototype.compileNode = function(o) { var code; code = this.expression.compile(o); - code = IDENTIFIER.test(code) && !o.scope.check(code) ? ("typeof " + (code) + " !== \"undefined\" && " + (code) + " !== null") : ("" + (code) + " != null"); - return this.parenthetical ? code : ("(" + (code) + ")"); + code = IDENTIFIER.test(code) && !o.scope.check(code) ? ("typeof " + code + " !== \"undefined\" && " + code + " !== null") : ("" + code + " != null"); + return this.parenthetical ? code : ("(" + code + ")"); }; return ExistenceNode; })(); @@ -1584,7 +1584,7 @@ if (this.parenthetical || this.isStatement(o)) { return top ? this.tab + code + ';' : code; } - return "(" + (code) + ")"; + return "(" + code + ")"; }; return ParentheticalNode; })(); @@ -1676,20 +1676,20 @@ sourcePart = ''; } else { ref = scope.freeVariable('ref'); - sourcePart = ("" + (ref) + " = " + (svar) + ";"); + sourcePart = ("" + ref + " = " + svar + ";"); svar = ref; } - namePart = this.pattern ? new AssignNode(this.name, literal("" + (svar) + "[" + (ivar) + "]")).compile(merge(o, { + namePart = this.pattern ? new AssignNode(this.name, literal("" + svar + "[" + ivar + "]")).compile(merge(o, { top: true - })) : (name ? ("" + (name) + " = " + (svar) + "[" + (ivar) + "]") : undefined); + })) : (name ? ("" + name + " = " + svar + "[" + ivar + "]") : undefined); if (!(this.object)) { lvar = scope.freeVariable('len'); - stepPart = this.step ? ("" + (ivar) + " += " + (this.step.compile(o))) : ("" + (ivar) + "++"); - forPart = ("" + (ivar) + " = 0, " + (lvar) + " = " + (svar) + ".length; " + (ivar) + " < " + (lvar) + "; " + (stepPart)); + stepPart = this.step ? ("" + ivar + " += " + (this.step.compile(o))) : ("" + ivar + "++"); + forPart = ("" + ivar + " = 0, " + lvar + " = " + svar + ".length; " + ivar + " < " + lvar + "; " + stepPart); } } - sourcePart = (rvar ? ("" + (rvar) + " = []; ") : '') + sourcePart; - sourcePart = sourcePart ? ("" + (this.tab) + (sourcePart) + "\n" + (this.tab)) : this.tab; + sourcePart = (rvar ? ("" + rvar + " = []; ") : '') + sourcePart; + sourcePart = sourcePart ? ("" + (this.tab) + sourcePart + "\n" + (this.tab)) : this.tab; returnResult = this.compileReturnValue(rvar, o); if (!(topLevel)) { body = PushNode.wrap(rvar, body); @@ -1699,32 +1699,32 @@ } if (codeInBody) { if (range) { - body.unshift(literal("var " + (name) + " = " + (ivar))); + body.unshift(literal("var " + name + " = " + ivar)); } if (namePart) { - body.unshift(literal("var " + (namePart))); + body.unshift(literal("var " + namePart)); } if (index) { - body.unshift(literal("var " + (index) + " = " + (ivar))); + body.unshift(literal("var " + index + " = " + ivar)); } body = ClosureNode.wrap(body, true); } else { if (namePart) { - varPart = ("" + (idt1) + (namePart) + ";\n"); + varPart = ("" + idt1 + namePart + ";\n"); } } if (this.object) { - forPart = ("" + (ivar) + " in " + (svar)); + forPart = ("" + ivar + " in " + svar); if (!(this.raw)) { - guardPart = ("\n" + (idt1) + "if (!" + (utility('hasProp')) + ".call(" + (svar) + ", " + (ivar) + ")) continue;"); + guardPart = ("\n" + idt1 + "if (!" + (utility('hasProp')) + ".call(" + svar + ", " + ivar + ")) continue;"); } } body = body.compile(merge(o, { indent: idt1, top: true })); - vars = range ? name : ("" + (name) + ", " + (ivar)); - return "" + (sourcePart) + "for (" + (forPart) + ") {" + (guardPart) + "\n" + (varPart) + (body) + "\n" + (this.tab) + "}" + (returnResult); + vars = range ? name : ("" + name + ", " + ivar); + return "" + sourcePart + "for (" + forPart + ") {" + guardPart + "\n" + varPart + body + "\n" + (this.tab) + "}" + returnResult; }; return ForNode; })(); @@ -1775,7 +1775,7 @@ } code += ("\n" + (block.compile(o))); if (!(last(exprs) instanceof ReturnNode)) { - code += ("\n" + (idt) + "break;"); + code += ("\n" + idt + "break;"); } } if (this.otherwise) { @@ -1868,7 +1868,7 @@ ifDent = child || (top && !this.isStatement(o)) ? '' : this.idt(); comDent = child ? this.idt() : ''; body = this.body.compile(o); - ifPart = ("" + (ifDent) + "if (" + (this.compileCondition(condO)) + ") {\n" + (body) + "\n" + (this.tab) + "}"); + ifPart = ("" + ifDent + "if (" + (this.compileCondition(condO)) + ") {\n" + body + "\n" + (this.tab) + "}"); if (!(this.elseBody)) { return ifPart; } @@ -1876,7 +1876,7 @@ indent: this.idt(), chainChild: true })) : (" else {\n" + (this.elseBody.compile(o)) + "\n" + (this.tab) + "}"); - return "" + (ifPart) + (elsePart); + return "" + ifPart + elsePart; }; IfNode.prototype.compileTernary = function(o) { var code, elsePart, ifPart; @@ -1886,8 +1886,8 @@ } ifPart = this.condition.compile(o) + ' ? ' + this.bodyNode().compile(o); elsePart = this.elseBody ? this.elseBodyNode().compile(o) : 'undefined'; - code = ("" + (ifPart) + " : " + (elsePart)); - return this.tags.operation ? ("(" + (code) + ")") : code; + code = ("" + ifPart + " : " + elsePart); + return this.tags.operation ? ("(" + code + ")") : code; }; return IfNode; })(); @@ -1944,7 +1944,7 @@ }; utility = function(name) { var ref; - ref = ("__" + (name)); + ref = ("__" + name); Scope.root.assign(ref, UTILITIES[name]); return ref; }; diff --git a/lib/optparse.js b/lib/optparse.js index ca24b813..a541aa13 100755 --- a/lib/optparse.js +++ b/lib/optparse.js @@ -29,7 +29,7 @@ } } if (isOption && !matchedRule) { - throw new Error("unrecognized option: " + (arg)); + throw new Error("unrecognized option: " + arg); } if (!isOption) { options.arguments = args.slice(i, args.length); diff --git a/lib/rewriter.js b/lib/rewriter.js index 1f453ab8..ffac0c06 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -65,7 +65,7 @@ } else { tokens.splice(i, 0, after); } - } else if (!('TERMINATOR' === (_ref = ((prev != null) ? prev[0] : undefined)) || 'INDENT' === _ref || 'OUTDENT' === _ref)) { + } else if (prev && !('TERMINATOR' === (_ref = prev[0]) || 'INDENT' === _ref || 'OUTDENT' === _ref)) { if (((post != null) ? post[0] === 'TERMINATOR' : undefined) && ((after != null) ? after[0] === 'OUTDENT' : undefined)) { tokens.splice.apply(tokens, [i + 2, 0].concat(tokens.splice(i, 2))); if (tokens[i + 2][0] !== 'TERMINATOR') { diff --git a/lib/scope.js b/lib/scope.js index c38a8955..75a2d1a0 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -118,7 +118,7 @@ if (!__hasProp.call(_ref2, key)) continue; val = _ref2[key]; if (val.assigned) { - _result.push("" + (key) + " = " + (val.value)); + _result.push("" + key + " = " + (val.value)); } } return _result; diff --git a/src/lexer.coffee b/src/lexer.coffee index 4d9c8bd9..c7cf730b 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -33,7 +33,7 @@ exports.Lexer = class Lexer # Before returning the token stream, run it through the [Rewriter](rewriter.html) # unless explicitly asked not to. tokenize: (code, options) -> - code = code.replace(/\r/g, '').replace /\s+$/, '' + code = code.replace(/\r/g, '').replace TRAILING_SPACES, '' o = options or {} @code = code # The remainder of the source code. @i = 0 # Current character position we're parsing. @@ -124,8 +124,11 @@ exports.Lexer = class Lexer return false unless match = SIMPLESTR.exec @chunk @token 'STRING', (string = match[0]).replace MULTILINER, '\\\n' when '"' - return false unless string = @balancedToken ['"', '"'], ['#{', '}'] - @interpolateString string + return false unless string = @balancedString @chunk, [['"', '"'], ['#{', '}']] + if 0 < string.indexOf '#{', 1 + @interpolateString string.slice 1, -1 + else + @token 'STRING', @escapeLines string else return false @line += count string, '\n' @@ -135,14 +138,14 @@ exports.Lexer = class Lexer # Matches heredocs, adjusting indentation to the correct level, as heredocs # preserve whitespace, but ignore indentation to the left. heredocToken: -> - return false unless match = @chunk.match HEREDOC + return false unless match = HEREDOC.exec @chunk heredoc = match[0] quote = heredoc.charAt 0 doc = @sanitizeHeredoc match[2], {quote, indent: null} - if quote is '"' - @interpolateString quote + doc + quote, heredoc: yes + if quote is '"' and 0 <= doc.indexOf '#{' + @interpolateString doc, heredoc: yes else - @token 'STRING', quote + doc + quote + @token 'STRING', @makeString doc, quote, yes @line += count heredoc, '\n' @i += heredoc.length true @@ -168,31 +171,41 @@ exports.Lexer = class Lexer # Matches regular expression literals. Lexing regular expressions is difficult # to distinguish from division, so we borrow some basic heuristics from - # JavaScript and Ruby, borrow slash balancing from `@balancedToken`, and - # borrow interpolation from `@interpolateString`. + # JavaScript and Ruby. regexToken: -> - return false unless first = @chunk.match REGEX_START - return false if first[1] is ' ' and @tag() not in ['CALL_START', '='] + return false if @chunk.charAt(0) isnt '/' + return @heregexToken match if match = HEREGEX.exec @chunk 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] - if REGEX_INTERPOLATION.test regex - str = regex.slice 1, -1 - str = str.replace REGEX_ESCAPE, '\\$&' - @tokens.push ['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '('] - @interpolateString "\"#{str}\"", escapeQuotes: yes - @tokens.push [',', ','], ['STRING', "\"#{flags}\""] if flags - @tokens.push [')', ')'], [')', ')'] - else - @token 'REGEX', regex + flags - @i += regex.length + flags.length + return false unless match = REGEX.exec @chunk + @token 'REGEX', match[0] + @i += match[0].length true - # Matches a token in which the passed delimiter pairs must be correctly - # balanced (ie. strings, JS literals). - balancedToken: (delimited...) -> - @balancedString @chunk, delimited + # Matches experimental, multiline and extended regular expression literals. + heregexToken: (match) -> + [heregex, body, flags] = match + @i += heregex.length + if 0 > body.indexOf '#{' + re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/') + @token 'REGEX', "/#{ re or '(?:)' }/#{flags}" + return true + @token 'IDENTIFIER', 'RegExp' + @tokens.push ['CALL_START', '('] + tokens = [] + for [tag, value] in @interpolateString(body, regex: yes) + if tag is 'TOKENS' + tokens.push value... + else + continue unless value = value.replace HEREGEX_OMIT, '' + value = value.replace /\\/g, '\\\\' + tokens.push ['STRING', @makeString(value, '"', yes)] + tokens.push ['+', '+'] + tokens.pop() + @tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0]?[0] is 'STRING' + @tokens.push tokens... + @tokens.push [',', ','], ['STRING', '"' + flags + '"'] if flags + @tokens.push ['CALL_END', ')'] + true # Matches newlines, indents, and outdents, and determines which is which. # If we can detect that the current line is continued onto the the next line, @@ -332,22 +345,17 @@ exports.Lexer = class Lexer prev[0] is '@' if accessor then 'accessor' else false - # Sanitize a heredoc or herecomment by escaping internal double quotes and + # Sanitize a heredoc or herecomment by # erasing all external indentation on the left-hand side. sanitizeHeredoc: (doc, options) -> {indent, herecomment} = options - return doc if herecomment and not include doc, '\n' + return doc if herecomment and 0 > doc.indexOf '\n' unless herecomment - while (match = HEREDOC_INDENT.exec doc) + while match = HEREDOC_INDENT.exec doc attempt = match[1] indent = attempt if indent is null or 0 < attempt.length < indent.length - doc = doc.replace /\n#{ indent }/g, '\n' if indent - return doc if herecomment - {quote} = options - doc = doc.replace /^\n/, '' - doc = doc.replace /\\([\s\S])/g, (m, c) -> if c in ['\n', quote] then c else m - doc = doc.replace /#{quote}/g, '\\$&' - doc = @escapeLines doc, yes if quote is "'" + doc = doc.replace /// \n #{indent} ///g, '\n' if indent + doc = doc.replace /^\n/, '' unless herecomment doc # A source of ambiguity in our grammar used to be parameter lists in function @@ -384,7 +392,6 @@ exports.Lexer = class Lexer # interpolations within strings, ad infinitum. balancedString: (str, delimited, options) -> options or= {} - slash = delimited[0][0] is '/' levels = [] i = 0 slen = str.length @@ -399,14 +406,13 @@ exports.Lexer = class Lexer i += close.length - 1 i += 1 unless levels.length break - else if starts str, open, i + if starts str, open, i levels.push(pair) i += open.length - 1 break - break if not levels.length or slash and str.charAt(i) is '\n' + break if not levels.length i += 1 if levels.length - return false if slash throw new Error "SyntaxError: Unterminated #{levels.pop()[0]} starting on line #{@line + 1}" if not i then false else str[0...i] @@ -419,49 +425,39 @@ exports.Lexer = class Lexer # new Lexer, tokenize the interpolated contents, and merge them into the # token stream. interpolateString: (str, options) -> - {heredoc, escapeQuotes} = options or {} - quote = str.charAt 0 - return @token 'STRING', str if quote isnt '"' or str.length < 3 - lexer = new Lexer + {heredoc, regex} = options or= {} tokens = [] - i = pi = 1 - end = str.length - 1 - while i < end - if str.charAt(i) is '\\' + pi = 0 + i = -1 + while letter = str.charAt i += 1 + if letter is '\\' i += 1 - else if expr = @balancedString str[i..], [['#{', '}']] - if pi < i - s = quote + @escapeLines(str[pi...i], heredoc) + quote - tokens.push ['STRING', s] - inner = expr.slice(2, -1).replace /^[ \t]*\n/, '' - if inner.length - inner = inner.replace RegExp('\\\\' + quote, 'g'), quote if heredoc - nested = lexer.tokenize "(#{inner})", line: @line - (tok[0] = ')') for tok, idx in nested when tok[0] is 'CALL_END' - nested.pop() - tokens.push ['TOKENS', nested] - else - tokens.push ['STRING', quote + quote] - i += expr.length - 1 - pi = i + 1 - i += 1 - if i > pi < str.length - 1 - s = str[pi...i].replace MULTILINER, if heredoc then '\\n' else '' - tokens.push ['STRING', quote + s + quote] - tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING' - interpolated = tokens.length > 1 - @token '(', '(' if interpolated - {push} = tokens - for token, i in tokens - [tag, value] = token + continue + unless letter is '#' and str.charAt(i+1) is '{' and + (expr = @balancedString str[i+1..], [['{', '}']]) + continue + tokens.push ['TO_BE_STRING', str[pi...i]] if pi < i + inner = expr.slice(1, -1).replace(LEADING_SPACES, '').replace(TRAILING_SPACES, '') + if inner.length + nested = new Lexer().tokenize inner, line: @line, rewrite: off + nested.pop() + if nested.length > 1 + nested.unshift ['(', '('] + nested.push [')', ')'] + tokens.push ['TOKENS', nested] + i += expr.length + pi = i + 1 + tokens.push ['TO_BE_STRING', str[pi..]] if i > pi < str.length + return tokens if regex + return @token 'STRING', '""' unless tokens.length + @token '(', '(' if interpolated = tokens.length > 1 + @tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0][0] is 'TO_BE_STRING' + for [tag, value], i in tokens + @token '+', '+' if i if tag is 'TOKENS' - push.apply @tokens, value - else if tag is 'STRING' and escapeQuotes - escaped = value.slice(1, -1).replace(/"/g, '\\"') - @token tag, "\"#{escaped}\"" + @tokens.push value... else - @token tag, value - @token '+', '+' if i < tokens.length - 1 + @token 'STRING', @makeString value, '"', heredoc @token ')', ')' if interpolated tokens @@ -492,6 +488,13 @@ exports.Lexer = class Lexer escapeLines: (str, heredoc) -> str.replace MULTILINER, if heredoc then '\\n' else '' + # Constructs a string token by escaping quotes and newlines. + makeString: (body, quote, heredoc) -> + body = body.replace /\\([\s\S])/g, ($amp, $1) -> + if $1 in ['\n', quote] then $1 else $amp + body = body.replace /// #{quote} ///g, '\\$&' + quote + @escapeLines(body, heredoc) + quote + # Constants # --------- @@ -533,7 +536,7 @@ JS_FORBIDDEN = JS_KEYWORDS.concat RESERVED # Token matching regexes. IDENTIFIER = /^[a-zA-Z_$][\w$]*/ NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i -HEREDOC = /^("""|''')([\s\S]*?)\n?[ \t]*\1/ +HEREDOC = /^("""|''')([\s\S]*?)(?:\n[ \t]*)?\1/ OPERATOR = /^(?:-[-=>]?|\+[+=]?|[*&|\/%=<>^:!?]+)(?=([ \t]*))/ WHITESPACE = /^[ \t]+/ COMMENT = /^###([^#][\s\S]*?)(?:###[ \t]*\n|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/ @@ -543,17 +546,32 @@ SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/ JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/ # Regex-matching-regexes. -REGEX_START = /^\/([^\/])/ -REGEX_INTERPOLATION = /[^\\]#\{.*[^\\]\}/ -REGEX_END = /^[imgy]{0,4}(?![a-zA-Z])/ -REGEX_ESCAPE = /\\[^#]/g +REGEX = /// ^ + / (?!\s) # disallow leading whitespace + (?: [^ [ / \n \\ ]+ # every other thing + | \\. # anything escaped + | \[ ( [^\\\]]+ | \\. )* ] # character class + )+ + / [imgy]{0,4} (?![A-Za-z]) +/// +HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/ +HEREGEX_OMIT = /\s+(?:#.*)?/g # Token cleaning regexes. MULTILINER = /\n/g -NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/ HEREDOC_INDENT = /\n+([ \t]*)/g ASSIGNED = /^\s*@?[$A-Za-z_][$\w]*[ \t]*?[:=][^:=>]/ NEXT_CHARACTER = /^\s*(\S?)/ +LEADING_SPACES = /^\s+/ +TRAILING_SPACES = /\s+$/ +NO_NEWLINE = /// ^ + (?: # lookahead + [-+*&|/%=<>!.\\][<>=&|]* | # symbol operators + and | or | is(?:nt)? | n(?:ot|ew) | # word operators + delete |typeof | instanceof + )$ +/// + # Compound assignment tokens. COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='] diff --git a/src/rewriter.coffee b/src/rewriter.coffee index 70ea972f..3cb57c6c 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -73,7 +73,7 @@ class exports.Rewriter tokens.splice i - 2, 1 else tokens.splice i, 0, after - else if prev?[0] not in ['TERMINATOR', 'INDENT', 'OUTDENT'] + else if prev and prev[0] not in ['TERMINATOR', 'INDENT', 'OUTDENT'] if post?[0] is 'TERMINATOR' and after?[0] is 'OUTDENT' tokens.splice i + 2, 0, tokens.splice(i, 2)... if tokens[i + 2][0] isnt 'TERMINATOR' diff --git a/test/test_heredocs.coffee b/test/test_heredocs.coffee index 5c727483..330ecdf7 100644 --- a/test/test_heredocs.coffee +++ b/test/test_heredocs.coffee @@ -89,8 +89,7 @@ a = """ """ ok a is "one\ntwo\n" - -equal ''' line 0 +eq ''' line 0 should not be relevant to the indent level ''', ' @@ -99,10 +98,14 @@ should not be relevant\n to the indent level ' +eq ''' '\\\' ''', " '\\' " +eq """ "\\\" """, ' "\\" ' -equal 'multiline nested interpolations work', """multiline #{ +eq ''' <- keep these spaces -> ''', ' <- keep these spaces -> ' + +eq 'multiline nested "interpolations" work', """multiline #{ "nested #{(-> ok yes - "interpolations" + "\"interpolations\"" )()}" } work""" diff --git a/test/test_regexp_interpolation.coffee b/test/test_regexp_interpolation.coffee deleted file mode 100644 index 95b921cb..00000000 --- a/test/test_regexp_interpolation.coffee +++ /dev/null @@ -1,20 +0,0 @@ -# Interpolate regular expressions. -name = 'Moe' - -ok not not '"Moe"'.match(/^"#{name}"$/i) -ok '"Moe!"'.match(/^"#{name}"$/i) is null - -ok not not 'Moe'.match(/^#{name}$/) -ok 'Moe!'.match(/^#{name}/) - -ok 'Moe!'.match(/#{"#{"#{"#{name}"}"}"}/imgy) - -ok '$a$b$c'.match(/\$A\$B\$C/i) - -a = 1 -b = 2 -c = 3 -ok '123'.match(/#{a}#{b}#{c}/i) - -[a, b, c] = [1, 2, /\d+/] -ok (/#{a}#{b}#{c}$/i).toString() is '/12/\\d+/$/i' diff --git a/test/test_regexps.coffee b/test/test_regexps.coffee index 4b63f67a..e78e89de 100644 --- a/test/test_regexps.coffee +++ b/test/test_regexps.coffee @@ -12,7 +12,7 @@ g = 1 ok y / x/g is 2 -ok 'http://google.com'.match(/:\/\/goog/) +ok /:\/[/]goog/.test 'http://google.com' obj = { width: -> 10 @@ -20,9 +20,17 @@ obj = { } id = 2 -ok ' '.match(/ /)[0] is ' ' - -regexp = / / -ok ' '.match regexp - ok (obj.width()/id - obj.height()/id) is -5 + +eq /^I'm\s+Heregex?\/\/\//gim + '', /// + ^ I'm \s+ Heregex? / // # or not +///gim + '' +eq '\\\\#{}\\\\\\\"', /// + #{ + "#{ '\\' }" # normal comment + } + # regex comment + \#{} + \\ \" +///.source +eq /// /// + '', '/(?:)/'