From af8ce1d47ab3b3609450a3c42a5f981d48124b09 Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Thu, 21 Nov 2024 12:08:07 +0100 Subject: [PATCH] web, runtime, compiler, connections --- compiler/mod.ts | 14 ++++++ web/deno.lock | 4 ++ web/main.ts | 105 ++++++++++++++++++++++++++++++++++++++++--- web/public/index.js | 9 ++-- web/public/style.css | 4 +- web/runtime.ts | 45 +++++++++++++++++++ 6 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 compiler/mod.ts create mode 100644 web/runtime.ts diff --git a/compiler/mod.ts b/compiler/mod.ts new file mode 100644 index 0000000..be72e5a --- /dev/null +++ b/compiler/mod.ts @@ -0,0 +1,14 @@ +import { Stmt } from "./ast.ts"; +import { Lexer } from "./Lexer.ts"; +import { Parser } from "./Parser.ts"; + +export * from "./Parser.ts"; +export * from "./ast.ts"; +export * from "./arch.ts"; +export * from "./Lexer.ts"; +export * from "./Token.ts"; + +export async function compileWithDebug(filepath: string): Promise { + const text = await Deno.readTextFile(filepath); + return new Parser(new Lexer(text)).parseStmts(); +} diff --git a/web/deno.lock b/web/deno.lock index cdf7a03..36c9b3b 100644 --- a/web/deno.lock +++ b/web/deno.lock @@ -6,6 +6,7 @@ "jsr:@std/assert@1": "1.0.8", "jsr:@std/bytes@1": "1.0.4", "jsr:@std/bytes@^1.0.2": "1.0.4", + "jsr:@std/cli@*": "1.0.6", "jsr:@std/crypto@1": "1.0.3", "jsr:@std/encoding@1": "1.0.5", "jsr:@std/encoding@^1.0.5": "1.0.5", @@ -48,6 +49,9 @@ "@std/bytes@1.0.4": { "integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc" }, + "@std/cli@1.0.6": { + "integrity": "d22d8b38c66c666d7ad1f2a66c5b122da1704f985d3c47f01129f05abb6c5d3d" + }, "@std/crypto@1.0.3": { "integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f" }, diff --git a/web/main.ts b/web/main.ts index e98f8cc..6313814 100644 --- a/web/main.ts +++ b/web/main.ts @@ -1,13 +1,110 @@ import { Application, Router } from "jsr:@oak/oak"; +import { parseArgs } from "jsr:@std/cli/parse-args"; +import { Runtime } from "./runtime.ts"; +import * as compiler from "../compiler/mod.ts"; +const port = 8000; -const app = new Application(); +const flags = parseArgs(Deno.args, { + boolean: ["flame-graph", "code-coverage"], +}); + +if (flags._.length !== 1) { + throw new Error("please specify a filename"); +} + +const filepath = flags._[0] as string; +const text = await Deno.readTextFile(filepath); + +const runtime = new Runtime(13370); + +async function compileProgram(filepath: string) { + const result = await compiler.compileWithDebug(filepath); +} + +async function runProgramWithDebug(program: string) { + const connection = await runtime.connect(); + connection.send({ + type: "run-debug", + program, + }); + const res = await connection.receive<{ + ok: boolean; + }>(); + connection.close(); + if (!res.ok) { + throw new Error("could not run code"); + } +} const router = new Router(); +router.get("/api/source", (ctx) => { + ctx.response.body = { ok: true, filepath, text }; + ctx.response.status = 200; + ctx.respond = true; +}); + +router.get("/api/status", async (ctx) => { + const connection = await runtime.connect(); + connection.send({ type: "status" }); + const res = await connection.receive<{ + ok: boolean; + status: "running" | "done"; + }>(); + connection.close(); + if (!res.ok) { + ctx.response.body = { ok: false }; + ctx.response.status = 500; + ctx.respond = true; + return; + } + ctx.response.body = { ok: true, status: res.status }; + ctx.response.status = 200; + ctx.respond = true; +}); + +router.get("/api/flame-graph", async (ctx) => { + const connection = await runtime.connect(); + connection.send({ type: "flame-graph" }); + const res = await connection.receive<{ + ok: boolean; + flameGraph: string; + }>(); + connection.close(); + if (!res.ok) { + ctx.response.body = { ok: false }; + ctx.response.status = 500; + ctx.respond = true; + return; + } + ctx.response.body = { ok: true, flameGraph: res.flameGraph }; + ctx.response.status = 200; + ctx.respond = true; +}); + +router.get("/api/code-coverage", async (ctx) => { + const connection = await runtime.connect(); + connection.send({ type: "code-coverage" }); + const res = await connection.receive<{ + ok: boolean; + codeCoverage: string; + }>(); + connection.close(); + if (!res.ok) { + ctx.response.body = { ok: false }; + ctx.response.status = 500; + ctx.respond = true; + return; + } + ctx.response.body = { ok: true, codeCoverage: res.codeCoverage }; + ctx.response.status = 200; + ctx.respond = true; +}); + +const app = new Application(); app.use(router.routes()); app.use(router.allowedMethods()); - app.use(async (ctx, next) => { try { await ctx.send({ root: "./public", index: "index.html" }); @@ -15,8 +112,6 @@ app.use(async (ctx, next) => { next(); } }); - -const port = 8000; const listener = app.listen({ port }); -console.log(`Listening at http://localhost:${port}/`); +console.log(`Devtools at http://localhost:${port}/`); await listener; diff --git a/web/public/index.js b/web/public/index.js index 2431495..f5d18b0 100644 --- a/web/public/index.js +++ b/web/public/index.js @@ -30,6 +30,11 @@ function loadCodeCoverage(text, codeCoverageData) { let line = 1; let col = 1; for (let index = 0; index < text.length; ++index) { + if (text[index] == "\n") { + col = 1; + line += 1; + continue; + } const entry = entries.find((entry) => index >= entry.index); charEntries[`${line}-${col}`] = entry; ctx.fillStyle = color(Math.min(entry.covers / 25, 1)); @@ -40,10 +45,6 @@ function loadCodeCoverage(text, codeCoverageData) { chHeight, ); col += 1; - if (text[index] == "\n") { - col = 1; - line += 1; - } } const tooltip = document.getElementById("covers-tooltip"); diff --git a/web/public/style.css b/web/public/style.css index 27d694d..c1a4d71 100644 --- a/web/public/style.css +++ b/web/public/style.css @@ -50,7 +50,7 @@ main { } #code-coverage #covers-tooltip { z-index: 2; - position: absolute; + position: fixed; top: 0; left: 0; padding: 3px; @@ -78,7 +78,7 @@ main { } #flame-graph #flame-graph-tooltip { z-index: 2; - position: absolute; + position: fixed; top: 0; left: 0; padding: 3px; diff --git a/web/runtime.ts b/web/runtime.ts new file mode 100644 index 0000000..97fbbce --- /dev/null +++ b/web/runtime.ts @@ -0,0 +1,45 @@ +export class Runtime { + constructor(private port: number) {} + + async connect(): Promise { + return new RuntimeConnection( + await Deno.connect({ + port: this.port, + }), + ); + } +} + +export class RuntimeConnection { + constructor(private connection: Deno.Conn) {} + + async write(text: string): Promise { + const req = new TextEncoder().encode(text); + await this.connection.write(req); + } + + async send(value: T): Promise { + await this.write(JSON.stringify(value)); + } + + async read(): Promise { + let result = ""; + while (true) { + const buf = new Uint8Array(256); + const readRes = await this.connection.read(buf); + result += new TextDecoder().decode(buf); + if (readRes == null) { + break; + } + } + return result; + } + + async receive(): Promise { + return JSON.parse(await this.read()) as T; + } + + close() { + this.connection.close(); + } +}