diff options
Diffstat (limited to 'lib/handlebars')
-rw-r--r-- | lib/handlebars/ast.js | 53 | ||||
-rw-r--r-- | lib/handlebars/handlebars_lexer.js | 71 | ||||
-rw-r--r-- | lib/handlebars/jison_ext.js | 58 | ||||
-rw-r--r-- | lib/handlebars/printer.js | 92 |
4 files changed, 274 insertions, 0 deletions
diff --git a/lib/handlebars/ast.js b/lib/handlebars/ast.js new file mode 100644 index 0000000..d37d378 --- /dev/null +++ b/lib/handlebars/ast.js @@ -0,0 +1,53 @@ +var ProgramNode = function(statements, inverse) { + this.type = "program"; + this.statements = statements; + this.inverse = inverse; +}; + +var MustacheNode = function(params) { + this.type = "mustache"; + this.id = params[0]; + this.params = params.slice(1); +}; + +var PartialNode = function(id) { + this.type = "partial"; + this.id = id; +}; + +var BlockNode = function(mustache, program) { + this.type = "block"; + this.mustache = mustache; + this.program = program; +}; + +var ContentNode = function(string) { + this.type = "content"; + this.string = string; +} + +var IdNode = function(id) { + this.type = "ID" + this.id = id; +} + +StringNode = function(string) { + this.type = "STRING"; + this.string = string; +} + +CommentNode = function(comment) { + this.type = "comment"; + this.comment = comment; +} + +if(exports) { + exports.ProgramNode = ProgramNode; + exports.MustacheNode = MustacheNode; + exports.ContentNode = ContentNode; + exports.IdNode = IdNode; + exports.StringNode = StringNode; + exports.PartialNode = PartialNode; + exports.CommentNode = CommentNode; + exports.BlockNode = BlockNode; +} diff --git a/lib/handlebars/handlebars_lexer.js b/lib/handlebars/handlebars_lexer.js new file mode 100644 index 0000000..f92fc17 --- /dev/null +++ b/lib/handlebars/handlebars_lexer.js @@ -0,0 +1,71 @@ +if(require) { + var Lexer = require("handlebars/jison_ext").Lexer +} + +var HandlebarsLexer = function() { + this.state = "CONTENT"; +}; +HandlebarsLexer.prototype = new Lexer; + +HandlebarsLexer.prototype.lex = function() { + if(this.input === "") return; + + this.setupLex(); + + var lookahead = this.peek(2); + var result = ''; + + if(this.state == "MUSTACHE") { + // chomp optional whitespace + while(this.peek() === " ") { this.readchar(); } + + if(this.peek(2) === "}}") { + this.state = "CONTENT" + this.getchar(2); + + if(this.peek() == "}") this.getchar(); + return "CLOSE"; + } else if(this.peek() === '"') { + this.getchar(); + while(this.peek() !== '"') { this.getchar() } + this.getchar(); + return "STRING"; + } else { + while(this.peek().match(/[A-Za-z]/)) { this.getchar() } + return "ID" + } + } else if(lookahead == "{{") { + this.state = "MUSTACHE"; + this.getchar(2); + + var peek = this.peek(); + + if(peek === ">") { + this.getchar(); + return "OPEN_PARTIAL"; + } else if(peek === "#") { + this.getchar(); + return "OPEN_BLOCK"; + } else if(peek === "/") { + this.getchar(); + return "OPEN_ENDBLOCK"; + } else if(peek === "^") { + this.getchar(); + return "OPEN_INVERSE" + } else if(peek === "!") { + this.getchar(); + this.setupLex(); + while(this.peek(2) !== "}}") { this.getchar(); }; + this.readchar(2); + this.state = "CONTENT" + return "COMMENT"; + } else { + return "OPEN"; + } + } else { + while(this.peek(2) !== "{{" && this.peek(2) !== "") { result = result + this.getchar(); } + return "CONTENT" + } +}; + +if(exports) { exports.Lexer = HandlebarsLexer; } diff --git a/lib/handlebars/jison_ext.js b/lib/handlebars/jison_ext.js new file mode 100644 index 0000000..0608034 --- /dev/null +++ b/lib/handlebars/jison_ext.js @@ -0,0 +1,58 @@ +var Lexer = function() {}; + +Lexer.prototype = { + setInput: function(input) { + this.input = input; + this.yylineno = 0; + }, + + setupLex: function() { + this.yyleng = 0; + this.yytext = ''; + }, + + getchar: function(n) { + n = n || 1; + var char = ""; + + for(var i=0; i<n; i++) { + char += this.input[0]; + this.yytext += this.input[0]; + this.yyleng++; + + if(char === "\n") this.yylineno++; + + this.input = this.input.slice(1); + } + return char; + }, + + readchar: function(n) { + n = n || 1; + var char; + + for(var i=0; i<n; i++) { + char = this.input[i]; + if(char === "\n") this.yylineno++; + } + + this.input = this.input.slice(n); + }, + + peek: function(n) { + return this.input.slice(0, n || 1); + } +}; + +var Visitor = function() {}; + +Visitor.prototype = { + accept: function(object) { + return this[object.type](object); + } +} + +if(exports) { + exports.Lexer = Lexer; + exports.Visitor = Visitor; +} diff --git a/lib/handlebars/printer.js b/lib/handlebars/printer.js new file mode 100644 index 0000000..542cfcf --- /dev/null +++ b/lib/handlebars/printer.js @@ -0,0 +1,92 @@ +if(exports) { var Visitor = require("handlebars/jison_ext").Visitor } + +PrintVisitor = function() { this.padding = 0; }; +PrintVisitor.prototype = new Visitor; + +PrintVisitor.prototype.pad = function(string, newline) { + var out = ""; + + for(var i=0,l=this.padding; i<l; i++) { + out = out + " "; + } + + out = out + string; + + if(newline !== false) { out = out + "\n" } + return out; +}; + +PrintVisitor.prototype.program = function(program) { + var out = this.pad("PROGRAM:"), + statements = program.statements, + inverse = program.inverse; + + this.padding++; + + for(var i=0, l=statements.length; i<l; i++) { + out = out + this.accept(statements[i]); + } + + this.padding--; + + if(inverse) { + out = out + this.pad("{{^}}"); + + this.padding++; + + for(var i=0, l=inverse.length; i<l; i++) { + out = out + this.accept(inverse[i]); + } + } + + this.padding--; + + return out; +}; + +PrintVisitor.prototype.block = function(block) { + var out = ""; + + out = out + this.pad("BLOCK:"); + this.padding++; + out = out + this.accept(block.mustache); + out = out + this.accept(block.program); + this.padding--; + + return out; +}; + +PrintVisitor.prototype.mustache = function(mustache) { + var params = mustache.params, paramStrings = []; + + for(var i=0, l=params.length; i<l; i++) { + paramStrings.push(this.accept(params[i])); + } + + var params = "[" + paramStrings.join(", ") + "]"; + return this.pad("{{ " + this.accept(mustache.id) + " " + params + "}}"); +}; + +PrintVisitor.prototype.partial = function(partial) { + return this.pad("{{> " + this.accept(partial.id) + " }}"); +}; + +PrintVisitor.prototype.STRING = function(string) { + return string.string; +}; + +PrintVisitor.prototype.ID = function(id) { + return "ID:" + id.id; +}; + +PrintVisitor.prototype.content = function(content) { + return this.pad("CONTENT[ '" + content.string + "' ]"); +}; + +PrintVisitor.prototype.comment = function(comment) { + return this.pad("{{! '" + comment.comment + "' }}"); +} + +if(exports) { + exports.PrintVisitor = PrintVisitor; +} |