add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
const OperationType = {
LOGIC: 'LOGIC',
STRING: 'STRING',
COMPARISON: 'COMPARISON',
MATH: 'MATH',
};
export class Lexer {
// operation table
static get optable() {
return {
'=': OperationType.LOGIC,
'&': OperationType.LOGIC,
'|': OperationType.LOGIC,
'?': OperationType.LOGIC,
':': OperationType.LOGIC,
'\'': OperationType.STRING,
'"': OperationType.STRING,
'!': OperationType.COMPARISON,
'>': OperationType.COMPARISON,
'<': OperationType.COMPARISON,
'(': OperationType.MATH,
')': OperationType.MATH,
'+': OperationType.MATH,
'-': OperationType.MATH,
'*': OperationType.MATH,
'/': OperationType.MATH,
'%': OperationType.MATH,
};
}
/**
* Constructor
* @param {*} expression -
*/
constructor(expression) {
this.currentIndex = 0;
this.input = expression;
this.tokenList = [];
}
getTokens() {
let tok;
do {
// read current token, so step should be -1
tok = this.pickNext(-1);
const pos = this.currentIndex;
switch (Lexer.optable[tok]) {
case OperationType.LOGIC:
// == && || ===
this.readLogicOpt(tok);
break;
case OperationType.STRING:
this.readString(tok);
break;
case OperationType.COMPARISON:
this.readCompare(tok);
break;
case OperationType.MATH:
this.receiveToken();
break;
default:
this.readValue(tok);
}
// if the pos not changed, this loop will go into a infinite loop, every step of while loop,
// we must move the pos forward
// so here we should throw error, for example `1 & 2`
if (pos === this.currentIndex && tok !== undefined) {
const err = new Error(`unkonw token ${tok} from input string ${this.input}`);
err.name = 'UnknowToken';
throw err;
}
} while (tok !== undefined)
return this.tokenList;
}
/**
* read next token, the index param can set next step, default go foward 1 step
*
* @param index next postion
*/
pickNext(index = 0) {
return this.input[index + this.currentIndex + 1];
}
/**
* Store token into result tokenList, and move the pos index
*
* @param index
*/
receiveToken(index = 1) {
const tok = this.input.slice(this.currentIndex, this.currentIndex + index).trim();
// skip empty string
if (tok) {
this.tokenList.push(tok);
}
this.currentIndex += index;
}
// ' or "
readString(tok) {
let next;
let index = 0;
do {
next = this.pickNext(index);
index += 1;
} while (next !== tok && next !== undefined);
this.receiveToken(index + 1);
}
// > or < or >= or <= or !==
// tok in (>, <, !)
readCompare(tok) {
if (this.pickNext() !== '=') {
this.receiveToken(1);
return;
}
// !==
if (tok === '!' && this.pickNext(1) === '=') {
this.receiveToken(3);
return;
}
this.receiveToken(2);
}
// === or ==
// && ||
readLogicOpt(tok) {
if (this.pickNext() === tok) {
// ===
if (tok === '=' && this.pickNext(1) === tok) {
return this.receiveToken(3);
}
// == && ||
return this.receiveToken(2);
}
// handle as &&
// a ? b : c is equal to a && b || c
if (tok === '?' || tok === ':') {
return this.receiveToken(1);
}
}
readValue(tok) {
if (!tok) {
return;
}
let index = 0;
while (!Lexer.optable[tok] && tok !== undefined) {
tok = this.pickNext(index);
index += 1;
}
this.receiveToken(index);
}
}
export default function token(expression) {
const lexer = new Lexer(expression);
return lexer.getTokens();
}

View File

@@ -0,0 +1,159 @@
export const OPERATION = {
'!': 5,
'*': 4,
'/': 4,
'%': 4,
'+': 3,
'-': 3,
'>': 2,
'<': 2,
'>=': 2,
'<=': 2,
'===': 2,
'!==': 2,
'==': 2,
'!=': 2,
'&&': 1,
'||': 1,
'?': 1,
':': 1,
};
// export interface Node {
// left: Node | string | null;
// right: Node | string | null;
// operation: string;
// grouped?: boolean;
// };
export default class Parser {
constructor(token) {
this.index = -1;
this.blockLevel = 0;
this.token = token;
}
/**
*
* @return {Node | string} =-
*/
parse() {
let tok;
let root = {
left: null,
right: null,
operation: null,
};
do {
tok = this.parseStatement();
if (tok === null || tok === undefined) {
break;
}
if (root.left === null) {
root.left = tok;
root.operation = this.nextToken();
if (!root.operation) {
return tok;
}
root.right = this.parseStatement();
} else {
if (typeof tok !== 'string') {
throw new Error('operation must be string, but get ' + JSON.stringify(tok));
}
root = this.addNode(tok, this.parseStatement(), root);
}
} while (tok);
return root;
}
nextToken() {
this.index += 1;
return this.token[this.index];
}
prevToken() {
return this.token[this.index - 1];
}
/**
*
* @param {string} operation
* @param {Node|String|null} right
* @param {Node} root
*/
addNode(operation, right, root) {
let pre = root;
if (this.compare(pre.operation, operation) < 0 && !pre.grouped) {
while (pre.right !== null &&
typeof pre.right !== 'string' &&
this.compare(pre.right.operation, operation) < 0 && !pre.right.grouped) {
pre = pre.right;
}
pre.right = {
operation,
left: pre.right,
right,
};
return root;
}
return {
left: pre,
right,
operation,
}
}
/**
*
* @param {String} a
* @param {String} b
*/
compare(a, b) {
if (!OPERATION.hasOwnProperty(a) || !OPERATION.hasOwnProperty(b)) {
throw new Error(`unknow operation ${a} or ${b}`);
}
return OPERATION[a] - OPERATION[b];
}
/**
* @return string | Node | null
*/
parseStatement() {
const token = this.nextToken();
if (token === '(') {
this.blockLevel += 1;
const node = this.parse();
this.blockLevel -= 1;
if (typeof node !== 'string') {
node.grouped = true;
}
return node;
}
if (token === ')') {
return null;
}
if (token === '!') {
return { left: null, operation: token, right: this.parseStatement() }
}
// 3 > -12 or -12 + 10
if (token === '-' && (OPERATION[this.prevToken()] > 0 || this.prevToken() === undefined)) {
return { left: '0', operation: token, right: this.parseStatement(), grouped: true };
}
return token;
}
}

View File

@@ -0,0 +1,61 @@
import { OPERATION } from './Parser';
export default class QueryParser {
constructor(tree, queries) {
this.tree = tree;
this.queries = queries;
this.query = null;
}
setQuery(query) {
this.query = query.clone();
}
parse() {
return this.parseNode(this.tree);
}
parseNode(node) {
if (typeof node === 'string') {
const nodeQuery = this.getQuery(node);
return (query) => { nodeQuery(query); };
}
if (OPERATION[node.operation] === undefined) {
throw new Error(`unknow expression ${node.operation}`);
}
const leftQuery = this.getQuery(node.left);
const rightQuery = this.getQuery(node.right);
switch (node.operation) {
case '&&':
case 'AND':
default:
return (nodeQuery) => nodeQuery.where((query) => {
query.where((q) => { leftQuery(q); });
query.andWhere((q) => { rightQuery(q); });
});
case '||':
case 'OR':
return (nodeQuery) => nodeQuery.where((query) => {
query.where((q) => { leftQuery(q); });
query.orWhere((q) => { rightQuery(q); });
});
}
}
getQuery(node) {
if (typeof node !== 'string' && node !== null) {
return this.parseNode(node);
}
const value = parseFloat(node);
if (!isNaN(value)) {
if (typeof this.queries[node] === 'undefined') {
throw new Error(`unknow query under index ${node}`);
}
return this.queries[node];
}
return null;
}
}