Skip to content

Commit f2eb12b

Browse files
committed
first commit
added select and some unit tests
1 parent e7d27bf commit f2eb12b

13 files changed

+11292
-11070
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
# lql-parser
22

33
parser for my database project
4+
5+
Resources:
6+
7+
https://nearley.js.org/docs/parser
8+
9+
https://github.com/oguimbal/pgsql-ast-parser
10+
11+
https://github.com/no-context/moo

grammar/base.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Generated automatically by nearley, version 2.20.1
2+
// https://github.com/Hardmath123/nearley
3+
(function () {
4+
function id(x) { return x[0]; }
5+
6+
function unwrap(e) {
7+
if (Array.isArray(e) && e.length === 1) {
8+
e = unwrap(e[0]);
9+
}
10+
if (Array.isArray(e) && !e.length) {
11+
return null;
12+
}
13+
return e;
14+
}
15+
var grammar = {
16+
Lexer: undefined,
17+
ParserRules: [
18+
{"name": "string$ebnf$1", "symbols": [/[a-zA-Z0-9]/]},
19+
{"name": "string$ebnf$1", "symbols": ["string$ebnf$1", /[a-zA-Z0-9]/], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
20+
{"name": "string", "symbols": ["string$ebnf$1"], "postprocess": unwrap}
21+
]
22+
, ParserStart: "string"
23+
}
24+
if (typeof module !== 'undefined'&& typeof module.exports !== 'undefined') {
25+
module.exports = grammar;
26+
} else {
27+
window.grammar = grammar;
28+
}
29+
})();

grammar/base.ne

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@{%
2+
const lexer = require("./lexer")
3+
%}
4+
5+
@lexer lexer
6+
7+
kw_from -> %keywords
8+
kw_select -> %keywords
9+
10+
word -> %word {% d => d[0].value %}
11+
word -> %wordNumber {% d => d[0].value %}
12+
13+
word -> %quotationWord {% d => d[0].value.replaceAll('"', '') %}

grammar/grammar.js

+29-22
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,39 @@
22
// https://github.com/Hardmath123/nearley
33
(function () {
44
function id(x) { return x[0]; }
5+
6+
const lexer = require("./lexer")
57
var grammar = {
6-
Lexer: undefined,
8+
Lexer: lexer,
79
ParserRules: [
8-
{"name": "select_statement", "symbols": ["kw_select", {"literal":" "}, "select_what", {"literal":" "}, "kw_from", {"literal":" "}, "table_name"]},
9-
{"name": "select_what", "symbols": [{"literal":"*"}]},
10-
{"name": "select_what", "symbols": [{"literal":"["}, "select_what_option", {"literal":"]"}]},
11-
{"name": "select_what", "symbols": ["column_name"]},
10+
{"name": "kw_from", "symbols": [(lexer.has("keywords") ? {type: "keywords"} : keywords)]},
11+
{"name": "kw_select", "symbols": [(lexer.has("keywords") ? {type: "keywords"} : keywords)]},
12+
{"name": "word", "symbols": [(lexer.has("word") ? {type: "word"} : word)], "postprocess": d => d[0].value},
13+
{"name": "word", "symbols": [(lexer.has("wordNumber") ? {type: "wordNumber"} : wordNumber)], "postprocess": d => d[0].value},
14+
{"name": "word", "symbols": [(lexer.has("quotationWord") ? {type: "quotationWord"} : quotationWord)], "postprocess": d => d[0].value.replaceAll('"', '')},
15+
{"name": "select_statement", "symbols": ["kw_select", (lexer.has("ws") ? {type: "ws"} : ws), "select_what", (lexer.has("ws") ? {type: "ws"} : ws), "kw_from", (lexer.has("ws") ? {type: "ws"} : ws), "table_name"], "postprocess": d => {
16+
return{
17+
"type":"select",
18+
"columns" : d[2],
19+
"table": d[6]
20+
}
21+
} },
22+
{"name": "select_what", "symbols": [(lexer.has("star") ? {type: "star"} : star)], "postprocess": d => "star"},
23+
{"name": "select_what", "symbols": [(lexer.has("lBracket") ? {type: "lBracket"} : lBracket), "select_what_option", (lexer.has("rBracket") ? {type: "rBracket"} : rBracket)], "postprocess": d => d[1][0]},
24+
{"name": "select_what", "symbols": ["column_name"], "postprocess": d => [d[0]]},
1225
{"name": "select_what_option", "symbols": ["selection_array"]},
1326
{"name": "select_what_option", "symbols": []},
14-
{"name": "selection_array", "symbols": ["table_name"]},
15-
{"name": "selection_array$string$1", "symbols": [{"literal":","}, {"literal":" "}], "postprocess": function joiner(d) {return d.join('');}},
16-
{"name": "selection_array", "symbols": ["selection_array", "selection_array$string$1", "table_name"]},
17-
{"name": "table_name", "symbols": ["string"]},
18-
{"name": "column_name", "symbols": ["string"]},
19-
{"name": "string$ebnf$1", "symbols": [/[a-zA-Z0-9]/]},
20-
{"name": "string$ebnf$1", "symbols": ["string$ebnf$1", /[a-zA-Z0-9]/], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
21-
{"name": "string", "symbols": ["string$ebnf$1"]},
22-
{"name": "kw_from$string$1", "symbols": [{"literal":"f"}, {"literal":"r"}, {"literal":"o"}, {"literal":"m"}], "postprocess": function joiner(d) {return d.join('');}},
23-
{"name": "kw_from", "symbols": ["kw_from$string$1"]},
24-
{"name": "kw_from$string$2", "symbols": [{"literal":"F"}, {"literal":"R"}, {"literal":"O"}, {"literal":"M"}], "postprocess": function joiner(d) {return d.join('');}},
25-
{"name": "kw_from", "symbols": ["kw_from$string$2"]},
26-
{"name": "kw_select$string$1", "symbols": [{"literal":"S"}, {"literal":"E"}, {"literal":"L"}, {"literal":"E"}, {"literal":"C"}, {"literal":"T"}], "postprocess": function joiner(d) {return d.join('');}},
27-
{"name": "kw_select", "symbols": ["kw_select$string$1"]},
28-
{"name": "kw_select$string$2", "symbols": [{"literal":"s"}, {"literal":"e"}, {"literal":"l"}, {"literal":"e"}, {"literal":"c"}, {"literal":"t"}], "postprocess": function joiner(d) {return d.join('');}},
29-
{"name": "kw_select", "symbols": ["kw_select$string$2"]},
30-
{"name": "statement", "symbols": ["select_statement"]}
27+
{"name": "selection_array", "symbols": ["column_name"], "postprocess": d => [d[0]]},
28+
{"name": "selection_array", "symbols": ["selection_array", (lexer.has("comma") ? {type: "comma"} : comma), (lexer.has("ws") ? {type: "ws"} : ws), "column_name"], "postprocess": d =>{
29+
let array = d[0];
30+
31+
array.push(d[3])
32+
33+
return array
34+
} },
35+
{"name": "table_name", "symbols": ["word"], "postprocess": d => d[0]},
36+
{"name": "column_name", "symbols": ["word"], "postprocess": d => d[0]},
37+
{"name": "statement", "symbols": ["select_statement"], "postprocess": d => d[0]}
3138
]
3239
, ParserStart: "statement"
3340
}

grammar/grammar.ne

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
@include "select.ne"
22

3-
statement -> select_statement
3+
statement -> select_statement {% d => d[0] %}

grammar/lexer.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const moo = require("moo");
2+
3+
const raw_keywords = ["select", "from"];
4+
5+
let keywords_map = {};
6+
7+
for (const kw of raw_keywords) {
8+
keywords_map[`kw_${kw}`] = kw;
9+
}
10+
11+
const case_insensitive_keywords = (map) => {
12+
const transform = moo.keywords(map);
13+
return (text) => transform(text.toUpperCase());
14+
};
15+
16+
const lexer = moo.compile({
17+
keywords: raw_keywords,
18+
star: "*",
19+
ws: /[ \t]+/,
20+
number: /[0-9]+/,
21+
word: /[a-zA-Z]+/,
22+
wordNumber: /[a-zA-Z0-9]+/,
23+
quotationWord: /"[a-zA-Z]+"/,
24+
quotationWordNumber: /"[a-zA-Z0-9]+"/,
25+
lBracket: "[",
26+
rBracket: "]",
27+
comma: ",",
28+
});
29+
30+
module.exports = lexer;

grammar/select.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,37 @@
22
// https://github.com/Hardmath123/nearley
33
(function () {
44
function id(x) { return x[0]; }
5+
6+
function unwrap(e) {
7+
if (Array.isArray(e) && e.length === 1) {
8+
e = unwrap(e[0]);
9+
}
10+
if (Array.isArray(e) && !e.length) {
11+
return null;
12+
}
13+
return e;
14+
}
515
var grammar = {
616
Lexer: undefined,
717
ParserRules: [
18+
{"name": "string$ebnf$1", "symbols": [/[a-zA-Z0-9]/]},
19+
{"name": "string$ebnf$1", "symbols": ["string$ebnf$1", /[a-zA-Z0-9]/], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
20+
{"name": "string", "symbols": ["string$ebnf$1"], "postprocess": unwrap},
821
{"name": "select_statement", "symbols": ["kw_select", {"literal":" "}, "select_what", {"literal":" "}, "kw_from", {"literal":" "}, "table_name"]},
922
{"name": "select_what", "symbols": [{"literal":"*"}]},
1023
{"name": "select_what", "symbols": [{"literal":"["}, "select_what_option", {"literal":"]"}]},
1124
{"name": "select_what", "symbols": ["column_name"]},
1225
{"name": "select_what_option", "symbols": ["selection_array"]},
1326
{"name": "select_what_option", "symbols": []},
14-
{"name": "selection_array", "symbols": ["table_name"]},
27+
{"name": "selection_array", "symbols": ["column_name"]},
1528
{"name": "selection_array$string$1", "symbols": [{"literal":","}, {"literal":" "}], "postprocess": function joiner(d) {return d.join('');}},
16-
{"name": "selection_array", "symbols": ["selection_array", "selection_array$string$1", "table_name"]},
17-
{"name": "table_name", "symbols": ["string"], "postprocess": d => string},
18-
{"name": "column_name", "symbols": ["string"], "postprocess": d => string},
19-
{"name": "string$ebnf$1", "symbols": [/[a-zA-Z0-9]/]},
20-
{"name": "string$ebnf$1", "symbols": ["string$ebnf$1", /[a-zA-Z0-9]/], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
21-
{"name": "string", "symbols": ["string$ebnf$1"]},
29+
{"name": "selection_array", "symbols": ["selection_array", "selection_array$string$1", "column_name"]},
30+
{"name": "table_name", "symbols": ["string"], "postprocess": d => {
31+
return {
32+
"table_name": unwrap(d[0])
33+
}
34+
} },
35+
{"name": "column_name", "symbols": ["string"]},
2236
{"name": "kw_from$subexpression$1", "symbols": [/[fF]/, /[rR]/, /[oO]/, /[mM]/], "postprocess": function(d) {return d.join(""); }},
2337
{"name": "kw_from", "symbols": ["kw_from$subexpression$1"]},
2438
{"name": "kw_select$subexpression$1", "symbols": [/[sS]/, /[eE]/, /[lL]/, /[eE]/, /[cC]/, /[tT]/], "postprocess": function(d) {return d.join(""); }},

grammar/select.ne

+21-14
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
1-
select_statement -> kw_select " " select_what " " kw_from " " table_name
1+
@include "base.ne"
2+
@lexer lexer
23

3-
select_what -> "*"
4-
select_what -> "[" select_what_option "]"
5-
select_what -> column_name
4+
select_statement -> kw_select %ws select_what %ws kw_from %ws table_name {% d => {
5+
return{
6+
"type":"select",
7+
"columns" : d[2],
8+
"table": d[6]
9+
}
10+
} %}
11+
12+
select_what -> %star {% d => "star" %}
13+
select_what -> %lBracket select_what_option %rBracket {% d => d[1][0] %}
14+
select_what -> column_name {% d => [d[0]] %}
615

716
select_what_option -> selection_array
817
select_what_option -> null
918

10-
selection_array -> table_name
11-
selection_array -> selection_array ", " table_name
12-
13-
14-
15-
table_name -> string {% d => string %}
19+
selection_array -> column_name {% d => [d[0]] %}
20+
selection_array -> selection_array %comma %ws column_name {% d =>{
21+
let array = d[0];
1622

17-
column_name -> string {% d => string %}
23+
array.push(d[3])
1824

19-
string -> [a-zA-Z0-9]:+
25+
return array
26+
} %}
2027

2128

22-
kw_from -> "from"i
29+
table_name -> word {% d => d[0] %}
2330

24-
kw_select -> "select"i
31+
column_name -> word {% d => d[0] %}

index.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
const nearley = require("nearley");
22
const grammar = require("./grammar/grammar.js");
3-
const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
3+
const parser = function () {
4+
return new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
5+
};
46

57
const parseInput = function (input) {
68
try {
7-
parser.feed(input);
9+
let thisParser = parser();
10+
11+
thisParser.feed(input);
12+
13+
const results = thisParser.finish();
814

9-
return parser;
15+
return results;
1016
} catch (error) {
1117
console.log(error);
1218

0 commit comments

Comments
 (0)