Compare commits
	
		
			No commits in common. "fee5666971fd3fb6510da9ddee1f1183c5907601" and "b2bdf471f096b7c1d7a83fe4c00a856389b1ae6f" have entirely different histories.
		
	
	
		
			fee5666971
			...
			b2bdf471f0
		
	
		
@ -22,8 +22,6 @@ export const Ops = {
 | 
			
		||||
    Jump: 0x0e,
 | 
			
		||||
    JumpIfTrue: 0x0f,
 | 
			
		||||
    Builtin: 0x10,
 | 
			
		||||
    Duplicate: 0x11,
 | 
			
		||||
    Swap: 0x12,
 | 
			
		||||
    Add: 0x20,
 | 
			
		||||
    Subtract: 0x21,
 | 
			
		||||
    Multiply: 0x22,
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { opToString } from "./arch.ts";
 | 
			
		||||
import { Ops, opToString } from "./arch.ts";
 | 
			
		||||
 | 
			
		||||
export type Line = { labels?: string[]; ins: Ins };
 | 
			
		||||
 | 
			
		||||
@ -14,28 +14,7 @@ export type Refs = { [key: number]: string };
 | 
			
		||||
export class Assembler {
 | 
			
		||||
    private lines: Line[] = [];
 | 
			
		||||
    private addedLabels: string[] = [];
 | 
			
		||||
 | 
			
		||||
    private constructor(private labelCounter: number) {}
 | 
			
		||||
 | 
			
		||||
    public static newRoot(): Assembler {
 | 
			
		||||
        return new Assembler(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public fork(): Assembler {
 | 
			
		||||
        return new Assembler(this.labelCounter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public join(assembler: Assembler) {
 | 
			
		||||
        this.labelCounter = assembler.labelCounter;
 | 
			
		||||
        if (assembler.lines.length < 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (assembler.lines[0].labels !== undefined) {
 | 
			
		||||
            this.addedLabels.push(...assembler.lines[0].labels);
 | 
			
		||||
        }
 | 
			
		||||
        this.add(...assembler.lines[0].ins);
 | 
			
		||||
        this.lines.push(...assembler.lines.slice(1));
 | 
			
		||||
    }
 | 
			
		||||
    private labelCounter = 0;
 | 
			
		||||
 | 
			
		||||
    public add(...ins: Ins): Assembler {
 | 
			
		||||
        if (this.addedLabels.length > 0) {
 | 
			
		||||
@ -47,6 +26,17 @@ export class Assembler {
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public concat(assembler: Assembler) {
 | 
			
		||||
        if (assembler.lines.length < 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (assembler.lines[0].labels !== undefined) {
 | 
			
		||||
            this.addedLabels.push(...assembler.lines[0].labels);
 | 
			
		||||
        }
 | 
			
		||||
        this.add(...assembler.lines[0].ins);
 | 
			
		||||
        this.lines.push(...assembler.lines.slice(1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public makeLabel(): Label {
 | 
			
		||||
        return { label: `.L${(this.labelCounter++).toString()}` };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import { Pos } from "./token.ts";
 | 
			
		||||
export type FnNamesMap = { [pc: number]: string };
 | 
			
		||||
 | 
			
		||||
export class Lowerer {
 | 
			
		||||
    private program = Assembler.newRoot();
 | 
			
		||||
    private program = new Assembler();
 | 
			
		||||
    private locals: Locals = new LocalsFnRoot();
 | 
			
		||||
    private fnStmtIdLabelMap: { [stmtId: number]: string } = {};
 | 
			
		||||
    private fnLabelNameMap: { [name: string]: string } = {};
 | 
			
		||||
@ -19,16 +19,16 @@ export class Lowerer {
 | 
			
		||||
 | 
			
		||||
    public lower(stmts: Stmt[]) {
 | 
			
		||||
        this.addClearingSourceMap();
 | 
			
		||||
        this.program.add(Ops.PushPtr, { label: "main" });
 | 
			
		||||
        this.program.add(Ops.Call, 0);
 | 
			
		||||
        this.program.add(Ops.PushPtr, { label: "_exit" });
 | 
			
		||||
        this.program.add(Ops.PushPtr, { label: "_start" });
 | 
			
		||||
        this.program.add(Ops.Jump);
 | 
			
		||||
        this.scoutFnHeaders(stmts);
 | 
			
		||||
        for (const stmt of stmts) {
 | 
			
		||||
            this.lowerStaticStmt(stmt);
 | 
			
		||||
        }
 | 
			
		||||
        this.program.setLabel({ label: "_exit" });
 | 
			
		||||
        this.program.setLabel({ label: "_start" });
 | 
			
		||||
        this.addSourceMap(this.lastPos);
 | 
			
		||||
        this.program.add(Ops.PushPtr, { label: "main" });
 | 
			
		||||
        this.program.add(Ops.Call, 0);
 | 
			
		||||
        this.program.add(Ops.Pop);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -171,7 +171,7 @@ export class Lowerer {
 | 
			
		||||
        const returnLabel = this.program.makeLabel();
 | 
			
		||||
        this.returnStack.push(returnLabel);
 | 
			
		||||
 | 
			
		||||
        this.program = outerProgram.fork();
 | 
			
		||||
        this.program = new Assembler();
 | 
			
		||||
        this.locals = fnRoot;
 | 
			
		||||
        for (const { ident } of stmt.kind.params) {
 | 
			
		||||
            this.locals.allocSym(ident);
 | 
			
		||||
@ -185,17 +185,18 @@ export class Lowerer {
 | 
			
		||||
        }
 | 
			
		||||
        this.locals = outerLocals;
 | 
			
		||||
 | 
			
		||||
        this.returnStack.pop();
 | 
			
		||||
        this.program.setLabel(returnLabel);
 | 
			
		||||
 | 
			
		||||
        const localAmount = fnRoot.stackReserved() -
 | 
			
		||||
            stmt.kind.params.length;
 | 
			
		||||
        for (let i = 0; i < localAmount; ++i) {
 | 
			
		||||
            outerProgram.add(Ops.PushNull);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.returnStack.pop();
 | 
			
		||||
        this.program.setLabel(returnLabel);
 | 
			
		||||
        this.program.add(Ops.Return);
 | 
			
		||||
 | 
			
		||||
        outerProgram.join(this.program);
 | 
			
		||||
        outerProgram.concat(this.program);
 | 
			
		||||
        this.program = outerProgram;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -352,8 +353,9 @@ export class Lowerer {
 | 
			
		||||
            switch (expr.kind.unaryType) {
 | 
			
		||||
                case "-": {
 | 
			
		||||
                    this.program.add(Ops.PushInt, 0);
 | 
			
		||||
                    this.program.add(Ops.Swap);
 | 
			
		||||
                    this.program.add(Ops.PushInt, 1);
 | 
			
		||||
                    this.program.add(Ops.Subtract);
 | 
			
		||||
                    this.program.add(Ops.Multiply);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                default:
 | 
			
		||||
@ -371,25 +373,9 @@ export class Lowerer {
 | 
			
		||||
        if (expr.kind.type !== "binary") {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
        const vtype = expr.kind.left.vtype!;
 | 
			
		||||
        if (vtype.type === "bool") {
 | 
			
		||||
            if (["or", "and"].includes(expr.kind.binaryType)) {
 | 
			
		||||
                const shortCircuitLabel = this.program.makeLabel();
 | 
			
		||||
                this.lowerExpr(expr.kind.left);
 | 
			
		||||
                this.program.add(Ops.Duplicate);
 | 
			
		||||
                if (expr.kind.binaryType === "and") {
 | 
			
		||||
                    this.program.add(Ops.Not);
 | 
			
		||||
                }
 | 
			
		||||
                this.program.add(Ops.PushPtr, shortCircuitLabel);
 | 
			
		||||
                this.program.add(Ops.JumpIfTrue);
 | 
			
		||||
                this.program.add(Ops.Pop);
 | 
			
		||||
                this.lowerExpr(expr.kind.right);
 | 
			
		||||
                this.program.setLabel(shortCircuitLabel);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.lowerExpr(expr.kind.left);
 | 
			
		||||
        this.lowerExpr(expr.kind.right);
 | 
			
		||||
        const vtype = expr.kind.left.vtype!;
 | 
			
		||||
        if (vtype.type === "int") {
 | 
			
		||||
            switch (expr.kind.binaryType) {
 | 
			
		||||
                case "+":
 | 
			
		||||
@ -401,9 +387,6 @@ export class Lowerer {
 | 
			
		||||
                case "*":
 | 
			
		||||
                    this.program.add(Ops.Multiply);
 | 
			
		||||
                    return;
 | 
			
		||||
                case "/":
 | 
			
		||||
                    this.program.add(Ops.Multiply);
 | 
			
		||||
                    return;
 | 
			
		||||
                case "==":
 | 
			
		||||
                    this.program.add(Ops.Equal);
 | 
			
		||||
                    return;
 | 
			
		||||
@ -414,15 +397,6 @@ export class Lowerer {
 | 
			
		||||
                case "<":
 | 
			
		||||
                    this.program.add(Ops.LessThan);
 | 
			
		||||
                    return;
 | 
			
		||||
                case ">":
 | 
			
		||||
                    this.program.add(Ops.Swap);
 | 
			
		||||
                    this.program.add(Ops.LessThan);
 | 
			
		||||
                    return;
 | 
			
		||||
                case "<=":
 | 
			
		||||
                    this.program.add(Ops.Swap);
 | 
			
		||||
                    this.program.add(Ops.LessThan);
 | 
			
		||||
                    this.program.add(Ops.Not);
 | 
			
		||||
                    return;
 | 
			
		||||
                case ">=":
 | 
			
		||||
                    this.program.add(Ops.LessThan);
 | 
			
		||||
                    this.program.add(Ops.Not);
 | 
			
		||||
@ -445,6 +419,16 @@ export class Lowerer {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (vtype.type === "bool") {
 | 
			
		||||
            if (expr.kind.binaryType === "or") {
 | 
			
		||||
                this.program.add(Ops.Or);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (expr.kind.binaryType === "and") {
 | 
			
		||||
                this.program.add(Ops.And);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new Error(
 | 
			
		||||
            `unhandled binaryType` +
 | 
			
		||||
                ` '${vtypeToString(expr.vtype!)}' aka. ` +
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,10 @@ fn string_to_int_impl(text: string) -> int {
 | 
			
		||||
 | 
			
		||||
    let len = string_length(text);
 | 
			
		||||
    if len == 0 {
 | 
			
		||||
        return -1;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    println("hello world");
 | 
			
		||||
 | 
			
		||||
    if text[0] == "0"[0] {
 | 
			
		||||
        if len == 1 {
 | 
			
		||||
            0
 | 
			
		||||
@ -39,28 +41,27 @@ fn parse_digits(text: string, base: int, digit_set: string) -> int {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn char_val(ch: int) -> int {
 | 
			
		||||
    if ch >= "0"[0] and ch <= "9"[0] {
 | 
			
		||||
    if ch >= "0"[0] and "9"[0] >= ch {
 | 
			
		||||
        ch - "0"[0]
 | 
			
		||||
    } else if ch >= "a"[0] and ch <= "f"[0] {
 | 
			
		||||
        ch - "a"[0] + 10
 | 
			
		||||
    } else if ch >= "a"[0] and "f"[0] >= ch {
 | 
			
		||||
        ch - "a"[0]
 | 
			
		||||
    } else {
 | 
			
		||||
        -1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_string_to_int_impl() -> bool {
 | 
			
		||||
        test("should convert zero",      assert_int_equal(string_to_int_impl("0"), 0))
 | 
			
		||||
    and test("should convert decimal",   assert_int_equal(string_to_int_impl("10"), 10))
 | 
			
		||||
    and test("should convert binary",    assert_int_equal(string_to_int_impl("0b110"), 6))
 | 
			
		||||
    and test("should convert octal",     assert_int_equal(string_to_int_impl("071"), 57))
 | 
			
		||||
    and test("should convert hex",       assert_int_equal(string_to_int_impl("0xaa"), 170))
 | 
			
		||||
    and test("should fail",              assert_int_equal(string_to_int_impl("john"), -1))
 | 
			
		||||
    and test("should fail",              assert_int_equal(string_to_int_impl(""), -1))
 | 
			
		||||
       test("should convert zero",          assert_int_equal(string_to_int_impl("0"), 0))
 | 
			
		||||
    or test("should convert decimal",       assert_int_equal(string_to_int_impl("10"), 10))
 | 
			
		||||
    or test("should convert binary",        assert_int_equal(string_to_int_impl("0b110"), 6))
 | 
			
		||||
    or test("should convert octal",         assert_int_equal(string_to_int_impl("071"), 51))
 | 
			
		||||
    or test("should convert hexadecimal",   assert_int_equal(string_to_int_impl("0xaa"), 170))
 | 
			
		||||
    or test("should fail",                  assert_int_equal(string_to_int_impl("john"), -1))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn assert_int_equal(value: int, target: int) -> bool {
 | 
			
		||||
    if value != target {
 | 
			
		||||
        println("assertion failed: " + itos(value) + " != " + itos(target));
 | 
			
		||||
        println("assertion failed: " + int_to_string(value) + " != " + int_to_string(target));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    true
 | 
			
		||||
@ -108,8 +109,6 @@ fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
 | 
			
		||||
fn file_flush(file: int) #[builtin(FileFlush)] {}
 | 
			
		||||
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
 | 
			
		||||
 | 
			
		||||
fn itos(number: int) -> string #[builtin(IntToString)] {}
 | 
			
		||||
fn stoi(str: string) -> int #[builtin(StringToInt)] {}
 | 
			
		||||
 | 
			
		||||
fn stdin() -> int { 0 }
 | 
			
		||||
fn stdout() -> int { 1 }
 | 
			
		||||
@ -176,11 +175,11 @@ fn string_split(str: string, seperator: int) -> [string] {
 | 
			
		||||
fn string_slice(str: string, from: int, to: int) -> string {
 | 
			
		||||
    let result = "";
 | 
			
		||||
    let len = string_length(str);
 | 
			
		||||
    let abs_to =
 | 
			
		||||
    let actual_to =
 | 
			
		||||
        if to >= len { len }
 | 
			
		||||
        else if to < 0 { len + to + 1 }
 | 
			
		||||
        else if to < 0 { len - to }
 | 
			
		||||
        else { to };
 | 
			
		||||
    for (let i = from; i < abs_to; i += 1) {
 | 
			
		||||
    for (let i = from; i < actual_to; i += i) {
 | 
			
		||||
        result = string_push_char(result, str[i]);
 | 
			
		||||
    }
 | 
			
		||||
    result
 | 
			
		||||
 | 
			
		||||
@ -24,8 +24,6 @@ enum class Op : uint32_t {
 | 
			
		||||
    Jump = 0x0e,
 | 
			
		||||
    JumpIfTrue = 0x0f,
 | 
			
		||||
    Builtin = 0x10,
 | 
			
		||||
    Duplicate = 0x11,
 | 
			
		||||
    Swap = 0x12,
 | 
			
		||||
    Add = 0x20,
 | 
			
		||||
    Subtract = 0x21,
 | 
			
		||||
    Multiply = 0x22,
 | 
			
		||||
 | 
			
		||||
@ -33,8 +33,6 @@ size_t VM::instruction_size(size_t i) const
 | 
			
		||||
            return 1;
 | 
			
		||||
        case Op::Builtin:
 | 
			
		||||
            return 2;
 | 
			
		||||
        case Op::Duplicate:
 | 
			
		||||
        case Op::Swap:
 | 
			
		||||
        case Op::Add:
 | 
			
		||||
        case Op::Subtract:
 | 
			
		||||
        case Op::Multiply:
 | 
			
		||||
 | 
			
		||||
@ -25,8 +25,6 @@ auto sliger::maybe_op_to_string(uint32_t value) -> std::string
 | 
			
		||||
        case Op::Jump: return "Jump";
 | 
			
		||||
        case Op::JumpIfTrue: return "JumpIfTrue";
 | 
			
		||||
        case Op::Builtin: return "Builtin";
 | 
			
		||||
        case Op::Duplicate: return "Duplicate";
 | 
			
		||||
        case Op::Swap: return "Swap";
 | 
			
		||||
        case Op::Add: return "Add";
 | 
			
		||||
        case Op::Subtract: return "Subtract";
 | 
			
		||||
        case Op::Multiply: return "Multiply";
 | 
			
		||||
@ -39,9 +37,10 @@ auto sliger::maybe_op_to_string(uint32_t value) -> std::string
 | 
			
		||||
        case Op::Xor: return "Xor";
 | 
			
		||||
        case Op::Not: return "Not";
 | 
			
		||||
        case Op::SourceMap: return "SourceMap";
 | 
			
		||||
            /* clang-format on */
 | 
			
		||||
        /* clang-format on */
 | 
			
		||||
        default:
 | 
			
		||||
            return std::to_string(value);
 | 
			
		||||
    }
 | 
			
		||||
    return std::to_string(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sliger::maybe_builtin_to_string(uint32_t value) -> std::string
 | 
			
		||||
 | 
			
		||||
@ -177,21 +177,6 @@ void VM::run_instruction()
 | 
			
		||||
            run_builtin(static_cast<Builtin>(builtin_id));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case Op::Duplicate: {
 | 
			
		||||
            assert_stack_has(1);
 | 
			
		||||
            auto value = stack_pop();
 | 
			
		||||
            stack_push(value);
 | 
			
		||||
            stack_push(value);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case Op::Swap: {
 | 
			
		||||
            assert_stack_has(2);
 | 
			
		||||
            auto right = stack_pop();
 | 
			
		||||
            auto left = stack_pop();
 | 
			
		||||
            stack_push(right);
 | 
			
		||||
            stack_push(left);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case Op::Add: {
 | 
			
		||||
            assert_stack_has(2);
 | 
			
		||||
            auto right = stack_pop().as_int().value;
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,6 @@ fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
 | 
			
		||||
fn file_flush(file: int) #[builtin(FileFlush)] {}
 | 
			
		||||
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
 | 
			
		||||
 | 
			
		||||
fn itos(number: int) -> string #[builtin(IntToString)] {}
 | 
			
		||||
fn stoi(str: string) -> int #[builtin(StringToInt)] {}
 | 
			
		||||
 | 
			
		||||
fn stdin() -> int { 0 }
 | 
			
		||||
fn stdout() -> int { 1 }
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,6 @@
 | 
			
		||||
<html>
 | 
			
		||||
    <head>
 | 
			
		||||
        <meta charset="utf-8">
 | 
			
		||||
 | 
			
		||||
        <link rel="preconnect" href="https://fonts.googleapis.com">
 | 
			
		||||
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
 | 
			
		||||
        <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">
 | 
			
		||||
 | 
			
		||||
        <link rel="stylesheet" href="style.css">
 | 
			
		||||
        <script src="dist/bundle.js" type="module" defer></script>
 | 
			
		||||
    </head>
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ export function loadFlameGraph(
 | 
			
		||||
    canvas.width = 1000;
 | 
			
		||||
    canvas.height = 500;
 | 
			
		||||
 | 
			
		||||
    const fnNameFont = "600 14px 'Roboto Mono'";
 | 
			
		||||
    const fnNameFont = "600 14px monospace";
 | 
			
		||||
 | 
			
		||||
    const ctx = canvas.getContext("2d")!;
 | 
			
		||||
    ctx.font = fnNameFont;
 | 
			
		||||
@ -39,7 +39,7 @@ export function loadFlameGraph(
 | 
			
		||||
        h: number;
 | 
			
		||||
        title: string;
 | 
			
		||||
        percent: string;
 | 
			
		||||
        fgNode: data.FlameGraphNode;
 | 
			
		||||
        fgNode: FlameGraphNode;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function calculateNodeRects(
 | 
			
		||||
@ -91,40 +91,14 @@ export function loadFlameGraph(
 | 
			
		||||
        for (const node of nodes) {
 | 
			
		||||
            const { x, y, w, h } = node;
 | 
			
		||||
            ctx.fillStyle = "rgb(255, 125, 0)";
 | 
			
		||||
            ctx.fillRect(x + 2, y + 2, w - 4, h - 4);
 | 
			
		||||
 | 
			
		||||
            const textCanvas = drawTextCanvas(node);
 | 
			
		||||
            ctx.drawImage(textCanvas, x + 4, y);
 | 
			
		||||
 | 
			
		||||
            const edgePadding = 4;
 | 
			
		||||
            const edgeWidth = 8;
 | 
			
		||||
 | 
			
		||||
            const leftGradient = ctx.createLinearGradient(
 | 
			
		||||
                x + 2 + edgePadding,
 | 
			
		||||
                0,
 | 
			
		||||
                x + 2 + edgeWidth,
 | 
			
		||||
                0,
 | 
			
		||||
            );
 | 
			
		||||
            leftGradient.addColorStop(1, "rgba(255, 125, 0, 0.0)");
 | 
			
		||||
            leftGradient.addColorStop(0, "rgba(255, 125, 0, 1.0)");
 | 
			
		||||
            ctx.fillStyle = leftGradient;
 | 
			
		||||
            ctx.fillRect(x + 2, y + 2, Math.min(edgeWidth, (w - 4) / 2), h - 4);
 | 
			
		||||
 | 
			
		||||
            const rightGradient = ctx.createLinearGradient(
 | 
			
		||||
                x + w - 2 - edgeWidth,
 | 
			
		||||
                0,
 | 
			
		||||
                x + w - 2 - edgePadding,
 | 
			
		||||
                0,
 | 
			
		||||
            );
 | 
			
		||||
            rightGradient.addColorStop(0, "rgba(255, 125, 0, 0.0)");
 | 
			
		||||
            rightGradient.addColorStop(1, "rgba(255, 125, 0, 1.0)");
 | 
			
		||||
            ctx.fillStyle = rightGradient;
 | 
			
		||||
            ctx.fillRect(
 | 
			
		||||
                x + w - 2 - Math.min(edgeWidth, (w - 4) / 2),
 | 
			
		||||
                x + 2,
 | 
			
		||||
                y + 2,
 | 
			
		||||
                Math.min(edgeWidth, (w - 4) / 2),
 | 
			
		||||
                w - 4,
 | 
			
		||||
                h - 4,
 | 
			
		||||
            );
 | 
			
		||||
            const textCanvas = drawTextCanvas(node);
 | 
			
		||||
            ctx.drawImage(textCanvas, x + 4, y);
 | 
			
		||||
        }
 | 
			
		||||
        const tooltip = document.getElementById("flame-graph-tooltip")!;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -40,200 +40,6 @@ async function checkStatus(): Promise<"running" | "done"> {
 | 
			
		||||
    return "done";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function syntaxHighlight(code: string): string {
 | 
			
		||||
    const colors = {
 | 
			
		||||
        colorBackground: "#282828",
 | 
			
		||||
        colorForeground: "#fbf1c7",
 | 
			
		||||
        colorKeyword: "#fb4934",
 | 
			
		||||
        colorIdentifier: "#83a598",
 | 
			
		||||
        colorOperator: "#fe8019",
 | 
			
		||||
        colorSpecial: "#fe8019",
 | 
			
		||||
        colorType: "#fabd2f",
 | 
			
		||||
        colorBoolean: "#d3869b",
 | 
			
		||||
        colorNumber: "#d3869b",
 | 
			
		||||
        colorString: "#b8bb26",
 | 
			
		||||
        colorComment: "#928374",
 | 
			
		||||
        colorFunction: "#b8bb26",
 | 
			
		||||
        colorLineNumber: "#7c6f64",
 | 
			
		||||
    } as const;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    Keyword = { link = "GruvboxRed" },
 | 
			
		||||
    Identifier = { link = "GruvboxBlue" },
 | 
			
		||||
    Operator = { fg = colors.orange, italic = config.italic.operators },
 | 
			
		||||
    Special = { link = "GruvboxOrange" },
 | 
			
		||||
    Type = { link = "GruvboxYellow" },
 | 
			
		||||
    Boolean = { link = "GruvboxPurple" },
 | 
			
		||||
    Number = { link = "GruvboxPurple" },
 | 
			
		||||
    String = { fg = colors.green, italic = config.italic.strings },
 | 
			
		||||
    Comment = { fg = colors.gray, italic = config.italic.comments },
 | 
			
		||||
    Function = { link = "GruvboxGreenBold" },
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    let matches: {
 | 
			
		||||
        index: number;
 | 
			
		||||
        length: number;
 | 
			
		||||
        color: string;
 | 
			
		||||
        extra: string;
 | 
			
		||||
    }[] = [];
 | 
			
		||||
 | 
			
		||||
    function addMatches(color: string, re: RegExp, extra = "") {
 | 
			
		||||
        for (const match of code.matchAll(re)) {
 | 
			
		||||
            matches.push({
 | 
			
		||||
                index: match.index,
 | 
			
		||||
                length: match[1].length,
 | 
			
		||||
                color,
 | 
			
		||||
                extra,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function addKeywordMatches(color: string, keywords: string[]) {
 | 
			
		||||
        addMatches(
 | 
			
		||||
            color,
 | 
			
		||||
            new RegExp(
 | 
			
		||||
                `(?<!\\w)(${
 | 
			
		||||
                    keywords.map((kw) => `(?:${kw})`).join("|")
 | 
			
		||||
                })(?!\\w)`,
 | 
			
		||||
                "g",
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < code.length; ++i) {
 | 
			
		||||
        if (code[i] !== '"') {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        let last = code[i];
 | 
			
		||||
        const index = i;
 | 
			
		||||
        i += 1;
 | 
			
		||||
        while (i < code.length && !(code[i] === '"' && last !== "\\")) {
 | 
			
		||||
            last = code[i];
 | 
			
		||||
            i += 1;
 | 
			
		||||
        }
 | 
			
		||||
        if (i < code.length) {
 | 
			
		||||
            i += 1;
 | 
			
		||||
        }
 | 
			
		||||
        matches.push({
 | 
			
		||||
            index,
 | 
			
		||||
            length: i - index,
 | 
			
		||||
            color: colors.colorString,
 | 
			
		||||
            extra: "font-style: italic;",
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        let last = "";
 | 
			
		||||
        for (let i = 0; i < code.length; ++i) {
 | 
			
		||||
            if (last === "/" && code[i] === "/") {
 | 
			
		||||
                const index = i - 1;
 | 
			
		||||
                while (i < code.length && code[i] !== "\n") {
 | 
			
		||||
                    i += 1;
 | 
			
		||||
                }
 | 
			
		||||
                matches.push({
 | 
			
		||||
                    index,
 | 
			
		||||
                    length: i - index,
 | 
			
		||||
                    color: colors.colorComment,
 | 
			
		||||
                    extra: "font-style: italic;",
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            last = code[i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addKeywordMatches(
 | 
			
		||||
        colors.colorKeyword,
 | 
			
		||||
        [
 | 
			
		||||
            "break",
 | 
			
		||||
            "return",
 | 
			
		||||
            "let",
 | 
			
		||||
            "fn",
 | 
			
		||||
            "if",
 | 
			
		||||
            "else",
 | 
			
		||||
            "struct",
 | 
			
		||||
            "import",
 | 
			
		||||
            "or",
 | 
			
		||||
            "and",
 | 
			
		||||
            "not",
 | 
			
		||||
            "while",
 | 
			
		||||
            "for",
 | 
			
		||||
            "in",
 | 
			
		||||
        ],
 | 
			
		||||
    );
 | 
			
		||||
    addKeywordMatches(colors.colorSpecial, ["null"]);
 | 
			
		||||
    addKeywordMatches(colors.colorType, ["int", "string", "bool"]);
 | 
			
		||||
    addKeywordMatches(colors.colorBoolean, ["false", "true"]);
 | 
			
		||||
    addMatches(
 | 
			
		||||
        colors.colorOperator,
 | 
			
		||||
        new RegExp(
 | 
			
		||||
            `(${
 | 
			
		||||
                [
 | 
			
		||||
                    "\\+=",
 | 
			
		||||
                    "\\-=",
 | 
			
		||||
                    "\\+",
 | 
			
		||||
                    "\\->",
 | 
			
		||||
                    "\\-",
 | 
			
		||||
                    "\\*",
 | 
			
		||||
                    "/",
 | 
			
		||||
                    "==",
 | 
			
		||||
                    "!=",
 | 
			
		||||
                    "<=",
 | 
			
		||||
                    ">=",
 | 
			
		||||
                    "=",
 | 
			
		||||
                    "<",
 | 
			
		||||
                    ">",
 | 
			
		||||
                    "\\.",
 | 
			
		||||
                    "::<",
 | 
			
		||||
                    "::",
 | 
			
		||||
                    ":",
 | 
			
		||||
                ].map((kw) => `(?:${kw})`).join("|")
 | 
			
		||||
            })`,
 | 
			
		||||
            "g",
 | 
			
		||||
        ),
 | 
			
		||||
    );
 | 
			
		||||
    addMatches(
 | 
			
		||||
        colors.colorNumber,
 | 
			
		||||
        /(0|(?:[1-9][0-9]*)|(?:0[0-7]+)|(?:0x[0-9a-fA-F]+)|(?:0b[01]+))/g,
 | 
			
		||||
    );
 | 
			
		||||
    addMatches(
 | 
			
		||||
        colors.colorFunction,
 | 
			
		||||
        /([a-zA-Z_]\w*(?=\())/g,
 | 
			
		||||
        "font-weight: 700;",
 | 
			
		||||
    );
 | 
			
		||||
    addMatches(colors.colorIdentifier, /([a-z_]\w*)/g);
 | 
			
		||||
    addMatches(colors.colorType, /([A-Z_]\w*)/g);
 | 
			
		||||
 | 
			
		||||
    matches = matches.reduce<typeof matches>(
 | 
			
		||||
        (acc, match) =>
 | 
			
		||||
            acc.find((m) => m.index === match.index) === undefined
 | 
			
		||||
                ? (acc.push(match), acc)
 | 
			
		||||
                : acc,
 | 
			
		||||
        [],
 | 
			
		||||
    );
 | 
			
		||||
    matches.sort((a, b) => a.index - b.index);
 | 
			
		||||
 | 
			
		||||
    let highlighted = "";
 | 
			
		||||
    let i = 0;
 | 
			
		||||
    for (const match of matches) {
 | 
			
		||||
        if (match.index < i) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        while (i < match.index) {
 | 
			
		||||
            highlighted += code[i];
 | 
			
		||||
            i += 1;
 | 
			
		||||
        }
 | 
			
		||||
        const section = code.slice(match.index, match.index + match.length);
 | 
			
		||||
        highlighted +=
 | 
			
		||||
            `<span style="color: ${match.color};${match.extra}">${section}</span>`;
 | 
			
		||||
        i += section.length;
 | 
			
		||||
    }
 | 
			
		||||
    while (i < code.length) {
 | 
			
		||||
        highlighted += code[i];
 | 
			
		||||
        i += 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return highlighted;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sourceCode(view: Element, codeData: string) {
 | 
			
		||||
    const outerContainer = document.createElement("div");
 | 
			
		||||
    outerContainer.classList.add("code-container");
 | 
			
		||||
@ -245,7 +51,7 @@ function sourceCode(view: Element, codeData: string) {
 | 
			
		||||
    const code = document.createElement("pre");
 | 
			
		||||
 | 
			
		||||
    code.classList.add("code-source");
 | 
			
		||||
    code.innerHTML = syntaxHighlight(codeData);
 | 
			
		||||
    code.innerText = codeData;
 | 
			
		||||
    innerContainer.append(lines, code);
 | 
			
		||||
    outerContainer.append(innerContainer);
 | 
			
		||||
    view.replaceChildren(outerContainer);
 | 
			
		||||
 | 
			
		||||
@ -11,10 +11,6 @@
 | 
			
		||||
    --white: #ecebe9;
 | 
			
		||||
    --white-transparent: #ecebe9aa;
 | 
			
		||||
    --code-status: var(--white);
 | 
			
		||||
 | 
			
		||||
    --code-bg: #282828;
 | 
			
		||||
    --code-fg: #fbf1c7;
 | 
			
		||||
    --code-linenr: #7c6f64;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
* {
 | 
			
		||||
@ -103,21 +99,11 @@ main #cover {
 | 
			
		||||
#view .code-container {
 | 
			
		||||
    max-height: 100%;
 | 
			
		||||
    overflow: scroll;
 | 
			
		||||
    background-color: var(--code-bg);
 | 
			
		||||
    background-color: rgba(255, 255, 255, 0.1);
 | 
			
		||||
    padding: 0.5rem;
 | 
			
		||||
    border-radius: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#view .code-container pre {
 | 
			
		||||
    font-family: "Roboto Mono", monospace;
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
    color: var(--code-fg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#view .code-container pre.code-lines {
 | 
			
		||||
    color: var(--code-linenr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#view .code-container.code-coverage {
 | 
			
		||||
    max-height: calc(100% - 103px);
 | 
			
		||||
}
 | 
			
		||||
@ -130,6 +116,7 @@ main #cover {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#view .code-lines {
 | 
			
		||||
    color: var(--white-transparent);
 | 
			
		||||
    border-right: 1px solid currentcolor;
 | 
			
		||||
    padding-right: 0.5rem;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user