2
0
mirror of https://github.com/tenrok/BBob.git synced 2026-05-15 11:59:37 +03:00
Files
bbob/benchmark/lexer_old.js
T
Nikolay Kost 8797f7f363 feat: typescript support (#185)
* feat: initial typescript support

* feat: typescript support

* feat(plugin-helper): move files to typescript

* chore: update lock files

* feat: preset types

* fix: build

* fix: benchmark

* fix: remove pnpm cache

* fix: bench action

* fix: pnpm recursive install

* fix: nx cache

* fix: lock file

* fix: workflows

* fix: lerna support in pnpm

* fix: pnpm workspace

* fix: remove unused files

* fix: pnpm lock file

* fix: update lerna for support pnpm

* fix: lerna bootstrap

* fix: rollup build

* fix: update nx

* fix: build

* fix: add nx dep target

* fix: remove nx cache

* fix: workflow run on push only for master

* fix: test workflow run on push only for master

* fix: remove parallel for gen types

* fix: benchmark

* fix: benchmark imports

* fix: pnpm

* fix: types errors and pnpm

* fix: types

* fix: types

* refactor: parser

* fix(parser): tests

* fix: preset tests

* fix: react types

* fix: react type declarations

* fix: pnpm lock file

* fix: react preset types

* fix: lock file

* fix: vue2 types

* feat: dev container support

* fix: types

* fix: types

* refactor: rewrite pkg-task, add nx gen-types deps, fix react/render.ts

* refactor: types

* fix: types

* fix: rename gen-types to types

* fix: nx build order

* fix: nx reset

* fix: define nx deps explicit

* fix: build

* fix: nx

* fix: nx order build

* fix: nx deps

* fix: bbob cli tests

* fix: tests

* fix: cli tests and import

* fix: test cover

* fix: cli cover
2024-04-23 21:11:14 +02:00

242 lines
6.8 KiB
JavaScript
Vendored

/* eslint-disable no-plusplus,no-param-reassign */
const {
OPEN_BRAKET,
CLOSE_BRAKET,
QUOTEMARK,
BACKSLASH,
SLASH,
SPACE,
TAB,
EQ,
N,
} = require('@bbob/plugin-helper');
const {
Token, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_NEW_LINE, TYPE_SPACE, TYPE_TAG, TYPE_WORD,
} = require('@bbob/parser/Token');
const { createCharGrabber, trimChar, unquote } = require('@bbob/parser/utils');
// for cases <!-- -->
const EM = '!';
/**
* Creates a Token entity class
* @param {String} type
* @param {String} value
* @param {Number} r line number
* @param {Number} cl char number in line
*/
const createToken = (type, value, r = 0, cl = 0) => new Token(type, value, r, cl);
/**
* @typedef {Object} Lexer
* @property {Function} tokenize
* @property {Function} isTokenNested
*/
/**
* @param {String} buffer
* @param {Object} options
* @param {Function} options.onToken
* @param {String} options.openTag
* @param {String} options.closeTag
* @param {Boolean} options.enableEscapeTags
* @return {Lexer}
*/
function createLexer(buffer, options = {}) {
let row = 0;
let col = 0;
let tokenIndex = -1;
const tokens = new Array(Math.floor(buffer.length));
const openTag = options.openTag || OPEN_BRAKET;
const closeTag = options.closeTag || CLOSE_BRAKET;
const escapeTags = options.enableEscapeTags;
const RESERVED_CHARS = [closeTag, openTag, QUOTEMARK, BACKSLASH, SPACE, TAB, EQ, N, EM];
const NOT_CHAR_TOKENS = [
// ...(options.enableEscapeTags ? [BACKSLASH] : []),
openTag, SPACE, TAB, N,
];
const WHITESPACES = [SPACE, TAB];
const SPECIAL_CHARS = [EQ, SPACE, TAB];
const isCharReserved = (char) => (RESERVED_CHARS.indexOf(char) >= 0);
const isWhiteSpace = (char) => (WHITESPACES.indexOf(char) >= 0);
const isCharToken = (char) => (NOT_CHAR_TOKENS.indexOf(char) === -1);
const isSpecialChar = (char) => (SPECIAL_CHARS.indexOf(char) >= 0);
const isEscapableChar = (char) => (char === openTag || char === closeTag || char === BACKSLASH);
const isEscapeChar = (char) => char === BACKSLASH;
/**
* Emits newly created token to subscriber
* @param token
*/
const emitToken = (token) => {
if (options.onToken) {
options.onToken(token);
}
tokenIndex += 1;
tokens[tokenIndex] = token;
};
/**
* Parses params inside [myTag---params goes here---]content[/myTag]
* @param str
* @returns {{tag: *, attrs: Array}}
*/
const parseAttrs = (str) => {
let tagName = null;
let skipSpecialChars = false;
const attrTokens = [];
const attrCharGrabber = createCharGrabber(str);
const validAttr = (char) => {
const isEQ = char === EQ;
const isWS = isWhiteSpace(char);
const prevChar = attrCharGrabber.getPrev();
const nextChar = attrCharGrabber.getNext();
const isPrevSLASH = prevChar === BACKSLASH;
const isTagNameEmpty = tagName === null;
if (isTagNameEmpty) {
return (isEQ || isWS || attrCharGrabber.isLast()) === false;
}
if (skipSpecialChars && isSpecialChar(char)) {
return true;
}
if (char === QUOTEMARK && !isPrevSLASH) {
skipSpecialChars = !skipSpecialChars;
if (!skipSpecialChars && !(nextChar === EQ || isWhiteSpace(nextChar))) {
return false;
}
}
return (isEQ || isWS) === false;
};
const nextAttr = () => {
const attrStr = attrCharGrabber.grabWhile(validAttr);
const currChar = attrCharGrabber.getCurr();
// first string before space is a tag name [tagName params...]
if (tagName === null) {
tagName = attrStr;
} else if (isWhiteSpace(currChar) || currChar === QUOTEMARK || !attrCharGrabber.hasNext()) {
const escaped = unquote(trimChar(attrStr, QUOTEMARK));
attrTokens.push(createToken(TYPE_ATTR_VALUE, escaped, row, col));
} else {
attrTokens.push(createToken(TYPE_ATTR_NAME, attrStr, row, col));
}
attrCharGrabber.skip();
};
while (attrCharGrabber.hasNext()) {
nextAttr();
}
return { tag: tagName, attrs: attrTokens };
};
const bufferGrabber = createCharGrabber(buffer, {
onSkip: () => {
col++;
},
});
const next = () => {
const currChar = bufferGrabber.getCurr();
const nextChar = bufferGrabber.getNext();
if (currChar === N) {
bufferGrabber.skip();
col = 0;
row++;
emitToken(createToken(TYPE_NEW_LINE, currChar, row, col));
} else if (isWhiteSpace(currChar)) {
const str = bufferGrabber.grabWhile(isWhiteSpace);
emitToken(createToken(TYPE_SPACE, str, row, col));
} else if (escapeTags && isEscapeChar(currChar) && isEscapableChar(nextChar)) {
bufferGrabber.skip(); // skip the \ without emitting anything
bufferGrabber.skip(); // skip past the [, ] or \ as well
emitToken(createToken(TYPE_WORD, nextChar, row, col));
} else if (currChar === openTag) {
bufferGrabber.skip(); // skip openTag
// detect case where we have '[My word [tag][/tag]' or we have '[My last line word'
const substr = bufferGrabber.substrUntilChar(closeTag);
const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0;
if (isCharReserved(nextChar) || hasInvalidChars || bufferGrabber.isLast()) {
emitToken(createToken(TYPE_WORD, currChar, row, col));
} else {
const str = bufferGrabber.grabWhile((val) => val !== closeTag);
bufferGrabber.skip(); // skip closeTag
// [myTag ]
const isNoAttrsInTag = str.indexOf(EQ) === -1;
// [/myTag]
const isClosingTag = str[0] === SLASH;
if (isNoAttrsInTag || isClosingTag) {
emitToken(createToken(TYPE_TAG, str, row, col));
} else {
const parsed = parseAttrs(str);
emitToken(createToken(TYPE_TAG, parsed.tag, row, col));
parsed.attrs.map(emitToken);
}
}
} else if (currChar === closeTag) {
bufferGrabber.skip(); // skip closeTag
emitToken(createToken(TYPE_WORD, currChar, row, col));
} else if (isCharToken(currChar)) {
if (escapeTags && isEscapeChar(currChar) && !isEscapableChar(nextChar)) {
bufferGrabber.skip();
emitToken(createToken(TYPE_WORD, currChar, row, col));
} else {
const str = bufferGrabber.grabWhile((char) => {
if (escapeTags) {
return isCharToken(char) && !isEscapeChar(char);
}
return isCharToken(char);
});
emitToken(createToken(TYPE_WORD, str, row, col));
}
}
};
const tokenize = () => {
while (bufferGrabber.hasNext()) {
next();
}
tokens.length = tokenIndex + 1;
return tokens;
};
const isTokenNested = (token) => {
const value = openTag + SLASH + token.getValue();
// potential bottleneck
return buffer.indexOf(value) > -1;
};
return {
tokenize,
isTokenNested,
};
}
module.exports.createLexer = createLexer;