From 76dac9c09c634ae2e3cc93c2be22e0a5b6cfe4c6 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 10 Feb 2010 00:05:56 -0500 Subject: [PATCH] got about as far as I think I can with operator nodes for now, without zach's optimizations --- lib/coffee_script/nodes.js | 61 ++++++++++++-- lib/coffee_script/nodes.rb | 3 +- lib/coffee_script/parser.js | 155 +++++++++++++++++++++--------------- src/nodes.coffee | 35 +++++++- src/parser.coffee | 91 +++++++++++---------- 5 files changed, 223 insertions(+), 122 deletions(-) diff --git a/lib/coffee_script/nodes.js b/lib/coffee_script/nodes.js index 5a5e06c0..3572e2ec 100644 --- a/lib/coffee_script/nodes.js +++ b/lib/coffee_script/nodes.js @@ -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(''); } })); })(); \ No newline at end of file diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 19e7382e..7a46131c 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -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) diff --git a/lib/coffee_script/parser.js b/lib/coffee_script/parser.js index fa9ae7c7..55343029 100644 --- a/lib/coffee_script/parser.js +++ b/lib/coffee_script/parser.js @@ -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); diff --git a/src/nodes.coffee b/src/nodes.coffee index 93b17015..6c452430 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -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('') } diff --git a/src/parser.coffee b/src/parser.coffee index 8894f069..13f96ee6 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -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: [