From 4d7998eb9f642178396853078645ac62c0ade572 Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 16 Mar 2026 22:01:54 +0100 Subject: [PATCH] use reporter --- src/diagnostics.ts | 82 ++++++++++++++++++++++++++++++++++++++------ src/front/check.ts | 24 +++---------- src/front/parse.ts | 13 ++++--- src/front/resolve.ts | 15 +++----- src/main.ts | 11 ++++-- tests/array.ethlang | 2 +- 6 files changed, 95 insertions(+), 52 deletions(-) diff --git a/src/diagnostics.ts b/src/diagnostics.ts index b0e2e9c..95d9ce8 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,7 +1,72 @@ export class Reporter { - report() {} + private reports: Report[] = []; + private errorOccured = false; + + report(report: Report) { + if (report.severity === "error") { + this.errorOccured = true; + } + this.reports.push(report); + } + + ok(): boolean { + return !this.errorOccured; + } + + printAll() { + for (const report of this.reports) { + printReport(report); + } + } + + abort(): never { + this.printAll(); + console.error("fatal: a fatal error occured"); + Deno.exit(1); + } + + ofFile(file: FileInfo): FileReporter { + return new FileReporter(this, file); + } } +export class FileReporter { + constructor( + private reporter: Reporter, + private file: FileInfo, + ) {} + + error(loc: Loc, message: string) { + this.reporter.report({ + severity: "error", + file: this.file, + loc, + message, + }); + } + info(loc: Loc, message: string) { + this.reporter.report({ + severity: "info", + file: this.file, + loc, + message, + }); + } + + abort(): never { + this.reporter.abort(); + } +} + +export type Report = { + severity: Severity; + file: FileInfo; + loc: Loc; + message: string; +}; + +export type Severity = "error" | "info"; + export type Loc = { idx: number; line: number; @@ -13,14 +78,9 @@ export type FileInfo = { text: string; }; -export function printDiagnostics( - filename: string, - loc: Loc, - severity: "error" | "info", - message: string, - text?: string, -) { - const line = loc.line; +function printReport(report: Report) { + const { severity, file: { filename, text }, message, loc } = report; + const { line, col } = loc; const severityColor = ({ "error": "red", @@ -49,12 +109,12 @@ export function printDiagnostics( `${" ".repeat(lineNumberText.length)}%c|\n` + `${lineNumberText}|%c${lineText}\n` + `${" ".repeat(lineNumberText.length)}%c|` + - `%c${"~".repeat(lineText.length)}\n` + + `${" ".repeat(col - 1)}%c^\n` + `${" ".repeat(lineNumberText.length)}%c|%c`, "color: cyan;", "color: lightwhite;", "color: cyan;", - `color: ${severityColor};`, + `color: ${severityColor}; font-weight: bold;`, "color: cyan;", "", ); diff --git a/src/front/check.ts b/src/front/check.ts index c5281c6..2d7ca43 100644 --- a/src/front/check.ts +++ b/src/front/check.ts @@ -1,5 +1,5 @@ import * as ast from "../ast.ts"; -import { Loc, printDiagnostics } from "../diagnostics.ts"; +import { FileReporter, Loc } from "../diagnostics.ts"; import { Ty } from "../ty.ts"; import { builtins } from "./builtins.ts"; import { ResolveMap } from "./resolve.ts"; @@ -14,10 +14,8 @@ export class Checker { private nodeTys = new Map(); constructor( - private filename: string, - private text: string, - private file: ast.Node, private resols: ResolveMap, + private reporter: FileReporter, ) {} check(node: ast.Node): Ty { @@ -338,27 +336,15 @@ export class Checker { } private error(loc: Loc, message: string) { - printDiagnostics( - this.filename, - loc, - "error", - message, - this.text, - ); + this.reporter.error(loc, message); } private info(loc: Loc, message: string) { - printDiagnostics( - this.filename, - loc, - "info", - message, - this.text, - ); + this.reporter.info(loc, message); } private fail(): never { - Deno.exit(1); + this.reporter.abort(); } } diff --git a/src/front/parse.ts b/src/front/parse.ts index b50f90d..b29c21e 100644 --- a/src/front/parse.ts +++ b/src/front/parse.ts @@ -1,11 +1,11 @@ import * as ast from "../ast.ts"; -import { Loc, printDiagnostics } from "../diagnostics.ts"; +import { FileReporter, Loc } from "../diagnostics.ts"; export function parse( - filename: string, text: string, + reporter: FileReporter, ): ast.Node { - return new Parser(filename, text).parseFile(); + return new Parser(text, reporter).parseFile(); } export class Parser { @@ -15,8 +15,8 @@ export class Parser { private prevTok: Tok | null = null; constructor( - private filename: string, private text: string, + private reporter: FileReporter, ) { this.toks = tokenize(text); } @@ -334,9 +334,8 @@ export class Parser { } private error(message: string, loc: Loc): never { - printDiagnostics(this.filename, loc, "error", message, this.text); - throw new Error(); - Deno.exit(1); + this.reporter.error(loc, message); + this.reporter.abort(); } private eat(type: string): boolean { diff --git a/src/front/resolve.ts b/src/front/resolve.ts index 4895dd4..e1a2939 100644 --- a/src/front/resolve.ts +++ b/src/front/resolve.ts @@ -1,5 +1,5 @@ import * as ast from "../ast.ts"; -import { printDiagnostics } from "../diagnostics.ts"; +import { FileReporter } from "../diagnostics.ts"; import { builtins } from "./builtins.ts"; export class ResolveMap { @@ -33,9 +33,8 @@ export type Sym = }; export function resolve( - filename: string, - text: string, file: ast.Node, + reporter: FileReporter, ): ResolveMap { let syms = ResolverSyms.root(); const resols = new Map(); @@ -81,14 +80,8 @@ export function resolve( if (k.tag === "IdentExpr") { const sym = syms.resolveExpr(k.ident); if (sym === null) { - printDiagnostics( - filename, - node.loc, - "error", - `undefined symbol '${k.ident}'`, - text, - ); - Deno.exit(1); + reporter.error(node.loc, `undefined symbol '${k.ident}'`); + reporter.abort(); } resols.set(node.id, sym); } diff --git a/src/main.ts b/src/main.ts index 5d5c300..2362ca9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,19 @@ import * as ast from "./ast.ts"; +import { Reporter } from "./diagnostics.ts"; import * as front from "./front/mod.ts"; import * as middle from "./middle.ts"; import { FnInterpreter } from "./mir_interpreter.ts"; +const reporter = new Reporter(); + const filename = Deno.args[0]; const text = await Deno.readTextFile(filename); -const fileAst = front.parse(filename, text); -const resols = front.resolve(filename, text, fileAst); -const checker = new front.Checker(filename, text, fileAst, resols); +const fileRep = reporter.ofFile({ filename, text }); + +const fileAst = front.parse(text, fileRep); +const resols = front.resolve(fileAst, fileRep); +const checker = new front.Checker(resols, fileRep); let mainFn: ast.NodeWithKind<"FnStmt"> | null = null; diff --git a/tests/array.ethlang b/tests/array.ethlang index ac8eaed..ad59c4a 100644 --- a/tests/array.ethlang +++ b/tests/array.ethlang @@ -1,7 +1,7 @@ fn main() { - let array: [int; 3] = [1, 2, 3]; + let array: [int; 3] = [1, 2, false]; // let a = 4; // let b = a;