UNPKG

khufu

Version:

A template language for incremental-dom or DSL for javascript views

295 lines (263 loc) 10.3 kB
"use strict"; 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; }); }