nested constructors, shallow enums

This commit is contained in:
Simon 2024-07-24 10:31:06 +00:00
parent 07737d23c2
commit 99383fd9c1
4 changed files with 144 additions and 75 deletions

2
ast
View File

@ -25,3 +25,5 @@ Name(
col: number,
)
// vim: syntax=elm

View File

@ -1,53 +1,61 @@
// Generated file by ast_generator
export type EnumStatement = {
kind: "Enum";
[0]: Enum;
export type Statement_Enum = {
kind: "Enum",
[0]: Enum,
};
export const EnumStatement = (v0: Enum): EnumStatement => ({ kind: "Enum", [0]: v0 });
export type NodeStatement = {
kind: "Node";
[0]: Node;
export const Statement_Enum = (v0: Enum): Statement_Enum => ({ kind: "Enum", [0]: v0 });
export type Statement_Node = {
kind: "Node",
[0]: Node,
};
export const NodeStatement = (v0: Node): NodeStatement => ({ kind: "Node", [0]: v0 });
export type Statement = EnumStatement | NodeStatement;
export const Statement_Node = (v0: Node): Statement_Node => ({ kind: "Node", [0]: v0 });
export type Statement = Statement_Enum | Statement_Node;
export const Statement = { Enum: Statement_Enum, Node: Statement_Node } as const;
export type Enum = {
name: Name;
nodes: Node[];
};
export const Enum = (name: Name, nodes: Node[]): Enum => ({ name, nodes });
export type Node = {
name: Name;
params: Param[];
};
export const Node = (name: Name, params: Param[]): Node => ({ name, params });
export type NamedParam = {
kind: "Named";
name: Name;
type_: Type;
export type Param_Named = {
kind: "Named",
name: Name,
type_: Type,
};
export const NamedParam = (name: Name, type_: Type): NamedParam => ({ kind: "Named", name, type_ });
export type UnnamedParam = {
kind: "Unnamed";
[0]: Type;
export const Param_Named = (name: Name, type_: Type): Param_Named => ({ kind: "Named", name, type_ });
export type Param_Unnamed = {
kind: "Unnamed",
[0]: Type,
};
export const UnnamedParam = (v0: Type): UnnamedParam => ({ kind: "Unnamed", [0]: v0 });
export type Param = NamedParam | UnnamedParam;
export type NameType = {
kind: "Name";
[0]: Name;
export const Param_Unnamed = (v0: Type): Param_Unnamed => ({ kind: "Unnamed", [0]: v0 });
export type Param = Param_Named | Param_Unnamed;
export const Param = { Named: Param_Named, Unnamed: Param_Unnamed } as const;
export type Type_Name = {
kind: "Name",
[0]: Name,
};
export const NameType = (v0: Name): NameType => ({ kind: "Name", [0]: v0 });
export type OptionalType = {
kind: "Optional";
[0]: Type;
export const Type_Name = (v0: Name): Type_Name => ({ kind: "Name", [0]: v0 });
export type Type_Optional = {
kind: "Optional",
[0]: Type,
};
export const OptionalType = (v0: Type): OptionalType => ({ kind: "Optional", [0]: v0 });
export type MultipleType = {
kind: "Multiple";
[0]: Type;
export const Type_Optional = (v0: Type): Type_Optional => ({ kind: "Optional", [0]: v0 });
export type Type_Multiple = {
kind: "Multiple",
[0]: Type,
};
export const MultipleType = (v0: Type): MultipleType => ({ kind: "Multiple", [0]: v0 });
export type Type = NameType | OptionalType | MultipleType;
export const Type_Multiple = (v0: Type): Type_Multiple => ({ kind: "Multiple", [0]: v0 });
export type Type = Type_Name | Type_Optional | Type_Multiple;
export const Type = { Name: Type_Name, Optional: Type_Optional, Multiple: Type_Multiple } as const;
export type Name = {
[0]: string;
line: number;
@ -55,3 +63,4 @@ export type Name = {
};
export const Name = (v0: string, line: number, col: number): Name => ({ [0]: v0, line, col });

View File

@ -4,7 +4,15 @@ import {
} from "https://deno.land/x/nearley@2.19.7-deno/mod.ts";
import compiledParserGrammar from "./parser.out.ts";
import { Enum, Name, NamedParam, Node, Statement, Type } from "./ast.out.ts";
import {
Enum,
Name,
Node,
Param,
Param_Named,
Statement,
Type,
} from "./ast.out.ts";
class TypescriptGenerator {
private result = "";
@ -20,35 +28,75 @@ class TypescriptGenerator {
this.generateNode(statement[0]);
break;
}
this.result += "\n";
}
return this.result;
}
private generateEnum(enum_: Enum) {
if (this.isShallowEnum(enum_)) {
return this.generateShallowEnumBody(enum_);
} else {
return this.generateEnumBody(enum_);
}
}
private isShallowEnum(enum_: Enum): boolean {
return !enum_.nodes.some((node) => node.params.length > 0);
}
private generateShallowEnumBody(enum_: Enum) {
const name = this.makeName(enum_.name);
const fields = enum_.nodes.map((node) => this.makeName(node.name)).map(
(name) => `${name}: "${name}"`,
).join(", ");
this.result += `export const ${name} = { ${fields} } as const;\n`;
this.result += `export type ${name} = keyof typeof ${name};\n`;
}
private generateEnumBody(enum_: Enum) {
const enumName = this.makeName(enum_.name);
const nodeNames: string[] = [];
const nodeNames: [string, 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);
const name = this.makeName(node.name);
const fullName = `${enumName}_${name}`;
nodeNames.push([name, fullName]);
this.result += `export type ${fullName} = {\n`;
this.result += ` kind: "${name}",\n`;
this.result += node.params.map((p, i) => this.makeParamField(p, i))
.map((field) => ` ${field},\n`).join("");
this.result += `};\n`;
const fnParams = this.makeFnParams(node.params);
const fields = this.makeFnFields(node.params);
this.result += `export const ${fullName}` +
` = (${fnParams}): ${fullName}` +
` => ({ kind: "${name}", ${fields} });\n`;
}
this.result += `export type ${enumName} = ${nodeNames.join(" | ")};\n`;
const typeList = nodeNames
.map(([_, fullName]) => fullName)
.join(" | ");
this.result += `export type ${enumName} = ${typeList};\n`;
const enumFieldFns = nodeNames
.map(([name, fullName]) => `${name}: ${fullName}`)
.join(", ");
this.result +=
`export const ${enumName} = { ${enumFieldFns} } as const;\n`;
}
private generateNode(node: Node) {
this.generateNodeBody(node);
private makeParamField(param: Param, index: number): string {
switch (param.kind) {
case "Named":
return this.makeNamedParam(param);
case "Unnamed":
return `[${index}]: ${this.makeType(param[0])}`;
}
}
private generateNodeBody(
private generateNode(
node: Node,
name = this.makeName(node.name),
kind?: string,
) {
const name = this.makeName(node.name);
this.result += `export type ${name} = {\n`;
if (kind) {
this.result += ` kind: "${kind}";\n`;
}
for (
const [param, index] of node.params
.map((v, i) => [v, i] as const)
@ -57,14 +105,23 @@ class TypescriptGenerator {
case "Named":
this.result += ` ${this.makeNamedParam(param)};\n`;
break;
case "Unnamed":
this.result += ` [${index}]: ${
this.makeType(param[0])
};\n`;
case "Unnamed": {
const type_ = this.makeType(param[0]);
this.result += ` [${index}]: ${type_};\n`;
break;
}
}
}
this.result += "};\n";
const fnParams = node.params
const fnParams = this.makeFnParams(node.params);
const fields = this.makeFnFields(node.params);
this.result += `export const ${name}` +
` = (${fnParams}): ${name}` +
` => ({ ${fields} });\n`;
}
private makeFnParams(params: Param[]): string {
return params
.map((param, index) => {
switch (param.kind) {
case "Named":
@ -74,22 +131,21 @@ class TypescriptGenerator {
}
})
.join(", ");
const fields = [
...(kind ? [`kind: "${kind}"`] : []),
...node.params.map((param, index) => {
switch (param.kind) {
case "Named":
return this.makeName(param.name);
case "Unnamed":
return `[${index}]: v${index}`;
}
}),
].join(", ");
this.result +=
`export const ${name} = (${fnParams}): ${name} => ({ ${fields} });\n`;
}
private makeNamedParam(param: NamedParam): string {
private makeFnFields(params: Param[]): string {
return params.map((param, index) => {
switch (param.kind) {
case "Named":
return this.makeName(param.name);
case "Unnamed":
return `[${index}]: v${index}`;
}
})
.join(", ");
}
private makeNamedParam(param: Param_Named): string {
const name = this.makeName(param.name);
const type_ = this.makeType(param.type_);
return `${name}: ${type_}`;

View File

@ -40,8 +40,8 @@ elements -> _ (element elementTail _):? {% v => v[1] ? [v[1][0], ...v[1][1]] : [
elementTail -> (nl__ element):* {% v => v[0].map(w => w[1]) %}
element -> enum {% v => ast.EnumStatement(v[0]) %}
| node {% v => ast.NodeStatement(v[0]) %}
element -> enum {% v => ast.Statement.Enum(v[0]) %}
| node {% v => ast.Statement.Node(v[0]) %}
enum -> name _ "{" nodes "}" {% v => ast.Enum(v[0], v[3]) %}
@ -49,7 +49,9 @@ nodes -> _ (node nodeTail _):? {% v => v[1] ? [v[1][0], ...v[1][1]] : [] %}
nodeTail -> (nl__ node):* {% v => v[0].map(w => w[1]) %}
node -> name _ "(" params ")" {% v => ast.Node(v[0], v[3]) %}
node -> name (_ paramList):? {% v => ast.Node(v[0], v[1] ? v[1][1] : []) %}
paramList -> "(" params ")" {% v => v[1] %}
params -> _ (param paramTail (_ ","):? _):? {% v => v[1] ? [v[1][0], ...v[1][1]] : [] %}
@ -58,22 +60,22 @@ paramTail -> ("," _ param):* {% v => v[0].map(w => w[2]) %}
param -> namedParam {% id %}
| unnamedParam {% id %}
namedParam -> name _ ":" _ type {% v => ast.NamedParam(v[0], v[4]) %}
namedParam -> name _ ":" _ type {% v => ast.Param.Named(v[0], v[4]) %}
unnamedParam -> type {% v => ast.UnnamedParam(v[0]) %}
unnamedParam -> type {% v => ast.Param.Unnamed(v[0]) %}
type -> optional {% id %}
| multiple {% id %}
| name {% v => ast.NameType(v[0]) %}
| name {% v => ast.Type.Name(v[0]) %}
optional -> type "?" {% v => ast.OptionalType(v[0]) %}
optional -> type "?" {% v => ast.Type.Optional(v[0]) %}
multiple -> type "[" "]" {% v => ast.MultipleType(v[0]) %}
multiple -> type "[" "]" {% v => ast.Type.Multiple(v[0]) %}
name -> %name {% v => ast.Name(v[0].value, v[0].line, v[0].col) %}
_ -> __:?
__ -> (%whitespace|%newline|%singeLineComment|%multiLineComment):+
__ -> (%whitespace|%newline|%singleLineComment|%multiLineComment):+
nl__ -> sl_ (%newline sl_):+