khufu
Version:
A template language for incremental-dom or DSL for javascript views
295 lines (263 loc) • 10.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function () {
var lexer = new _baselexer2.default();
/********************* Common tokens *****************************/
lexer.addRule(/\/\/.*$/m, lex(function () {}), []);
lexer.addRule(/^\s*\/\/.*\n/m, lex(function () {}), []);
/// The order of whitespace rules matter
lexer.addRule(/\s*$/g, lex(function (lexeme) {
var tokens = ['NL'];
while (this.indent.length > 1) {
tokens.push("DEDENT");
this.indent.shift();
}
tokens.push('EOF');
return tokens;
}), []);
lexer.addRule(/\n/g, lex(function (lexeme) {
if (this.brackets.length == 0 && !this.newline) {
this.newline = true;
return 'NL';
}
}), []);
lexer.addRule(/^[\t ]*/gm, lex(function (lexeme) {
if (this.brackets.length != 0) {
return;
}
var indentation = lexeme.length;
if (this.state == VIEW) {
this.state = VIEW_LINESTART;
}
if (indentation > this.indent[0]) {
this.indent.unshift(indentation);
return "INDENT";
}
var tokens = [];
if (indentation < this.indent[0]) {
while (indentation < this.indent[0]) {
tokens.push("DEDENT");
this.indent.shift();
}
if (indentation != this.indent[0]) {
throw Error("Invalid indentation, expected " + (this.indent[0] + ", got " + indentation + " ") + ("at line " + this.yylineno));
}
}
if (this.indent[0] == 0) {
this.state = TOPLEVEL;
}
if (tokens.length) return tokens;
}), []);
lexer.addRule(/[ \t]+/, lex(function () {}), []);
lexer.addRule(/[({\[<]/, lex(function (lexeme) {
if (lexeme == '<') {
if (this.brackets.length == 0 && this.state == VIEW_LINESTART) {
this.brackets.unshift(lexeme);
this.state = VIEW_TAG;
} else {
this.reject = true;
}
} else {
this.brackets.unshift(lexeme);
if (this.state == VIEW_TAG) {
this.state = VIEW;
}
}
return lexeme;
}), []);
lexer.addRule(/[)}\]>]/, lex(function (lexeme) {
this.yytext = lexeme;
if (MATCHING_BRACKET[this.brackets[0]] != lexeme) {
this.reject = true;
return;
}
if (lexeme == '>') {
if (this.brackets.length == 1 && this.brackets[0] == '<') {
this.brackets.shift();
this.state = VIEW;
} else {
this.reject = true;
}
} else {
this.brackets.shift();
if (this.brackets[0] == '<') {
this.state = VIEW_TAG;
}
}
return lexeme;
}), []);
lexer.addRule(/"(?:\\["'bfnrt/\\]|\\u[a-fA-F0-9]{4}|[^"\\])*"/, lex(function (lex) {
this.yytext = unquote(lex.substr(1, lex.length - 2));
return 'STRING';
}), [TOPLEVEL, VIEW, VIEW_TAG, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/'(?:\\["'bfnrt/\\]|\\u[a-fA-F0-9]{4}|[^'\\])*'/, lex(function (lex) {
this.yytext = unquote(lex.substr(1, lex.length - 2));
return 'STRING';
}), [TOPLEVEL, VIEW, VIEW_TAG, VIEW_LINESTART, VIEW_TEMPLATE]);
/********************* Toplevel tokens ***************************/
lexer.addRule(/[a-zA-Z_][a-zA-Z0-9_]*/, lex(function (lexeme) {
if (TOPLEVEL_KW.indexOf(lexeme) >= 0) {
switch (lexeme) {
case "style":
this.state = STYLE;
break;
case "view":
this.state = VIEW;
break;
}
return lexeme;
} else {
return 'IDENT';
}
}), [TOPLEVEL]);
lexer.addRule(/[/*+\\-^,\\.]/, lex(function (lexeme) {
return lexeme;
}), [TOPLEVEL]);
/********************* Style tokens ***************************/
var css_esc = "\\\\[^\\n0-9a-fA-F]|\\\\[0-9a-fA-F]{1,6} ?";
var css_ident = "-*" + ("(?:[a-zA-Z_\\u0080-\\uffff]|" + css_esc + ")") + ("(?:[a-zA-Z_0-9\\u0080-\\uffff-]|" + css_esc + ")*");
var css_number = "[+-]?(?:[0-9]+\\.[0-9]+|[0-9]+|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?";
var css_unquoted = "(?:[^\"'()\\s\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]" + ("|" + css_esc + ")*");
var css_string = "(?:\"(?:[^\"\\\\\\n]|" + css_esc + "|\\\\\\n)*\"" + ("|'(?:[^'\\\\\\n]|" + css_esc + "|\\\\\\n)*')");
lexer.addRule(/[:.,]/, lex(function (x) {
return x;
}), [STYLE]);
lexer.addRule(/@media/, lex("MEDIA"), [STYLE]);
lexer.addRule(new RegExp(css_ident), lex("IDENT_TOKEN"), [STYLE]);
lexer.addRule(new RegExp(css_number), lex("NUMBER"), [STYLE]);
lexer.addRule(new RegExp(css_number + css_ident), lex("DIMENSION"), [STYLE]);
lexer.addRule(new RegExp(css_number + '%'), lex("PERCENTAGE_TOKEN"), [STYLE]);
lexer.addRule(new RegExp("url\\(\\s*(?:" + css_string + "|" + css_unquoted + ")\s*\\)"), lex("URL"), [STYLE]);
// I'm not sure it's useful in normal css but it's used
// in `composes: xx from "file"` in CSS Modules
lexer.addRule(new RegExp(css_string), lex("CSS_STRING"), [STYLE]);
lexer.addRule(new RegExp("#(?:[a-zA-Z_0-9\\u0080-\\uffff-]|${css_esc})*"), lex("HASH_TOKEN"), [STYLE]);
/********************* View tokens ***************************/
lexer.addRule(/->/, lex(function (x) {
return x;
}), [VIEW, VIEW_LINESTART]);
lexer.addRule(/<-/, lex(function (x) {
return x;
}), [VIEW, VIEW_LINESTART]);
lexer.addRule(/[!=][=][=]/, lex(function (x) {
return x;
}), [VIEW, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/[><!=]\=/, lex(function (x) {
return x;
}), [VIEW, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/[|:,.*+/%=<>!?-]/, lex(function (x) {
return x;
}), [VIEW, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/[a-zA-Z_][a-zA-Z0-9_]*/, lex(function (lexeme) {
if (VIEW_KW.indexOf(lexeme) >= 0) {
return lexeme;
} else {
return 'IDENT';
}
}), [VIEW, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/@[a-zA-Z_][a-zA-Z0-9_]*/, lex(function (lexeme) {
this.yytext = lexeme.substr(1);
return 'STORE';
}), [TOPLEVEL, VIEW, VIEW_LINESTART, VIEW_TAG, VIEW_TEMPLATE]);
lexer.addRule(/[a-zA-Z_][a-zA-Z0-9_-]*/, lex('TAG_NAME'), [VIEW_TAG]);
lexer.addRule(/[=.?+-]/, lex(function (x) {
return x;
}), [VIEW_TAG]);
lexer.addRule(new RegExp("(?:[0-9]|[1-9][0-9]+)" + // integer part
"(?:\\.[0-9]+)?" + // fractional part
"(?:[eE][-+]?[0-9]+)?\\b"), // exponent
lex("NUMBER"), [VIEW, VIEW_TAG, VIEW_LINESTART, VIEW_TEMPLATE]);
lexer.addRule(/`(?:\\["'bfnrt/\\]|\\u[a-fA-F0-9]{4}|\$(?!\{)|[^$`\\])*(?:`|\$\{)/, lex(function (lex) {
if (lex.charAt(lex.length - 1) == '`') {
this.yytext = unquote(lex.substr(1, lex.length - 2));
return 'TEMPLATE_STRING';
} else {
this.yytext = unquote(lex.substr(1, lex.length - 3));
this.templates += 1;
this.state = VIEW_TEMPLATE;
return 'TEMPLATE_BEGIN';
}
}), [VIEW, VIEW_TAG, VIEW_LINESTART]);
lexer.addRule(/}(?:\\["'bfnrt/\\]|\\u[a-fA-F0-9]{4}|\$(?!\{)|[^$`\\])*(?:`|\$\{)/, lex(function (lex) {
if (lex.charAt(lex.length - 1) == '`') {
this.yytext = unquote(lex.substr(1, lex.length - 2));
this.templates -= 1;
if (!this.templates) {
if (this.brackets[0] == '<') {
this.state = VIEW_TAG;
} else {
this.state = VIEW;
}
}
return 'TEMPLATE_END';
} else {
this.yytext = unquote(lex.substr(1, lex.length - 3));
return 'TEMPLATE_INTER';
}
}), [VIEW_TEMPLATE]);
return lexer.factory();
};
var _baselexer = require("./baselexer");
var _baselexer2 = _interopRequireDefault(_baselexer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var TOPLEVEL = 0;
var TOPLEVEL_KW = ["import", "from", "style", "view", "as"];
var NEWLINE = 1;
var VIEW_KW = ["store", "link", "let", "and", "or", "not", "for", "in", "of", "key", "if", "else", "elif", "catch"].concat(TOPLEVEL_KW); // reserve them for better error reporting
var STYLE = 2;
var VIEW = 10;
var VIEW_LINESTART = 12; // "<" at tag open
var VIEW_TAG = 14; // "abc-def" identifiers
var VIEW_TEMPLATE = 16; // ${vars} identifiers
var MATCHING_BRACKET = { '<': '>', '(': ')', '[': ']', '{': '}' };
function lex(value) {
if (typeof value == 'string') {
return lex(function (lexeme) {
return value;
});
} else {
return function (lexeme) {
this.yytext = lexeme;
var old_state = this.state;
var result = value.call(this, lexeme);
if (result && result != 'NL') {
this.newline = false;
}
if (old_state == this.state && this.state == VIEW_LINESTART) {
this.state = VIEW;
}
return result;
};
}
}
function unquote(value) {
return value.replace(/\\["'bfnrt/\\]|\\u[a-fA-F0-9]{4}/g, function (x) {
switch (x.charAt(1)) {
case '"':
return '"';
case "'":
return "'";
case "b":
return "\b";
case "f":
return "\f";
case "n":
return "\n";
case "r":
return "\r";
case "t":
return "\t";
case "/":
return "/";
case "\\":
return "\\";
case "u":
return String.fromCharCode(parseInt(x.substr(2), 16));
default:
return x;
}
return x;
});
}