var Handlebars = {}; Handlebars.Lexer = require("handlebars/jison_ext").Lexer; // BEGIN(BROWSER) Handlebars.HandlebarsLexer = function() { this.state = "CONTENT"; }; Handlebars.HandlebarsLexer.prototype = new Handlebars.Lexer(); // The HandlebarsLexer uses a Lexer interface that is compatible // with Jison. // // setupLex reset internal state for a new token // peek(n) lookahead n characters and return (default 1) // getchar(n) remove n characters from the input and add // them to the matched text (default 1) // readchar(n) remove n characters from the input, but do not // add them to the matched text (default 1) // ignorechar(n) remove n characters from the input, and act // as though they were already matched in a // previous lex. this will ensure that the // pointer in the case of parse errors is in // the right place. Handlebars.HandlebarsLexer.prototype.lex = function() { if(this.input === "") { return; } this.setupLex(); var lookahead = this.peek(2); var result = ''; var peek; if(lookahead === "") { return; } if(this.state == "MUSTACHE") { if(this.peek() === "/") { this.getchar(); return "SEP"; } // chomp optional whitespace while(this.peek() === " ") { this.ignorechar(); } lookahead = this.peek(2); // in a mustache, but less than 2 characters left => error if(lookahead.length != 2) { return; } // if the next characters are '}}', the mustache is done if(lookahead === "}}") { this.state = "CONTENT"; this.getchar(2); // handle the case of {{{ foo }}} by always chomping // a final }. TODO: Track escape state and handle the // error condition here if(this.peek() == "}") { this.getchar(); } return "CLOSE"; // if the next character is a quote => enter a String } else if(this.peek() === '"') { this.readchar(); // scan the String until another quote is reached, skipping over escaped quotes while(this.peek() !== '"') { if(this.peek(2) === '\\"') { this.readchar(); } this.getchar(); } this.readchar(); return "STRING"; // All other cases are IDs or errors } else { // grab alphanumeric characters while(this.peek().match(/[_0-9A-Za-z\.]/)) { this.getchar(); } peek = this.peek(); if(peek !== "}" && peek !== " " && peek !== "/") { return; } // if any characters were grabbed => ID if(this.yytext.length) { return "ID"; } // Otherwise => Error else { return; } } // Next chars are {{ => Open mustache } else if(lookahead == "{{") { this.state = "MUSTACHE"; this.getchar(2); 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 === "{" || peek === "&") { this.getchar(); return "OPEN_UNESCAPED"; } else if(peek === "!") { this.readchar(); this.setupLex(); // reset the lexer state so the yytext is the comment only while(this.peek(2) !== "}}") { this.getchar(); } this.readchar(2); this.state = "CONTENT"; return "COMMENT"; } else { return "OPEN"; } // Otherwise => content section } else { while(this.peek(2) !== "{{" && this.peek(2) !== "") { result = result + this.getchar(); } return "CONTENT"; } }; // END(BROWSER) exports.Lexer = Handlebars.HandlebarsLexer;