New query rendering engine with handlebars
This commit is contained in:
@@ -86,7 +86,7 @@ export function extractMeta(parseTree: ParseTree, remove = false): any {
|
||||
|
||||
export async function queryProvider({
|
||||
query,
|
||||
}: QueryProviderEvent): Promise<string> {
|
||||
}: QueryProviderEvent): Promise<any[]> {
|
||||
let allData: any[] = [];
|
||||
for (let { key, page, value } of await scanPrefixGlobal("data:")) {
|
||||
let [, pos] = key.split("@");
|
||||
@@ -96,6 +96,5 @@ export async function queryProvider({
|
||||
pos: +pos,
|
||||
});
|
||||
}
|
||||
let resultData = applyQuery(query, allData);
|
||||
return jsonToMDTable(resultData);
|
||||
return applyQuery(query, allData);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,25 @@ test("Test parser", () => {
|
||||
"name",
|
||||
"age",
|
||||
]);
|
||||
|
||||
expect(
|
||||
parseQuery(`gh-events where type in ["PushEvent", "somethingElse"]`)
|
||||
).toStrictEqual({
|
||||
table: "gh-events",
|
||||
filter: [
|
||||
{
|
||||
op: "in",
|
||||
prop: "type",
|
||||
value: ["PushEvent", "somethingElse"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(parseQuery(`something render "template/table"`)).toStrictEqual({
|
||||
table: "something",
|
||||
filter: [],
|
||||
render: "template/table",
|
||||
});
|
||||
});
|
||||
|
||||
test("Test performing the queries", () => {
|
||||
@@ -92,4 +111,8 @@ test("Test performing the queries", () => {
|
||||
expect(
|
||||
applyQuery(parseQuery(`page where age > 30 select name`), data)
|
||||
).toStrictEqual([{ name: "Pete" }]);
|
||||
|
||||
expect(
|
||||
applyQuery(parseQuery(`page where name in ["Pete"] select name`), data)
|
||||
).toStrictEqual([{ name: "Pete" }]);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import { collectNodesOfType, findNodeOfType, replaceNodesMatching } from "@silverbulletmd/common/tree";
|
||||
import {
|
||||
collectNodesOfType,
|
||||
findNodeOfType,
|
||||
ParseTree,
|
||||
replaceNodesMatching,
|
||||
} from "@silverbulletmd/common/tree";
|
||||
import { lezerToParseTree } from "@silverbulletmd/common/parse_tree";
|
||||
import Handlebars from "handlebars";
|
||||
import YAML from "yaml";
|
||||
|
||||
// @ts-ignore
|
||||
import { parser } from "./parse-query";
|
||||
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
||||
import { niceDate } from "../core/dates";
|
||||
|
||||
export type QueryProviderEvent = {
|
||||
query: ParsedQuery;
|
||||
@@ -22,6 +31,7 @@ export type ParsedQuery = {
|
||||
limit?: number;
|
||||
filter: Filter[];
|
||||
select?: string[];
|
||||
render?: string;
|
||||
};
|
||||
|
||||
export function parseQuery(query: string): ParsedQuery {
|
||||
@@ -56,34 +66,14 @@ export function parseQuery(query: string): ParsedQuery {
|
||||
let limitNode = findNodeOfType(queryNode, "LimitClause");
|
||||
if (limitNode) {
|
||||
let nameNode = findNodeOfType(limitNode, "Number");
|
||||
parsedQuery.limit = +nameNode!.children![0].text!;
|
||||
parsedQuery.limit = valueNodeToVal(nameNode!);
|
||||
}
|
||||
|
||||
let filterNodes = collectNodesOfType(queryNode, "FilterExpr");
|
||||
for (let filterNode of filterNodes) {
|
||||
let val: any = undefined;
|
||||
let valNode = filterNode.children![2].children![0];
|
||||
switch (valNode.type) {
|
||||
case "Number":
|
||||
val = valNode.children![0].text!;
|
||||
break;
|
||||
case "Bool":
|
||||
val = valNode.children![0].text! === "true";
|
||||
break;
|
||||
case "Null":
|
||||
val = null;
|
||||
break;
|
||||
case "Name":
|
||||
val = valNode.children![0].text!;
|
||||
break;
|
||||
case "Regex":
|
||||
val = valNode.children![0].text!;
|
||||
val = val.substring(1, val.length - 1);
|
||||
break;
|
||||
case "String":
|
||||
val = valNode.children![0].text!;
|
||||
val = val.substring(1, val.length - 1);
|
||||
break;
|
||||
}
|
||||
val = valueNodeToVal(valNode);
|
||||
let f: Filter = {
|
||||
prop: filterNode.children![0].children![0].text!,
|
||||
op: filterNode.children![1].text!,
|
||||
@@ -93,7 +83,7 @@ export function parseQuery(query: string): ParsedQuery {
|
||||
}
|
||||
let selectNode = findNodeOfType(queryNode, "SelectClause");
|
||||
if (selectNode) {
|
||||
console.log("Select node", JSON.stringify(selectNode));
|
||||
// console.log("Select node", JSON.stringify(selectNode));
|
||||
parsedQuery.select = [];
|
||||
collectNodesOfType(selectNode, "Name").forEach((t) => {
|
||||
parsedQuery.select!.push(t.children![0].text!);
|
||||
@@ -102,10 +92,39 @@ export function parseQuery(query: string): ParsedQuery {
|
||||
// parsedQuery.limit = +nameNode!.children![0].text!;
|
||||
}
|
||||
|
||||
let renderNode = findNodeOfType(queryNode, "RenderClause");
|
||||
if (renderNode) {
|
||||
let renderNameNode = findNodeOfType(renderNode, "String");
|
||||
parsedQuery.render = valueNodeToVal(renderNameNode!);
|
||||
}
|
||||
|
||||
// console.log(JSON.stringify(queryNode, null, 2));
|
||||
return parsedQuery;
|
||||
}
|
||||
|
||||
function valueNodeToVal(valNode: ParseTree): any {
|
||||
switch (valNode.type) {
|
||||
case "Number":
|
||||
return +valNode.children![0].text!;
|
||||
case "Bool":
|
||||
return valNode.children![0].text! === "true";
|
||||
case "Null":
|
||||
return null;
|
||||
case "Name":
|
||||
return valNode.children![0].text!;
|
||||
case "Regex":
|
||||
let val = valNode.children![0].text!;
|
||||
return val.substring(1, val.length - 1);
|
||||
case "String":
|
||||
let stringVal = valNode.children![0].text!;
|
||||
return stringVal.substring(1, stringVal.length - 1);
|
||||
case "List":
|
||||
return collectNodesOfType(valNode, "Value").map((t) =>
|
||||
valueNodeToVal(t.children![0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function applyQuery<T>(parsedQuery: ParsedQuery, records: T[]): T[] {
|
||||
let resultRecords: any[] = [];
|
||||
if (parsedQuery.filter.length === 0) {
|
||||
@@ -156,6 +175,11 @@ export function applyQuery<T>(parsedQuery: ParsedQuery, records: T[]): T[] {
|
||||
continue recordLoop;
|
||||
}
|
||||
break;
|
||||
case "in":
|
||||
if (!value.includes(recordAny[prop])) {
|
||||
continue recordLoop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
resultRecords.push(recordAny);
|
||||
@@ -191,3 +215,33 @@ export function applyQuery<T>(parsedQuery: ParsedQuery, records: T[]): T[] {
|
||||
}
|
||||
return resultRecords;
|
||||
}
|
||||
|
||||
export async function renderQuery(
|
||||
parsedQuery: ParsedQuery,
|
||||
data: any[]
|
||||
): Promise<string> {
|
||||
if (parsedQuery.render) {
|
||||
Handlebars.registerHelper("json", (v) => JSON.stringify(v));
|
||||
Handlebars.registerHelper("niceDate", (ts) => niceDate(new Date(ts)));
|
||||
Handlebars.registerHelper("yaml", (v, prefix) => {
|
||||
if (typeof prefix === "string") {
|
||||
let yaml = YAML.stringify(v)
|
||||
.split("\n")
|
||||
.join("\n" + prefix)
|
||||
.trim();
|
||||
if (Array.isArray(v)) {
|
||||
return "\n" + prefix + yaml;
|
||||
} else {
|
||||
return yaml;
|
||||
}
|
||||
} else {
|
||||
return YAML.stringify(v).trim();
|
||||
}
|
||||
});
|
||||
let { text: templateText } = await readPage(parsedQuery.render);
|
||||
let template = Handlebars.compile(templateText, { noEscape: true });
|
||||
return template(data);
|
||||
}
|
||||
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
writePage,
|
||||
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
||||
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
|
||||
import { parseQuery } from "./engine";
|
||||
import { parseQuery, renderQuery } from "./engine";
|
||||
import { replaceTemplateVars } from "../core/template";
|
||||
import { queryRegex, removeQueries } from "./util";
|
||||
import { jsonToMDTable, queryRegex, removeQueries } from "./util";
|
||||
import { dispatch } from "@plugos/plugos-syscall/event";
|
||||
import { replaceAsync } from "../lib/util";
|
||||
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
|
||||
@@ -57,7 +57,12 @@ export async function updateMaterializedQueriesOnPage(pageName: string) {
|
||||
if (results.length === 0) {
|
||||
return `${startQuery}\n${endQuery}`;
|
||||
} else if (results.length === 1) {
|
||||
return `${startQuery}\n${results[0]}\n${endQuery}`;
|
||||
if (parsedQuery.render) {
|
||||
let rendered = await renderQuery(parsedQuery, results[0]);
|
||||
return `${startQuery}\n${rendered.trim()}\n${endQuery}`;
|
||||
} else {
|
||||
return `${startQuery}\n${jsonToMDTable(results[0])}\n${endQuery}`;
|
||||
}
|
||||
} else {
|
||||
console.error("Too many query results", results);
|
||||
return fullMatch;
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import { LRParser } from "@lezer/lr";
|
||||
|
||||
import {LRParser} from "@lezer/lr"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 13,
|
||||
states:
|
||||
"%WOVQPOOO[QQO'#C^QOQPOOOmQPO'#C`OrQQO'#CjOwQPO'#ClO|QPO'#CmOOQO'#Cn'#CnO!RQQO,58xO!dQPO'#CcO#OQQO'#CaOOQO'#Ca'#CaOOQO,58z,58zO#dQPO,59UOOQO,59W,59WO#iQQO'#DWOOQO,59X,59XOOQO-E6l-E6lO#}QQO,58}OmQPO,58|O$cQQO1G.pO$zQPO'#CoO%PQQO,59rOOQO'#Cg'#CgOOQO'#Ci'#CiOOQO'#Cd'#CdOOQO1G.i1G.iOOQO1G.h1G.hOOQO'#Ck'#CkOOQO7+$[7+$[OOQO,59Z,59ZOOQO-E6m-E6m",
|
||||
stateData:
|
||||
"%e~OfOS~ORPO~OgROtSOxTOyUOdQX~ORXO~Ou]O~OX^O~OR_O~OgROtSOxTOyUOdQa~OhbOlbOmbOnbOobOpbOqbOrbO~OscOdTXgTXtTXxTXyTX~ORdO~O{eOdzXgzXtzXxzXyzX~OXiOYiO[iOigOjgOkhO~OvlOwlOd^ig^it^ix^iy^i~ORnO~O{eOdzagzatzaxzayza~O",
|
||||
goto: "!y{PP|P!P!T!W!Z!aPP!dP!d!P!g!P!P!j!pPPPPPPPPPPPPPPPPPPPPPP!vRQOTVPWR[RRZRQYRRkcRjbRibRmdQWPRaWQf_RofR`U",
|
||||
nodeNames:
|
||||
"⚠ Program Query Name WhereClause LogicalExpr AndExpr FilterExpr Value Number String Bool Regex Null OrderClause Order LimitClause SelectClause",
|
||||
maxTerm: 43,
|
||||
states: "&fOVQPOOOmQQO'#C^QOQPOOOtQPO'#C`OyQQO'#CkO!OQPO'#CmO!TQPO'#CnO!YQPO'#CoOOQO'#Cp'#CpO!_QQO,58xO!fQQO'#CcO#TQQO'#CaOOQO'#Ca'#CaOOQO,58z,58zO#lQPO,59VOOQO,59X,59XO#qQQO'#D`OOQO,59Y,59YOOQO,59Z,59ZOOQO-E6n-E6nO$YQQO,58}OtQPO,58|O$qQQO1G.qO%]QPO'#CrO%bQQO,59zOOQO'#Cg'#CgOOQO'#Ci'#CiO$YQQO'#CjOOQO'#Cd'#CdOOQO1G.i1G.iOOQO1G.h1G.hOOQO'#Cl'#ClOOQO7+$]7+$]OOQO,59^,59^OOQO-E6p-E6pO%yQPO'#C|O&RQPO,59UO$YQQO'#CqO&WQPO,59hOOQO1G.p1G.pOOQO,59],59]OOQO-E6o-E6o",
|
||||
stateData: "&`~OiOS~ORPO~OjRO|SO!QTO!RUO!TVO~OgQX~P[ORYO~O}^O~OX_O~OR`O~OYbO~OgQa~P[OkdOsdOtdOudOvdOwdOxdOydOzdO~O{eOgTXjTX|TX!QTX!RTX!TTX~ORfO~OqgOg!SXj!SX|!SX!Q!SX!R!SX!T!SX~OXlOYlO[lOliOmiOnjOokO~O!OoO!PoOg_ij_i|_i!Q_i!R_i!T_i~ORqO~OqgOg!Saj!Sa|!Sa!Q!Sa!R!Sa!T!Sa~OquOrpX~OrwO~OquOrpa~O",
|
||||
goto: "#d!TPP!UP!X!]!`!c!iPP!rP!r!r!X!w!X!X!X!z#Q#WPPPPPPPPP#^PPPPPPPPPPPPPPPPP#aRQOTWPXR]RR[RQZRRneQmdQskRxuVldkuRpfQXPRcXQvsRyvQh`RrhRtkRaU",
|
||||
nodeNames: "⚠ Program Query Name WhereClause LogicalExpr AndExpr FilterExpr Value Number String Bool Regex Null List OrderClause Order LimitClause SelectClause RenderClause",
|
||||
maxTerm: 51,
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 2,
|
||||
tokenData:
|
||||
"=_~RxX^#opq#oqr$drs$w|}%c}!O%h!P!Q%y!Q![&p!^!_&x!_!`'V!`!a'd!c!}%h#R#S%h#T#U'q#U#V*W#V#W%h#W#X+S#X#Y%h#Y#Z-O#Z#`%h#`#a/`#a#b%h#b#c1s#c#d3o#d#g%h#g#h6S#h#i9O#i#k%h#k#l:z#l#o%h#y#z#o$f$g#o#BY#BZ#o$IS$I_#o$Ip$Iq$w$Iq$Ir$w$I|$JO#o$JT$JU#o$KV$KW#o&FU&FV#o~#tYf~X^#opq#o#y#z#o$f$g#o#BY#BZ#o$IS$I_#o$I|$JO#o$JT$JU#o$KV$KW#o&FU&FV#o~$gP!_!`$j~$oPn~#r#s$r~$wOr~~$zUOr$wrs%^s$Ip$w$Ip$Iq%^$Iq$Ir%^$Ir~$w~%cOY~~%hO{~P%mSRP}!O%h!c!}%h#R#S%h#T#o%h~&OV[~OY%yZ]%y^!P%y!P!Q&e!Q#O%y#O#P&j#P~%y~&jO[~~&mPO~%y~&uPX~!Q![&p~&}Ph~!_!`'Q~'VOl~~'[Pm~#r#s'_~'dOq~~'iPp~!_!`'l~'qOo~R'vWRP}!O%h!c!}%h#R#S%h#T#b%h#b#c(`#c#g%h#g#h)[#h#o%hR(eURP}!O%h!c!}%h#R#S%h#T#W%h#W#X(w#X#o%hR)OSsQRP}!O%h!c!}%h#R#S%h#T#o%hR)aURP}!O%h!c!}%h#R#S%h#T#V%h#V#W)s#W#o%hR)zSwQRP}!O%h!c!}%h#R#S%h#T#o%hR*]URP}!O%h!c!}%h#R#S%h#T#m%h#m#n*o#n#o%hR*vSuQRP}!O%h!c!}%h#R#S%h#T#o%hR+XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y+k#Y#o%hR+pURP}!O%h!c!}%h#R#S%h#T#g%h#g#h,S#h#o%hR,XURP}!O%h!c!}%h#R#S%h#T#V%h#V#W,k#W#o%hR,rSvQRP}!O%h!c!}%h#R#S%h#T#o%hR-TTRP}!O%h!c!}%h#R#S%h#T#U-d#U#o%hR-iURP}!O%h!c!}%h#R#S%h#T#`%h#`#a-{#a#o%hR.QURP}!O%h!c!}%h#R#S%h#T#g%h#g#h.d#h#o%hR.iURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y.{#Y#o%hR/SSjQRP}!O%h!c!}%h#R#S%h#T#o%hR/eURP}!O%h!c!}%h#R#S%h#T#]%h#]#^/w#^#o%hR/|URP}!O%h!c!}%h#R#S%h#T#a%h#a#b0`#b#o%hR0eURP}!O%h!c!}%h#R#S%h#T#]%h#]#^0w#^#o%hR0|URP}!O%h!c!}%h#R#S%h#T#h%h#h#i1`#i#o%hR1gSxQRP}!O%h!c!}%h#R#S%h#T#o%hR1xURP}!O%h!c!}%h#R#S%h#T#i%h#i#j2[#j#o%hR2aURP}!O%h!c!}%h#R#S%h#T#`%h#`#a2s#a#o%hR2xURP}!O%h!c!}%h#R#S%h#T#`%h#`#a3[#a#o%hR3cSkQRP}!O%h!c!}%h#R#S%h#T#o%hR3tURP}!O%h!c!}%h#R#S%h#T#f%h#f#g4W#g#o%hR4]URP}!O%h!c!}%h#R#S%h#T#W%h#W#X4o#X#o%hR4tURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y5W#Y#o%hR5]URP}!O%h!c!}%h#R#S%h#T#f%h#f#g5o#g#o%hR5vStQRP}!O%h!c!}%h#R#S%h#T#o%hR6XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y6k#Y#o%hR6pURP}!O%h!c!}%h#R#S%h#T#`%h#`#a7S#a#o%hR7XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y7k#Y#o%hR7pURP}!O%h!c!}%h#R#S%h#T#V%h#V#W8S#W#o%hR8XURP}!O%h!c!}%h#R#S%h#T#h%h#h#i8k#i#o%hR8rSyQRP}!O%h!c!}%h#R#S%h#T#o%hR9TURP}!O%h!c!}%h#R#S%h#T#f%h#f#g9g#g#o%hR9lURP}!O%h!c!}%h#R#S%h#T#i%h#i#j:O#j#o%hR:TURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y:g#Y#o%hR:nSiQRP}!O%h!c!}%h#R#S%h#T#o%hR;PURP}!O%h!c!}%h#R#S%h#T#[%h#[#];c#]#o%hR;hURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y;z#Y#o%hR<PURP}!O%h!c!}%h#R#S%h#T#f%h#f#g<c#g#o%hR<hURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y<z#Y#o%hR=RSgQRP}!O%h!c!}%h#R#S%h#T#o%h",
|
||||
repeatNodeCount: 3,
|
||||
tokenData: "Ap~R}X^$Opq$Oqr$srs%W|}%r}!O%w!P!Q&Y!Q!['P!^!_'X!_!`'f!`!a's!c!}%w!}#O(Q#P#Q(V#R#S%w#T#U([#U#V*q#V#W%w#W#X+m#X#Y%w#Y#Z-i#Z#]%w#]#^/y#^#`%w#`#a0u#a#b%w#b#c3Y#c#d5U#d#f%w#f#g7i#g#h:e#h#i=a#i#k%w#k#l?]#l#o%w#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$Ip$Iq%W$Iq$Ir%W$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$TYi~X^$Opq$O#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$vP!_!`$y~%OPu~#r#s%R~%WOy~~%ZUOr%Wrs%ms$Ip%W$Ip$Iq%m$Iq$Ir%m$Ir~%W~%rOY~~%wOq~P%|SRP}!O%w!c!}%w#R#S%w#T#o%w~&_V[~OY&YZ]&Y^!P&Y!P!Q&t!Q#O&Y#O#P&y#P~&Y~&yO[~~&|PO~&Y~'UPX~!Q!['P~'^Pk~!_!`'a~'fOs~~'kPt~#r#s'n~'sOx~~'xPw~!_!`'{~(QOv~~(VOo~~([Or~R(aWRP}!O%w!c!}%w#R#S%w#T#b%w#b#c(y#c#g%w#g#h)u#h#o%wR)OURP}!O%w!c!}%w#R#S%w#T#W%w#W#X)b#X#o%wR)iS{QRP}!O%w!c!}%w#R#S%w#T#o%wR)zURP}!O%w!c!}%w#R#S%w#T#V%w#V#W*^#W#o%wR*eS!PQRP}!O%w!c!}%w#R#S%w#T#o%wR*vURP}!O%w!c!}%w#R#S%w#T#m%w#m#n+Y#n#o%wR+aS}QRP}!O%w!c!}%w#R#S%w#T#o%wR+rURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y,U#Y#o%wR,ZURP}!O%w!c!}%w#R#S%w#T#g%w#g#h,m#h#o%wR,rURP}!O%w!c!}%w#R#S%w#T#V%w#V#W-U#W#o%wR-]S!OQRP}!O%w!c!}%w#R#S%w#T#o%wR-nTRP}!O%w!c!}%w#R#S%w#T#U-}#U#o%wR.SURP}!O%w!c!}%w#R#S%w#T#`%w#`#a.f#a#o%wR.kURP}!O%w!c!}%w#R#S%w#T#g%w#g#h.}#h#o%wR/SURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y/f#Y#o%wR/mSmQRP}!O%w!c!}%w#R#S%w#T#o%wR0OURP}!O%w!c!}%w#R#S%w#T#b%w#b#c0b#c#o%wR0iSzQRP}!O%w!c!}%w#R#S%w#T#o%wR0zURP}!O%w!c!}%w#R#S%w#T#]%w#]#^1^#^#o%wR1cURP}!O%w!c!}%w#R#S%w#T#a%w#a#b1u#b#o%wR1zURP}!O%w!c!}%w#R#S%w#T#]%w#]#^2^#^#o%wR2cURP}!O%w!c!}%w#R#S%w#T#h%w#h#i2u#i#o%wR2|S!QQRP}!O%w!c!}%w#R#S%w#T#o%wR3_URP}!O%w!c!}%w#R#S%w#T#i%w#i#j3q#j#o%wR3vURP}!O%w!c!}%w#R#S%w#T#`%w#`#a4Y#a#o%wR4_URP}!O%w!c!}%w#R#S%w#T#`%w#`#a4q#a#o%wR4xSnQRP}!O%w!c!}%w#R#S%w#T#o%wR5ZURP}!O%w!c!}%w#R#S%w#T#f%w#f#g5m#g#o%wR5rURP}!O%w!c!}%w#R#S%w#T#W%w#W#X6U#X#o%wR6ZURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y6m#Y#o%wR6rURP}!O%w!c!}%w#R#S%w#T#f%w#f#g7U#g#o%wR7]S|QRP}!O%w!c!}%w#R#S%w#T#o%wR7nURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y8Q#Y#o%wR8VURP}!O%w!c!}%w#R#S%w#T#b%w#b#c8i#c#o%wR8nURP}!O%w!c!}%w#R#S%w#T#W%w#W#X9Q#X#o%wR9VURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y9i#Y#o%wR9nURP}!O%w!c!}%w#R#S%w#T#f%w#f#g:Q#g#o%wR:XS!TQRP}!O%w!c!}%w#R#S%w#T#o%wR:jURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y:|#Y#o%wR;RURP}!O%w!c!}%w#R#S%w#T#`%w#`#a;e#a#o%wR;jURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y;|#Y#o%wR<RURP}!O%w!c!}%w#R#S%w#T#V%w#V#W<e#W#o%wR<jURP}!O%w!c!}%w#R#S%w#T#h%w#h#i<|#i#o%wR=TS!RQRP}!O%w!c!}%w#R#S%w#T#o%wR=fURP}!O%w!c!}%w#R#S%w#T#f%w#f#g=x#g#o%wR=}URP}!O%w!c!}%w#R#S%w#T#i%w#i#j>a#j#o%wR>fURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y>x#Y#o%wR?PSlQRP}!O%w!c!}%w#R#S%w#T#o%wR?bURP}!O%w!c!}%w#R#S%w#T#[%w#[#]?t#]#o%wR?yURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y@]#Y#o%wR@bURP}!O%w!c!}%w#R#S%w#T#f%w#f#g@t#g#o%wR@yURP}!O%w!c!}%w#R#S%w#T#X%w#X#YA]#Y#o%wRAdSjQRP}!O%w!c!}%w#R#S%w#T#o%w",
|
||||
tokenizers: [0, 1],
|
||||
topRules: { Program: [0, 1] },
|
||||
tokenPrec: 0,
|
||||
});
|
||||
topRules: {"Program":[0,1]},
|
||||
tokenPrec: 0
|
||||
})
|
||||
|
||||
@@ -13,7 +13,9 @@ export const
|
||||
Bool = 11,
|
||||
Regex = 12,
|
||||
Null = 13,
|
||||
OrderClause = 14,
|
||||
Order = 15,
|
||||
LimitClause = 16,
|
||||
SelectClause = 17
|
||||
List = 14,
|
||||
OrderClause = 15,
|
||||
Order = 16,
|
||||
LimitClause = 17,
|
||||
SelectClause = 18,
|
||||
RenderClause = 19
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@top Program { Query }
|
||||
|
||||
Query {
|
||||
Name ( WhereClause | OrderClause | LimitClause | SelectClause )*
|
||||
Name ( WhereClause | OrderClause | LimitClause | SelectClause | RenderClause )*
|
||||
}
|
||||
|
||||
commaSep<content> { content ("," content)* }
|
||||
@@ -12,12 +12,13 @@ WhereClause { "where" LogicalExpr }
|
||||
OrderClause { "order" "by" Name Order? }
|
||||
LimitClause { "limit" Number }
|
||||
SelectClause { "select" commaSep<Name> }
|
||||
RenderClause { "render" String }
|
||||
|
||||
Order {
|
||||
"desc" | "asc"
|
||||
}
|
||||
|
||||
Value { Number | String | Bool | Regex | Null }
|
||||
Value { Number | String | Bool | Regex | Null | List }
|
||||
|
||||
LogicalExpr { AndExpr | FilterExpr }
|
||||
|
||||
@@ -32,8 +33,11 @@ FilterExpr {
|
||||
| Name ">" Value
|
||||
| Name "=~" Value
|
||||
| Name "!=~" Value
|
||||
| Name "in" Value
|
||||
}
|
||||
|
||||
List { "[" commaSep<Value> "]" }
|
||||
|
||||
@skip { space }
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user