got about as far as I think I can with operator nodes for now, without zach's optimizations

This commit is contained in:
Jeremy Ashkenas 2010-02-10 00:05:56 -05:00
parent 9339058fc3
commit 76dac9c09c
5 changed files with 223 additions and 122 deletions

View File

@ -1,5 +1,5 @@
(function(){
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, Expressions, ExtendsNode, IndexNode, LiteralNode, Node, ObjectNode, OpNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ValueNode, WhileNode, any, compact, del, dup, flatten, inherit, merge, statement;
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, Expressions, ExtendsNode, IDENTIFIER, IndexNode, LiteralNode, Node, ObjectNode, OpNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ValueNode, WhileNode, any, compact, del, dup, flatten, inherit, merge, statement;
var __hasProp = Object.prototype.hasOwnProperty;
process.mixin(require('./scope'));
// The abstract base class for all CoffeeScript nodes.
@ -151,6 +151,8 @@
// Tabs are two spaces for pretty printing.
TAB = ' ';
TRAILING_WHITESPACE = /\s+$/g;
// Keep the identifier regex in sync with the Lexer.
IDENTIFIER = /^[a-zA-Z$_](\w|\$)*$/;
// Flatten nested arrays recursively.
flatten = function flatten(list) {
var __a, __b, item, memo;
@ -842,8 +844,6 @@
});
// Setting the value of a local variable, or the value of an object property.
AssignNode = (exports.AssignNode = inherit(Node, {
// Keep the identifier regex in sync with the Lexer.
IDENTIFIER: /^([a-zA-Z$_](\w|\$)*)/,
PROTO_ASSIGN: /^(\S+)\.prototype/,
LEADING_DOT: /^\.(prototype\.)?/,
constructor: function constructor(variable, value, context) {
@ -875,7 +875,7 @@
match = name.match(this.PROTO_ASSIGN);
proto = match && match[1];
if (this.value instanceof CodeNode) {
if (last.match(this.IDENTIFIER)) {
if (last.match(IDENTIFIER)) {
this.value.name = last;
}
if (proto) {
@ -1058,9 +1058,60 @@
return this.CHAINABLE.indexOf(this.operator) >= 0;
},
compile_node: function compile_node(o) {
if (this.is_chainable() && (this.first.unwrap() instanceof OpNode) && this.first.unwrap().is_chainable()) {
if (this.is_chainable() && this.first.unwrap() instanceof OpNode && this.first.unwrap().is_chainable()) {
return this.compile_chain(o);
}
if (this.ASSIGNMENT.indexOf(this.operator) >= 0) {
return this.compile_assignment(o);
}
if (this.is_unary()) {
return this.compile_unary(o);
}
if (this.operator === '?') {
return this.compile_existence(o);
}
return this.first.compile(o) + ' ' + this.operator + ' ' + this.second.compile(o);
},
// Mimic Python's chained comparisons. See:
// http://docs.python.org/reference/expressions.html#notin
compile_chain: function compile_chain(o) {
var __a, shared;
shared = this.first.unwrap().second;
if (shared instanceof CallNode) {
__a = shared.compile_reference(o);
this.first.second = __a[0];
shared = __a[1];
}
return '(' + this.first.compile(o) + ') && (' + shared.compile(o) + ' ' + this.operator + ' ' + this.second.compile(o) + ')';
},
compile_assignment: function compile_assignment(o) {
var __a, first, second;
__a = [this.first.compile(o), this.second.compile(o)];
first = __a[0];
second = __a[1];
if (this.first.unwrap.match(IDENTIFIER)) {
o.scope.find(first);
}
if (this.operator === '?=') {
return first + ' = ' + ExistenceNode.compile_test(o, this.first) + ' ? ' + first + ' : ' + second;
}
return first + ' = ' + first + ' ' + this.operator.substr(0, 2) + ' ' + second;
},
compile_existence: function compile_existence(o) {
var __a, first, second;
__a = [this.first.compile(o), this.second.compile(o)];
first = __a[0];
second = __a[1];
return ExistenceNode.compile_test(o, this.first) + ' ? ' + first + ' : ' + second;
},
compile_unary: function compile_unary(o) {
var parts, space;
space = this.PREFIX_OPERATORS.indexOf(this.operator) >= 0 ? ' ' : '';
parts = [this.operator, space, this.first.compile(o)];
if (this.flip) {
parts = parts.reverse();
}
return parts.join('');
}
}));
})();

View File

@ -783,9 +783,8 @@ module CoffeeScript
def compile_assignment(o)
first, second = @first.compile(o), @second.compile(o)
o[:scope].find(first) if @first.unwrap.is_a?(Value)
sym = @operator[0..1]
return "#{first} = #{ExistenceNode.compile_test(o, @first)} ? #{first} : #{second}" if @operator == '?='
"#{first} = #{first} #{sym} #{second}"
"#{first} = #{first} #{@operator[0..1]} #{second}"
end
def compile_existence(o)

View File

@ -7,17 +7,17 @@
// Detect functions: [
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
// Quickie DSL for Jison access.
o = function o(pattern_string, func) {
o = function o(pattern_string, func, options) {
var match;
if (func) {
func = (match = (func + "").match(unwrap)) ? match[1] : '(' + func + '())';
return [pattern_string, '$$ = ' + func + ';'];
return [pattern_string, '$$ = ' + func + ';', options];
} else {
return [pattern_string, '$$ = $1;'];
return [pattern_string, '$$ = $1;', options];
}
};
// Precedence ===========================================================
operators = [["left", '?'], ["right", 'NOT', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["right", '==', '!=', 'IS', 'ISNT'], ["left", '&&', '||', 'AND', 'OR'], ["right", '-=', '+=', '/=', '*=', '%='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY'], ["right", 'THROW', 'FOR', 'NEW', 'SUPER'], ["left", 'EXTENDS'], ["left", '||=', '&&=', '?='], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE', 'WHILE']];
operators = [["left", '?'], ["right", 'UMINUS', 'UPLUS', 'NOT', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["right", '==', '!=', 'IS', 'ISNT'], ["left", '&&', '||', 'AND', 'OR'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY'], ["right", 'THROW', 'FOR', 'NEW', 'SUPER'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE', 'WHILE']];
// Grammar ==============================================================
grammar = {
// All parsing will end in this rule, being the trunk of the AST.
@ -106,67 +106,92 @@
return new CommentNode(yytext);
})
],
//
// # Arithmetic and logical operators
// # For Ruby's Operator precedence, see: [
// # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
// Operation: [
// o "! Expression", -> new OpNode($1, $2)
// o "!! Expression", -> new OpNode($1, $2)
// o "- Expression", -> new OpNode($1, $2)
// o "+ Expression", -> new OpNode($1, $2)
// o "NOT Expression", -> new OpNode($1, $2)
// o "~ Expression", -> new OpNode($1, $2)
// o "-- Expression", -> new OpNode($1, $2)
// o "++ Expression", -> new OpNode($1, $2)
// o "DELETE Expression", -> new OpNode($1, $2)
// o "TYPEOF Expression", -> new OpNode($1, $2)
// o "Expression --", -> new OpNode($2, $1, null, true)
// o "Expression ++", -> new OpNode($2, $1, null, true)
//
// o "Expression * Expression", -> new OpNode($2, $1, $3)
// o "Expression / Expression", -> new OpNode($2, $1, $3)
// o "Expression % Expression", -> new OpNode($2, $1, $3)
//
// o "Expression + Expression", -> new OpNode($2, $1, $3)
// o "Expression - Expression", -> new OpNode($2, $1, $3)
//
// o "Expression << Expression", -> new OpNode($2, $1, $3)
// o "Expression >> Expression", -> new OpNode($2, $1, $3)
// o "Expression >>> Expression", -> new OpNode($2, $1, $3)
//
// o "Expression & Expression", -> new OpNode($2, $1, $3)
// o "Expression | Expression", -> new OpNode($2, $1, $3)
// o "Expression ^ Expression", -> new OpNode($2, $1, $3)
//
// o "Expression <= Expression", -> new OpNode($2, $1, $3)
// o "Expression < Expression", -> new OpNode($2, $1, $3)
// o "Expression > Expression", -> new OpNode($2, $1, $3)
// o "Expression >= Expression", -> new OpNode($2, $1, $3)
//
// o "Expression == Expression", -> new OpNode($2, $1, $3)
// o "Expression != Expression", -> new OpNode($2, $1, $3)
// o "Expression IS Expression", -> new OpNode($2, $1, $3)
// o "Expression ISNT Expression", -> new OpNode($2, $1, $3)
//
// o "Expression && Expression", -> new OpNode($2, $1, $3)
// o "Expression || Expression", -> new OpNode($2, $1, $3)
// o "Expression AND Expression", -> new OpNode($2, $1, $3)
// o "Expression OR Expression", -> new OpNode($2, $1, $3)
// o "Expression ? Expression", -> new OpNode($2, $1, $3)
//
// o "Expression -= Expression", -> new OpNode($2, $1, $3)
// o "Expression += Expression", -> new OpNode($2, $1, $3)
// o "Expression /= Expression", -> new OpNode($2, $1, $3)
// o "Expression *= Expression", -> new OpNode($2, $1, $3)
// o "Expression %= Expression", -> new OpNode($2, $1, $3)
// o "Expression ||= Expression", -> new OpNode($2, $1, $3)
// o "Expression &&= Expression", -> new OpNode($2, $1, $3)
// o "Expression ?= Expression", -> new OpNode($2, $1, $3)
//
// o "Expression INSTANCEOF Expression", -> new OpNode($2, $1, $3)
// o "Expression IN Expression", -> new OpNode($2, $1, $3)
// ]
// Arithmetic and logical operators
// For Ruby's Operator precedence, see: [
// https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
Operation: [o("! Expression", function() {
return new OpNode('!', $2);
}), o("!! Expression", function() {
return new OpNode('!!', $2);
}), o("- Expression", (function() {
return new OpNode('-', $2);
}), {
prec: 'UMINUS'
}), o("+ Expression", (function() {
return new OpNode('+', $2);
}), {
prec: 'UPLUS'
}), o("NOT Expression", function() {
return new OpNode('not', $2);
}), o("~ Expression", function() {
return new OpNode('~', $2);
}), o("-- Expression", function() {
return new OpNode('--', $2);
}), o("++ Expression", function() {
return new OpNode('++', $2);
}), o("DELETE Expression", function() {
return new OpNode('delete', $2);
}), o("TYPEOF Expression", function() {
return new OpNode('typeof', $2);
}), o("Expression --", function() {
return new OpNode('--', $1, null, true);
}), o("Expression ++", function() {
return new OpNode('++', $1, null, true);
}), o("Expression * Expression", function() {
return new OpNode('*', $1, $3);
}), o("Expression / Expression", function() {
return new OpNode('/', $1, $3);
}), o("Expression % Expression", function() {
return new OpNode('%', $1, $3);
}), o("Expression + Expression", function() {
return new OpNode('+', $1, $3);
}), o("Expression - Expression", function() {
return new OpNode('-', $1, $3);
}), o("Expression << Expression", function() {
return new OpNode('<<', $1, $3);
}), o("Expression >> Expression", function() {
return new OpNode('>>', $1, $3);
}), o("Expression >>> Expression", function() {
return new OpNode('>>>', $1, $3);
}), o("Expression & Expression", function() {
return new OpNode('&', $1, $3);
}), o("Expression | Expression", function() {
return new OpNode('|', $1, $3);
}), o("Expression ^ Expression", function() {
return new OpNode('^', $1, $3);
}), o("Expression <= Expression", function() {
return new OpNode('<=', $1, $3);
}), o("Expression < Expression", function() {
return new OpNode('<', $1, $3);
}), o("Expression > Expression", function() {
return new OpNode('>', $1, $3);
}), o("Expression >= Expression", function() {
return new OpNode('>=', $1, $3);
}), o("Expression == Expression", function() {
return new OpNode('==', $1, $3);
}),
// o "Expression != Expression", -> new OpNode($2, $1, $3)
// o "Expression IS Expression", -> new OpNode($2, $1, $3)
// o "Expression ISNT Expression", -> new OpNode($2, $1, $3)
//
// o "Expression && Expression", -> new OpNode($2, $1, $3)
// o "Expression || Expression", -> new OpNode($2, $1, $3)
// o "Expression AND Expression", -> new OpNode($2, $1, $3)
// o "Expression OR Expression", -> new OpNode($2, $1, $3)
// o "Expression ? Expression", -> new OpNode($2, $1, $3)
//
// o "Expression -= Expression", -> new OpNode($2, $1, $3)
// o "Expression += Expression", -> new OpNode($2, $1, $3)
// o "Expression /= Expression", -> new OpNode($2, $1, $3)
// o "Expression *= Expression", -> new OpNode($2, $1, $3)
// o "Expression %= Expression", -> new OpNode($2, $1, $3)
// o "Expression ||= Expression", -> new OpNode($2, $1, $3)
// o "Expression &&= Expression", -> new OpNode($2, $1, $3)
// o "Expression ?= Expression", -> new OpNode($2, $1, $3)
//
// o "Expression INSTANCEOF Expression", -> new OpNode($2, $1, $3)
// o "Expression IN Expression", -> new OpNode($2, $1, $3)
],
// The existence operator.
Existence: [o("Expression ?", function() {
return new ExistenceNode($1);

View File

@ -44,6 +44,9 @@ exports.Expressions.wrap : (values) -> @values: values
TAB: ' '
TRAILING_WHITESPACE: /\s+$/g
# Keep the identifier regex in sync with the Lexer.
IDENTIFIER: /^[a-zA-Z$_](\w|\$)*$/
# Flatten nested arrays recursively.
flatten: (list) ->
memo: []
@ -587,8 +590,6 @@ ClosureNode: exports.ClosureNode: {
# Setting the value of a local variable, or the value of an object property.
AssignNode: exports.AssignNode: inherit Node, {
# Keep the identifier regex in sync with the Lexer.
IDENTIFIER: /^([a-zA-Z$_](\w|\$)*)/
PROTO_ASSIGN: /^(\S+)\.prototype/
LEADING_DOT: /^\.(prototype\.)?/
@ -616,7 +617,7 @@ AssignNode: exports.AssignNode: inherit Node, {
match: name.match(@PROTO_ASSIGN)
proto: match and match[1]
if @value instanceof CodeNode
@value.name: last if last.match(@IDENTIFIER)
@value.name: last if last.match(IDENTIFIER)
@value.proto: proto if proto
return name + ': ' + @value.compile(o) if @context is 'object'
o.scope.find(name) unless @is_value() and @variable.has_properties()
@ -773,8 +774,34 @@ OpNode: exports.OpNode: inherit Node, {
@CHAINABLE.indexOf(@operator) >= 0
compile_node: (o) ->
return @compile_chain(o) if @is_chainable() and (@first.unwrap() instanceof OpNode) and @first.unwrap().is_chainable()
return @compile_chain(o) if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable()
return @compile_assignment(o) if @ASSIGNMENT.indexOf(@operator) >= 0
return @compile_unary(o) if @is_unary()
return @compile_existence(o) if @operator is '?'
@first.compile(o) + ' ' + @operator + ' ' + @second.compile(o)
# Mimic Python's chained comparisons. See:
# http://docs.python.org/reference/expressions.html#notin
compile_chain: (o) ->
shared: @first.unwrap().second
[@first.second, shared]: shared.compile_reference(o) if shared instanceof CallNode
'(' + @first.compile(o) + ') && (' + shared.compile(o) + ' ' + @operator + ' ' + @second.compile(o) + ')'
compile_assignment: (o) ->
[first, second]: [@first.compile(o), @second.compile(o)]
o.scope.find(first) if @first.unwrap.match(IDENTIFIER)
return first + ' = ' + ExistenceNode.compile_test(o, @first) + ' ? ' + first + ' : ' + second if @operator is '?='
first + ' = ' + first + ' ' + @operator.substr(0, 2) + ' ' + second
compile_existence: (o) ->
[first, second]: [@first.compile(o), @second.compile(o)]
ExistenceNode.compile_test(o, @first) + ' ? ' + first + ' : ' + second
compile_unary: (o) ->
space: if @PREFIX_OPERATORS.indexOf(@operator) >= 0 then ' ' else ''
parts: [@operator, space, @first.compile(o)]
parts: parts.reverse() if @flip
parts.join('')
}

View File

@ -7,18 +7,18 @@ process.mixin require './nodes'
unwrap: /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
# Quickie DSL for Jison access.
o: (pattern_string, func) ->
o: (pattern_string, func, options) ->
if func
func: if match: (func + "").match(unwrap) then match[1] else '(' + func + '())'
[pattern_string, '$$ = ' + func + ';']
[pattern_string, '$$ = ' + func + ';', options]
else
[pattern_string, '$$ = $1;']
[pattern_string, '$$ = $1;', options]
# Precedence ===========================================================
operators: [
["left", '?']
["right", 'NOT', '!', '!!', '~', '++', '--']
["right", 'UMINUS', 'UPLUS', 'NOT', '!', '!!', '~', '++', '--']
["left", '*', '/', '%']
["left", '+', '-']
["left", '<<', '>>', '>>>']
@ -27,14 +27,13 @@ operators: [
["right", 'DELETE', 'INSTANCEOF', 'TYPEOF']
["right", '==', '!=', 'IS', 'ISNT']
["left", '&&', '||', 'AND', 'OR']
["right", '-=', '+=', '/=', '*=', '%=']
["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=']
["left", '.']
["right", 'INDENT']
["left", 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY']
["right", 'THROW', 'FOR', 'NEW', 'SUPER']
["left", 'EXTENDS']
["left", '||=', '&&=', '?=']
["right", 'ASSIGN', 'RETURN']
["right", '->', '=>', 'UNLESS', 'IF', 'ELSE', 'WHILE']
]
@ -133,45 +132,45 @@ grammar: {
Comment: [
o "COMMENT", -> new CommentNode(yytext)
]
#
# # Arithmetic and logical operators
# # For Ruby's Operator precedence, see: [
# # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
# Operation: [
# o "! Expression", -> new OpNode($1, $2)
# o "!! Expression", -> new OpNode($1, $2)
# o "- Expression", -> new OpNode($1, $2)
# o "+ Expression", -> new OpNode($1, $2)
# o "NOT Expression", -> new OpNode($1, $2)
# o "~ Expression", -> new OpNode($1, $2)
# o "-- Expression", -> new OpNode($1, $2)
# o "++ Expression", -> new OpNode($1, $2)
# o "DELETE Expression", -> new OpNode($1, $2)
# o "TYPEOF Expression", -> new OpNode($1, $2)
# o "Expression --", -> new OpNode($2, $1, null, true)
# o "Expression ++", -> new OpNode($2, $1, null, true)
#
# o "Expression * Expression", -> new OpNode($2, $1, $3)
# o "Expression / Expression", -> new OpNode($2, $1, $3)
# o "Expression % Expression", -> new OpNode($2, $1, $3)
#
# o "Expression + Expression", -> new OpNode($2, $1, $3)
# o "Expression - Expression", -> new OpNode($2, $1, $3)
#
# o "Expression << Expression", -> new OpNode($2, $1, $3)
# o "Expression >> Expression", -> new OpNode($2, $1, $3)
# o "Expression >>> Expression", -> new OpNode($2, $1, $3)
#
# o "Expression & Expression", -> new OpNode($2, $1, $3)
# o "Expression | Expression", -> new OpNode($2, $1, $3)
# o "Expression ^ Expression", -> new OpNode($2, $1, $3)
#
# o "Expression <= Expression", -> new OpNode($2, $1, $3)
# o "Expression < Expression", -> new OpNode($2, $1, $3)
# o "Expression > Expression", -> new OpNode($2, $1, $3)
# o "Expression >= Expression", -> new OpNode($2, $1, $3)
#
# o "Expression == Expression", -> new OpNode($2, $1, $3)
# Arithmetic and logical operators
# For Ruby's Operator precedence, see: [
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
Operation: [
o "! Expression", -> new OpNode('!', $2)
o "!! Expression", -> new OpNode('!!', $2)
o "- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'}
o "+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'}
o "NOT Expression", -> new OpNode('not', $2)
o "~ Expression", -> new OpNode('~', $2)
o "-- Expression", -> new OpNode('--', $2)
o "++ Expression", -> new OpNode('++', $2)
o "DELETE Expression", -> new OpNode('delete', $2)
o "TYPEOF Expression", -> new OpNode('typeof', $2)
o "Expression --", -> new OpNode('--', $1, null, true)
o "Expression ++", -> new OpNode('++', $1, null, true)
o "Expression * Expression", -> new OpNode('*', $1, $3)
o "Expression / Expression", -> new OpNode('/', $1, $3)
o "Expression % Expression", -> new OpNode('%', $1, $3)
o "Expression + Expression", -> new OpNode('+', $1, $3)
o "Expression - Expression", -> new OpNode('-', $1, $3)
o "Expression << Expression", -> new OpNode('<<', $1, $3)
o "Expression >> Expression", -> new OpNode('>>', $1, $3)
o "Expression >>> Expression", -> new OpNode('>>>', $1, $3)
o "Expression & Expression", -> new OpNode('&', $1, $3)
o "Expression | Expression", -> new OpNode('|', $1, $3)
o "Expression ^ Expression", -> new OpNode('^', $1, $3)
o "Expression <= Expression", -> new OpNode('<=', $1, $3)
o "Expression < Expression", -> new OpNode('<', $1, $3)
o "Expression > Expression", -> new OpNode('>', $1, $3)
o "Expression >= Expression", -> new OpNode('>=', $1, $3)
o "Expression == Expression", -> new OpNode('==', $1, $3)
# o "Expression != Expression", -> new OpNode($2, $1, $3)
# o "Expression IS Expression", -> new OpNode($2, $1, $3)
# o "Expression ISNT Expression", -> new OpNode($2, $1, $3)
@ -193,7 +192,7 @@ grammar: {
#
# o "Expression INSTANCEOF Expression", -> new OpNode($2, $1, $3)
# o "Expression IN Expression", -> new OpNode($2, $1, $3)
# ]
]
# The existence operator.
Existence: [