460 lines
9.4 KiB
TypeScript
460 lines
9.4 KiB
TypeScript
import { parse } from "$common/markdown_parser/parse_tree.ts";
|
|
import { type AST, collectNodesOfType, parseTreeToAST } from "./tree.ts";
|
|
import { assert, assertEquals } from "@std/assert";
|
|
import { astToKvQuery } from "./parse_query.ts";
|
|
import { languageFor } from "$common/languages.ts";
|
|
|
|
function wrapQueryParse(query: string): AST | null {
|
|
const tree = parse(languageFor("query")!, query);
|
|
// console.log("tree", tree);
|
|
// Check for no ambiguitiies
|
|
if (collectNodesOfType(tree, "⚠").length > 0) {
|
|
console.error("Parse error:", JSON.stringify(tree, null, 2));
|
|
assert(false);
|
|
}
|
|
|
|
return parseTreeToAST(tree.children![0]);
|
|
}
|
|
|
|
Deno.test("Test query parser", () => {
|
|
// const query = ;
|
|
// console.log("query", query);
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where name = "test"`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], ["string", "test"]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where true`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["boolean", true],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where .`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["attr"],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where name = "hello"`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], [
|
|
"string",
|
|
"hello",
|
|
]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse("page where `name`")!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["attr", "name"],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse("page where `something`.`name`")!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["attr", ["attr", "something"], "name"],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse("page select 10 as `something`, `name`")!),
|
|
{
|
|
querySource: "page",
|
|
select: [{ name: "something", expr: ["number", 10] }, { name: "name" }],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where @page`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["global", "page"],
|
|
},
|
|
);
|
|
|
|
// Comment check
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page # comment`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
},
|
|
);
|
|
|
|
// Nested query check
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page select {page} as p`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
select: [{
|
|
expr: ["query", { querySource: "page" }],
|
|
name: "p",
|
|
}],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where not true`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["not", ["boolean", true]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where !isSomething`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["not", ["attr", "isSomething"]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where name =~ /test/`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=~", ["attr", "name"], ["regexp", "test", "i"]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(wrapQueryParse(`page where parent.name = "test"`)!),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", ["attr", "parent"], "name"], ["string", "test"]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = [[my page]]`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], [
|
|
"pageref",
|
|
"my page",
|
|
]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = {"name": "Pete", "age": 27}`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], ["object", [
|
|
["name", ["string", "Pete"]],
|
|
["age", ["number", 27]],
|
|
]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where json({})`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["call", "json", [["object", []]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = [1, 2, 3]`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], ["array", [["number", 1], ["number", 2], [
|
|
"number",
|
|
3,
|
|
]]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = []`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["=", ["attr", "name"], ["array", []]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = "test" and age > 20`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["and", ["=", ["attr", "name"], ["string", "test"]], [">", [
|
|
"attr",
|
|
"age",
|
|
], ["number", 20]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where name = "test" and age > 20 or done = true`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["or", ["and", ["=", ["attr", "name"], ["string", "test"]], [
|
|
">",
|
|
[
|
|
"attr",
|
|
"age",
|
|
],
|
|
["number", 20],
|
|
]], ["=", ["attr", "done"], ["boolean", true]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where (age <= 20) or task.done = null`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["or", ["<=", ["attr", "age"], ["number", 20]], [
|
|
"=",
|
|
[
|
|
"attr",
|
|
[
|
|
"attr",
|
|
"task",
|
|
],
|
|
"done",
|
|
],
|
|
["null"],
|
|
]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task order by lastModified asc`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
orderBy: [{ expr: ["attr", "lastModified"], desc: false }],
|
|
},
|
|
);
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task order by lastModified`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
orderBy: [{ expr: ["attr", "lastModified"], desc: false }],
|
|
},
|
|
);
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task order by lastModified desc, name, age asc`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
orderBy: [{ expr: ["attr", "lastModified"], desc: true }, {
|
|
expr: ["attr", "name"],
|
|
desc: false,
|
|
}, { expr: ["attr", "age"], desc: false }],
|
|
},
|
|
);
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task order by lastModified desc limit 5`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
orderBy: [{ expr: ["attr", "lastModified"], desc: true }],
|
|
limit: ["number", 5],
|
|
},
|
|
);
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task select name, lastModified + 20 as modified`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
select: [{ name: "name" }, {
|
|
name: "modified",
|
|
expr: ["+", ["attr", "lastModified"], ["number", 20]],
|
|
}],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task render [[my/page]]`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
render: "my/page",
|
|
renderAll: false,
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task render each [[my/page]]`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
render: "my/page",
|
|
renderAll: false,
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task render all [[my/page]]`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
render: "my/page",
|
|
renderAll: true,
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task where name in ["hello", 1]`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
filter: ["in", ["attr", "name"], ["array", [["string", "hello"], [
|
|
"number",
|
|
1,
|
|
]]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task where myCall().thing`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
filter: ["attr", ["call", "myCall", []], "thing"],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task select today() as today2`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
select: [{
|
|
name: "today2",
|
|
expr: ["call", "today", []],
|
|
}],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`task select today(1, 2, 3) as today`)!,
|
|
),
|
|
{
|
|
querySource: "task",
|
|
select: [{
|
|
name: "today",
|
|
expr: ["call", "today", [["number", 1], ["number", 2], ["number", 3]]],
|
|
}],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page select 8 > 3 ? "yes" : "no" as truth`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
select: [{
|
|
name: "truth",
|
|
expr: ["?", [">", ["number", 8], ["number", 3]], ["string", "yes"], [
|
|
"string",
|
|
"no",
|
|
]],
|
|
}],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where myCall(-3) > 4 - 2`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: [">", ["call", "myCall", [["-", ["number", 3]]]], ["-", [
|
|
"number",
|
|
4,
|
|
], ["number", 2]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where 1 * 2 - 3`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["-", ["*", ["number", 1], ["number", 2]], ["number", 3]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where 1*-2`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["*", ["number", 1], ["-", ["number", 2]]],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where 10.2`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: ["number", 10.2],
|
|
},
|
|
);
|
|
|
|
assertEquals(
|
|
astToKvQuery(
|
|
wrapQueryParse(`page where 10.2 > 2.2`)!,
|
|
),
|
|
{
|
|
querySource: "page",
|
|
filter: [">", ["number", 10.2], ["number", 2.2]],
|
|
},
|
|
);
|
|
});
|