Add undefined and null literal support
This adds the UndefinedLiteral and NullLiteral to AST. Fixes #990
This commit is contained in:
@@ -164,6 +164,14 @@ interface NumberLiteral <: Literal {
|
||||
value: number;
|
||||
original: number;
|
||||
}
|
||||
|
||||
interface UndefinedLiteral <: Literal {
|
||||
type: "UndefinedLiteral";
|
||||
}
|
||||
|
||||
interface NullLiteral <: Literal {
|
||||
type: "NullLiteral";
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -101,6 +101,18 @@ var AST = {
|
||||
this.value = bool === 'true';
|
||||
},
|
||||
|
||||
UndefinedLiteral: function(locInfo) {
|
||||
this.loc = locInfo;
|
||||
this.type = 'UndefinedLiteral';
|
||||
this.original = this.value = undefined;
|
||||
},
|
||||
|
||||
NullLiteral: function(locInfo) {
|
||||
this.loc = locInfo;
|
||||
this.type = 'NullLiteral';
|
||||
this.original = this.value = null;
|
||||
},
|
||||
|
||||
Hash: function(pairs, locInfo) {
|
||||
this.loc = locInfo;
|
||||
this.type = 'Hash';
|
||||
|
||||
@@ -273,6 +273,14 @@ Compiler.prototype = {
|
||||
this.opcode('pushLiteral', bool.value);
|
||||
},
|
||||
|
||||
UndefinedLiteral: function() {
|
||||
this.opcode('pushLiteral', 'undefined');
|
||||
},
|
||||
|
||||
NullLiteral: function() {
|
||||
this.opcode('pushLiteral', 'null');
|
||||
},
|
||||
|
||||
Hash: function(hash) {
|
||||
var pairs = hash.pairs, i, l;
|
||||
|
||||
|
||||
@@ -124,6 +124,14 @@ PrintVisitor.prototype.BooleanLiteral = function(bool) {
|
||||
return "BOOLEAN{" + bool.value + "}";
|
||||
};
|
||||
|
||||
PrintVisitor.prototype.UndefinedLiteral = function() {
|
||||
return 'UNDEFINED';
|
||||
};
|
||||
|
||||
PrintVisitor.prototype.NullLiteral = function() {
|
||||
return 'NULL';
|
||||
};
|
||||
|
||||
PrintVisitor.prototype.Hash = function(hash) {
|
||||
var pairs = hash.pairs;
|
||||
var joinedPairs = [];
|
||||
|
||||
@@ -110,6 +110,8 @@ Visitor.prototype = {
|
||||
StringLiteral: function(/* string */) {},
|
||||
NumberLiteral: function(/* number */) {},
|
||||
BooleanLiteral: function(/* bool */) {},
|
||||
UndefinedLiteral: function(/* literal */) {},
|
||||
NullLiteral: function(/* literal */) {},
|
||||
|
||||
Hash: function(hash) {
|
||||
this.acceptArray(hash.pairs);
|
||||
|
||||
@@ -66,6 +66,16 @@ describe("basic context", function() {
|
||||
shouldCompileTo('val: {{{val1/val2}}}', {val1: {val2: false}}, 'val: false');
|
||||
});
|
||||
|
||||
it('should handle undefined and null', function() {
|
||||
shouldCompileTo('{{awesome undefined null}}',
|
||||
{
|
||||
awesome: function(_undefined, _null, options) {
|
||||
return (_undefined === undefined) + ' ' + (_null === null) + ' ' + (typeof options);
|
||||
}
|
||||
},
|
||||
'true true object');
|
||||
});
|
||||
|
||||
it("newlines", function() {
|
||||
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
||||
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('javascript-compiler api', function() {
|
||||
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
|
||||
// to avoid errors in older browsers.
|
||||
it('should handle reserved words', function() {
|
||||
shouldCompileTo("{{foo}} {{~null~}}", { foo: "food" }, "food");
|
||||
shouldCompileTo("{{foo}} {{~[null]~}}", { foo: "food" }, "food");
|
||||
});
|
||||
});
|
||||
describe('#compilerInfo', function() {
|
||||
|
||||
@@ -58,6 +58,10 @@ describe('parser', function() {
|
||||
equals(ast_for("{{foo false}}"), "{{ PATH:foo [BOOLEAN{false}] }}\n");
|
||||
});
|
||||
|
||||
it('parses mustaches with undefined and null parameters', function() {
|
||||
equals(ast_for("{{foo undefined null}}"), "{{ PATH:foo [UNDEFINED, NULL] }}\n");
|
||||
});
|
||||
|
||||
it('parses mutaches with DATA parameters', function() {
|
||||
equals(ast_for("{{foo @bar}}"), "{{ PATH:foo [@PATH:bar] }}\n");
|
||||
});
|
||||
|
||||
@@ -322,6 +322,13 @@ describe('Tokenizer', function() {
|
||||
shouldBeToken(result[2], "BOOLEAN", "false");
|
||||
});
|
||||
|
||||
it('tokenizes undefined and null', function() {
|
||||
var result = tokenize('{{ foo undefined null }}');
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'UNDEFINED', 'NULL', 'CLOSE']);
|
||||
shouldBeToken(result[2], 'UNDEFINED', 'undefined');
|
||||
shouldBeToken(result[3], 'NULL', 'null');
|
||||
});
|
||||
|
||||
it('tokenizes hash arguments', function() {
|
||||
var result = tokenize("{{ foo bar=baz }}");
|
||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ describe('Visitor', function() {
|
||||
// Simply run the thing and make sure it does not fail and that all of the
|
||||
// stub methods are executed
|
||||
var visitor = new Handlebars.Visitor();
|
||||
visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'));
|
||||
visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'));
|
||||
});
|
||||
|
||||
it('should traverse to stubs', function() {
|
||||
|
||||
@@ -102,6 +102,8 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
<mu>"@" return 'DATA';
|
||||
<mu>"true"/{LITERAL_LOOKAHEAD} return 'BOOLEAN';
|
||||
<mu>"false"/{LITERAL_LOOKAHEAD} return 'BOOLEAN';
|
||||
<mu>"undefined"/{LITERAL_LOOKAHEAD} return 'UNDEFINED';
|
||||
<mu>"null"/{LITERAL_LOOKAHEAD} return 'NULL';
|
||||
<mu>\-?[0-9]+(?:\.[0-9]+)?/{LITERAL_LOOKAHEAD} return 'NUMBER';
|
||||
<mu>"as"\s+"|" return 'OPEN_BLOCK_PARAMS';
|
||||
<mu>"|" return 'CLOSE_BLOCK_PARAMS';
|
||||
|
||||
@@ -83,6 +83,8 @@ partial
|
||||
param
|
||||
: helperName -> $1
|
||||
| sexpr -> $1
|
||||
| UNDEFINED -> new yy.UndefinedLiteral(yy.locInfo(@$))
|
||||
| NULL -> new yy.NullLiteral(yy.locInfo(@$))
|
||||
;
|
||||
|
||||
sexpr
|
||||
|
||||
Reference in New Issue
Block a user