summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkpdecker <kpdecker@gmail.com>2015-04-07 23:37:38 -0500
committerkpdecker <kpdecker@gmail.com>2015-04-07 23:37:38 -0500
commit2d149e779764cb373be0585c5e493da405062be6 (patch)
tree97ff4511e445c7d0a70161a0b3bbe02e7e5551e9
parent81a4d50955b13e7d5e5052d904bb3e8c993ebb88 (diff)
downloadhandlebars.js-2d149e779764cb373be0585c5e493da405062be6.zip
handlebars.js-2d149e779764cb373be0585c5e493da405062be6.tar.gz
handlebars.js-2d149e779764cb373be0585c5e493da405062be6.tar.bz2
Add undefined and null literal support
This adds the UndefinedLiteral and NullLiteral to AST. Fixes #990
-rw-r--r--docs/compiler-api.md8
-rw-r--r--lib/handlebars/compiler/ast.js12
-rw-r--r--lib/handlebars/compiler/compiler.js8
-rw-r--r--lib/handlebars/compiler/printer.js8
-rw-r--r--lib/handlebars/compiler/visitor.js2
-rw-r--r--spec/basic.js10
-rw-r--r--spec/javascript-compiler.js2
-rw-r--r--spec/parser.js4
-rw-r--r--spec/tokenizer.js7
-rw-r--r--spec/visitor.js2
-rw-r--r--src/handlebars.l2
-rw-r--r--src/handlebars.yy2
12 files changed, 65 insertions, 2 deletions
diff --git a/docs/compiler-api.md b/docs/compiler-api.md
index 40ded8f..c09414f 100644
--- a/docs/compiler-api.md
+++ b/docs/compiler-api.md
@@ -164,6 +164,14 @@ interface NumberLiteral <: Literal {
value: number;
original: number;
}
+
+interface UndefinedLiteral <: Literal {
+ type: "UndefinedLiteral";
+}
+
+interface NullLiteral <: Literal {
+ type: "NullLiteral";
+}
```
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index 45212b3..e5904c6 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -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';
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index e1b4dfb..a39082d 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -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;
diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js
index 55232cc..dcc73c3 100644
--- a/lib/handlebars/compiler/printer.js
+++ b/lib/handlebars/compiler/printer.js
@@ -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 = [];
diff --git a/lib/handlebars/compiler/visitor.js b/lib/handlebars/compiler/visitor.js
index 4101a4f..6e4168e 100644
--- a/lib/handlebars/compiler/visitor.js
+++ b/lib/handlebars/compiler/visitor.js
@@ -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);
diff --git a/spec/basic.js b/spec/basic.js
index 9b6678a..11002e4 100644
--- a/spec/basic.js
+++ b/spec/basic.js
@@ -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");
diff --git a/spec/javascript-compiler.js b/spec/javascript-compiler.js
index fb78658..f47ddeb 100644
--- a/spec/javascript-compiler.js
+++ b/spec/javascript-compiler.js
@@ -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() {
diff --git a/spec/parser.js b/spec/parser.js
index 3d3dccf..6f5a660 100644
--- a/spec/parser.js
+++ b/spec/parser.js
@@ -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");
});
diff --git a/spec/tokenizer.js b/spec/tokenizer.js
index 0a5143b..26a1c3b 100644
--- a/spec/tokenizer.js
+++ b/spec/tokenizer.js
@@ -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']);
diff --git a/spec/visitor.js b/spec/visitor.js
index 3217230..8b9ef9f 100644
--- a/spec/visitor.js
+++ b/spec/visitor.js
@@ -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() {
diff --git a/src/handlebars.l b/src/handlebars.l
index ace0263..55d8efc 100644
--- a/src/handlebars.l
+++ b/src/handlebars.l
@@ -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';
diff --git a/src/handlebars.yy b/src/handlebars.yy
index 18f03b9..d724578 100644
--- a/src/handlebars.yy
+++ b/src/handlebars.yy
@@ -83,6 +83,8 @@ partial
param
: helperName -> $1
| sexpr -> $1
+ | UNDEFINED -> new yy.UndefinedLiteral(yy.locInfo(@$))
+ | NULL -> new yy.NullLiteral(yy.locInfo(@$))
;
sexpr