(experimental) made `new` a unary operator

This commit is contained in:
satyr 2010-09-25 17:39:19 +09:00
parent 9a3b736174
commit c24e1eacb9
10 changed files with 32 additions and 42 deletions

View File

@ -53,7 +53,7 @@
__dirname = path.dirname(__filename); __dirname = path.dirname(__filename);
return eval(exports.compile(code, options)); return eval(exports.compile(code, options));
}; };
lexer = new Lexer(); lexer = new Lexer;
parser.lexer = { parser.lexer = {
lex: function() { lex: function() {
var token; var token;

View File

@ -11,7 +11,7 @@
exec = _ref.exec; exec = _ref.exec;
_ref = require('events'); _ref = require('events');
EventEmitter = _ref.EventEmitter; EventEmitter = _ref.EventEmitter;
helpers.extend(CoffeeScript, new EventEmitter()); helpers.extend(CoffeeScript, new EventEmitter);
global.CoffeeScript = CoffeeScript; global.CoffeeScript = CoffeeScript;
BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee'; 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']]; 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']];

View File

@ -15,9 +15,9 @@
grammar = { grammar = {
Root: [ Root: [
o("", function() { o("", function() {
return new Expressions(); return new Expressions;
}), o("TERMINATOR", function() { }), o("TERMINATOR", function() {
return new Expressions(); return new Expressions;
}), o("Body"), o("Block TERMINATOR") }), o("Body"), o("Block TERMINATOR")
], ],
Body: [ Body: [
@ -37,12 +37,12 @@
return new LiteralNode($1); 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: [ Block: [
o("INDENT Body OUTDENT", function() { o("INDENT Body OUTDENT", function() {
return $2; return $2;
}), o("INDENT OUTDENT", function() { }), o("INDENT OUTDENT", function() {
return new Expressions(); return new Expressions;
}), o("TERMINATOR Comment", function() { }), o("TERMINATOR Comment", function() {
return Expressions.wrap([$2]); return Expressions.wrap([$2]);
}) })
@ -257,13 +257,6 @@
return $2; return $2;
}) })
], ],
Call: [
o("Invocation"), o("NEW Invocation", function() {
return $2.newInstance();
}), o("NEW Value", function() {
return (new CallNode($2, [])).newInstance();
})
],
Extends: [ Extends: [
o("SimpleAssignable EXTENDS Value", function() { o("SimpleAssignable EXTENDS Value", function() {
return new ExtendsNode($1, $3); 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 = []; tokens = [];
_ref = grammar; _ref = grammar;
for (name in _ref) { for (name in _ref) {

View File

@ -29,7 +29,7 @@
if (o.rewrite === false) { if (o.rewrite === false) {
return this.tokens; return this.tokens;
} }
return (new Rewriter()).rewrite(this.tokens); return (new Rewriter).rewrite(this.tokens);
}; };
Lexer.prototype.extractNextToken = function() { 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(); 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)) { if (REGEX_INTERPOLATION.test(regex)) {
str = regex.slice(1, -1); str = regex.slice(1, -1);
str = str.replace(REGEX_ESCAPE, '\\$&'); str = str.replace(REGEX_ESCAPE, '\\$&');
this.tokens.push(['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']); this.tokens.push(['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']);
this.interpolateString("\"" + (str) + "\"", { this.interpolateString("\"" + (str) + "\"", {
escapeQuotes: true escapeQuotes: true
}); });
@ -474,7 +474,7 @@
if (str.length < 3 || str.charAt(0) !== '"') { if (str.length < 3 || str.charAt(0) !== '"') {
return this.token('STRING', str); return this.token('STRING', str);
} else { } else {
lexer = new Lexer(); lexer = new Lexer;
tokens = []; tokens = [];
quote = str.charAt(0); quote = str.charAt(0);
_ref2 = [1, 1]; _ref2 = [1, 1];
@ -605,12 +605,12 @@
REGEX_END = /^[imgy]{0,4}(?![a-zA-Z])/; REGEX_END = /^[imgy]{0,4}(?![a-zA-Z])/;
REGEX_ESCAPE = /\\[^#]/g; REGEX_ESCAPE = /\\[^#]/g;
MULTILINER = /\n/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; HEREDOC_INDENT = /\n+([ \t]*)|^([ \t]+)/g;
ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/; ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/;
NEXT_CHARACTER = /^\s*(\S)/; NEXT_CHARACTER = /^\s*(\S)/;
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']; COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE']; UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'NEW', 'TYPEOF', 'DELETE'];
LOGIC = ['&', '|', '^', '&&', '||']; LOGIC = ['&', '|', '^', '&&', '||'];
SHIFT = ['<<', '>>', '>>>']; SHIFT = ['<<', '>>', '>>>'];
COMPARE = ['<=', '<', '>', '>=']; COMPARE = ['<=', '<', '>', '>='];

View File

@ -868,7 +868,7 @@
this.variable = literal(o.scope.freeVariable('ctor')); this.variable = literal(o.scope.freeVariable('ctor'));
} }
extension = this.parent && new ExtendsNode(this.variable, this.parent); extension = this.parent && new ExtendsNode(this.variable, this.parent);
props = new Expressions(); props = new Expressions;
o.top = true; o.top = true;
me = null; me = null;
className = this.variable.compile(o); className = this.variable.compile(o);
@ -877,7 +877,7 @@
applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]); applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]);
constructor = new CodeNode([], new Expressions([new CallNode(applied, [literal('this'), literal('arguments')])])); constructor = new CodeNode([], new Expressions([new CallNode(applied, [literal('this'), literal('arguments')])]));
} else { } else {
constructor = new CodeNode(); constructor = new CodeNode;
} }
_ref2 = this.properties; _ref2 = this.properties;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) { for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
@ -1055,7 +1055,7 @@
this.params = _arg; this.params = _arg;
CodeNode.__super__.constructor.call(this); CodeNode.__super__.constructor.call(this);
this.params || (this.params = []); this.params || (this.params = []);
this.body || (this.body = new Expressions()); this.body || (this.body = (new Expressions));
this.bound = tag === 'boundfunc'; this.bound = tag === 'boundfunc';
if (this.bound) { if (this.bound) {
this.context = 'this'; this.context = 'this';
@ -1311,6 +1311,8 @@
this.flip = !!flip; this.flip = !!flip;
if (this.first instanceof ValueNode && this.first.base instanceof ObjectNode) { if (this.first instanceof ValueNode && this.first.base instanceof ObjectNode) {
this.first = new ParentheticalNode(this.first); this.first = new ParentheticalNode(this.first);
} else if (this.operator === 'new' && this.first instanceof CallNode) {
return this.first.newInstance();
} }
this.first.tags.operation = true; this.first.tags.operation = true;
if (this.second) { if (this.second) {
@ -1329,7 +1331,7 @@
}; };
OpNode.prototype.CHAINABLE = ['<', '>', '>=', '<=', '===', '!==']; OpNode.prototype.CHAINABLE = ['<', '>', '>=', '<=', '===', '!=='];
OpNode.prototype.ASSIGNMENT = ['||=', '&&=', '?=']; OpNode.prototype.ASSIGNMENT = ['||=', '&&=', '?='];
OpNode.prototype.PREFIX_OPERATORS = ['typeof', 'delete']; OpNode.prototype.PREFIX_OPERATORS = ['new', 'typeof', 'delete'];
OpNode.prototype["class"] = 'OpNode'; OpNode.prototype["class"] = 'OpNode';
OpNode.prototype.children = ['first', 'second']; OpNode.prototype.children = ['first', 'second'];
OpNode.prototype.isUnary = function() { OpNode.prototype.isUnary = function() {

View File

@ -9,9 +9,9 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy) {
var $$ = arguments[5],$0=arguments[5].length; var $$ = arguments[5],$0=arguments[5].length;
switch(arguments[4]) { switch(arguments[4]) {
case 1:return this.$ = new yy.Expressions(); case 1:return this.$ = new yy.Expressions;
break; break;
case 2:return this.$ = new yy.Expressions(); case 2:return this.$ = new yy.Expressions;
break; break;
case 3:return this.$ = $$[$0-1+1-1]; case 3:return this.$ = $$[$0-1+1-1];
break; break;
@ -67,7 +67,7 @@ case 28:this.$ = $$[$0-1+1-1];
break; break;
case 29:this.$ = $$[$0-3+2-1]; case 29:this.$ = $$[$0-3+2-1];
break; break;
case 30:this.$ = new yy.Expressions(); case 30:this.$ = new yy.Expressions;
break; break;
case 31:this.$ = yy.Expressions.wrap([$$[$0-2+2-1]]); case 31:this.$ = yy.Expressions.wrap([$$[$0-2+2-1]]);
break; break;
@ -701,7 +701,7 @@ exports.main = function commonjsMain(args) {
} }
return exports.parser.parse(source); 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); exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
} }
} }

View File

@ -88,7 +88,6 @@ grammar =
# them somewhat circular. # them somewhat circular.
Expression: [ Expression: [
o "Value" o "Value"
o "Call"
o "Code" o "Code"
o "Operation" o "Operation"
o "Assign" o "Assign"
@ -298,13 +297,6 @@ grammar =
o "{ ClassBody }", -> $2 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 # Extending an object by setting its prototype chain to reference a parent
# object. # object.
Extends: [ Extends: [
@ -575,7 +567,7 @@ grammar =
# #
# (2 + 3) * 4 # (2 + 3) * 4
operators = [ operators = [
["right", '?', 'NEW'] ["right", '?']
["left", 'CALL_START', 'CALL_END'] ["left", 'CALL_START', 'CALL_END']
["nonassoc", '++', '--'] ["nonassoc", '++', '--']
["right", 'UNARY'] ["right", 'UNARY']

View File

@ -178,7 +178,7 @@ exports.Lexer = class Lexer
if REGEX_INTERPOLATION.test regex if REGEX_INTERPOLATION.test regex
str = regex.slice 1, -1 str = regex.slice 1, -1
str = str.replace REGEX_ESCAPE, '\\$&' str = str.replace REGEX_ESCAPE, '\\$&'
@tokens.push ['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '('] @tokens.push ['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']
@interpolateString "\"#{str}\"", escapeQuotes: yes @interpolateString "\"#{str}\"", escapeQuotes: yes
@tokens.push [',', ','], ['STRING', "\"#{flags}\""] if flags @tokens.push [',', ','], ['STRING', "\"#{flags}\""] if flags
@tokens.push [')', ')'], [')', ')'] @tokens.push [')', ')'], [')', ')']
@ -553,7 +553,7 @@ REGEX_ESCAPE = /\\[^#]/g
# Token cleaning regexes. # Token cleaning regexes.
MULTILINER = /\n/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 HEREDOC_INDENT = /\n+([ \t]*)|^([ \t]+)/g
ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/ ASSIGNED = /^\s*((?:[a-zA-Z$_@]\w*|["'][^\n]+?["']|\d+)[ \t]*?[:=][^:=])/
NEXT_CHARACTER = /^\s*(\S)/ NEXT_CHARACTER = /^\s*(\S)/
@ -562,7 +562,7 @@ NEXT_CHARACTER = /^\s*(\S)/
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='] COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']
# Unary tokens. # Unary tokens.
UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE'] UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'NEW', 'TYPEOF', 'DELETE']
# Logical tokens. # Logical tokens.
LOGIC = ['&', '|', '^', '&&', '||'] LOGIC = ['&', '|', '^', '&&', '||']

View File

@ -1121,7 +1121,7 @@ exports.OpNode = class OpNode extends BaseNode
ASSIGNMENT: ['||=', '&&=', '?='] ASSIGNMENT: ['||=', '&&=', '?=']
# Operators must come before their operands with a space. # Operators must come before their operands with a space.
PREFIX_OPERATORS: ['typeof', 'delete'] PREFIX_OPERATORS: ['new', 'typeof', 'delete']
class: 'OpNode' class: 'OpNode'
children: ['first', 'second'] children: ['first', 'second']
@ -1132,6 +1132,8 @@ exports.OpNode = class OpNode extends BaseNode
@flip = !!flip @flip = !!flip
if @first instanceof ValueNode and @first.base instanceof ObjectNode if @first instanceof ValueNode and @first.base instanceof ObjectNode
@first = new ParentheticalNode @first @first = new ParentheticalNode @first
else if @operator is 'new' and @first instanceof CallNode
return @first.newInstance()
@first.tags.operation = yes @first.tags.operation = yes
@second.tags.operation = yes if @second @second.tags.operation = yes if @second

View File

@ -156,5 +156,6 @@ ok c is 3
# Instanceof. # Instanceof.
ok new String instanceof String # FIXME: These parentheses are workaround of #
ok new Number not instanceof String ok (new String) instanceof String
ok (new Number) not instanceof String