import Tokenizr from 'tokenizr' import { Op } from 'sequelize' export { ParsingError } from 'tokenizr' export class OrderByParser { lexer = new Tokenizr() constructor() { this.lexer.rule(/,/, (ctx, m) => { ctx.accept('spacer') }) this.lexer.rule(/ASC|DESC/, (ctx, m) => { ctx.accept('direction', m[0]) }) this.lexer.rule(/[a-zA-Z]+/, (ctx, m) => { ctx.accept('column', m[0]) }) this.lexer.rule(/\s/, (ctx, m) => { ctx.ignore() }) } parse(orderBy: string) { const output = [] let holding = [] this.lexer .input(orderBy) .tokens() .forEach((token) => { switch (token.type) { case 'spacer': output.push(holding) holding = [] break case 'column': case 'direction': holding.push(token.value) break } }) if (holding) { output.push(holding) } return output } } const openGroupRegex = /\(/ const closeGroupRegex = /\)/ const conjunctinoRegex = /AND|OR/ const equalityRegex = /([a-zA-Z]+)\s?(=|!=|<|>|<=|>=|:)\s?([a-zA-Z'"\d]+)/ const stringEqualityRegex = /([a-zA-Z]+)\s?(=|!=|<|>|<=|>=|:)\s?\"([a-zA-Z'"\d\s]*)\"/ const spacerRegex = /\s/ const opperatorMap = { '=': Op.eq, '!=': Op.ne, '<=': Op.lte, '>=': Op.gte, '>': Op.gt, '<': Op.lt, ':': Op.like, AND: Op.and, OR: Op.or } export class FilterParser { lexer: Tokenizr constructor() { this.lexer = new Tokenizr() this.lexer.rule(openGroupRegex, (ctx, m) => { ctx.accept('opengroup') }) this.lexer.rule(closeGroupRegex, (ctx, m) => { ctx.accept('closegroup') }) this.lexer.rule(conjunctinoRegex, (ctx, m) => { ctx.accept('conjunction', m[0]) }) this.lexer.rule(equalityRegex, (ctx, m) => { ctx.accept('column', m[1]) ctx.accept('opperator', m[2]) ctx.accept('value', m[3]) }) this.lexer.rule(stringEqualityRegex, (ctx, m) => { ctx.accept('column', m[1]) ctx.accept('opperator', m[2]) ctx.accept('value', m[3]) }) this.lexer.rule(spacerRegex, (ctx, m) => { ctx.ignore() }) } parse(filter: string) { this.lexer.input(filter) let block = this.parseBlock('AND') this.lexer.consume('EOF') this.lexer.reset() return block } private parseBlock(conjunciton: string) { let items = [] let activeCon = conjunciton for (;;) { let nextItem = this.lexer.alternatives( () => this.parseEquality(), () => this.parseGroup(), () => { let conToken = this.lexer.consume('conjunction') if (items.length === 1) { activeCon = conToken.value } if (conToken.value === activeCon) { return this.parseEquality() } else { return this.parseBlock(conToken.value) } }, () => this.parseEmpty() ) if (nextItem === undefined) { break } items.push(nextItem) } return { [opperatorMap[activeCon]]: items } } private parseEquality() { let columnToken = this.lexer.consume('column') let opperatorToken = this.lexer.consume('opperator') let valueToken = this.lexer.consume('value') if (opperatorToken.value === ':') { return { [columnToken.value]: { [opperatorMap[opperatorToken.value]]: `%${valueToken.value.toString()}%` } } } else { return { [columnToken.value]: { [opperatorMap[opperatorToken.value]]: valueToken.value.toString() } } } } private parseGroup() { this.lexer.consume('opengroup') let block = this.parseBlock('AND') this.lexer.consume('closegroup') return block } private parseEmpty() { return undefined } }