about summary refs log tree commit diff
path: root/boot/transpile.c
blob: 38a700b9bce4a470f7735d5e9a608897b7f9a1ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
 * transpiler from a catskill intermediate translation unit,
 * to the c programming language, as the main translation target.
 *
 * walks a fully-lowered translation unit and emits (slightly ugly)
 * c source code.
 *
 * Copyright (c) 2025-2026, Mel G. <mel@rnrd.eu>
 *
 * SPDX-License-Identifier: MPL-2.0
 */

#pragma once

#include "catboot.h"

// the output of the transpilation pass.
// used for both file output and string buffer output.
struct Transpile_Output
{
    FILE* file;
    struct String_Buffer string;
};

struct Transpile_Output
transpile_output_from_file(FILE* file)
{
    check(file != nil, "transpile output file is nil");
    return (struct Transpile_Output){
        .file = file,
        .string = string_buffer_empty(),
    };
}

#define TRANSPILE_OUTPUT_MAX_LENGTH 262144 // 256 KiB

struct Transpile_Output
transpile_output_from_string(void)
{
    return (struct Transpile_Output){
        .file = nil,
        .string = string_buffer_new(TRANSPILE_OUTPUT_MAX_LENGTH),
    };
}

#define TRANSPILE_OUTPUT_MAX_WRITE_LENGTH 4096

void
transpile_output_write(struct Transpile_Output* output, const ascii* format, ...)
{
    va_list args;
    va_start(args, format);

    if (output->file) {
        vfprintf(output->file, format, args);
    } else {
        ascii buffer[TRANSPILE_OUTPUT_MAX_WRITE_LENGTH];
        int written = vsnprintf(buffer, TRANSPILE_OUTPUT_MAX_WRITE_LENGTH, format, args);
        check(written >= 0 && (uint)written < TRANSPILE_OUTPUT_MAX_LENGTH,
              "transpile output string buffer overflow");

        string_buffer_append_c_str(&output->string, buffer);
    }

    va_end(args);
}

struct String
transpile_output_string(struct Transpile_Output* output)
{
    check(output->file == nil, "transpile output is not a string");
    return string_buffer_to_string(&output->string);
}

FILE*
transpile_output_file(struct Transpile_Output* output)
{
    check(output->file != nil, "transpile output is not a file");
    return output->file;
}

struct Transpiler
{
    struct Transpile_Output output;
};

void
transpiler_new(struct Transpiler* transpiler, struct Transpile_Output output)
{
    *transpiler = (struct Transpiler){
        .output = output,
    };
}

// walk a lowered translation unit and emit c source into the transpiler's output.
// TODO: actual emission!
int
transpile_unit(struct Transpiler* transpiler, struct Unit* unit)
{
    (void)transpiler;
    (void)unit;
    return 0;
}