switching 'a in b' to 'a of b', and adding an array presence check for 'a in b'.
This commit is contained in:
parent
38a9b7166b
commit
0fcfb80be4
|
@ -739,8 +739,12 @@
|
||||||
}), o("Expression INSTANCEOF Expression", function() {
|
}), o("Expression INSTANCEOF Expression", function() {
|
||||||
return new OpNode('instanceof', $1, $3);
|
return new OpNode('instanceof', $1, $3);
|
||||||
}), o("Expression IN Expression", function() {
|
}), o("Expression IN Expression", function() {
|
||||||
|
return new InNode($1, $3);
|
||||||
|
}), o("Expression OF Expression", function() {
|
||||||
return new OpNode('in', $1, $3);
|
return new OpNode('in', $1, $3);
|
||||||
}), o("Expression ! IN Expression", function() {
|
}), o("Expression ! IN Expression", function() {
|
||||||
|
return new OpNode('!', new InNode($1, $4));
|
||||||
|
}), o("Expression ! OF Expression", function() {
|
||||||
return new OpNode('!', new ParentheticalNode(new OpNode('in', $1, $4)));
|
return new OpNode('!', new ParentheticalNode(new OpNode('in', $1, $4)));
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
86
lib/nodes.js
86
lib/nodes.js
|
@ -1,5 +1,5 @@
|
||||||
(function(){
|
(function(){
|
||||||
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, IndexNode, LiteralNode, NUMBER, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, flatten, helpers, include, indexOf, literal, merge, starts, utility;
|
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NUMBER, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, flatten, helpers, include, indexOf, literal, merge, starts, utility;
|
||||||
var __extends = function(child, parent) {
|
var __extends = function(child, parent) {
|
||||||
var ctor = function(){ };
|
var ctor = function(){ };
|
||||||
ctor.prototype = parent.prototype;
|
ctor.prototype = parent.prototype;
|
||||||
|
@ -79,14 +79,21 @@
|
||||||
// If the code generation wishes to use the result of a complex expression
|
// If the code generation wishes to use the result of a complex expression
|
||||||
// in multiple places, ensure that the expression is only ever evaluated once,
|
// in multiple places, ensure that the expression is only ever evaluated once,
|
||||||
// by assigning it to a temporary variable.
|
// by assigning it to a temporary variable.
|
||||||
BaseNode.prototype.compileReference = function(o, onlyIfNecessary) {
|
BaseNode.prototype.compileReference = function(o, options) {
|
||||||
var compiled, reference;
|
var compiled, pair, reference;
|
||||||
if (onlyIfNecessary && !(this instanceof CallNode || this instanceof ValueNode && (!(this.base instanceof LiteralNode) || this.hasProperties()))) {
|
pair = (function() {
|
||||||
return [this, this];
|
if (!(this instanceof CallNode || this instanceof ValueNode && (!(this.base instanceof LiteralNode) || this.hasProperties()))) {
|
||||||
|
return [this, this];
|
||||||
|
} else {
|
||||||
|
reference = literal(o.scope.freeVariable());
|
||||||
|
compiled = new AssignNode(reference, this);
|
||||||
|
return [compiled, reference];
|
||||||
|
}
|
||||||
|
}).call(this);
|
||||||
|
if (!(options && options.precompile)) {
|
||||||
|
return pair;
|
||||||
}
|
}
|
||||||
reference = literal(o.scope.freeVariable());
|
return [pair[0].compile(o), pair[1].compile(o)];
|
||||||
compiled = new AssignNode(reference, this);
|
|
||||||
return [compiled, reference];
|
|
||||||
};
|
};
|
||||||
// Convenience method to grab the current indentation level, plus tabbing in.
|
// Convenience method to grab the current indentation level, plus tabbing in.
|
||||||
BaseNode.prototype.idt = function(tabs) {
|
BaseNode.prototype.idt = function(tabs) {
|
||||||
|
@ -704,10 +711,10 @@
|
||||||
// But only if they need to be cached to avoid double evaluation.
|
// But only if they need to be cached to avoid double evaluation.
|
||||||
RangeNode.prototype.compileVariables = function(o) {
|
RangeNode.prototype.compileVariables = function(o) {
|
||||||
var _b, _c, parts;
|
var _b, _c, parts;
|
||||||
_b = this.from.compileReference(o, true);
|
_b = this.from.compileReference(o);
|
||||||
this.from = _b[0];
|
this.from = _b[0];
|
||||||
this.fromVar = _b[1];
|
this.fromVar = _b[1];
|
||||||
_c = this.to.compileReference(o, true);
|
_c = this.to.compileReference(o);
|
||||||
this.to = _c[0];
|
this.to = _c[0];
|
||||||
this.toVar = _c[1];
|
this.toVar = _c[1];
|
||||||
parts = [];
|
parts = [];
|
||||||
|
@ -1425,6 +1432,63 @@
|
||||||
};
|
};
|
||||||
return OpNode;
|
return OpNode;
|
||||||
})();
|
})();
|
||||||
|
//### InNode
|
||||||
|
exports.InNode = (function() {
|
||||||
|
InNode = function(object, array) {
|
||||||
|
this.object = object;
|
||||||
|
this.array = array;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
__extends(InNode, BaseNode);
|
||||||
|
InNode.prototype['class'] = 'InNode';
|
||||||
|
InNode.prototype.children = ['object', 'array'];
|
||||||
|
InNode.prototype.isArray = function() {
|
||||||
|
return this.array instanceof ValueNode && this.array.isArray();
|
||||||
|
};
|
||||||
|
InNode.prototype.compileNode = function(o) {
|
||||||
|
var _b;
|
||||||
|
_b = this.object.compileReference(o, {
|
||||||
|
precompile: true
|
||||||
|
});
|
||||||
|
this.obj1 = _b[0];
|
||||||
|
this.obj2 = _b[1];
|
||||||
|
if (this.isArray()) {
|
||||||
|
return this.compileOrTest(o);
|
||||||
|
} else {
|
||||||
|
return this.compileLoopTest(o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
InNode.prototype.compileOrTest = function(o) {
|
||||||
|
var _b, _c, _d, i, item, tests;
|
||||||
|
tests = (function() {
|
||||||
|
_b = []; _c = this.array.base.objects;
|
||||||
|
for (i = 0, _d = _c.length; i < _d; i++) {
|
||||||
|
item = _c[i];
|
||||||
|
_b.push(("" + (item.compile(o)) + " === " + (i ? this.obj2 : this.obj1)));
|
||||||
|
}
|
||||||
|
return _b;
|
||||||
|
}).call(this);
|
||||||
|
return "(" + (tests.join(' || ')) + ")";
|
||||||
|
};
|
||||||
|
InNode.prototype.compileLoopTest = function(o) {
|
||||||
|
var _b, _c, body, i, l;
|
||||||
|
_b = this.array.compileReference(o, {
|
||||||
|
precompile: true
|
||||||
|
});
|
||||||
|
this.arr1 = _b[0];
|
||||||
|
this.arr2 = _b[1];
|
||||||
|
_c = [o.scope.freeVariable(), o.scope.freeVariable()];
|
||||||
|
i = _c[0];
|
||||||
|
l = _c[1];
|
||||||
|
body = ("!!(function(){ for (var " + i + "=0, " + l + "=" + (this.arr1) + ".length; " + i + "<" + l + "; " + i + "++) if (" + (this.arr2) + "[" + i + "] === " + this.obj2 + ") return true; })()");
|
||||||
|
if (this.obj1 !== this.obj2) {
|
||||||
|
return "" + this.obj1 + ";\n" + this.tab + body;
|
||||||
|
} else {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return InNode;
|
||||||
|
})();
|
||||||
//### TryNode
|
//### TryNode
|
||||||
// A classic *try/catch/finally* block.
|
// A classic *try/catch/finally* block.
|
||||||
exports.TryNode = (function() {
|
exports.TryNode = (function() {
|
||||||
|
@ -1506,7 +1570,7 @@
|
||||||
// Be careful not to double-evaluate anything.
|
// Be careful not to double-evaluate anything.
|
||||||
ExistenceNode.compileTest = function(o, variable) {
|
ExistenceNode.compileTest = function(o, variable) {
|
||||||
var _b, first, second;
|
var _b, first, second;
|
||||||
_b = variable.compileReference(o, true);
|
_b = variable.compileReference(o);
|
||||||
first = _b[0];
|
first = _b[0];
|
||||||
second = _b[1];
|
second = _b[1];
|
||||||
return "(typeof " + (first.compile(o)) + " !== \"undefined\" && " + (second.compile(o)) + " !== null)";
|
return "(typeof " + (first.compile(o)) + " !== \"undefined\" && " + (second.compile(o)) + " !== null)";
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -180,7 +180,7 @@
|
||||||
stack[stack.length - 2] += stack.pop();
|
stack[stack.length - 2] += stack.pop();
|
||||||
}
|
}
|
||||||
open = stack[stack.length - 1] > 0;
|
open = stack[stack.length - 1] > 0;
|
||||||
if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag) && !(tag === '!' && post[0] === 'IN')) {
|
if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, tag) && !(tag === '!' && (post[0] === 'IN' || post[0] === 'OF'))) {
|
||||||
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
|
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
|
||||||
stack[stack.length - 1] += 1;
|
stack[stack.length - 1] += 1;
|
||||||
if (include(EXPRESSION_START, tag)) {
|
if (include(EXPRESSION_START, tag)) {
|
||||||
|
|
|
@ -565,8 +565,10 @@ grammar: {
|
||||||
o "Expression ?= Expression", -> new OpNode '?=', $1, $3
|
o "Expression ?= Expression", -> new OpNode '?=', $1, $3
|
||||||
|
|
||||||
o "Expression INSTANCEOF Expression", -> new OpNode 'instanceof', $1, $3
|
o "Expression INSTANCEOF Expression", -> new OpNode 'instanceof', $1, $3
|
||||||
o "Expression IN Expression", -> new OpNode 'in', $1, $3
|
o "Expression IN Expression", -> new InNode $1, $3
|
||||||
o "Expression ! IN Expression", -> new OpNode '!', new ParentheticalNode new OpNode 'in', $1, $4
|
o "Expression OF Expression", -> new OpNode 'in', $1, $3
|
||||||
|
o "Expression ! IN Expression", -> new OpNode '!', new InNode $1, $4
|
||||||
|
o "Expression ! OF Expression", -> new OpNode '!', new ParentheticalNode new OpNode 'in', $1, $4
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,14 +61,16 @@ exports.BaseNode: class BaseNode
|
||||||
# If the code generation wishes to use the result of a complex expression
|
# If the code generation wishes to use the result of a complex expression
|
||||||
# in multiple places, ensure that the expression is only ever evaluated once,
|
# in multiple places, ensure that the expression is only ever evaluated once,
|
||||||
# by assigning it to a temporary variable.
|
# by assigning it to a temporary variable.
|
||||||
compileReference: (o, onlyIfNecessary) ->
|
compileReference: (o, options) ->
|
||||||
if onlyIfNecessary and not
|
pair: if not (this instanceof CallNode or this instanceof ValueNode and
|
||||||
(this instanceof CallNode or
|
(not (@base instanceof LiteralNode) or @hasProperties()))
|
||||||
this instanceof ValueNode and (not (@base instanceof LiteralNode) or @hasProperties()))
|
[this, this]
|
||||||
return [this, this]
|
else
|
||||||
reference: literal o.scope.freeVariable()
|
reference: literal o.scope.freeVariable()
|
||||||
compiled: new AssignNode reference, this
|
compiled: new AssignNode reference, this
|
||||||
[compiled, reference]
|
[compiled, reference]
|
||||||
|
return pair unless options and options.precompile
|
||||||
|
[pair[0].compile(o), pair[1].compile(o)]
|
||||||
|
|
||||||
# Convenience method to grab the current indentation level, plus tabbing in.
|
# Convenience method to grab the current indentation level, plus tabbing in.
|
||||||
idt: (tabs) ->
|
idt: (tabs) ->
|
||||||
|
@ -518,8 +520,8 @@ exports.RangeNode: class RangeNode extends BaseNode
|
||||||
# Compiles the range's source variables -- where it starts and where it ends.
|
# Compiles the range's source variables -- where it starts and where it ends.
|
||||||
# But only if they need to be cached to avoid double evaluation.
|
# But only if they need to be cached to avoid double evaluation.
|
||||||
compileVariables: (o) ->
|
compileVariables: (o) ->
|
||||||
[@from, @fromVar]: @from.compileReference o, true
|
[@from, @fromVar]: @from.compileReference o
|
||||||
[@to, @toVar]: @to.compileReference o, true
|
[@to, @toVar]: @to.compileReference o
|
||||||
parts: []
|
parts: []
|
||||||
parts.push @from.compile o if @from isnt @fromVar
|
parts.push @from.compile o if @from isnt @fromVar
|
||||||
parts.push @to.compile o if @to isnt @toVar
|
parts.push @to.compile o if @to isnt @toVar
|
||||||
|
@ -1052,14 +1054,42 @@ exports.OpNode: class OpNode extends BaseNode
|
||||||
parts: parts.reverse() if @flip
|
parts: parts.reverse() if @flip
|
||||||
parts.join('')
|
parts.join('')
|
||||||
|
|
||||||
|
#### InNode
|
||||||
|
exports.InNode: class InNode extends BaseNode
|
||||||
|
|
||||||
|
class: 'InNode'
|
||||||
|
children: ['object', 'array']
|
||||||
|
|
||||||
|
constructor: (object, array) ->
|
||||||
|
@object: object
|
||||||
|
@array: array
|
||||||
|
|
||||||
|
isArray: ->
|
||||||
|
@array instanceof ValueNode and @array.isArray()
|
||||||
|
|
||||||
|
compileNode: (o) ->
|
||||||
|
[@obj1, @obj2]: @object.compileReference o, {precompile: yes}
|
||||||
|
if @isArray() then @compileOrTest(o) else @compileLoopTest(o)
|
||||||
|
|
||||||
|
compileOrTest: (o) ->
|
||||||
|
tests: for item, i in @array.base.objects
|
||||||
|
"${item.compile(o)} === ${if i then @obj2 else @obj1}"
|
||||||
|
"(${tests.join(' || ')})"
|
||||||
|
|
||||||
|
compileLoopTest: (o) ->
|
||||||
|
[@arr1, @arr2]: @array.compileReference o, {precompile: yes}
|
||||||
|
[i, l]: [o.scope.freeVariable(), o.scope.freeVariable()]
|
||||||
|
body: "!!(function(){ for (var $i=0, $l=${@arr1}.length; $i<$l; $i++) if (${@arr2}[$i] === $@obj2) return true; })()"
|
||||||
|
if @obj1 isnt @obj2 then "$@obj1;\n$@tab$body" else body
|
||||||
|
|
||||||
#### TryNode
|
#### TryNode
|
||||||
|
|
||||||
# A classic *try/catch/finally* block.
|
# A classic *try/catch/finally* block.
|
||||||
exports.TryNode: class TryNode extends BaseNode
|
exports.TryNode: class TryNode extends BaseNode
|
||||||
|
|
||||||
class: 'TryNode'
|
class: 'TryNode'
|
||||||
children: ['attempt', 'recovery', 'ensure']
|
children: ['attempt', 'recovery', 'ensure']
|
||||||
isStatement: -> yes
|
isStatement: -> yes
|
||||||
|
|
||||||
constructor: (attempt, error, recovery, ensure) ->
|
constructor: (attempt, error, recovery, ensure) ->
|
||||||
@attempt: attempt
|
@attempt: attempt
|
||||||
|
@ -1122,7 +1152,7 @@ exports.ExistenceNode: class ExistenceNode extends BaseNode
|
||||||
# because other nodes like to check the existence of their variables as well.
|
# because other nodes like to check the existence of their variables as well.
|
||||||
# Be careful not to double-evaluate anything.
|
# Be careful not to double-evaluate anything.
|
||||||
@compileTest: (o, variable) ->
|
@compileTest: (o, variable) ->
|
||||||
[first, second]: variable.compileReference o, true
|
[first, second]: variable.compileReference o
|
||||||
"(typeof ${first.compile(o)} !== \"undefined\" && ${second.compile(o)} !== null)"
|
"(typeof ${first.compile(o)} !== \"undefined\" && ${second.compile(o)} !== null)"
|
||||||
|
|
||||||
#### ParentheticalNode
|
#### ParentheticalNode
|
||||||
|
|
|
@ -127,7 +127,7 @@ exports.Rewriter: class Rewriter
|
||||||
stack[stack.length - 2]: + stack.pop() if tag is 'OUTDENT'
|
stack[stack.length - 2]: + stack.pop() if tag is 'OUTDENT'
|
||||||
open: stack[stack.length - 1] > 0
|
open: stack[stack.length - 1] > 0
|
||||||
if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) and
|
if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, tag) and
|
||||||
not (tag is '!' and post[0] is 'IN')
|
not (tag is '!' and (post[0] is 'IN' or post[0] is 'OF'))
|
||||||
@tokens.splice i, 0, ['CALL_START', '(', token[2]]
|
@tokens.splice i, 0, ['CALL_START', '(', token[2]]
|
||||||
stack[stack.length - 1]: + 1
|
stack[stack.length - 1]: + 1
|
||||||
stack.push 0 if include(EXPRESSION_START, tag)
|
stack.push 0 if include(EXPRESSION_START, tag)
|
||||||
|
|
|
@ -56,7 +56,7 @@ ok evens.join(', ') is '4, 6, 8'
|
||||||
|
|
||||||
|
|
||||||
# The in operator still works, standalone.
|
# The in operator still works, standalone.
|
||||||
ok 2 in evens
|
ok 2 of evens
|
||||||
|
|
||||||
|
|
||||||
# Ensure that the closure wrapper preserves local variables.
|
# Ensure that the closure wrapper preserves local variables.
|
||||||
|
|
|
@ -38,5 +38,11 @@ ok val is 1
|
||||||
|
|
||||||
# Allow "if x not in y"
|
# Allow "if x not in y"
|
||||||
obj: {a: true}
|
obj: {a: true}
|
||||||
ok 'b' not in obj
|
ok 'a' of obj
|
||||||
ok not ('a' not in obj)
|
ok 'b' not of obj
|
||||||
|
|
||||||
|
# And for "a in b" with array presence.
|
||||||
|
ok 100 in [100, 200, 300]
|
||||||
|
array: [100, 200, 300]
|
||||||
|
ok 100 in array
|
||||||
|
ok 1 not in array
|
Loading…
Reference in New Issue