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;
|
||||
};
|
||||
|
||||
Handlebars.AST.HashNode = function(pairs) {
|
||||
this.type = "hash";
|
||||
this.pairs = pairs;
|
||||
};
|
||||
|
||||
Handlebars.AST.IdNode = function(parts) {
|
||||
this.type = "ID";
|
||||
this.original = parts.join("/");
|
||||
|
||||
@@ -89,6 +89,19 @@ Handlebars.PrintVisitor.prototype.partial = function(partial) {
|
||||
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) {
|
||||
return '"' + string.string + '"';
|
||||
};
|
||||
|
||||
@@ -87,6 +87,10 @@ describe "Parser" do
|
||||
string.inspect
|
||||
end
|
||||
|
||||
def hash(*pairs)
|
||||
"HASH{" + pairs.map {|k,v| "#{k}=#{v}" }.join(", ") + "}"
|
||||
end
|
||||
|
||||
def id(id)
|
||||
"ID:#{id}"
|
||||
end
|
||||
@@ -112,6 +116,24 @@ describe "Parser" do
|
||||
ast_for("{{foo bar}}").should == program { mustache id("foo"), id("bar") }
|
||||
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
|
||||
ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), id("bar"), string("baz")}
|
||||
end
|
||||
|
||||
+22
-2
@@ -12,8 +12,10 @@ describe "Tokenizer" do
|
||||
out = []
|
||||
|
||||
while token = lexer.lex
|
||||
result = parser.terminals_[token]
|
||||
break if !result || result == "EOF"
|
||||
# p token
|
||||
result = parser.terminals_[token] || token
|
||||
# p result
|
||||
break if !result || result == "EOF" || result == "INVALID"
|
||||
out << Token.new(result, lexer.yytext)
|
||||
end
|
||||
|
||||
@@ -163,6 +165,24 @@ describe "Tokenizer" do
|
||||
result[2].should be_token("STRING", %{bar"baz})
|
||||
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
|
||||
Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
|
||||
end
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
<mu>"{{!".*?"}}" { yytext = yytext.substr(3,yyleng-5); this.begin("INITIAL"); return 'COMMENT'; }
|
||||
<mu>"{{" { return 'OPEN'; }
|
||||
|
||||
<mu>"=" { return 'EQUALS'; }
|
||||
<mu>"."/[} ] { return 'ID'; }
|
||||
<mu>".." { return 'ID'; }
|
||||
<mu>[/.] { return 'SEP'; }
|
||||
@@ -23,7 +24,7 @@
|
||||
<mu>"}}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
||||
<mu>"}}" { this.begin("INITIAL"); return 'CLOSE'; }
|
||||
<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'; }
|
||||
|
||||
<INITIAL,mu><<EOF>> { return 'EOF'; }
|
||||
|
||||
+17
-1
@@ -54,7 +54,9 @@ simpleInverse
|
||||
;
|
||||
|
||||
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] }
|
||||
;
|
||||
|
||||
@@ -68,6 +70,20 @@ param
|
||||
| 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
|
||||
: pathSegments { $$ = new yy.IdNode($1) }
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user