Skip to content

Commit

Permalink
codegen moment
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanThatOneKid committed Apr 19, 2024
1 parent 4f8dd96 commit dadf556
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 69 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![GitHub Actions](https://github.com/FartLabs/labs/actions/workflows/check.yaml/badge.svg)](https://github.com/FartLabs/labs/actions/workflows/check.yaml)

🧪 Labs by [**@FartLabs**](https://github.com/FartLabs)
🧪 Labs by [FartLabs](https://github.com/FartLabs)

## Overview

Expand Down Expand Up @@ -36,8 +36,8 @@ myLab.execute("links.link", {

- ["Your First Lab"](https://github.com/FartLabs/labs/discussions/2) is an
example that demonstrates the basic concepts of Labs.
- [example.ts](./example.ts) contains a more advanced example showcasing the
convenience of composing Labs.
- [./example/example.ts](./example/example.ts) contains a more advanced example
showcasing the convenience of composing Labs.

## Concepts

Expand Down Expand Up @@ -88,6 +88,13 @@ Run `deno fmt` to format the code.

Run `deno lint` to lint the code.

### Maintenance

Run `deno task generate:example` to generate the generated code for the example.

Run `deno task example` to run the example,
[./example/example.ts](./example/example.ts).

---

Developed with ❤️ [**@FartLabs**](https://github.com/FartLabs)
24 changes: 0 additions & 24 deletions codegen.ts

This file was deleted.

85 changes: 85 additions & 0 deletions codegen/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import type {
ExtendDescriptor,
ImportName,
ImportSource,
LabDescriptor,
ProcedureDescriptor,
VariableDescriptor,
} from "./types.ts";
import { importsOf } from "./imports.ts";

export const LAB_IMPORT_SOURCE = "./labs.ts";

export function generateLab(descriptor: LabDescriptor): string {
const instructions = generateInstructions(descriptor);
return `import { Lab } from "${
descriptor.labsImportSource ?? LAB_IMPORT_SOURCE
}";
${generateImports(importsOf(descriptor))}
export const ${descriptor.name} = new Lab()${
instructions.length === 0 ? "" : `\n${indent(...instructions)}`
};`;
}

export function generateImports(
imports: Map<ImportSource, Set<ImportName>>,
): string {
return Array.from(imports)
.toSorted(([a], [b]) => a.localeCompare(b))
.map(([importSource, names]) => {
return generateImport(Array.from(names).toSorted(), importSource);
})
.join("\n");
}

export function generateImport(names: string[], importSource: string): string {
return `import { ${names.join(", ")} } from "${importSource}"`;
}

export function generateInstructions(descriptor: LabDescriptor): string[] {
const instructions: string[] = [];
if (descriptor.extends) {
instructions.push(...generateExtends(descriptor.extends));
}

if (descriptor.variables) {
instructions.push(...generateVariables(descriptor.variables));
}

if (descriptor.procedures) {
instructions.push(...generateProcedures(descriptor.procedures));
}

return instructions;
}

export function generateExtends(descriptors: ExtendDescriptor[]): string[] {
return descriptors.map((descriptor) => generateExtend(descriptor));
}

export function generateExtend(descriptor: ExtendDescriptor): string {
return `.extend(${descriptor.import.name})`;
}

export function generateVariables(descriptors: VariableDescriptor[]): string[] {
return descriptors.map((descriptor) => generateVariable(descriptor));
}

export function generateVariable(descriptor: VariableDescriptor): string {
return `.variable(${descriptor.name}, ${descriptor.value})`;
}

export function generateProcedures(
descriptors: ProcedureDescriptor[],
): string[] {
return descriptors.map((descriptor) => generateProcedure(descriptor));
}

export function generateProcedure(descriptor: ProcedureDescriptor): string {
return `.procedure(${descriptor.name}, ${descriptor.import.name})`;
}

export function indent(...lines: string[]): string {
return lines.map((line) => ` ${line}`).join("\n");
}
24 changes: 24 additions & 0 deletions codegen/imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ImportName, ImportSource, LabDescriptor } from "./types.ts";

export function importsOf(
descriptor: LabDescriptor,
): Map<ImportSource, Set<ImportName>> {
const imports = new Map<ImportSource, Set<ImportName>>();
for (const extend of descriptor.extends ?? []) {
if (!imports.has(extend.import.source)) {
imports.set(extend.import.source, new Set());
}

imports.get(extend.import.source)!.add(extend.import.name);
}

for (const procedure of descriptor.procedures ?? []) {
if (!imports.has(procedure.import.source)) {
imports.set(procedure.import.source, new Set());
}

imports.get(procedure.import.source)!.add(procedure.import.name);
}

return imports;
}
29 changes: 29 additions & 0 deletions codegen/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export interface LabDescriptor {
name: string;
labsImportSource?: ImportSource;
extends?: ExtendDescriptor[];
variables?: VariableDescriptor[];
procedures?: ProcedureDescriptor[];
}

export interface VariableDescriptor {
name: string;
value: string;
}

export interface ExtendDescriptor {
import: Importable;
}

export interface ProcedureDescriptor {
name: string;
import: Importable;
}

export interface Importable {
name: ImportName;
source: ImportSource;
}

export type ImportSource = string;
export type ImportName = string;
6 changes: 6 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tasks": {
"example": "deno run ./example/example.ts",
"generate:example": "deno run -Ar https://deno.land/x/generate/cli/main.ts ./example/gen.ts"
}
}
42 changes: 0 additions & 42 deletions example.ts

This file was deleted.

13 changes: 13 additions & 0 deletions example/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { generateLab } from "../codegen/codegen.ts";

await Deno.writeTextFile(
"./my_lab.ts",
generateLab({
name: "myLab",
labsImportSource: "../labs.ts",
extends: [
{ import: { name: "notesLab", source: "../notes.ts" } },
{ import: { name: "linksLab", source: "../links.ts" } },
],
}),
);
26 changes: 26 additions & 0 deletions example/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { myLab } from "./my_lab.ts";

if (import.meta.main) {
main();
}

function main() {
const note1 = myLab.execute(
"notes.add",
{ content: "Hello, world!" },
);
const note2 = myLab.execute(
"notes.add",
{ content: "Goodbye, world!" },
);
myLab.execute("links.link", { ids: [note1.id, note2.id] });

console.log(`Note 1: ${note1.id}`);
console.log(`Note 2: ${note2.id}`);
console.log("Linked notes:");
for (const linkable of myLab.execute("links.list", {})) {
console.log(
`- ${linkable.id} => ${linkable.links.map(({ id }) => id).join(", ")}`,
);
}
}
1 change: 1 addition & 0 deletions example/gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//deno:generate deno run --allow-write codegen.ts
7 changes: 7 additions & 0 deletions example/my_lab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Lab } from "../labs.ts";
import { linksLab } from "../links.ts";
import { notesLab } from "../notes.ts";

export const myLab = new Lab()
.extend(notesLab)
.extend(linksLab);

0 comments on commit dadf556

Please sign in to comment.