mirror of
https://github.com/h-sphere/sql-seal.git
synced 2026-07-22 10:10:28 +00:00
feat: poc of the grammar
This commit is contained in:
parent
a335444d73
commit
d761e33c38
5 changed files with 186 additions and 0 deletions
|
|
@ -61,6 +61,7 @@
|
|||
"jsonpath": "^1.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-table-ts": "^1.0.3",
|
||||
"ohm-js": "^17.1.0",
|
||||
"papaparse": "^5.4.1",
|
||||
"sql-parser-cst": "^0.32.0",
|
||||
"unidecode": "^1.1.0",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ importers:
|
|||
markdown-table-ts:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
ohm-js:
|
||||
specifier: ^17.1.0
|
||||
version: 17.1.0
|
||||
papaparse:
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1
|
||||
|
|
@ -2616,6 +2619,10 @@ packages:
|
|||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
|
||||
ohm-js@17.1.0:
|
||||
resolution: {integrity: sha512-xc3B5dgAjTBQGHaH7B58M2Pmv6WvzrJ/3/7LeUzXNg0/sY3jQPdSd/S2SstppaleO77rifR1tyhdfFGNIwxf2Q==}
|
||||
engines: {node: '>=0.12.1'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
|
|
@ -6156,6 +6163,8 @@ snapshots:
|
|||
'@types/codemirror': 5.60.8
|
||||
moment: 2.29.4
|
||||
|
||||
ohm-js@17.1.0: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
|
|
|||
22
src/grammar/ohmGrammar.ts
Normal file
22
src/grammar/ohmGrammar.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/***
|
||||
SQLSealLang {
|
||||
Grammar = (GrammarEntry | Flag)* SelectStmt*
|
||||
SelectStmt = ("WITH" | "SELECT") any+
|
||||
GrammarEntry = TableExpression | RendererExpression
|
||||
Flag = "REPEAT" | "NO REPEAT" | "EXPLAIN"
|
||||
TableExpression = "TABLE" identifier "=" "file(" filename ")"
|
||||
identifier = alnum+
|
||||
filename = (alnum | ".")+
|
||||
RendererExpression = "GRID" anyObject -- grid
|
||||
| "HTML" -- html
|
||||
| "MARKDOWN" --markdown
|
||||
NewLine = "\n"
|
||||
sp = " "
|
||||
blank = sp* nl // blank line has only newline
|
||||
endline = (~nl any)+ end
|
||||
objectContent = (alnum | "." | ":" | "," | sp | nl | tab)+
|
||||
anyObject = "{" (~"}" any)* "}"
|
||||
nl = "\n" // new line
|
||||
tab = "\t"
|
||||
}
|
||||
*/
|
||||
65
src/grammar/parser.test.ts
Normal file
65
src/grammar/parser.test.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { parse } from './parser'
|
||||
|
||||
describe('Ohm parser', () => {
|
||||
|
||||
it('should parse simple table expression', () => {
|
||||
expect(parse('TABLE x = file(a.csv)')).toEqual({
|
||||
query: '',
|
||||
tables: [{ filename: 'a.csv', type: 'csv', identifier: 'x'}],
|
||||
flags: {
|
||||
explain: false,
|
||||
refresh: true
|
||||
},
|
||||
renderer: {
|
||||
name: 'GRID',
|
||||
options: ''
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should parse SELECT statement alone', () => {
|
||||
expect(parse('SELECT * FROM files')).toEqual({
|
||||
query: 'SELECT * FROM files',
|
||||
tables: [],
|
||||
flags: { explain: false, refresh: true },
|
||||
renderer: { name: 'GRID', options: '' }
|
||||
})
|
||||
})
|
||||
|
||||
it('should not parse incorrect statement', () => {
|
||||
expect(() => parse('this is not valid entry')).toThrow()
|
||||
})
|
||||
|
||||
it('should parse statement with table and select', () => {
|
||||
expect(parse(`TABLE x = file(a.csv)
|
||||
SELECT * FROM x`)).toEqual({
|
||||
tables: [{ type: 'csv', filename: 'a.csv', identifier: 'x' }],
|
||||
flags: { explain: false, refresh: true },
|
||||
renderer: { name: 'GRID', options: '' },
|
||||
query: 'SELECT * FROM x'
|
||||
})
|
||||
})
|
||||
|
||||
it('should have table and flags', () => {
|
||||
expect(parse(`
|
||||
TABLE x = file(a.csv)
|
||||
REFRESH
|
||||
EXPLAIN
|
||||
SELECT * FROM x`)).toEqual({
|
||||
tables: [{
|
||||
filename: 'a.csv',
|
||||
identifier: 'x',
|
||||
type: 'csv'
|
||||
}],
|
||||
renderer: {
|
||||
name: 'GRID',
|
||||
options: ''
|
||||
},
|
||||
flags: {
|
||||
explain: false,
|
||||
refresh: true
|
||||
},
|
||||
query: 'SELECT * FROM x'
|
||||
})
|
||||
})
|
||||
})
|
||||
89
src/grammar/parser.ts
Normal file
89
src/grammar/parser.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import * as ohm from 'ohm-js';
|
||||
|
||||
|
||||
const SQLSealGrammar = String.raw`
|
||||
SQLSealLang {
|
||||
Grammar = (TableExpression | RendererExpression | FlagExpression)* SelectStmt*
|
||||
SelectStmt = ("WITH" | "SELECT") any+
|
||||
FlagExpression = "REFRESH" -- refresh
|
||||
| "NO REFRESH" -- norefresh
|
||||
| "EXPLAIN" -- explain
|
||||
TableExpression = "TABLE" identifier "=" "file(" filename ")"
|
||||
identifier = alnum+
|
||||
filename = (alnum | ".")+
|
||||
RendererExpression = "GRID" anyObject -- grid
|
||||
| "HTML" -- html
|
||||
| "MARKDOWN" --markdown
|
||||
anyObject = "{" (~"}" any)* "}"
|
||||
}
|
||||
`
|
||||
|
||||
const generateSemantic = (grammar: ohm.Grammar) => {
|
||||
const s = grammar.createSemantics()
|
||||
s.addOperation<any>('toObject', {
|
||||
Grammar: (entries, selectStatement) => {
|
||||
console.log('GRAMMAR', entries, selectStatement)
|
||||
const res = {
|
||||
flags: {
|
||||
refresh: true,
|
||||
explain: false
|
||||
},
|
||||
renderer: {
|
||||
name: 'GRID',
|
||||
options: ''
|
||||
},
|
||||
tables: [] as any[],
|
||||
query: ''
|
||||
}
|
||||
if (entries.children.length) {
|
||||
entries.children.forEach(c => {
|
||||
console.log(c.ctorName)
|
||||
switch (c.ctorName) {
|
||||
case 'TableExpression':
|
||||
res.tables.push(c.toObject())
|
||||
break;
|
||||
case 'RendererExpression':
|
||||
res.renderer = c.toObject()
|
||||
break
|
||||
case 'Flag':
|
||||
res.flags = {...res.flags, ...c.toObject()}
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
if (selectStatement) {
|
||||
res.query = selectStatement.sourceString
|
||||
}
|
||||
|
||||
return res
|
||||
},
|
||||
TableExpression: (_table, identifier, _eq, _file, filename, _close) => {
|
||||
return { identifier: identifier.sourceString , filename: filename.sourceString, type: 'csv' }
|
||||
},
|
||||
FlagExpression_refresh: (v) => {
|
||||
return { refresh: true }
|
||||
},
|
||||
FlagExpression_norefresh: (v) => {
|
||||
return {refresh: false}
|
||||
},
|
||||
FlagExpression_explain: (v) => {
|
||||
return { explain: true }
|
||||
}
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
export const parse = (query: string) => {
|
||||
|
||||
|
||||
const grammar = ohm.grammar(SQLSealGrammar)
|
||||
const match = grammar.match(query)
|
||||
if (match.succeeded()) {
|
||||
// Converting
|
||||
const s = generateSemantic(grammar)(match)
|
||||
return s.toObject()
|
||||
} else {
|
||||
throw new Error(match.message || 'Unknown parsing error')
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue