diff --git a/dist/handlebars.js b/dist/handlebars.js index 87214df29..5bd69b5c8 100644 --- a/dist/handlebars.js +++ b/dist/handlebars.js @@ -554,84 +554,86 @@ lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_STA var YYSTATE=YY_START switch($avoiding_name_collisions) { -case 0: +case 0: yy_.yytext = "\\"; return 14; +break; +case 1: if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); if(yy_.yytext) return 14; break; -case 1: return 14; +case 2: return 14; break; -case 2: +case 3: if(yy_.yytext.slice(-1) !== "\\") this.popState(); if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); return 14; break; -case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; -break; -case 4: this.begin("par"); return 24; +case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; break; -case 5: return 16; +case 5: this.begin("par"); return 24; break; -case 6: return 20; +case 6: return 16; break; -case 7: return 19; +case 7: return 20; break; case 8: return 19; break; -case 9: return 23; +case 9: return 19; break; case 10: return 23; break; -case 11: this.popState(); this.begin('com'); +case 11: return 23; break; -case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; +case 12: this.popState(); this.begin('com'); break; -case 13: return 22; +case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; break; -case 14: return 36; +case 14: return 22; break; -case 15: return 35; +case 15: return 36; break; case 16: return 35; break; -case 17: return 39; +case 17: return 35; break; -case 18: /*ignore whitespace*/ +case 18: return 39; break; -case 19: this.popState(); return 18; +case 19: /*ignore whitespace*/ break; case 20: this.popState(); return 18; break; -case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; +case 21: this.popState(); return 18; break; -case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; +case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; break; -case 23: yy_.yytext = yy_.yytext.substr(1); return 28; +case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; break; -case 24: return 32; +case 24: yy_.yytext = yy_.yytext.substr(1); return 28; break; case 25: return 32; break; -case 26: return 31; +case 26: return 32; +break; +case 27: return 31; break; -case 27: return 35; +case 28: return 35; break; -case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; +case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; break; -case 29: return 'INVALID'; +case 30: return 'INVALID'; break; -case 30: /*ignore whitespace*/ +case 31: /*ignore whitespace*/ break; -case 31: this.popState(); return 37; +case 32: this.popState(); return 37; break; -case 32: return 5; +case 33: return 5; break; } }; -lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/]; -lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; +lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/]; +lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}}; return lexer;})() parser.lexer = lexer; function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js index f12e44cd4..3de93116a 100644 --- a/spec/qunit_spec.js +++ b/spec/qunit_spec.js @@ -92,6 +92,12 @@ test("most basic", function() { shouldCompileTo("{{foo}}", { foo: "foo" }, "foo"); }); +test("escaping", function() { + shouldCompileTo("\\{{foo}}", { foo: "food" }, "{{foo}}"); + shouldCompileTo("\\\\{{foo}}", { foo: "food" }, "\\food"); + shouldCompileTo("\\\\ {{foo}}", { foo: "food" }, "\\\\ food"); +}); + test("compiling with a basic context", function() { shouldCompileTo("Goodbye\n{{cruel}}\n{{world}}!", {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!", "It works if all the required keys are provided"); diff --git a/src/handlebars.l b/src/handlebars.l index b32e39c64..cbf048d56 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -3,6 +3,7 @@ %% +"\\\\"/("{{") { yytext = "\\"; return 'CONTENT'; } [^\x00]*?/("{{") { if(yytext.slice(-1) !== "\\") this.begin("mu"); if(yytext.slice(-1) === "\\") yytext = yytext.substr(0,yyleng-1), this.begin("emu");