Add support for hash args in the tokenizer and parser
This commit is contained in:
@@ -52,6 +52,11 @@ var Handlebars = require("handlebars");
|
|||||||
this.string = string;
|
this.string = string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Handlebars.AST.HashNode = function(pairs) {
|
||||||
|
this.type = "hash";
|
||||||
|
this.pairs = pairs;
|
||||||
|
};
|
||||||
|
|
||||||
Handlebars.AST.IdNode = function(parts) {
|
Handlebars.AST.IdNode = function(parts) {
|
||||||
this.type = "ID";
|
this.type = "ID";
|
||||||
this.original = parts.join("/");
|
this.original = parts.join("/");
|
||||||
|
|||||||
@@ -89,6 +89,19 @@ Handlebars.PrintVisitor.prototype.partial = function(partial) {
|
|||||||
return this.pad("{{> " + content + " }}");
|
return this.pad("{{> " + content + " }}");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Handlebars.PrintVisitor.prototype.hash = function(hash) {
|
||||||
|
var pairs = hash.pairs;
|
||||||
|
var joinedPairs = [], left, right;
|
||||||
|
|
||||||
|
for(var i=0, l=pairs.length; i<l; i++) {
|
||||||
|
left = pairs[i][0];
|
||||||
|
right = this.accept(pairs[i][1]);
|
||||||
|
joinedPairs.push( left + "=" + right );
|
||||||
|
}
|
||||||
|
|
||||||
|
return "HASH{" + joinedPairs.join(", ") + "}";
|
||||||
|
};
|
||||||
|
|
||||||
Handlebars.PrintVisitor.prototype.STRING = function(string) {
|
Handlebars.PrintVisitor.prototype.STRING = function(string) {
|
||||||
return '"' + string.string + '"';
|
return '"' + string.string + '"';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ describe "Parser" do
|
|||||||
string.inspect
|
string.inspect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def hash(*pairs)
|
||||||
|
"HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}"
|
||||||
|
end
|
||||||
|
|
||||||
def id(id)
|
def id(id)
|
||||||
"ID:#{id}"
|
"ID:#{id}"
|
||||||
end
|
end
|
||||||
@@ -112,6 +116,24 @@ describe "Parser" do
|
|||||||
ast_for("{{foo bar}}").should == program { mustache id("foo"), id("bar") }
|
ast_for("{{foo bar}}").should == program { mustache id("foo"), id("bar") }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "parses mustaches with hash arguments" do
|
||||||
|
ast_for("{{foo bar=baz}}").should == program do
|
||||||
|
mustache id("foo"), hash(["bar", "ID:baz"])
|
||||||
|
end
|
||||||
|
|
||||||
|
ast_for("{{foo bar=baz bat=bam}}").should == program do
|
||||||
|
mustache id("foo"), hash(["bar", "ID:baz"], ["bat", "ID:bam"])
|
||||||
|
end
|
||||||
|
|
||||||
|
ast_for("{{foo bar=baz bat=\"bam\"}}").should == program do
|
||||||
|
mustache id("foo"), hash(["bar", "ID:baz"], ["bat", "\"bam\""])
|
||||||
|
end
|
||||||
|
|
||||||
|
ast_for("{{foo omg bar=baz bat=\"bam\"}}").should == program do
|
||||||
|
mustache id("foo"), id("omg"), hash(["bar", id("baz")], ["bat", string("bam")])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "parses mustaches with string parameters" do
|
it "parses mustaches with string parameters" do
|
||||||
ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), id("bar"), string("baz")}
|
ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), id("bar"), string("baz")}
|
||||||
end
|
end
|
||||||
|
|||||||
+22
-2
@@ -12,8 +12,10 @@ describe "Tokenizer" do
|
|||||||
out = []
|
out = []
|
||||||
|
|
||||||
while token = lexer.lex
|
while token = lexer.lex
|
||||||
result = parser.terminals_[token]
|
# p token
|
||||||
break if !result || result == "EOF"
|
result = parser.terminals_[token] || token
|
||||||
|
# p result
|
||||||
|
break if !result || result == "EOF" || result == "INVALID"
|
||||||
out << Token.new(result, lexer.yytext)
|
out << Token.new(result, lexer.yytext)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -163,6 +165,24 @@ describe "Tokenizer" do
|
|||||||
result[2].should be_token("STRING", %{bar"baz})
|
result[2].should be_token("STRING", %{bar"baz})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "tokenizes hash arguments" do
|
||||||
|
result = tokenize("{{ foo bar=baz }}")
|
||||||
|
result.should match_tokens %w(OPEN ID ID EQUALS ID CLOSE)
|
||||||
|
|
||||||
|
result = tokenize("{{ foo bar baz=bat }}")
|
||||||
|
result.should match_tokens %w(OPEN ID ID ID EQUALS ID CLOSE)
|
||||||
|
|
||||||
|
result = tokenize("{{ foo bar baz=\"bat\" }}")
|
||||||
|
result.should match_tokens %w(OPEN ID ID ID EQUALS STRING CLOSE)
|
||||||
|
|
||||||
|
result = tokenize("{{ foo bar baz=\"bat\" bam=wot }}")
|
||||||
|
result.should match_tokens %w(OPEN ID ID ID EQUALS STRING ID EQUALS ID CLOSE)
|
||||||
|
|
||||||
|
result = tokenize("{{foo omg bar=baz bat=\"bam\"}}")
|
||||||
|
result.should match_tokens %w(OPEN ID ID ID EQUALS ID ID EQUALS STRING CLOSE)
|
||||||
|
result[2].should be_token("ID", "omg")
|
||||||
|
end
|
||||||
|
|
||||||
it "does not time out in a mustache with a single } followed by EOF" do
|
it "does not time out in a mustache with a single } followed by EOF" do
|
||||||
Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
|
Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
|
||||||
end
|
end
|
||||||
|
|||||||
+2
-1
@@ -16,6 +16,7 @@
|
|||||||
<mu>"{{!".*?"}}" { yytext = yytext.substr(3,yyleng-5); this.begin("INITIAL"); return 'COMMENT'; }
|
<mu>"{{!".*?"}}" { yytext = yytext.substr(3,yyleng-5); this.begin("INITIAL"); return 'COMMENT'; }
|
||||||
<mu>"{{" { return 'OPEN'; }
|
<mu>"{{" { return 'OPEN'; }
|
||||||
|
|
||||||
|
<mu>"=" { return 'EQUALS'; }
|
||||||
<mu>"."/[} ] { return 'ID'; }
|
<mu>"."/[} ] { return 'ID'; }
|
||||||
<mu>".." { return 'ID'; }
|
<mu>".." { return 'ID'; }
|
||||||
<mu>[/.] { return 'SEP'; }
|
<mu>[/.] { return 'SEP'; }
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
<mu>"}}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
<mu>"}}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
||||||
<mu>"}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
<mu>"}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
||||||
<mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
|
<mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
|
||||||
<mu>[a-zA-Z0-9_]+/[} /.] { return 'ID'; }
|
<mu>[a-zA-Z0-9_]+/[=} /.] { return 'ID'; }
|
||||||
<mu>. { return 'INVALID'; }
|
<mu>. { return 'INVALID'; }
|
||||||
|
|
||||||
<INITIAL,mu><<EOF>> { return 'EOF'; }
|
<INITIAL,mu><<EOF>> { return 'EOF'; }
|
||||||
|
|||||||
+17
-1
@@ -54,7 +54,9 @@ simpleInverse
|
|||||||
;
|
;
|
||||||
|
|
||||||
inMustache
|
inMustache
|
||||||
: path params { $$ = [$1].concat($2) }
|
: path params hash { $$ = [$1].concat($2).concat([$3]) }
|
||||||
|
| path params { $$ = [$1].concat($2) }
|
||||||
|
| path hash { $$ = [$1].concat([$2]) }
|
||||||
| path { $$ = [$1] }
|
| path { $$ = [$1] }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -68,6 +70,20 @@ param
|
|||||||
| STRING { $$ = new yy.StringNode($1) }
|
| STRING { $$ = new yy.StringNode($1) }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
hash
|
||||||
|
: hashSegments { $$ = new yy.HashNode($1) }
|
||||||
|
;
|
||||||
|
|
||||||
|
hashSegments
|
||||||
|
: hashSegments hashSegment { $1.push($2); $$ = $1 }
|
||||||
|
| hashSegment { $$ = [$1] }
|
||||||
|
;
|
||||||
|
|
||||||
|
hashSegment
|
||||||
|
: ID EQUALS path { $$ = [$1, $3] }
|
||||||
|
| ID EQUALS STRING { $$ = [$1, new yy.StringNode($3)] }
|
||||||
|
;
|
||||||
|
|
||||||
path
|
path
|
||||||
: pathSegments { $$ = new yy.IdNode($1) }
|
: pathSegments { $$ = new yy.IdNode($1) }
|
||||||
;
|
;
|
||||||
|
|||||||
Reference in New Issue
Block a user