silverbullet/common/space_lua/parse.ts

520 lines
17 KiB
TypeScript
Raw Normal View History

2024-09-12 03:17:56 +08:00
import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts";
import {
2024-09-29 21:09:13 +08:00
cleanTree,
type ParseTree,
2024-09-12 03:17:56 +08:00
} from "@silverbulletmd/silverbullet/lib/tree";
import { parser } from "./parse-lua.js";
import { styleTags } from "@lezer/highlight";
2024-09-24 16:15:22 +08:00
import type {
LuaAttName,
LuaBlock,
LuaExpression,
LuaFunctionBody,
LuaFunctionCallExpression,
LuaFunctionName,
LuaLValue,
LuaPrefixExpression,
LuaStatement,
LuaTableField,
} from "./ast.ts";
2024-09-12 03:17:56 +08:00
const luaStyleTags = styleTags({
// Identifier: t.variableName,
// TagIdentifier: t.variableName,
// GlobalIdentifier: t.variableName,
// String: t.string,
// Number: t.number,
// PageRef: ct.WikiLinkTag,
// BinExpression: t.operator,
// TernaryExpression: t.operator,
// Regex: t.regexp,
// "where limit select render Order OrderKW and or null as InKW NotKW BooleanKW each all":
// t.keyword,
});
export const highlightingQueryParser = parser.configure({
props: [
luaStyleTags,
],
});
2024-09-29 21:09:13 +08:00
function parseChunk(t: ParseTree): LuaBlock {
if (t.type !== "Chunk") {
throw new Error(`Expected Chunk, got ${t.type}`);
2024-09-12 03:17:56 +08:00
}
2024-09-29 21:09:13 +08:00
return parseBlock(t.children![0]);
2024-09-12 03:17:56 +08:00
}
2024-09-29 21:09:13 +08:00
function parseBlock(t: ParseTree): LuaBlock {
if (t.type !== "Block") {
throw new Error(`Expected Block, got ${t.type}`);
2024-09-12 03:17:56 +08:00
}
2024-09-29 21:09:13 +08:00
const statements = t.children!.map(parseStatement);
return { type: "Block", statements, from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
}
2024-09-29 21:09:13 +08:00
function parseStatement(t: ParseTree): LuaStatement {
switch (t.type) {
2024-09-12 03:17:56 +08:00
case "Block":
2024-09-29 21:09:13 +08:00
return parseChunk(t.children![0]);
2024-09-12 03:17:56 +08:00
case "Semicolon":
2024-09-29 21:09:13 +08:00
return { type: "Semicolon", from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
case "Label":
2024-09-29 21:09:13 +08:00
return {
type: "Label",
name: t.children![1].children![0].text!,
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
case "Break":
2024-09-29 21:09:13 +08:00
return { type: "Break", from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
case "Goto":
2024-09-29 21:09:13 +08:00
return {
type: "Goto",
name: t.children![1].children![0].text!,
from: t.from,
to: t.to,
};
2024-09-12 19:40:43 +08:00
case "Scope":
2024-09-29 21:09:13 +08:00
return parseBlock(t.children![1]);
2024-09-12 19:40:43 +08:00
case ";":
2024-09-29 21:09:13 +08:00
return { type: "Semicolon", from: t.from, to: t.to };
2024-09-12 19:40:43 +08:00
case "WhileStatement":
return {
type: "While",
2024-09-29 21:09:13 +08:00
condition: parseExpression(t.children![1]),
block: parseBlock(t.children![3]),
2024-09-12 19:40:43 +08:00
};
case "RepeatStatement":
return {
type: "Repeat",
2024-09-29 21:09:13 +08:00
block: parseBlock(t.children![1]),
condition: parseExpression(t.children![3]),
2024-09-12 19:40:43 +08:00
};
case "IfStatement": {
2024-09-29 21:09:13 +08:00
const conditions: {
condition: LuaExpression;
block: LuaBlock;
from?: number;
to?: number;
}[] = [];
2024-09-12 19:40:43 +08:00
let elseBlock: LuaBlock | undefined = undefined;
2024-09-29 21:09:13 +08:00
for (let i = 0; i < t.children!.length; i += 4) {
console.log("Looking at", t.children![i]);
const child = t.children![i];
if (
child.children![0].text === "if" ||
child.children![0].text === "elseif"
) {
2024-09-12 19:40:43 +08:00
conditions.push({
2024-09-29 21:09:13 +08:00
condition: parseExpression(t.children![i + 1]),
block: parseBlock(t.children![i + 3]),
from: child.from,
to: child.to,
2024-09-12 19:40:43 +08:00
});
2024-09-29 21:09:13 +08:00
} else if (child.children![0].text === "else") {
elseBlock = parseBlock(t.children![i + 1]);
} else if (child.children![0].text === "end") {
2024-09-12 19:40:43 +08:00
break;
} else {
2024-09-29 21:09:13 +08:00
throw new Error(
`Unknown if clause type: ${child.children![0].text}`,
);
2024-09-12 19:40:43 +08:00
}
}
return {
type: "If",
conditions,
elseBlock,
2024-09-29 21:09:13 +08:00
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
}
case "ForStatement":
2024-09-29 21:09:13 +08:00
if (t.children![1].type === "ForNumeric") {
const forNumeric = t.children![1];
2024-09-12 19:40:43 +08:00
return {
type: "For",
2024-09-29 21:09:13 +08:00
name: forNumeric.children![0].children![0].text!,
start: parseExpression(forNumeric.children![2]),
end: parseExpression(forNumeric.children![4]),
step: forNumeric.children![5]
? parseExpression(forNumeric.children![6])
2024-09-12 19:40:43 +08:00
: undefined,
2024-09-29 21:09:13 +08:00
block: parseBlock(t.children![3]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
} else {
2024-09-29 21:09:13 +08:00
const forGeneric = t.children![1];
2024-09-12 19:40:43 +08:00
return {
type: "ForIn",
2024-09-29 21:09:13 +08:00
names: parseNameList(forGeneric.children![0]),
expressions: parseExpList(forGeneric.children![2]),
block: parseBlock(t.children![3]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
}
case "Function":
return {
type: "Function",
2024-09-29 21:09:13 +08:00
name: parseFunctionName(t.children![1]),
body: parseFunctionBody(t.children![2]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
case "LocalFunction":
return {
type: "LocalFunction",
2024-09-29 21:09:13 +08:00
name: t.children![2].children![0].text!,
body: parseFunctionBody(t.children![3]),
2024-09-12 19:40:43 +08:00
};
2024-09-12 03:17:56 +08:00
case "FunctionCall":
return {
type: "FunctionCallStatement",
2024-09-29 21:09:13 +08:00
call: parseExpression(
{
type: "FunctionCall",
children: t.children!,
from: t.from,
to: t.to,
},
) as LuaFunctionCallExpression,
2024-09-12 19:40:43 +08:00
};
case "Assign":
return {
type: "Assignment",
2024-09-29 21:09:13 +08:00
variables: t.children![0].children!.filter((t) =>
t.type !== ","
).map(
parseLValue,
),
expressions: parseExpList(t.children![2]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
case "Local":
return {
type: "Local",
2024-09-29 21:09:13 +08:00
names: parseAttNames(t.children![1]),
expressions: t.children![3] ? parseExpList(t.children![3]) : [],
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
2024-09-27 23:09:25 +08:00
case "ReturnStatement": {
2024-09-29 21:09:13 +08:00
const expressions = t.children![1]
? parseExpList(t.children![1])
: [];
return { type: "Return", expressions, from: t.from, to: t.to };
2024-09-27 23:09:25 +08:00
}
2024-09-27 15:11:03 +08:00
case "break":
2024-09-29 21:09:13 +08:00
return { type: "Break", from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
default:
console.error(t);
2024-09-29 21:09:13 +08:00
throw new Error(`Unknown statement type: ${t.children![0].text}`);
2024-09-12 03:17:56 +08:00
}
}
2024-09-29 21:09:13 +08:00
function parseAttNames(t: ParseTree): LuaAttName[] {
if (t.type !== "AttNameList") {
throw new Error(`Expected AttNameList, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
return t.children!.filter((t) => t.type !== ",").map(parseAttName);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
function parseAttName(t: ParseTree): LuaAttName {
if (t.type !== "AttName") {
throw new Error(`Expected AttName, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
return {
type: "AttName",
2024-09-29 21:09:13 +08:00
name: t.children![0].children![0].text!,
attribute: t.children![1].children![1]
? t.children![1].children![1].children![0].text!
: undefined,
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
}
2024-09-29 21:09:13 +08:00
function parseLValue(t: ParseTree): LuaLValue {
switch (t.type) {
2024-09-12 19:40:43 +08:00
case "Name":
2024-09-29 21:09:13 +08:00
return {
type: "Variable",
name: t.children![0].text!,
from: t.from,
to: t.to,
};
2024-09-12 19:40:43 +08:00
case "Property":
return {
type: "PropertyAccess",
2024-09-29 21:09:13 +08:00
object: parsePrefixExpression(t.children![0]),
property: t.children![2].children![0].text!,
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
case "MemberExpression":
return {
type: "TableAccess",
2024-09-29 21:09:13 +08:00
object: parsePrefixExpression(t.children![0]),
key: parseExpression(t.children![2]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
default:
console.error(t);
2024-09-29 21:09:13 +08:00
throw new Error(`Unknown lvalue type: ${t.type}`);
2024-09-12 19:40:43 +08:00
}
}
2024-09-29 21:09:13 +08:00
function parseFunctionName(t: ParseTree): LuaFunctionName {
if (t.type !== "FuncName") {
throw new Error(`Expected FunctionName, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
const propNames: string[] = [];
let colonName: string | undefined = undefined;
2024-09-29 21:09:13 +08:00
for (let i = 0; i < t.children!.length; i += 2) {
const prop = t.children![i];
propNames.push(prop.children![0].text!);
if (t.children![i + 1] && t.children![i + 1].type === ":") {
colonName = t.children![i + 2].children![0].text!;
2024-09-12 19:40:43 +08:00
break;
}
}
2024-09-29 21:09:13 +08:00
return {
type: "FunctionName",
propNames,
colonName,
from: t.from,
to: t.to,
};
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
function parseNameList(t: ParseTree): string[] {
if (t.type !== "NameList") {
throw new Error(`Expected NameList, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
return t.children!.filter((t) => t.type === "Name").map((t) =>
t.children![0].text!
);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
function parseExpList(t: ParseTree): LuaExpression[] {
if (t.type !== "ExpList") {
throw new Error(`Expected ExpList, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
return t.children!.filter((t) => t.type !== ",").map(parseExpression);
2024-09-12 19:40:43 +08:00
}
2024-09-29 21:09:13 +08:00
function parseExpression(t: ParseTree): LuaExpression {
switch (t.type) {
2024-09-12 03:17:56 +08:00
case "LiteralString": {
2024-09-29 21:09:13 +08:00
let cleanString = t.children![0].text!;
2024-09-12 03:17:56 +08:00
// Remove quotes etc
cleanString = cleanString.slice(1, -1);
2024-09-29 21:09:13 +08:00
return {
type: "String",
value: cleanString,
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
}
case "Number":
2024-09-29 21:09:13 +08:00
return {
type: "Number",
value: parseFloat(t.children![0].text!),
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
case "BinaryExpression":
return {
type: "Binary",
2024-09-29 21:09:13 +08:00
operator: t.children![1].children![0].text!,
left: parseExpression(t.children![0]),
right: parseExpression(t.children![2]),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "UnaryExpression":
return {
type: "Unary",
2024-09-29 21:09:13 +08:00
operator: t.children![0].children![0].text!,
argument: parseExpression(t.children![1]),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "Property":
return {
type: "PropertyAccess",
2024-09-29 21:09:13 +08:00
object: parsePrefixExpression(t.children![0]),
property: t.children![2].children![0].text!,
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "Parens":
2024-09-29 21:09:13 +08:00
return parseExpression(t.children![1]);
2024-09-12 03:17:56 +08:00
case "FunctionCall": {
2024-09-29 21:09:13 +08:00
if (t.children![1].type === ":") {
2024-09-12 03:17:56 +08:00
return {
type: "FunctionCall",
2024-09-29 21:09:13 +08:00
prefix: parsePrefixExpression(t.children![0]),
name: t.children![2].children![0].text!,
args: parseFunctionArgs(t.children!.slice(3)),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
}
return {
type: "FunctionCall",
2024-09-29 21:09:13 +08:00
prefix: parsePrefixExpression(t.children![0]),
args: parseFunctionArgs(t.children!.slice(1)),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
}
case "FunctionDef": {
2024-09-29 21:09:13 +08:00
const body = parseFunctionBody(t.children![1]);
2024-09-12 19:40:43 +08:00
return {
type: "FunctionDefinition",
body,
2024-09-29 21:09:13 +08:00
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
}
case "Name":
2024-09-29 21:09:13 +08:00
return {
type: "Variable",
name: t.children![0].text!,
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
case "Ellipsis":
2024-09-29 21:09:13 +08:00
return { type: "Variable", name: "...", from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
case "true":
2024-09-29 21:09:13 +08:00
return { type: "Boolean", value: true, from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
case "false":
2024-09-29 21:09:13 +08:00
return { type: "Boolean", value: false, from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
case "TableConstructor":
return {
type: "TableConstructor",
2024-09-29 21:09:13 +08:00
fields: t.children!.slice(1, -1).filter((t) =>
["FieldExp", "FieldProp", "FieldDynamic"].includes(t.type!)
2024-09-12 03:17:56 +08:00
).map(parseTableField),
2024-09-29 21:09:13 +08:00
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "nil":
2024-09-29 21:09:13 +08:00
return { type: "Nil", from: t.from, to: t.to };
2024-09-12 03:17:56 +08:00
default:
console.error(t);
2024-09-29 21:09:13 +08:00
throw new Error(`Unknown expression type: ${t.type}`);
2024-09-12 03:17:56 +08:00
}
}
2024-09-29 21:09:13 +08:00
function parseFunctionArgs(ts: ParseTree[]): LuaExpression[] {
console.log("Parsing function args", JSON.stringify(ts, null, 2));
return ts.filter((t) => ![",", "(", ")"].includes(t.type!)).map(
2024-09-12 19:40:43 +08:00
parseExpression,
);
}
2024-09-29 21:09:13 +08:00
function parseFunctionBody(t: ParseTree): LuaFunctionBody {
if (t.type !== "FuncBody") {
throw new Error(`Expected FunctionBody, got ${t.type}`);
2024-09-12 19:40:43 +08:00
}
return {
type: "FunctionBody",
2024-09-29 21:09:13 +08:00
parameters: t.children![1].children!.filter((t) =>
["Name", "Ellipsis"].includes(t.type!)
2024-09-12 19:40:43 +08:00
)
2024-09-29 21:09:13 +08:00
.map((t) => t.children![0].text!),
block: parseBlock(t.children![3]),
from: t.from,
to: t.to,
2024-09-12 19:40:43 +08:00
};
}
2024-09-29 21:09:13 +08:00
function parsePrefixExpression(t: ParseTree): LuaPrefixExpression {
switch (t.type) {
2024-09-12 03:17:56 +08:00
case "Name":
2024-09-29 21:09:13 +08:00
return {
type: "Variable",
name: t.children![0].text!,
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
case "Property":
return {
type: "PropertyAccess",
2024-09-29 21:09:13 +08:00
object: parsePrefixExpression(t.children![0]),
property: t.children![2].children![0].text!,
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
2024-09-27 15:11:03 +08:00
case "MemberExpression":
return {
type: "TableAccess",
2024-09-29 21:09:13 +08:00
object: parsePrefixExpression(t.children![0]),
key: parseExpression(t.children![2]),
from: t.from,
to: t.to,
2024-09-27 15:11:03 +08:00
};
2024-09-12 03:17:56 +08:00
case "Parens":
2024-09-29 21:09:13 +08:00
return {
type: "Parenthesized",
expression: parseExpression(t.children![1]),
from: t.from,
to: t.to,
};
2024-09-12 03:17:56 +08:00
default:
console.error(t);
2024-09-29 21:09:13 +08:00
throw new Error(`Unknown prefix expression type: ${t.type}`);
2024-09-12 03:17:56 +08:00
}
}
2024-09-29 21:09:13 +08:00
function parseTableField(t: ParseTree): LuaTableField {
switch (t.type) {
2024-09-12 03:17:56 +08:00
case "FieldExp":
return {
type: "ExpressionField",
2024-09-29 21:09:13 +08:00
value: parseExpression(t.children![0]),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "FieldProp":
return {
type: "PropField",
2024-09-29 21:09:13 +08:00
key: t.children![0].children![0].text!,
value: parseExpression(t.children![2]),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
case "FieldDynamic":
return {
type: "DynamicField",
2024-09-29 21:09:13 +08:00
key: parseExpression(t.children![1]),
value: parseExpression(t.children![4]),
from: t.from,
to: t.to,
2024-09-12 03:17:56 +08:00
};
default:
console.error(t);
2024-09-29 21:09:13 +08:00
throw new Error(`Unknown table field type: ${t.type}`);
2024-09-12 03:17:56 +08:00
}
}
2024-09-29 21:09:13 +08:00
export function parse(s: string): LuaBlock {
const t = parseToCrudeAST(s);
console.log("Clean tree", JSON.stringify(t, null, 2));
const result = parseChunk(t);
2024-09-12 03:17:56 +08:00
console.log("Parsed AST", JSON.stringify(result, null, 2));
return result;
}
2024-09-29 21:09:13 +08:00
export function parseToCrudeAST(t: string): ParseTree {
return cleanTree(lezerToParseTree(t, parser.parse(t).topNode), true);
2024-09-12 03:17:56 +08:00
}