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, col: number,
) )
// vim: syntax=elm

View File

@ -1,53 +1,61 @@
// Generated file by ast_generator // Generated file by ast_generator
export type EnumStatement = { export type Statement_Enum = {
kind: "Enum"; kind: "Enum",
[0]: Enum; [0]: Enum,
}; };
export const EnumStatement = (v0: Enum): EnumStatement => ({ kind: "Enum", [0]: v0 }); export const Statement_Enum = (v0: Enum): Statement_Enum => ({ kind: "Enum", [0]: v0 });
export type NodeStatement = { export type Statement_Node = {
kind: "Node"; kind: "Node",
[0]: Node; [0]: Node,
}; };
export const NodeStatement = (v0: Node): NodeStatement => ({ kind: "Node", [0]: v0 }); export const Statement_Node = (v0: Node): Statement_Node => ({ kind: "Node", [0]: v0 });
export type Statement = EnumStatement | NodeStatement; export type Statement = Statement_Enum | Statement_Node;
export const Statement = { Enum: Statement_Enum, Node: Statement_Node } as const;
export type Enum = { export type Enum = {
name: Name; name: Name;
nodes: Node[]; nodes: Node[];
}; };
export const Enum = (name: Name, nodes: Node[]): Enum => ({ name, nodes }); export const Enum = (name: Name, nodes: Node[]): Enum => ({ name, nodes });
export type Node = { export type Node = {
name: Name; name: Name;
params: Param[]; params: Param[];
}; };
export const Node = (name: Name, params: Param[]): Node => ({ name, params }); export const Node = (name: Name, params: Param[]): Node => ({ name, params });
export type NamedParam = {
kind: "Named"; export type Param_Named = {
name: Name; kind: "Named",
type_: Type; name: Name,
type_: Type,
}; };
export const NamedParam = (name: Name, type_: Type): NamedParam => ({ kind: "Named", name, type_ }); export const Param_Named = (name: Name, type_: Type): Param_Named => ({ kind: "Named", name, type_ });
export type UnnamedParam = { export type Param_Unnamed = {
kind: "Unnamed"; kind: "Unnamed",
[0]: Type; [0]: Type,
}; };
export const UnnamedParam = (v0: Type): UnnamedParam => ({ kind: "Unnamed", [0]: v0 }); export const Param_Unnamed = (v0: Type): Param_Unnamed => ({ kind: "Unnamed", [0]: v0 });
export type Param = NamedParam | UnnamedParam; export type Param = Param_Named | Param_Unnamed;
export type NameType = { export const Param = { Named: Param_Named, Unnamed: Param_Unnamed } as const;
kind: "Name";
[0]: Name; export type Type_Name = {
kind: "Name",
[0]: Name,
}; };
export const NameType = (v0: Name): NameType => ({ kind: "Name", [0]: v0 }); export const Type_Name = (v0: Name): Type_Name => ({ kind: "Name", [0]: v0 });
export type OptionalType = { export type Type_Optional = {
kind: "Optional"; kind: "Optional",
[0]: Type; [0]: Type,
}; };
export const OptionalType = (v0: Type): OptionalType => ({ kind: "Optional", [0]: v0 }); export const Type_Optional = (v0: Type): Type_Optional => ({ kind: "Optional", [0]: v0 });
export type MultipleType = { export type Type_Multiple = {
kind: "Multiple"; kind: "Multiple",
[0]: Type; [0]: Type,
}; };
export const MultipleType = (v0: Type): MultipleType => ({ kind: "Multiple", [0]: v0 }); export const Type_Multiple = (v0: Type): Type_Multiple => ({ kind: "Multiple", [0]: v0 });
export type Type = NameType | OptionalType | MultipleType; 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 = { export type Name = {
[0]: string; [0]: string;
line: number; line: number;
@ -55,3 +63,4 @@ export type Name = {
}; };
export const Name = (v0: string, line: number, col: number): Name => ({ [0]: v0, line, col }); 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"; } from "https://deno.land/x/nearley@2.19.7-deno/mod.ts";
import compiledParserGrammar from "./parser.out.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 { class TypescriptGenerator {
private result = ""; private result = "";
@ -20,35 +28,75 @@ class TypescriptGenerator {
this.generateNode(statement[0]); this.generateNode(statement[0]);
break; break;
} }
this.result += "\n";
} }
return this.result; return this.result;
} }
private generateEnum(enum_: Enum) { 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 enumName = this.makeName(enum_.name);
const nodeNames: string[] = []; const nodeNames: [string, string][] = [];
for (const node of enum_.nodes) { for (const node of enum_.nodes) {
const kind = this.makeName(node.name); const name = this.makeName(node.name);
const name = `${kind}${enumName}`; const fullName = `${enumName}_${name}`;
this.generateNodeBody(node, name, kind); nodeNames.push([name, fullName]);
nodeNames.push(name); 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) { private makeParamField(param: Param, index: number): string {
this.generateNodeBody(node); switch (param.kind) {
case "Named":
return this.makeNamedParam(param);
case "Unnamed":
return `[${index}]: ${this.makeType(param[0])}`;
}
} }
private generateNodeBody( private generateNode(
node: Node, node: Node,
name = this.makeName(node.name),
kind?: string,
) { ) {
const name = this.makeName(node.name);
this.result += `export type ${name} = {\n`; this.result += `export type ${name} = {\n`;
if (kind) {
this.result += ` kind: "${kind}";\n`;
}
for ( for (
const [param, index] of node.params const [param, index] of node.params
.map((v, i) => [v, i] as const) .map((v, i) => [v, i] as const)
@ -57,14 +105,23 @@ class TypescriptGenerator {
case "Named": case "Named":
this.result += ` ${this.makeNamedParam(param)};\n`; this.result += ` ${this.makeNamedParam(param)};\n`;
break; break;
case "Unnamed": case "Unnamed": {
this.result += ` [${index}]: ${ const type_ = this.makeType(param[0]);
this.makeType(param[0]) this.result += ` [${index}]: ${type_};\n`;
};\n`; break;
}
} }
} }
this.result += "};\n"; 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) => { .map((param, index) => {
switch (param.kind) { switch (param.kind) {
case "Named": case "Named":
@ -74,22 +131,21 @@ class TypescriptGenerator {
} }
}) })
.join(", "); .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 name = this.makeName(param.name);
const type_ = this.makeType(param.type_); const type_ = this.makeType(param.type_);
return `${name}: ${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]) %} elementTail -> (nl__ element):* {% v => v[0].map(w => w[1]) %}
element -> enum {% v => ast.EnumStatement(v[0]) %} element -> enum {% v => ast.Statement.Enum(v[0]) %}
| node {% v => ast.NodeStatement(v[0]) %} | node {% v => ast.Statement.Node(v[0]) %}
enum -> name _ "{" nodes "}" {% v => ast.Enum(v[0], v[3]) %} 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]) %} 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]] : [] %} 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 %} param -> namedParam {% id %}
| unnamedParam {% 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 %} type -> optional {% id %}
| multiple {% 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) %} 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_):+ nl__ -> sl_ (%newline sl_):+