diff --git a/src/main.ts b/src/main.ts index 7904aae..ac17e42 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import * as front from "./front/mod.ts"; import * as middle from "./middle.ts"; import * as mir from "./mir.ts"; import { FnInterpreter } from "./mir_interpreter.ts"; +import * as stringify from "./stringify.ts"; const reporter = new Reporter(); @@ -40,58 +41,9 @@ if (!mainFn) { const m = new middle.MiddleLowerer(syms, tys); const mainMiddleFn = m.lowerFn(mainFn); -function printMirFn(fn: mir.Fn) { - const pretty = fn.pretty(); - const [lines, colors] = pretty - .split("\n") - .map<[string, string[]]>((line) => [line, []]) - .map<[string, string[]]>(([line, colors]) => { - line = line.replace(/%\d+/g, "__$&__"); - line = line.replace(/"(?:[^"\\]|\\.)*"/g, "__$&__"); - if (/^\s*__%\d+__ \(.*?\) =/.test(line)) { - line = line.replace( - /__%(\d+)__ \((.*?)\) =/g, - "%c%$1 %c($2)%c =", - ); - colors.push("color: lightblue;", "color: gray;", ""); - } - if (/[A-Z][a-zA-Z]+/.test(line)) { - line = line.replace(/([A-Z][a-zA-Z]+)/, "%c$1%c"); - colors.push("color: red; font-weight: bold;", ""); - } - if (/\bptr\b/.test(line)) { - line = line.replace(/\b(ptr)\b/, "%c$1%c"); - colors.push("color: gray;", ""); - } - while (true) { - if (/__%\d+__/.test(line)) { - line = line.replace(/__%(\d+)__/, "%c%$1%c"); - colors.push("color: lightblue;", ""); - } else if (/__"(?:[^"\\]|\\.)*"__/.test(line)) { - line = line.replace(/__("(?:[^"\\]|\\.)*")__/, "%c$1%c"); - colors.push("color: green;", ""); - } else { - break; - } - } - - return [line, colors]; - }) - .reduce<[string[], string[]]>( - ([linesAcc, colorsAcc], [line, colors]) => { - linesAcc.push(line); - colorsAcc.push(...colors); - return [linesAcc, colorsAcc]; - }, - [[], []], - ); - const text = lines.join("\n"); - console.log(text + "%c", ...colors, ""); -} - if (Deno.args.includes("--print-mir")) { for (const fn of m.allFns()) { - printMirFn(fn); + stringify.printWithConsoleColors(fn.pretty(stringify.consoleColors)); } } diff --git a/src/mir.ts b/src/mir.ts index a2f4b7a..40720c8 100644 --- a/src/mir.ts +++ b/src/mir.ts @@ -22,8 +22,8 @@ export class Fn { } } - pretty(): string { - return stringify.mirFnPretty(this); + pretty(colors?: stringify.PrettyColors): string { + return stringify.mirFnPretty(this, colors); } } diff --git a/src/stringify.ts b/src/stringify.ts index 23f1b72..e97248d 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -1,48 +1,114 @@ import * as ty from "./ty.ts"; import * as mir from "./mir.ts"; -export function tyPretty(ty: ty.Ty): string { +export type PrettyColors = { + punctuation: string; + operator: string; + literal: string; + string: string; + keyword: string; + varIdent: string; + fnIdent: string; + typeIdent: string; +}; + +export const noColors: PrettyColors = { + punctuation: "", + operator: "", + literal: "", + string: "", + keyword: "", + varIdent: "", + fnIdent: "", + typeIdent: "", +}; + +export const consoleColors: PrettyColors = { + punctuation: "\x1bpunctuation", + operator: "\x1boperator", + literal: "\x1bliteral", + string: "\x1bstring", + keyword: "\x1bkeyword", + varIdent: "\x1bvarIdent", + fnIdent: "\x1bfnIdent", + typeIdent: "\x1btypeIdent", +}; + +export function printWithConsoleColors(str: string) { + const keys = Object.keys(consoleColors).join("|"); + const pat = new RegExp(`\x1b(${keys})`, "g"); + const matches = str.matchAll(pat).toArray(); + + const replacers: Record = { + punctuation: "color: gray;", + operator: "color: #fb4934;", + literal: "color: pink;", + string: "color: green;", + keyword: "color: blue;", + varIdent: "color: lightblue;", + fnIdent: "color: green; font-weight: bold;", + typeIdent: "color: yellow;", + }; + + const colorArgs: string[] = []; + + for (const match of matches) { + str = str.replace(match[0], "%c"); + colorArgs.push(replacers[match[1]]); + } + str += "%c"; + colorArgs.push(""); + + console.log(str, ...colorArgs); +} + +export function tyPretty(ty: ty.Ty, colors = noColors): string { + const c = colors; switch (ty.kind.tag) { case "Error": - return ""; + return ``; case "Void": - return "void"; + return `${c.typeIdent}void`; case "IntLiteral": - return "{integer}"; + return `${c.typeIdent}{integer}`; case "Int": - return `${ty.kind.intTy}`; + return `${c.typeIdent}${ty.kind.intTy}`; case "Bool": - return "bool"; + return `${c.typeIdent}bool`; case "Ptr": - return `*${ty.kind.ty.pretty()}`; + return `${c.punctuation}*${ty.kind.ty.pretty(c)}`; case "PtrMut": - return `*mut ${ty.kind.ty.pretty()}`; + return `${c.punctuation}*${c.keyword}mut ${ty.kind.ty.pretty(c)}`; case "Array": - return `[${ty.kind.ty.pretty()}; ${ty.kind.length}]`; + return `${c.punctuation}[${ + ty.kind.ty.pretty(c) + }${c.punctuation}; ${ty.kind.length}${c.punctuation}]`; case "Slice": - return `[${ty.kind.ty.pretty()}]`; + return `${c.punctuation}[${ty.kind.ty.pretty(c)}${c.punctuation}]`; case "Range": - return `Range`; + return `${c.typeIdent}Range`; case "Fn": - return `fn (${ - ty.kind.params.map((param) => param.pretty()).join(", ") - }) -> ${ty.kind.retTy.pretty()}`; + return `${c.keyword}fn ${c.punctuation}(${ + ty.kind.params.map((param) => param.pretty(c)).join( + `${c.punctuation}, `, + ) + }${c.punctuation}) -> ${ty.kind.retTy.pretty(c)}`; case "FnStmt": if (!ty.kind.ty.is("Fn")) { throw new Error(); } - return `fn ${ty.kind.stmt.kind.ident}(${ - ty.kind.ty.kind.params.map((param) => param.pretty()) + return `${c.keyword}fn ${ty.kind.stmt.kind.ident}(${ + ty.kind.ty.kind.params.map((param) => param.pretty(c)) .join( - ", ", + `${c.punctuation}, `, ) - }) -> ${ty.kind.ty.kind.retTy.pretty()}`; + }${c.punctuation}) -> ${ty.kind.ty.kind.retTy.pretty(c)}`; } return ""; } -export function mirFnPretty(fn: mir.Fn): string { - return new MirFnPrettyStringifier(fn).stringify(); +export function mirFnPretty(fn: mir.Fn, colors = noColors): string { + return new MirFnPrettyStringifier(fn, colors).stringify(); } class MirFnPrettyStringifier { @@ -52,9 +118,12 @@ class MirFnPrettyStringifier { constructor( private fn: mir.Fn, + private colors: PrettyColors, ) {} stringify(): string { + const c = this.colors; + const fnTy = this.fn.ty.is("FnStmt") && this.fn.ty.kind.ty.is("Fn") ? this.fn.ty.kind.ty : null; @@ -65,21 +134,28 @@ class MirFnPrettyStringifier { const ident = this.fn.stmt.kind.ident; const params = fnTy.kind.params - .map((ty, idx) => `${idx}: ${ty.pretty()}`) - .join(", "); - const retTy = fnTy.kind.retTy.pretty(); + .map((ty, idx) => + `${c.varIdent}${idx}${c.punctuation}: ${ty.pretty(c)}` + ) + .join(`${c.punctuation}, `); + const retTy = fnTy.kind.retTy.pretty(c); - this.result += `fn ${ident}(${params}) -> ${retTy} {\n`; + this.result += + `${c.keyword}fn ${c.fnIdent}${ident}${c.punctuation}(${params}${c.punctuation}) -> ${retTy} ${c.punctuation}{\n`; for (const bb of this.fn.bbs) { this.basicBlock(bb); } + this.result += `${c.punctuation}}`; + return this.result; } private basicBlock(bb: mir.BasicBlock) { - this.result += `bb${this.bbId(bb)}:\n`; + const c = this.colors; + + this.result += `${c.varIdent}bb${this.bbId(bb)}${c.punctuation}:\n`; for (const inst of bb.insts) { this.inst(inst); @@ -93,15 +169,19 @@ class MirFnPrettyStringifier { return; } + const c = this.colors; + // inst is a value const expr = (tail: string) => { - this.result += ` %${ + this.result += ` ${c.varIdent}%${ this.instId(inst) - } (${inst.ty.pretty()}) = ${inst.kind.tag}${tail}\n`; + }${c.punctuation}: ${ + inst.ty.pretty(c) + }${c.punctuation} = ${c.operator}${inst.kind.tag}${tail}\n`; }; // inst is a statement const stmt = (tail: string) => { - this.result += ` ${inst.kind.tag}${tail}\n`; + this.result += ` ${c.operator}${inst.kind.tag}${tail}\n`; }; this.instTail(inst, expr, stmt); @@ -110,19 +190,21 @@ class MirFnPrettyStringifier { } private instArg(inst: mir.Inst): string { + const c = this.colors; + const inline: mir.InstKind["tag"][] = ["Void", "Int", "Bool", "Array"]; if ( inline.includes(inst.kind.tag) && !this.instsPrinted.has(inst) ) { - let str = `${inst.kind.tag}`; + let str = `${c.operator}${inst.kind.tag}`; this.instTail(inst, (s) => { str += s; }, () => {}); return str; } this.inst(inst); - return `%${this.instId(inst)}`; + return `${c.varIdent}%${this.instId(inst)}`; } private instTail( @@ -130,6 +212,7 @@ class MirFnPrettyStringifier { expr: (str: string) => void, stmt: (str: string) => void, ) { + const c = this.colors; const r = (inst: mir.Inst) => this.instArg(inst); const k = inst.kind; @@ -139,38 +222,66 @@ class MirFnPrettyStringifier { case "Void": return expr(``); case "Int": - return expr(` ${k.value}${k.intTy}`); + return expr(` ${c.literal}${k.value}${k.intTy}`); case "Bool": - return expr(` ${k.value}`); + return expr(` ${c.literal}${k.value}`); case "Str": - return expr(` ${JSON.stringify(k.value)}`); + return expr(` ${c.string}${JSON.stringify(k.value)}`); case "Array": - return expr(` [${k.values.map(r).join(", ")}]`); + return expr( + ` ${c.punctuation}[${ + k.values.map(r).join(`${c.punctuation}, `) + }${c.punctuation}]`, + ); case "Fn": - return expr(` ${k.fn.stmt.kind.ident}`); + return expr(` ${c.fnIdent}${k.fn.stmt.kind.ident}`); case "Param": - return expr(` ${k.idx}`); + return expr(` ${c.varIdent}${k.idx}`); case "GetElemPtr": - return expr(` &(ptr ${r(k.base)})[${r(k.offset)}]`); + return expr( + ` ${c.punctuation}&(${c.keyword}ptr ${ + r(k.base) + }${c.punctuation})[${r(k.offset)}${c.punctuation}]`, + ); case "Slice": return expr( - ` &[ptr ${r(k.value)}][${k.begin ? r(k.begin) : ""}..${ + ` ${c.punctuation}&(${c.keyword}ptr ${ + r(k.value) + }${c.punctuation})[${ + k.begin ? r(k.begin) : "" + }${c.punctuation}..${ k.end ? r(k.end) : "" - }]`, + }${c.punctuation}]`, ); case "Call": - return expr(` ${r(k.callee)} (${k.args.map(r).join(", ")})`); + return expr( + ` ${r(k.callee)} ${c.punctuation}(${ + k.args.map(r).join(`${c.punctuation}, `) + }${c.punctuation})`, + ); case "Alloca": return expr(``); case "Load": - return expr(` [ptr ${r(k.source)}]`); + return expr( + ` ${c.punctuation}[${c.keyword}ptr ${ + r(k.source) + }${c.punctuation}]`, + ); case "Store": - return stmt(` [ptr ${r(k.target)}] = ${r(k.source)}`); + return stmt( + ` ${c.punctuation}[${c.keyword}ptr ${ + r(k.target) + }${c.punctuation}] = ${r(k.source)}`, + ); case "Jump": - return stmt(` bb${this.bbId(k.target)}`); + return stmt(` ${c.varIdent}bb${this.bbId(k.target)}`); case "Branch": return stmt( - ` if ${r(k.cond)}: bb${this.bbId(k.truthy)}, else: bb${ + ` ${c.keyword}if ${ + r(k.cond) + }${c.punctuation}: ${c.varIdent}bb${ + this.bbId(k.truthy) + }${c.punctuation}, ${c.keyword}else${c.punctuation}: ${c.varIdent}bb${ this.bbId(k.falsy) }`, ); @@ -195,11 +306,11 @@ class MirFnPrettyStringifier { case "Mul": case "Div": case "Rem": - return expr(` ${r(k.left)}, ${r(k.right)}`); + return expr(` ${r(k.left)}${c.punctuation}, ${r(k.right)}`); case "Len": return expr(` ${r(k.source)}`); case "DebugPrint": - return stmt(` ${k.args.map(r).join(", ")}`); + return stmt(` ${k.args.map(r).join(`${c.punctuation}, `)}`); } } diff --git a/src/ty.ts b/src/ty.ts index ac029aa..bec9a77 100644 --- a/src/ty.ts +++ b/src/ty.ts @@ -145,8 +145,8 @@ export class Ty { return true; } - pretty(): string { - return stringify.tyPretty(this); + pretty(colors?: stringify.PrettyColors): string { + return stringify.tyPretty(this, colors); } }