123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- "use strict";
- var ensureString = require("type/string/ensure")
- , ensurePlainFunction = require("type/plain-function/ensure")
- , from = require("es5-ext/array/from")
- , primitiveSet = require("es5-ext/object/primitive-set")
- , eventEmitter = require("event-emitter")
- , allOff = require("event-emitter/all-off")
- , d = require("d")
- , eolSet = require("./lib/ws-eol")
- , wsSet = require("./lib/ws")
- , identStart = require("./lib/ident-start-pattern")
- , identNext = require("./lib/ident-next-pattern");
- var objHasOwnProperty = Object.prototype.hasOwnProperty
- , preRegExpSet = primitiveSet.apply(null, from(";{=([,<>+-*/%&|^!~?:}"))
- , nonNameSet = primitiveSet.apply(null, from(";{=([,<>+-*/%&|^!~?:})].`"))
- , reIdentStart = new RegExp(identStart)
- , reIdentNext = new RegExp(identNext);
- var code, index, char, state, columnIndex, line, quote, scopeDepth, templateContext, previousToken
- , followsWhitespace, results, followsSkip, collectedScopeDatum, collectedScopeData
- , collectedScopeDepth, commentDatum, shouldCollectComments;
- var handleEol = function () {
- if (char === "\r" && code[index + 1] === "\n") char = code[++index];
- columnIndex = index + 1;
- ++line;
- };
- var emitter = eventEmitter();
- var accessor = Object.create(null, {
- skipCodePart: d(function (codePart) {
- var codePartLength = codePart.length;
- for (var i = 0; i < codePartLength; ++i) {
- if (code[index + i] !== codePart[i]) return false;
- }
- index += codePartLength;
- char = code[index];
- previousToken = code[index - 1];
- followsWhitespace = false;
- followsSkip = true;
- return true;
- }),
- skipIdentifier: d(function () {
- if (!reIdentStart.test(char)) return null;
- var startIndex = index;
- var identifier = char;
- while ((char = code[++index]) && reIdentNext.test(char)) identifier += char;
- followsWhitespace = false;
- followsSkip = true;
- previousToken = code[index - 1];
- return { name: identifier, start: startIndex, end: index };
- }),
- skipWhitespace: d(function () {
- while (char) {
- if (objHasOwnProperty.call(wsSet, char)) {
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- } else if (char === "/") {
- if (code[index + 1] === "/") {
- // Single line comment
- if (shouldCollectComments) {
- commentDatum = {
- type: "comment",
- point: index,
- line: line,
- column: index - columnIndex
- };
- }
- index += 2;
- char = code[index];
- while (char) {
- if (objHasOwnProperty.call(eolSet, char)) {
- if (commentDatum) {
- commentDatum.endPoint = index;
- results.push(commentDatum);
- commentDatum = null;
- }
- handleEol();
- break;
- }
- char = code[++index];
- }
- } else if (code[index + 1] === "*") {
- if (shouldCollectComments) {
- commentDatum = {
- type: "comment",
- point: index,
- line: line,
- column: index - columnIndex
- };
- }
- index += 2;
- char = code[index];
- while (char) {
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- if (char === "*" && code[index + 1] === "/") {
- if (commentDatum) {
- commentDatum.endPoint = index + 2;
- results.push(commentDatum);
- commentDatum = null;
- }
- char = code[++index];
- break;
- }
- char = code[++index];
- }
- } else {
- break;
- }
- } else {
- break;
- }
- followsWhitespace = true;
- followsSkip = true;
- char = code[++index];
- }
- }),
- collectScope: d(function () {
- if (char !== "(") return;
- previousToken = char;
- char = code[++index];
- followsSkip = true;
- if (collectedScopeDatum) collectedScopeData.push(collectedScopeDepth, collectedScopeDatum);
- collectedScopeDepth = ++scopeDepth;
- collectedScopeDatum = {
- type: "scope",
- point: index + 1,
- line: line,
- column: index - columnIndex + 1
- };
- }),
- stop: d(function () { state = null; }),
- index: d.gs(function () { return index; }),
- previousToken: d.gs(function () { return previousToken; }),
- scopeDepth: d.gs(function () { return scopeDepth; }),
- shouldCollectComments: d.gs(
- function () { return shouldCollectComments; },
- function (value) { shouldCollectComments = Boolean(value); }
- )
- });
- module.exports = function (userCode, executor) {
- code = ensureString(userCode);
- executor = ensurePlainFunction(executor);
- allOff(emitter);
- executor(emitter, accessor);
- index = -1;
- state = "out";
- columnIndex = 0;
- line = 1;
- scopeDepth = 0;
- templateContext = [];
- previousToken = null;
- followsWhitespace = true;
- results = [];
- followsSkip = false;
- collectedScopeDatum = null;
- collectedScopeData = [];
- collectedScopeDepth = null;
- stateLoop: while (state) {
- if (followsSkip) followsSkip = false;
- else char = code[++index];
- if (!char) break;
- switch (state) {
- case "out":
- if (objHasOwnProperty.call(wsSet, char)) {
- if (objHasOwnProperty.call(eolSet, char)) {
- handleEol();
- }
- followsWhitespace = true;
- continue stateLoop;
- }
- if (char === "/") {
- if (previousToken && objHasOwnProperty.call(preRegExpSet, previousToken)) {
- state = "slashOrRegexp";
- } else {
- state = "slash";
- }
- } else if (char === "'" || char === "\"") {
- state = "string";
- quote = char;
- } else if (char === "`") {
- state = "template";
- } else if (char === "(" || char === "{" || char === "[") {
- ++scopeDepth;
- } else if (char === ")" || char === "}" || char === "]") {
- if (scopeDepth === collectedScopeDepth) {
- collectedScopeDatum.raw = code.slice(collectedScopeDatum.point - 1, index);
- results.push(collectedScopeDatum);
- collectedScopeDatum = collectedScopeData.pop();
- collectedScopeDepth = collectedScopeData.pop();
- }
- --scopeDepth;
- if (char === "}") {
- if (templateContext[templateContext.length - 1] === scopeDepth + 1) {
- templateContext.pop();
- state = "template";
- }
- }
- }
- if (
- !previousToken ||
- followsWhitespace ||
- objHasOwnProperty.call(nonNameSet, previousToken) ||
- objHasOwnProperty.call(nonNameSet, char)
- ) {
- emitter.emit("trigger:" + char, accessor);
- if (followsSkip) continue stateLoop;
- }
- previousToken = char;
- followsWhitespace = false;
- continue stateLoop;
- case "slashOrRegexp":
- case "slash":
- if (char === "/") {
- if (shouldCollectComments) {
- commentDatum = {
- type: "comment",
- point: index - 1,
- line: line,
- column: index - columnIndex - 1
- };
- }
- state = "singleLineComment";
- } else if (char === "*") {
- if (shouldCollectComments) {
- commentDatum = {
- type: "comment",
- point: index - 1,
- line: line,
- column: index - columnIndex - 1
- };
- }
- state = "multiLineComment";
- } else if (objHasOwnProperty.call(eolSet, char)) {
- handleEol();
- followsWhitespace = true;
- state = "out";
- continue stateLoop;
- } else if (state === "slashOrRegexp") {
- state = "regexp";
- } else {
- state = "out";
- continue stateLoop;
- }
- break;
- case "singleLineComment":
- if (objHasOwnProperty.call(eolSet, char)) {
- if (commentDatum) {
- commentDatum.endPoint = index;
- results.push(commentDatum);
- commentDatum = null;
- }
- handleEol();
- followsWhitespace = true;
- state = "out";
- }
- continue stateLoop;
- case "multiLineComment":
- if (char === "*") state = "multiLineCommentStar";
- else if (objHasOwnProperty.call(eolSet, char)) handleEol();
- continue stateLoop;
- case "multiLineCommentStar":
- if (char === "/") {
- followsWhitespace = true;
- state = "out";
- if (commentDatum) {
- commentDatum.endPoint = index + 1;
- results.push(commentDatum);
- commentDatum = null;
- }
- } else if (char !== "*") {
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- state = "multiLineComment";
- }
- continue stateLoop;
- case "string":
- if (char === "\\") state = "stringEscape";
- else if (char === quote) state = "out";
- break;
- case "stringEscape":
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- state = "string";
- break;
- case "template":
- if (char === "$") state = "templateDollar";
- else if (char === "\\") state = "templateEscape";
- else if (char === "`") state = "out";
- else if (objHasOwnProperty.call(eolSet, char)) handleEol();
- break;
- case "templateEscape":
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- state = "template";
- break;
- case "templateDollar":
- if (char === "{") {
- templateContext.push(++scopeDepth);
- state = "out";
- } else if (char !== "$") {
- if (objHasOwnProperty.call(eolSet, char)) handleEol();
- state = "template";
- }
- break;
- case "regexp":
- if (char === "\\") state = "regexpEscape";
- else if (char === "/") state = "out";
- break;
- case "regexpEscape":
- state = "regexp";
- break;
- /* istanbul ignore next */
- default:
- throw new Error("Unexpected state " + state);
- }
- previousToken = null;
- followsWhitespace = false;
- }
- return results;
- };
|