ast_generator/ast_generator.ts

105 lines
3.1 KiB
TypeScript
Raw Normal View History

2024-07-21 20:56:42 +01:00
import {
Grammar,
Parser,
} from "https://deno.land/x/nearley@2.19.7-deno/mod.ts";
import compiledParserGrammar from "./parser.out.ts";
import { Enum, Name, Node, Param, Statement, Type } from "./ast.out.ts";
class TypescriptGenerator {
private result = "";
public generate(ast: Statement[]): string {
this.result += "// Generated file by ast_generator\n";
for (const statement of ast) {
switch (statement.kind) {
case "Enum":
this.generateEnum(statement.enum_);
break;
case "Node":
this.generateNode(statement.node);
break;
}
}
return this.result;
}
private generateEnum(enum_: Enum) {
const enumName = this.makeName(enum_.name);
const nodeNames: string[] = [];
for (const node of enum_.nodes) {
const kind = this.makeName(node.name);
const name = `${kind}${enumName}`;
this.generateNodeBody(node, name, kind);
nodeNames.push(name);
}
this.result += `export type ${enumName} = ${nodeNames.join(" | ")};\n`;
}
private generateNode(node: Node) {
this.generateNodeBody(node);
}
private generateNodeBody(
node: Node,
name = this.makeName(node.name),
kind?: string,
) {
this.result += `export type ${name} = {\n`;
if (kind) {
this.result += ` kind: "${kind}";\n`;
}
for (const param of node.params) {
this.result += ` ${this.makeParam(param)};\n`;
}
this.result += "};\n";
const fnParams = node.params.map((param) => this.makeParam(param)).join(
", ",
);
const fields = [
...(kind ? [`kind: "${kind}"`] : []),
...node.params.map((param) => this.makeName(param.name)),
].join(", ");
this.result +=
`export const ${name} = (${fnParams}): ${name} => ({ ${fields} });\n`;
}
private makeParam(param: Param): string {
const name = this.makeName(param.name);
const type_ = this.makeType(param.type_);
return `${name}: ${type_}`;
}
private makeType(type_: Type): string {
switch (type_.kind) {
case "Name":
return this.makeName(type_.name);
case "Optional":
return `${this.makeType(type_.type_)} | null`;
case "Multiple":
return `${this.makeType(type_.type_)}[]`;
}
}
private makeName(name: Name): string {
return name.value;
}
}
const parser = new Parser(Grammar.fromCompiled(compiledParserGrammar));
if (Deno.args.length < 1) {
console.log("generate_ast <file>");
throw new Error("not enough args");
}
if (["-h", "--help"].includes(Deno.args[0])) {
console.log("generate_ast <file>");
Deno.exit(0);
}
const text = await Deno.readTextFile(Deno.args[0]);
parser.feed(text);
const ast = parser.results[0];
// console.log(JSON.stringify(ast, null, "│ "));
const ts = new TypescriptGenerator().generate(ast);
console.log(ts);