Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #123 from canjs/lookup-bracket-lookup
Handling expressions like foo[bar].baz
  • Loading branch information
phillipskevin committed Jan 11, 2017
2 parents 2c546c7 + 2c5f353 commit eb988bc
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 28 deletions.
41 changes: 32 additions & 9 deletions src/expression.js
Expand Up @@ -153,6 +153,10 @@ var ScopeLookup = function(key, root) {
Lookup.apply(this, arguments);
};
ScopeLookup.prototype.value = function(scope, helperOptions){
if (this.rootExpr) {
return lookupValueInResult(this.key, this.rootExpr, scope, {}, {}).value;
}

return lookupValue(this.key, scope, helperOptions).value;
};

Expand Down Expand Up @@ -438,7 +442,8 @@ Helper.prototype.value = function(scope, helperOptions, nodeList, truthyRenderer
// AT @NAME
//
var keyRegExp = /[\w\.\\\-_@\/\&%]+/,
tokensRegExp = /('.*?'|".*?"|=|[\w\.\\\-_@\/*%\$]+|[\(\)]|,|\~|\[|\]|\s*(?=\[))/g,
tokensRegExp = /('.*?'|".*?"|=|[\w\.\\\-_@\/*%\$]+|[\(\)]|,|\~|\[|\]\s*|\s*(?=\[))/g,
bracketSpaceRegExp = /\]\s+/,
literalRegExp = /^('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false|null|undefined)$/;

var isTokenKey = function(token){
Expand Down Expand Up @@ -603,7 +608,12 @@ var expression = {
tokenize: function(expression){
var tokens = [];
(expression.trim() + ' ').replace(tokensRegExp, function (whole, arg) {
tokens.push(arg);
if (bracketSpaceRegExp.test(arg)) {
tokens.push(arg[0]);
tokens.push(arg.slice(1));
} else {
tokens.push(arg);
}
});
return tokens;
},
Expand Down Expand Up @@ -789,13 +799,26 @@ var expression = {
key: token.slice(1) // remove leading `.`
});
}
else if(firstParent.type === 'Bracket' && !(firstParent.children && firstParent.children.length > 0)) {
stack.addToAndPush(["Bracket"], {type: "Lookup", key: token});
}
// This is to make sure in a helper like `helper foo[bar] car` that
// car would not be added to the Bracket expression.
else if(stack.first(["Helper", "Call", "Hash","Arg"]).type === 'Helper') {
stack.addToAndPush(["Helper"], {type: "Lookup", key: token});
else if(firstParent.type === 'Bracket') {
// a Bracket expression without children means we have
// parsed `foo[` of an expression like `foo[bar]`
// so we know to add the Lookup as a child of the Bracket expression
if (!(firstParent.children && firstParent.children.length > 0)) {
stack.addToAndPush(["Bracket"], {type: "Lookup", key: token});
} else {
// check if we are adding to a helper like `eq foo[bar] baz`
// but not at the `.baz` of `eq foo[bar].baz xyz`
if(stack.first(["Helper", "Call", "Hash", "Arg"]).type === 'Helper' && token[0] !== '.') {
stack.addToAndPush(["Helper"], {type: "Lookup", key: token});
} else {
// otherwise, handle the `.baz` in expressions like `foo[bar].baz`
stack.replaceTopAndPush({
type: "Lookup",
key: token.slice(1),
root: firstParent
});
}
}
}
else {
// if two scopes, that means a helper
Expand Down
67 changes: 48 additions & 19 deletions test/expression-test.js
Expand Up @@ -29,7 +29,7 @@ test("expression.tokenize", function(){

var bracket = "[foo] bar [baz]";
res = expression.tokenize(bracket);
deepEqual(res, ["[", "foo", "]", "bar", " ", "[", "baz", "]"]);
deepEqual(res, ["[", "foo", "]", " ", "bar", " ", "[", "baz", "]", " "]);

});

Expand Down Expand Up @@ -421,52 +421,81 @@ test("expression.ast - [] operator", function(){
children: [{type: "Lookup", key: "bar"}]
},
children: [{type: "Lookup", key: "baz"}]
},
"foo[bar][baz] valid"
);
}, "foo[bar][baz] valid");

deepEqual(expression.ast("foo[bar].baz"), {
type: "Lookup",
key: "baz",
root: {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
}
}, "foo[bar].baz");

deepEqual(expression.ast("eq foo[bar].baz xyz"), {
type: "Helper",
method: {
type: "Lookup",
key: "eq"
},
children: [{
type: "Lookup",
key: "baz",
root: {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
}
},
{
type: "Lookup",
key: "xyz"
}]
}, "eq foo[bar].baz xyz");
});

test("expression.parse - [] operator", function(){
var exprData = expression.parse("['propName']");
deepEqual(exprData,
deepEqual(expression.parse("['propName']"),
new expression.Bracket(
new expression.Literal('propName')
)
),
"['propName']"
);

exprData = expression.parse("[propName]");
deepEqual(exprData,
deepEqual(expression.parse("[propName]"),
new expression.Bracket(
new expression.Lookup('propName')
)
),
"[propName]"
);

exprData = expression.parse("foo['bar']");
deepEqual(exprData,
deepEqual(expression.parse("foo['bar']"),
new expression.Bracket(
new expression.Literal('bar'),
new expression.Lookup('foo')
)
),
"foo['bar']"
);

exprData = expression.parse("foo[bar]");
deepEqual(exprData,
deepEqual(expression.parse("foo[bar]"),
new expression.Bracket(
new expression.Lookup('bar'),
new expression.Lookup('foo')
)
),
"foo[bar]"
);

exprData = expression.parse("foo()[bar]");
deepEqual(exprData,
deepEqual(expression.parse("foo()[bar]"),
new expression.Bracket(
new expression.Lookup('bar'),
new expression.Call(
new expression.Lookup('@foo'),
[],
{}
)
)
),
"foo()[bar]"
);

exprData = expression.parse("foo[bar()]");
Expand Down
30 changes: 30 additions & 0 deletions test/stache-test.js
Expand Up @@ -5332,6 +5332,36 @@ function makeTest(name, doc, mutation) {
equal(innerHTML(p[0]), 'thudjeek', 'correct value for bar[%index] when iterating foo (plain object data)');
});

test("Bracket expression followed by Lookup expression", function () {
var template;
var div = doc.createElement('div');

template = stache('<p>{{ foo[bar].first }}</p><p>{{#is foo[bar].first "K"}}short{{else}}long{{/is}}</p>');

var data = new CanMap({
baz: 'first',
bar: 'name',
foo: {
name: {
first: 'K'
},
fullName: {
first: 'Kevin'
}
}
});
var dom = template(data);
div.appendChild(dom);
var p = div.getElementsByTagName('p');

equal(innerHTML(p[0]), 'K', 'correct value for foo[bar].first');
equal(innerHTML(p[1]), 'short', 'correct value for `is foo[bar].first "K"`');

data.attr('bar', 'fullName');

equal(innerHTML(p[0]), 'Kevin', 'updated value for foo[bar].first');
equal(innerHTML(p[1]), 'long', 'updated value for `is foo[bar].first "K"`');
});

// PUT NEW TESTS RIGHT BEFORE THIS!

Expand Down

0 comments on commit eb988bc

Please sign in to comment.