expression parser

This commit is contained in:
Jan Prochazka
2020-03-12 12:46:07 +01:00
parent e2c5c8163c
commit 064121376f
15 changed files with 1306 additions and 100 deletions

View File

@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['js'],
};

View File

@@ -14,12 +14,16 @@
],
"devDependencies": {
"@dbgate/types": "^0.1.0",
"@types/jest": "^25.1.4",
"@types/node": "^13.7.0",
"jest": "^25.1.0",
"ts-jest": "^25.2.1",
"typescript": "^3.7.5"
},
"dependencies": {
"@types/parsimmon": "^1.10.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"parsimmon": "^1.13.0"
}
}

View File

@@ -0,0 +1,17 @@
import { parseFilter } from './parseFilter';
import { FilterType } from './types';
import engines from '@dbgate/engines';
import { dumpSqlCondition, treeToSql } from '@dbgate/sqltree';
const ast = parseFilter(process.argv[2], process.argv[3] as FilterType);
console.log(JSON.stringify(ast, null, ' '));
console.log('***************** MS SQL ******************');
console.log(treeToSql(engines('mssql'), ast, dumpSqlCondition));
console.log('***************** MySql *******************');
console.log(treeToSql(engines('mysql'), ast, dumpSqlCondition));
console.log('***************** Postgre *****************');
console.log(treeToSql(engines('postgres'), ast, dumpSqlCondition));

View File

@@ -7,6 +7,10 @@ function token(parser) {
return parser.skip(whitespace);
}
function word(str) {
return P.string(str).thru(token);
}
function interpretEscapes(str) {
let escapes = {
b: '\b',
@@ -28,17 +32,61 @@ function interpretEscapes(str) {
});
}
const parser = P.createLanguage({
expr: r => P.alt(r.string),
const binaryCondition = operator => value => ({
conditionType: 'binary',
operator,
left: {
exprType: 'placeholder',
},
right: {
exprType: 'value',
value,
},
});
string: () =>
const compoudCondition = conditionType => conditions => {
if (conditions.length == 1) return conditions[0];
return {
conditionType,
conditions,
};
};
const parser = P.createLanguage({
string1: () =>
token(P.regexp(/"((?:\\.|.)*?)"/, 1))
.map(interpretEscapes)
.desc('string'),
.map(binaryCondition('='))
.desc('string quoted'),
string2: () =>
token(P.regexp(/'((?:\\.|.)*?)'/, 1))
.map(interpretEscapes)
.map(binaryCondition('='))
.desc('string quoted'),
number: () =>
token(P.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/))
.map(Number)
.map(binaryCondition('='))
.desc('number'),
noQuotedString: () =>
P.regexp(/[^\s]+/)
.desc('string unquoted')
.map(binaryCondition('=')),
comma: () => word(','),
not: () => word('NOT'),
notNull: r => r.not.then(r.null).map(() => 'NOT_NULL'),
null: () => word('NULL'),
element: r => P.alt(r.string1, r.string2, r.null, r.notNull, r.number, r.noQuotedString).trim(whitespace),
factor: r => r.element.sepBy(whitespace).map(compoudCondition('and')),
list: r => r.factor.sepBy(r.comma).map(compoudCondition('or')),
});
export function parseFilter(value: string, filterType: FilterType) {
const ast = parser.expr.tryParse(value);
console.log(ast);
const ast = parser.list.tryParse(value);
return ast;
}

View File

@@ -1,3 +1,7 @@
import parserFilter, { parseFilter } from './parseFilter';
import { parseFilter } from './parseFilter';
test('parse string', parseFilter('"123"', 'string'));
test('parse string', () => {
const ast = parseFilter('"123"', 'string');
console.log(JSON.stringify(ast));
expect(ast).toBe(3);
});