From f3cbbe59529d2975b14617b8efb3ee6516c3e0a6 Mon Sep 17 00:00:00 2001 From: Kabir Shah Date: Wed, 3 Apr 2019 12:17:54 -0700 Subject: [PATCH] build with new lexer --- packages/moon/dist/moon.js | 73 +++++++++++++++++++++++++++++----- packages/moon/dist/moon.min.js | 2 +- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/packages/moon/dist/moon.js b/packages/moon/dist/moon.js index e2822012..865ef755 100644 --- a/packages/moon/dist/moon.js +++ b/packages/moon/dist/moon.js @@ -359,9 +359,38 @@ return "".concat(prelude, ";return [function(_0){").concat(setElement(root.element, "_0;")).concat(create, "},function(){").concat(update, "},function(){").concat(destroy, "}];"); }; + /** + * Capture the tag name, attribute text, and closing slash from an opening tag. + */ var typeRE = /<([\w\d-_]+)([^>]*?)(\/?)>/g; + /** + * Capture a key, value, and expression from a list of whitespace-separated + * attributes. There cannot be a value and an expression, but both are captured + * due to the limits of regular expressions. One or both of them can be + * undefined. + */ + var attributeRE = /\s*([\w\d-_]*)(?:=(?:("[\w\d-_]*"|'[\w\d-_]*')|{([\w\d-_]*)}))?/g; + /** + * Lexer + * + * The lexer is responsible for taking an input view template and converting it + * into a list of tokens. To make the parser's job easier, it does some extra + * processing and handles tag names, attribute key/value pairs, and converting + * text into components. + * + * It works by running through the input text and checking for specific initial + * characters such as "<", "{", or any text. After identifying the type of + * token, it processes each part individually until the end of the token. The + * lexer appends the new token to a cumulative list and eventually returns it. + * + * @param {string} input + * @returns {Object[]} tokens + */ + function lex(input) { + // Remove leading and trailing whitespace because the lexer should only + // accept one element as an input, and whitespace counts as text. input = input.trim(); var tokens = []; @@ -372,6 +401,8 @@ var nextChar = input[i + 1]; if (nextChar === "/") { + // Append a closing tag token if a sequence of characters begins + // with "", i + 2); var _type = input.slice(i + 2, closeIndex); @@ -383,31 +414,48 @@ i = closeIndex + 1; continue; } else if (nextChar === "!" && input[i + 2] === "-" && input[i + 3] === "-") { + // Ignore input if a sequence of characters begins with "", i + 4) + 3; continue; - } + } // Set the last searched index of the tag type regular expression to + // the index of the character currently being processed. Since it is + // being executed on the whole input, this is required for getting the + // correct match and having better performance. + + + typeRE.lastIndex = i; // Execute the tag type regular expression on the input and store + // the match and captured groups. - typeRE.lastIndex = i; var typeExec = typeRE.exec(input); var typeMatch = typeExec[0]; var type = typeExec[1]; var attributesText = typeExec[2]; var closingSlash = typeExec[3]; var attributes = {}; - var attributeExec = void 0; + var attributeExec = void 0; // Keep matching for new attribute key/value pairs until there are no + // more in the attribute text. while ((attributeExec = attributeRE.exec(attributesText)) !== null) { + // Store the match and captured groups. var attributeMatch = attributeExec[0]; var attributeKey = attributeExec[1]; var attributeValue = attributeExec[2]; var attributeExpression = attributeExec[3]; if (attributeMatch.length === 0) { + // If nothing is matched, continue searching from the next + // character. This is required because the attribute regular + // expression can have empty matches and create an infinite + // loop. attributeRE.lastIndex += 1; } else { + // Store the key/value pair using the matched value or + // expression. attributes[attributeKey] = attributeExpression === undefined ? attributeValue : attributeExpression; } - } + } // Append an opening tag token with the type, attributes, and optional + // self-closing slash. + tokens.push({ type: "tagOpen", @@ -417,7 +465,9 @@ }); i += typeMatch.length; } else if (_char === "{") { - var expression = ""; + // If a sequence of characters begins with "{", process it as an + // expression token. + var expression = ""; // Consume the input until the end of the expression. for (i += 1; i < input.length; i++) { var _char2 = input[i]; @@ -427,7 +477,9 @@ } else { expression += _char2; } - } + } // Append the expression as a element with the appropriate + // text content attribute. + tokens.push({ type: "tagOpen", @@ -439,17 +491,20 @@ }); i += 1; } else { - var text = ""; + // If nothing has matched at this point, process the input as text. + var text = ""; // Consume the input until the start of a new tag or expression. for (; i < input.length; i++) { var _char3 = input[i]; - if (_char3 === "<") { + if (_char3 === "<" || _char3 === "{") { break; } else { text += _char3; } - } + } // Append the text as a element with the appropriate text + // content attribute. + tokens.push({ type: "tagOpen", diff --git a/packages/moon/dist/moon.min.js b/packages/moon/dist/moon.min.js index e50eb462..a07d2e52 100644 --- a/packages/moon/dist/moon.min.js +++ b/packages/moon/dist/moon.min.js @@ -4,4 +4,4 @@ * Released under the MIT License * https://kbrsh.github.io/moon */ -!function(e,t){"undefined"==typeof module?e.Moon=t():module.exports=t()}(this,function(){"use strict";function c(e){return(c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t]*?)(\/?)>/g,w=/\s*([\w\d-_]*)(?:=(?:("[\w\d-_]*"|'[\w\d-_]*')|{([\w\d-_]*)}))?/g;function l(e){var t=function(e){e=e.trim();for(var t=[],n=0;n",n+2),a=e.slice(n+2,r);t.push({type:"tagClose",value:a}),n=r+1;continue}if("!"===c&&"-"===e[n+2]&&"-"===e[n+3]){n=e.indexOf("--\x3e",n+4)+3;continue}_.lastIndex=n;for(var i=_.exec(e),u=i[0],l=i[1],f=i[2],s=i[3],v={},m=void 0;null!==(m=w.exec(f));){var d=m[0],p=m[1],h=m[2],y=m[3];0===d.length?w.lastIndex+=1:v[p]=void 0===y?h:y}t.push({type:"tagOpen",value:l,attributes:v,closed:"/"===s}),n+=u.length}else if("{"===o){var b="";for(n+=1;n]*?)(\/?)>/g,w=/\s*([\w\d-_]*)(?:=(?:("[\w\d-_]*"|'[\w\d-_]*')|{([\w\d-_]*)}))?/g;function l(e){var t=function(e){e=e.trim();for(var t=[],n=0;n",n+2),a=e.slice(n+2,r);t.push({type:"tagClose",value:a}),n=r+1;continue}if("!"===c&&"-"===e[n+2]&&"-"===e[n+3]){n=e.indexOf("--\x3e",n+4)+3;continue}_.lastIndex=n;for(var i=_.exec(e),u=i[0],l=i[1],f=i[2],s=i[3],v={},m=void 0;null!==(m=w.exec(f));){var d=m[0],p=m[1],h=m[2],y=m[3];0===d.length?w.lastIndex+=1:v[p]=void 0===y?h:y}t.push({type:"tagOpen",value:l,attributes:v,closed:"/"===s}),n+=u.length}else if("{"===o){var b="";for(n+=1;n