compiler: Print through checked output borrows

59c7667a9ed7508531a8647fb0e55dc1d20b2cb760fab1a74a261e3145fdd53a
Alexis Sellier committed ago 1 parent df6afe11
compiler/radiance.rad +1 -1
1312 1312
    let entryPkg = try getEntryPackage(ctx);
1313 1313
    if let directory = ctx.rilDirectory {
1314 1314
        try emitPackages(ctx, res, directory);
1315 1315
        return;
1316 1316
    }
1317 -
    let mut out = sexpr::Output::Stdout;
1317 +
    let mut out = sexpr::Stdout {};
1318 1318
1319 1319
    if ctx.dump == Dump::Il {
1320 1320
        // Lower all packages into a single unified IL program for dumping.
1321 1321
        let program = try lowerAllPackages(ctx, res);
1322 1322
        il::printer::printProgram(&mut out, &program);
lib/std/arch/rv64/asm/tests.rad +5 -2
74 74
}
75 75
76 76
unsafe fn printInstrText(instr: u32) -> *[u8] {
77 77
    let mut arena = alloc::new(&mut PRINT_ARENA_STORAGE[..]);
78 78
    let mut pos: u32 = 0;
79 -
    let mut out = sexpr::Output::Buffer { buf: &mut PRINT_BUFFER[..], pos: &mut pos };
80 -
    printer::printInstr(&mut out, &mut arena, instr);
79 +
    let bytes: 'output = &mut PRINT_BUFFER[..] in {
80 +
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
81 +
        printer::printInstr(&mut out, &mut arena, instr);
82 +
        set pos = out.pos;
83 +
    }
81 84
    return &PRINT_BUFFER[..pos];
82 85
}
83 86
84 87
@test unsafe fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) {
85 88
    let program = try assembleSource(
lib/std/arch/rv64/printer.rad +17 -17
38 38
///////////////////////
39 39
// Output Helpers    //
40 40
///////////////////////
41 41
42 42
/// Write a string to output.
43 -
unsafe fn write(out: &mut sexpr::Output, s: &[u8]) {
43 +
fn write(out: &mut opaque sexpr::Output, s: &[u8]) {
44 44
    sexpr::write(out, s);
45 45
}
46 46
47 47
/// Format `i32` into arena.
48 48
unsafe fn formatI32(a: &mut alloc::Arena, val: i32) -> *[u8] {
70 70
71 71
/// Mnemonic column width for alignment.
72 72
constant MNEMONIC_WIDTH: u32 = 8;
73 73
74 74
/// Write text wrapped in parentheses.
75 -
unsafe fn writeParens(out: &mut sexpr::Output, s: &[u8]) {
75 +
fn writeParens(out: &mut opaque sexpr::Output, s: &[u8]) {
76 76
    write(out, "(");
77 77
    write(out, s);
78 78
    write(out, ")");
79 79
}
80 80
81 81
/// Write strings separated by ", ".
82 -
unsafe fn writeDelim(out: &mut sexpr::Output, parts: &[*[u8]]) {
82 +
fn writeDelim(out: &mut opaque sexpr::Output, parts: &[*[u8]]) {
83 83
    for part, i in parts {
84 84
        if i > 0 {
85 85
            write(out, ", ");
86 86
        }
87 87
        write(out, part);
88 88
    }
89 89
}
90 90
91 91
/// Write mnemonic with padding for alignment.
92 -
unsafe fn writeMnem(out: &mut sexpr::Output, m: *[u8]) {
92 +
fn writeMnem(out: &mut opaque sexpr::Output, m: *[u8]) {
93 93
    write(out, m);
94 94
    let mut i = m.len;
95 95
    while i < MNEMONIC_WIDTH {
96 96
        write(out, " ");
97 97
        set i += 1;
101 101
////////////////////////////////
102 102
// Instruction Format Helpers //
103 103
////////////////////////////////
104 104
105 105
/// R-type: `op rd, rs1, rs2`.
106 -
unsafe fn fmtR(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) {
106 +
fn fmtR(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) {
107 107
    writeMnem(out, m);
108 108
    writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]);
109 109
}
110 110
111 111
/// I-type: `op rd, rs1, imm`.
112 -
unsafe fn fmtI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
112 +
unsafe fn fmtI(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
113 113
    writeMnem(out, m);
114 114
    writeDelim(out, &[regNameR(rd), regNameR(rs1), formatI32(a, imm)]);
115 115
}
116 116
117 117
/// 2-reg: `op rd, rs`.
118 -
unsafe fn fmt2R(out: &mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
118 +
fn fmt2R(out: &mut opaque sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
119 119
    writeMnem(out, m);
120 120
    writeDelim(out, &[regNameR(rd), regNameR(rs)]);
121 121
}
122 122
123 123
/// reg + imm: `op rd, imm`.
124 -
unsafe fn fmtRI(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
124 +
unsafe fn fmtRI(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
125 125
    writeMnem(out, m);
126 126
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
127 127
}
128 128
129 129
/// imm only: `op imm`.
130 -
unsafe fn fmtImm(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], imm: i32) {
130 +
unsafe fn fmtImm(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], imm: i32) {
131 131
    writeMnem(out, m);
132 132
    write(out, formatI32(a, imm));
133 133
}
134 134
135 135
/// 1-reg: `op rs`.
136 -
unsafe fn fmt1R(out: &mut sexpr::Output, m: *[u8], rs: gen::Reg) {
136 +
fn fmt1R(out: &mut opaque sexpr::Output, m: *[u8], rs: gen::Reg) {
137 137
    writeMnem(out, m);
138 138
    write(out, regNameR(rs));
139 139
}
140 140
141 141
/// Load: `op rd, imm(rs1)`.
142 -
unsafe fn fmtLoad(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
142 +
unsafe fn fmtLoad(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
143 143
    writeMnem(out, m);
144 144
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
145 145
    writeParens(out, regNameR(rs1));
146 146
}
147 147
148 148
/// Store: `op rs2, imm(rs1)`.
149 -
unsafe fn fmtStore(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
149 +
unsafe fn fmtStore(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
150 150
    writeMnem(out, m);
151 151
    writeDelim(out, &[regNameR(rs2), formatI32(a, imm)]);
152 152
    writeParens(out, regNameR(rs1));
153 153
}
154 154
155 155
/// Branch: `op rs1, rs2, imm`.
156 -
unsafe fn fmtB(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
156 +
unsafe fn fmtB(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
157 157
    writeMnem(out, m);
158 158
    writeDelim(out, &[regNameR(rs1), regNameR(rs2), formatI32(a, imm)]);
159 159
}
160 160
161 161
/// Branch zero: `op rs1, imm`.
162 -
unsafe fn fmtBz(out: &mut sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
162 +
unsafe fn fmtBz(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
163 163
    writeMnem(out, m);
164 164
    writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]);
165 165
}
166 166
167 167
/// Print a single instruction to output buffer.
168 -
export unsafe fn printInstr(out: &mut sexpr::Output, a: &mut alloc::Arena, instr: u32) {
168 +
export unsafe fn printInstr(out: &mut opaque sexpr::Output, a: &mut alloc::Arena, instr: u32) {
169 169
    let decoded = decode::decode(instr);
170 170
171 171
    match decoded {
172 172
        case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm),
173 173
        case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm),
325 325
        },
326 326
    }
327 327
}
328 328
329 329
/// Print code with labels to the given output.
330 -
export unsafe fn printCodeTo(out: &mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: &mut alloc::Arena) {
330 +
export unsafe fn printCodeTo(out: &mut opaque sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: &mut alloc::Arena) {
331 331
    // Package header.
332 332
    write(out, "# package `");
333 333
    write(out, pkgName);
334 334
    write(out, "`\n\n");
335 335
353 353
    }
354 354
    return nil;
355 355
}
356 356
357 357
/// Print a memory-ordering mask in canonical order.
358 -
unsafe fn fenceMask(out: &mut sexpr::Output, mask: u32) {
358 +
fn fenceMask(out: &mut opaque sexpr::Output, mask: u32) {
359 359
    if mask == 0 {
360 360
        write(out, "0");
361 361
        return;
362 362
    }
363 363
    if (mask & 8) <> 0 {
lib/std/lang/il/printer.rad +32 -30
6 6
///////////////////////
7 7
// String formatting //
8 8
///////////////////////
9 9
10 10
/// Write a `u32` before its stack buffer leaves scope.
11 -
unsafe fn writeU32(out: &mut sexpr::Output, val: u32) {
12 -
    let mut digits: [u8; 10] = undefined;
11 +
fn writeU32(out: &mut opaque sexpr::Output, val: u32) {
12 +
    let mut digits: [u8; 10] = [0; 10];
13 13
    let start = fmt::formatU32(val, &mut digits[..]);
14 14
    write(out, &digits[start..]);
15 15
}
16 16
17 17
/// Write an `i32` before its stack buffer leaves scope.
18 -
unsafe fn writeI32(out: &mut sexpr::Output, val: i32) {
19 -
    let mut digits: [u8; 12] = undefined;
18 +
fn writeI32(out: &mut opaque sexpr::Output, val: i32) {
19 +
    let mut digits: [u8; 12] = [0; 12];
20 20
    let start = fmt::formatI32(val, &mut digits[..]);
21 21
    write(out, &digits[start..]);
22 22
}
23 23
24 24
/// Write an `i64` before its stack buffer leaves scope.
25 -
unsafe fn writeI64(out: &mut sexpr::Output, val: i64) {
26 -
    let mut digits: [u8; 20] = undefined;
25 +
fn writeI64(out: &mut opaque sexpr::Output, val: i64) {
26 +
    let mut digits: [u8; 20] = [0; 20];
27 27
    let start = fmt::formatI64(val, &mut digits[..]);
28 28
    write(out, &digits[start..]);
29 29
}
30 30
31 31
/////////////////////
32 32
// Output helpers  //
33 33
/////////////////////
34 34
35 35
/// Write a string to the output.
36 -
unsafe fn write(out: &mut sexpr::Output, s: &[u8]) {
36 +
fn write(out: &mut opaque sexpr::Output, s: &[u8]) {
37 37
    sexpr::write(out, s);
38 38
}
39 39
40 40
/// Emit indentation.
41 -
unsafe fn indent(out: &mut sexpr::Output, depth: u32) {
41 +
fn indent(out: &mut opaque sexpr::Output, depth: u32) {
42 42
    for _ in 0..depth {
43 43
        write(out, "    ");
44 44
    }
45 45
}
46 46
53 53
    }
54 54
    return false;
55 55
}
56 56
57 57
/// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`.
58 -
unsafe fn writeSymbol(out: &mut sexpr::Output, name: *[u8]) {
58 +
fn writeSymbol(out: &mut opaque sexpr::Output, name: *[u8]) {
59 59
    write(out, "$");
60 60
    if needsQuoting(name) {
61 61
        write(out, "\"");
62 62
        write(out, name);
63 63
        write(out, "\"");
79 79
        case super::Type::W64 => return "w64",
80 80
    }
81 81
}
82 82
83 83
/// Write a type.
84 -
unsafe fn writeType(out: &mut sexpr::Output, typ: super::Type) {
84 +
fn writeType(out: &mut opaque sexpr::Output, typ: super::Type) {
85 85
    write(out, typeStr(typ));
86 86
}
87 87
88 88
////////////////////////
89 89
// Operation printing //
125 125
////////////////////
126 126
// Value printing //
127 127
////////////////////
128 128
129 129
/// Write a value (register, immediate, symbol, or undefined).
130 -
unsafe fn writeVal(out: &mut sexpr::Output, val: super::Val) {
130 +
fn writeVal(out: &mut opaque sexpr::Output, val: super::Val) {
131 131
    match val {
132 132
        case super::Val::Reg(reg) => writeReg(out, reg),
133 133
        case super::Val::Imm(v) => writeI64(out, v),
134 134
        case super::Val::DataSym(name) => writeSymbol(out, name),
135 135
        case super::Val::FnAddr(name) => writeSymbol(out, name),
136 136
        case super::Val::Undef => write(out, "undefined"),
137 137
    }
138 138
}
139 139
140 140
/// Write a register in one operation before its stack buffer leaves scope.
141 -
unsafe fn writeReg(out: &mut sexpr::Output, reg: super::Reg) {
142 -
    let mut buffer: [u8; 11] = undefined;
141 +
fn writeReg(out: &mut opaque sexpr::Output, reg: super::Reg) {
142 +
    let mut buffer: [u8; 11] = [0; 11];
143 143
    let start = fmt::formatU32(reg.n, &mut buffer[1..]);
144 144
    set buffer[start] = '%';
145 145
    write(out, &buffer[start..]);
146 146
}
147 147
148 148
/// Write a comma-separated argument list in parentheses.
149 -
unsafe fn writeArgs(out: &mut sexpr::Output, args: *unsafe [super::Val]) {
149 +
fn writeArgs(out: &mut opaque sexpr::Output, args: &[super::Val]) {
150 150
    write(out, "(");
151 151
    for arg, i in args {
152 152
        if i > 0 {
153 153
            write(out, ", ");
154 154
        }
156 156
    }
157 157
    write(out, ")");
158 158
}
159 159
160 160
/// Write a typed parameter (e.g., `w32 %0`).
161 -
unsafe fn writeParam(out: &mut sexpr::Output, param: super::Param) {
161 +
fn writeParam(out: &mut opaque sexpr::Output, param: super::Param) {
162 162
    writeType(out, param.type);
163 163
    write(out, " ");
164 164
    writeReg(out, param.value);
165 165
}
166 166
167 167
/// Write a comma-separated parameter list.
168 -
unsafe fn writeParams(out: &mut sexpr::Output, params: *unsafe [super::Param]) {
168 +
fn writeParams(out: &mut opaque sexpr::Output, params: &[super::Param]) {
169 169
    for param, i in params {
170 170
        if i > 0 {
171 171
            write(out, ", ");
172 172
        }
173 173
        writeParam(out, param);
177 177
//////////////////////////
178 178
// Instruction printing //
179 179
//////////////////////////
180 180
181 181
/// Write an instruction.
182 -
unsafe fn writeInstr(out: &mut sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) {
182 +
unsafe fn writeInstr(out: &mut opaque sexpr::Output, blocks: *unsafe [super::Block], inst: super::Instr) {
183 183
    match inst {
184 184
        // Memory operations.
185 185
        case super::Instr::Reserve { dst, size, alignment } => {
186 186
            write(out, "reserve ");
187 187
            writeReg(out, dst);
354 354
        }
355 355
    }
356 356
}
357 357
358 358
/// Write a typed binary operation: `op type %dst %a %b`.
359 -
unsafe fn writeTypedBinOp(
360 -
    out: &mut sexpr::Output,
359 +
fn writeTypedBinOp(
360 +
    out: &mut opaque sexpr::Output,
361 361
    name: *[u8],
362 362
    typ: super::Type,
363 363
    dst: super::Reg,
364 364
    va: super::Val,
365 365
    vb: super::Val
374 374
    write(out, " ");
375 375
    writeVal(out, vb);
376 376
}
377 377
378 378
/// Write a typed unary operation: `op type %dst %val`.
379 -
unsafe fn writeTypedUnaryOp(
380 -
    out: &mut sexpr::Output,
379 +
fn writeTypedUnaryOp(
380 +
    out: &mut opaque sexpr::Output,
381 381
    name: *[u8],
382 382
    typ: super::Type,
383 383
    dst: super::Reg,
384 384
    val: super::Val
385 385
) {
395 395
////////////////////
396 396
// Block printing //
397 397
////////////////////
398 398
399 399
/// Write a basic block.
400 -
unsafe fn writeBlock(out: &mut sexpr::Output, blocks: *unsafe [super::Block], block: *unsafe super::Block) {
400 +
unsafe fn writeBlock(out: &mut opaque sexpr::Output, blocks: *unsafe [super::Block], block: *unsafe super::Block) {
401 401
    // Block label.
402 402
    write(out, "  @");
403 403
    write(out, block.label);
404 404
405 405
    // Block parameters.
421 421
///////////////////////
422 422
// Function printing //
423 423
///////////////////////
424 424
425 425
/// Write a function.
426 -
unsafe fn writeFn(out: &mut sexpr::Output, f: *unsafe super::Fn) {
426 +
unsafe fn writeFn(out: &mut opaque sexpr::Output, f: *unsafe super::Fn) {
427 427
    // Function signature.
428 428
    if f.isExtern {
429 429
        write(out, "extern ");
430 430
    }
431 431
    write(out, "fn ");
455 455
///////////////////
456 456
// Data printing //
457 457
///////////////////
458 458
459 459
/// Write a data item.
460 -
unsafe fn writeDataItem(out: &mut sexpr::Output, item: super::DataItem) {
460 +
fn writeDataItem(out: &mut opaque sexpr::Output, item: super::DataItem) {
461 461
    match item {
462 462
        case super::DataItem::Val { typ, val } => {
463 463
            writeType(out, typ);
464 464
            write(out, " ");
465 465
            writeI64(out, val);
481 481
        }
482 482
    }
483 483
}
484 484
485 485
/// Write a data value (item with optional repeat count).
486 -
unsafe fn writeDataValue(out: &mut sexpr::Output, value: super::DataValue) {
486 +
fn writeDataValue(out: &mut opaque sexpr::Output, value: super::DataValue) {
487 487
    writeDataItem(out, value.item);
488 488
    if value.count > 1 {
489 489
        write(out, " * ");
490 490
        writeU32(out, value.count);
491 491
    }
492 492
}
493 493
494 494
/// Write global data.
495 -
unsafe fn writeData(out: &mut sexpr::Output, d: super::Data) {
495 +
fn writeData(out: &mut opaque sexpr::Output, d: super::Data) {
496 496
    write(out, "data ");
497 497
    if not d.readOnly {
498 498
        write(out, "mut ");
499 499
    }
500 500
    writeSymbol(out, d.name);
513 513
//////////////////////
514 514
// Program printing //
515 515
//////////////////////
516 516
517 517
/// Print a program.
518 -
export unsafe fn printProgram(out: &mut sexpr::Output, program: &super::Program) {
518 +
export unsafe fn printProgram(out: &mut opaque sexpr::Output, program: &super::Program) {
519 519
    // Data declarations.
520 520
    for data, i in program.data {
521 521
        writeData(out, data);
522 522
        if i < program.data.len - 1 or program.fns.len > 0 {
523 523
            write(out, "\n");
536 536
export unsafe fn printProgramToBuffer(
537 537
    program: &super::Program,
538 538
    buf: *mut [u8]
539 539
) -> *[u8] {
540 540
    let mut pos: u32 = 0;
541 -
    let raw: *unsafe mut [u8] = &mut buf[..];
542 -
    let mut out = sexpr::Output::Buffer { buf: raw, pos: &mut pos };
543 -
    printProgram(&mut out, program);
541 +
    let bytes: 'output = &mut buf[..] in {
542 +
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
543 +
        printProgram(&mut out, program);
544 +
        set pos = out.pos;
545 +
    }
544 546
545 547
    return &buf[..pos];
546 548
}
lib/std/lang/il/tests.rad +32 -0
1 1
//! Tests for RIL source register iteration.
2 2
3 3
use std::testing;
4 +
use std::lang::sexpr;
5 +
6 +
/// Buffered output truncates at capacity and preserves adjacent storage.
7 +
@test fn testSafePrintBuffer() throws (testing::TestError) {
8 +
    let mut storage: [u8; 7] = [42; 7];
9 +
    let bytes: 'output = &mut storage[1..6] in {
10 +
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
11 +
        sexpr::write(&mut out, "ab");
12 +
        sexpr::write(&mut out, "cdef");
13 +
        sexpr::write(&mut out, "ignored");
14 +
        try testing::expect(out.pos == 5);
15 +
        try testing::expectBytesEq(out.buf, "abcde");
16 +
    }
17 +
    try testing::expect(storage[0] == 42);
18 +
    try testing::expect(storage[6] == 42);
19 +
    let bytes: 'empty = &mut storage[0..0] in {
20 +
        let mut out = sexpr::Buffer 'empty { buf: bytes, pos: 0 };
21 +
        sexpr::write(&mut out, "ignored");
22 +
        try testing::expect(out.pos == 0);
23 +
    }
24 +
}
25 +
26 +
/// S-expression formatting uses checked output storage.
27 +
@test fn testSafeExpressionPrinter() throws (testing::TestError) {
28 +
    let mut storage: [u8; 64] = [0; 64];
29 +
    let bytes: 'output = &mut storage[..] in {
30 +
        let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 };
31 +
        sexpr::printTo(sexpr::Expr::Str("a\n\t\\"), 0, &mut out);
32 +
        sexpr::printTo(sexpr::Expr::Char('\n'), 0, &mut out);
33 +
        try testing::expectBytesEq(&out.buf[..out.pos], "\"a\\n\\t\\\\\"'\\n'");
34 +
    }
35 +
}
4 36
5 37
/// Fixed register cursors can be initialized in safe code.
6 38
@test fn testSafeRegisterCursor() throws (testing::TestError) {
7 39
    let instr = super::Instr::Ecall {
8 40
        dst: super::Reg { n: 0 },
lib/std/lang/sexpr.rad +43 -27
5 5
6 6
use std::io;
7 7
use std::lang::alloc;
8 8
9 9
/// Output target for S-expression printing.
10 -
export union Output: Copy {
11 -
    /// Print to stdout.
12 -
    Stdout,
13 -
    /// Write to a buffer, tracking position.
14 -
    Buffer { buf: *unsafe mut [u8], pos: *unsafe mut u32 },
10 +
export trait Output {
11 +
    /// Write text to the output target.
12 +
    fn (&mut Output) write(s: &[u8]);
13 +
}
14 +
15 +
/// Print to stdout.
16 +
export record Stdout {}
17 +
18 +
/// Write to a buffer, tracking position.
19 +
export record Buffer: 'output {
20 +
    /// Storage for the printed text.
21 +
    buf: &'output mut [u8],
22 +
    /// Number of bytes written.
23 +
    pos: u32,
24 +
}
25 +
26 +
instance Output for Stdout {
27 +
    fn (out: &mut Stdout) write(s: &[u8]) {
28 +
        io::print(s);
29 +
    }
30 +
}
31 +
32 +
instance Output for Buffer 'output {
33 +
    fn (out: &mut Buffer 'output) write(s: &[u8]) {
34 +
        assert out.pos <= out.buf.len;
35 +
        let remaining = out.buf.len - out.pos;
36 +
        let toWrite = remaining if s.len > remaining else s.len;
37 +
        for i in 0..toWrite {
38 +
            set out.buf[out.pos] = s[i];
39 +
            set out.pos += 1;
40 +
        }
41 +
    }
15 42
}
16 43
17 44
/// An S-expression element.
18 45
export union Expr: Copy {
19 46
    /// An empty expression.
77 104
export unsafe fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr {
78 105
    return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) };
79 106
}
80 107
81 108
/// Write a string to the output target.
82 -
export unsafe fn write(out: &mut Output, s: &[u8]) {
83 -
    match *out {
84 -
        case Output::Stdout => io::print(s),
85 -
        case Output::Buffer { buf, pos } => {
86 -
            let remaining = buf.len - *pos;
87 -
            let toWrite = remaining if s.len > remaining else s.len;
88 -
            for i in 0..toWrite {
89 -
                set buf[*pos] = s[i];
90 -
                set *pos += 1;
91 -
            }
92 -
        }
93 -
    }
109 +
export fn write(out: &mut opaque Output, s: &[u8]) {
110 +
    out.write(s);
94 111
}
95 112
96 113
/// Emit indentation to the output target.
97 -
unsafe fn indentTo(out: &mut Output, depth: u32) {
114 +
fn indentTo(out: &mut opaque Output, depth: u32) {
98 115
    for _ in 0..depth {
99 116
        write(out, "  ");
100 117
    }
101 118
}
102 119
103 120
/// Print a single character with escaping to the output target.
104 -
export unsafe fn printEscapedTo(out: &mut Output, c: u8) {
121 +
export fn printEscapedTo(out: &mut opaque Output, c: u8) {
105 122
    match c {
106 123
        case '\n' => write(out, "\\n"),
107 124
        case '\r' => write(out, "\\r"),
108 125
        case '\t' => write(out, "\\t"),
109 126
        case '\\' => write(out, "\\\\"),
111 128
        else => write(out, &[c]),
112 129
    }
113 130
}
114 131
115 132
/// Print a quoted string with escape sequences to the output target.
116 -
export unsafe fn printStringTo(out: &mut Output, s: &[u8]) {
133 +
export fn printStringTo(out: &mut opaque Output, s: &[u8]) {
117 134
    write(out, "\"");
118 135
    for i in 0..s.len {
119 136
        printEscapedTo(out, s[i]);
120 137
    }
121 138
    write(out, "\"");
122 139
}
123 140
124 141
/// Print a character literal with escape sequences to the output target.
125 -
export unsafe fn printCharTo(out: &mut Output, c: u8) {
142 +
export fn printCharTo(out: &mut opaque Output, c: u8) {
126 143
    write(out, "'");
127 144
    printEscapedTo(out, c);
128 145
    write(out, "'");
129 146
}
130 147
131 148
/// Print an S-expression to the given output target at the given depth.
132 -
export unsafe fn printTo(expr: Expr, depth: u32, out: &mut Output) {
149 +
export fn printTo(expr: Expr, depth: u32, out: &mut opaque Output) {
133 150
    match expr {
134 151
        case Expr::Null => {},
135 152
        case Expr::Sym(s) => write(out, s),
136 153
        case Expr::Str(s) => printStringTo(out, s),
137 154
        case Expr::Char(c) => printCharTo(out, c),
193 210
        }
194 211
    }
195 212
}
196 213
197 214
/// Print an S-expression to stdout at the given indentation depth.
198 -
export unsafe fn print(expr: Expr, depth: u32) {
199 -
    let mut out = Output::Stdout;
215 +
export fn print(expr: Expr, depth: u32) {
216 +
    let mut out = Stdout {};
200 217
    printTo(expr, depth, &mut out);
201 218
}
202 219
203 220
/// Emit indentation for `depth` levels to stdout.
204 -
export unsafe fn indent(depth: u32) {
205 -
    let mut out = Output::Stdout;
221 +
export fn indent(depth: u32) {
222 +
    let mut out = Stdout {};
206 223
    indentTo(&mut out, depth);
207 224
}
208 -