Skip to content

Commit

Permalink
feat(expressions): add support for the new operator
Browse files Browse the repository at this point in the history
Closes #273
  • Loading branch information
PK1A committed Sep 1, 2014
1 parent 6f9ac6d commit 205ac03
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
5 changes: 5 additions & 0 deletions hsp/expressions/evaluator.js
Expand Up @@ -52,6 +52,11 @@ var BINARY_OPERATORS = {
'(': function (left, right) { //function call on a scope
return left.apply(left, right);
},
'new': function (constructor, args) { //constructor invocation
var instance = Object.create(constructor.prototype);
var result = constructor.apply(instance, args);
return (result !== null && typeof result === 'object') ? result : instance;
},
'.': forgivingPropertyAccessor, //property access
'[': forgivingPropertyAccessor //dynamic property access
};
Expand Down
20 changes: 19 additions & 1 deletion hsp/expressions/parser.js
Expand Up @@ -94,6 +94,24 @@ symbol(":");
constant("true", true);
constant("false", false);
constant("null", null);
prefix("new", function(){
var args = [];
this.a = 'bnr';
this.l = expression(70);
advance("(");
if (token.v !== ')') {
while (true) {
args.push(expression(0));
if (token.id !== ",") {
break;
}
advance(",");
}
}
advance(")");
this.r = args;
return this;
});
prefix("-");
prefix("!");
prefix("(", function () {
Expand Down Expand Up @@ -182,7 +200,7 @@ infix("[", 80, function (left) {
advance("]");
return this;
});
infix("(", 80, function (left) {
infix("(", 70, function (left) {
var a = [];
if (left.id === "." || left.id === "[") {
this.a = 'tnr';
Expand Down
52 changes: 52 additions & 0 deletions test/expressions/manipulator.spec.js
Expand Up @@ -94,6 +94,18 @@ describe('getValue', function () {
expect(expression('obj[foo.bar("s" + "th")]').getValue({obj: {STH: 'else'}, foo: {bar: function(sth) {return sth.toUpperCase(); }}})).to.equal('else');
});

it('should allow chained function calls', function() {
expect(expression('foo().bar()').getValue({
foo: function () {
return {
bar: function() {
return 'STH';
}
};
}
})).to.equal('STH');
});

it('should evaluate expressions with the pipe (|) operator without args', function() {
expect(expression('collection | first').getValue({
collection: ['foo', 'bar'],
Expand Down Expand Up @@ -259,6 +271,46 @@ describe('getValue', function () {
expect(expression("{foo: 'bar', foo2: 'baz'}").getValue({})).to.eql({foo: 'bar', foo2: 'baz'});
expect(expression("{foo: {foo2: 'baz'}}").getValue({})).to.eql({foo: {foo2: 'baz'}});
});

describe('new operator', function () {

it('should create instances from constructor functions available on scope', function() {
expect(expression("new Foo()").getValue({
Foo: function(){
this.foo = 'bar';
}
})).to.eql({foo: 'bar'});
});

it('should create instances from constructor functions available on scope with args', function() {
expect(expression("new Foo(bar, 'baz')").getValue({
Foo: function(what){
this.foo = what;
},
bar: 'baz'
})).to.eql({foo: 'baz'});
});

it('should create instances from constructor functions available on objects', function() {
expect(expression("new foo.Foo()").getValue({
foo: {
Foo: function(){
this.foo = 'bar';
}
}
})).to.eql({foo: 'bar'});
});

it('should create instances from constructor functions returned by functions', function() {
expect(expression("new (foo())()").getValue({
foo: function() {
return function(){
this.foo = 'bar';
};
}
})).to.eql({foo: 'bar'});
});
});
});

describe('forgiving evaluation of expressions', function () {
Expand Down

0 comments on commit 205ac03

Please sign in to comment.