diff --git a/lib/coffee-script.js b/lib/coffee-script.js index 95d31302..883864f0 100755 --- a/lib/coffee-script.js +++ b/lib/coffee-script.js @@ -53,7 +53,7 @@ __dirname = path.dirname(__filename); return eval(exports.compile(code, options)); }; - lexer = new Lexer(); + lexer = new Lexer; parser.lexer = { lex: function() { var token; diff --git a/lib/command.js b/lib/command.js index ed44defa..44c31232 100644 --- a/lib/command.js +++ b/lib/command.js @@ -11,7 +11,7 @@ exec = _ref.exec; _ref = require('events'); EventEmitter = _ref.EventEmitter; - helpers.extend(CoffeeScript, new EventEmitter()); + helpers.extend(CoffeeScript, new EventEmitter); global.CoffeeScript = CoffeeScript; BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee'; SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JSLint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['--no-wrap', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']]; diff --git a/lib/grammar.js b/lib/grammar.js index cbd9e0a4..9e6e5123 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -15,9 +15,9 @@ grammar = { Root: [ o("", function() { - return new Expressions(); + return new Expressions; }), o("TERMINATOR", function() { - return new Expressions(); + return new Expressions; }), o("Body"), o("Block TERMINATOR") ], Body: [ @@ -37,12 +37,12 @@ return new LiteralNode($1); }) ], - Expression: [o("Value"), o("Call"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("While"), o("For"), o("Switch"), o("Extends"), o("Class"), o("Existence"), o("Comment")], + Expression: [o("Value"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("While"), o("For"), o("Switch"), o("Extends"), o("Class"), o("Existence"), o("Comment")], Block: [ o("INDENT Body OUTDENT", function() { return $2; }), o("INDENT OUTDENT", function() { - return new Expressions(); + return new Expressions; }), o("TERMINATOR Comment", function() { return Expressions.wrap([$2]); }) @@ -257,13 +257,6 @@ return $2; }) ], - Call: [ - o("Invocation"), o("NEW Invocation", function() { - return $2.newInstance(); - }), o("NEW Value", function() { - return (new CallNode($2, [])).newInstance(); - }) - ], Extends: [ o("SimpleAssignable EXTENDS Value", function() { return new ExtendsNode($1, $3); @@ -611,7 +604,7 @@ }) ] }; - operators = [["right", '?', 'NEW'], ["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'INSTANCEOF'], ["left", '==', '!='], ["left", 'LOGIC'], ["right", 'COMPOUND_ASSIGN'], ["left", '.'], ["nonassoc", 'INDENT', 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']]; + operators = [["right", '?'], ["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'INSTANCEOF'], ["left", '==', '!='], ["left", 'LOGIC'], ["right", 'COMPOUND_ASSIGN'], ["left", '.'], ["nonassoc", 'INDENT', 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']]; tokens = []; _ref = grammar; for (name in _ref) { diff --git a/lib/lexer.js b/lib/lexer.js index 22f1c388..22c14027 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -29,7 +29,7 @@ if (o.rewrite === false) { return this.tokens; } - return (new Rewriter()).rewrite(this.tokens); + return (new Rewriter).rewrite(this.tokens); }; Lexer.prototype.extractNextToken = function() { return this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken(); @@ -185,7 +185,7 @@ if (REGEX_INTERPOLATION.test(regex)) { str = regex.slice(1, -1); str = str.replace(REGEX_ESCAPE, '\\$&'); - this.tokens.push(['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']); + this.tokens.push(['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']); this.interpolateString("\"" + (str) + "\"", { escapeQuotes: true }); @@ -474,7 +474,7 @@ if (str.length < 3 || str.charAt(0) !== '"') { return this.token('STRING', str); } else { - lexer = new Lexer(); + lexer = new Lexer; tokens = []; quote = str.charAt(0); _ref2 = [1, 1]; @@ -605,12 +605,12 @@ REGEX_END = /^[imgy]{0,4}(?![a-zA-Z])/; REGEX_ESCAPE = /\\[^#]/g; MULTILINER = /\n/g; - NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|not|delete|typeof|instanceof)$/; + NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/; HEREDOC_INDENT = /\n+([ \t]*)|^([ \t]+)/g; ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/; NEXT_CHARACTER = /^\s*(\S)/; COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']; - UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE']; + UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'NEW', 'TYPEOF', 'DELETE']; LOGIC = ['&', '|', '^', '&&', '||']; SHIFT = ['<<', '>>', '>>>']; COMPARE = ['<=', '<', '>', '>=']; diff --git a/lib/nodes.js b/lib/nodes.js index 7d604ee5..20d5d12c 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -868,7 +868,7 @@ this.variable = literal(o.scope.freeVariable('ctor')); } extension = this.parent && new ExtendsNode(this.variable, this.parent); - props = new Expressions(); + props = new Expressions; o.top = true; me = null; className = this.variable.compile(o); @@ -877,7 +877,7 @@ applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]); constructor = new CodeNode([], new Expressions([new CallNode(applied, [literal('this'), literal('arguments')])])); } else { - constructor = new CodeNode(); + constructor = new CodeNode; } _ref2 = this.properties; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { @@ -1055,7 +1055,7 @@ this.params = _arg; CodeNode.__super__.constructor.call(this); this.params || (this.params = []); - this.body || (this.body = new Expressions()); + this.body || (this.body = (new Expressions)); this.bound = tag === 'boundfunc'; if (this.bound) { this.context = 'this'; @@ -1311,6 +1311,8 @@ this.flip = !!flip; if (this.first instanceof ValueNode && this.first.base instanceof ObjectNode) { this.first = new ParentheticalNode(this.first); + } else if (this.operator === 'new' && this.first instanceof CallNode) { + return this.first.newInstance(); } this.first.tags.operation = true; if (this.second) { @@ -1329,7 +1331,7 @@ }; OpNode.prototype.CHAINABLE = ['<', '>', '>=', '<=', '===', '!==']; OpNode.prototype.ASSIGNMENT = ['||=', '&&=', '?=']; - OpNode.prototype.PREFIX_OPERATORS = ['typeof', 'delete']; + OpNode.prototype.PREFIX_OPERATORS = ['new', 'typeof', 'delete']; OpNode.prototype["class"] = 'OpNode'; OpNode.prototype.children = ['first', 'second']; OpNode.prototype.isUnary = function() { diff --git a/lib/parser.js b/lib/parser.js index 32c603f6..1b310861 100755 --- a/lib/parser.js +++ b/lib/parser.js @@ -9,9 +9,9 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy) { var $$ = arguments[5],$0=arguments[5].length; switch(arguments[4]) { -case 1:return this.$ = new yy.Expressions(); +case 1:return this.$ = new yy.Expressions; break; -case 2:return this.$ = new yy.Expressions(); +case 2:return this.$ = new yy.Expressions; break; case 3:return this.$ = $$[$0-1+1-1]; break; @@ -67,7 +67,7 @@ case 28:this.$ = $$[$0-1+1-1]; break; case 29:this.$ = $$[$0-3+2-1]; break; -case 30:this.$ = new yy.Expressions(); +case 30:this.$ = new yy.Expressions; break; case 31:this.$ = yy.Expressions.wrap([$$[$0-2+2-1]]); break; @@ -701,7 +701,7 @@ exports.main = function commonjsMain(args) { } return exports.parser.parse(source); } -if (typeof module !== "undefined" && require.main === module) { +if (typeof module !== 'undefined' && require.main === module) { exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); } } \ No newline at end of file diff --git a/src/grammar.coffee b/src/grammar.coffee index 9413a7c7..2385da66 100644 --- a/src/grammar.coffee +++ b/src/grammar.coffee @@ -88,7 +88,6 @@ grammar = # them somewhat circular. Expression: [ o "Value" - o "Call" o "Code" o "Operation" o "Assign" @@ -298,13 +297,6 @@ grammar = o "{ ClassBody }", -> $2 ] - # The two flavors of function call: normal, and object instantiation with `new`. - Call: [ - o "Invocation" - o "NEW Invocation", -> $2.newInstance() - o "NEW Value", -> (new CallNode($2, [])).newInstance() - ] - # Extending an object by setting its prototype chain to reference a parent # object. Extends: [ @@ -575,7 +567,7 @@ grammar = # # (2 + 3) * 4 operators = [ - ["right", '?', 'NEW'] + ["right", '?'] ["left", 'CALL_START', 'CALL_END'] ["nonassoc", '++', '--'] ["right", 'UNARY'] diff --git a/src/lexer.coffee b/src/lexer.coffee index 0cc35db4..170f2d9e 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -178,7 +178,7 @@ exports.Lexer = class Lexer if REGEX_INTERPOLATION.test regex str = regex.slice 1, -1 str = str.replace REGEX_ESCAPE, '\\$&' - @tokens.push ['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '('] + @tokens.push ['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '('] @interpolateString "\"#{str}\"", escapeQuotes: yes @tokens.push [',', ','], ['STRING', "\"#{flags}\""] if flags @tokens.push [')', ')'], [')', ')'] @@ -553,7 +553,7 @@ REGEX_ESCAPE = /\\[^#]/g # Token cleaning regexes. MULTILINER = /\n/g -NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|not|delete|typeof|instanceof)$/ +NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/ HEREDOC_INDENT = /\n+([ \t]*)|^([ \t]+)/g ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/ NEXT_CHARACTER = /^\s*(\S)/ @@ -562,7 +562,7 @@ NEXT_CHARACTER = /^\s*(\S)/ COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='] # Unary tokens. -UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE'] +UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'NEW', 'TYPEOF', 'DELETE'] # Logical tokens. LOGIC = ['&', '|', '^', '&&', '||'] diff --git a/src/nodes.coffee b/src/nodes.coffee index 941a446c..a5e1d75b 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1121,7 +1121,7 @@ exports.OpNode = class OpNode extends BaseNode ASSIGNMENT: ['||=', '&&=', '?='] # Operators must come before their operands with a space. - PREFIX_OPERATORS: ['typeof', 'delete'] + PREFIX_OPERATORS: ['new', 'typeof', 'delete'] class: 'OpNode' children: ['first', 'second'] @@ -1132,6 +1132,8 @@ exports.OpNode = class OpNode extends BaseNode @flip = !!flip if @first instanceof ValueNode and @first.base instanceof ObjectNode @first = new ParentheticalNode @first + else if @operator is 'new' and @first instanceof CallNode + return @first.newInstance() @first.tags.operation = yes @second.tags.operation = yes if @second diff --git a/test/test_operations.coffee b/test/test_operations.coffee index 357ea581..5ca720c4 100644 --- a/test/test_operations.coffee +++ b/test/test_operations.coffee @@ -156,5 +156,6 @@ ok c is 3 # Instanceof. -ok new String instanceof String -ok new Number not instanceof String +# FIXME: These parentheses are workaround of # +ok (new String) instanceof String +ok (new Number) not instanceof String