compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
alloc/
ast/
gen/
il/
binary/
binary.rad
5.9 KiB
printer.rad
16.0 KiB
published.rad
13.4 KiB
publishedTests.rad
8.7 KiB
tests.rad
14.7 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.9 KiB
gen.rad
513 B
il.rad
20.4 KiB
lower.rad
321.7 KiB
module.rad
17.3 KiB
package.rad
1.3 KiB
parser.rad
92.2 KiB
resolver.rad
511.1 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/lang/il/printer.rad
raw
| 1 | //! IL pretty printer. |
| 2 | |
| 3 | use std::fmt; |
| 4 | use std::lang::sexpr; |
| 5 | |
| 6 | /////////////////////// |
| 7 | // String formatting // |
| 8 | /////////////////////// |
| 9 | |
| 10 | /// Write a `u32` before its stack buffer leaves scope. |
| 11 | fn writeU32(out: &mut opaque sexpr::Output, val: u32) { |
| 12 | let mut digits: [u8; 10] = [0; 10]; |
| 13 | let start = fmt::formatU32(val, &mut digits[..]); |
| 14 | write(out, &digits[start..]); |
| 15 | } |
| 16 | |
| 17 | /// Write an `i32` before its stack buffer leaves scope. |
| 18 | fn writeI32(out: &mut opaque sexpr::Output, val: i32) { |
| 19 | let mut digits: [u8; 12] = [0; 12]; |
| 20 | let start = fmt::formatI32(val, &mut digits[..]); |
| 21 | write(out, &digits[start..]); |
| 22 | } |
| 23 | |
| 24 | /// Write an `i64` before its stack buffer leaves scope. |
| 25 | fn writeI64(out: &mut opaque sexpr::Output, val: i64) { |
| 26 | let mut digits: [u8; 20] = [0; 20]; |
| 27 | let start = fmt::formatI64(val, &mut digits[..]); |
| 28 | write(out, &digits[start..]); |
| 29 | } |
| 30 | |
| 31 | ///////////////////// |
| 32 | // Output helpers // |
| 33 | ///////////////////// |
| 34 | |
| 35 | /// Write a string to the output. |
| 36 | fn write(out: &mut opaque sexpr::Output, s: &[u8]) { |
| 37 | sexpr::write(out, s); |
| 38 | } |
| 39 | |
| 40 | /// Emit indentation. |
| 41 | fn indent(out: &mut opaque sexpr::Output, depth: u32) { |
| 42 | for _ in 0..depth { |
| 43 | write(out, " "); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | /// Check if name contains the path separator. |
| 48 | fn needsQuoting(name: *[u8]) -> bool { |
| 49 | for ch in name { |
| 50 | if ch == ':' { |
| 51 | return true; |
| 52 | } |
| 53 | } |
| 54 | return false; |
| 55 | } |
| 56 | |
| 57 | /// Write a symbol name. Simple names use `$name`, qualified names use `$"name"`. |
| 58 | fn writeSymbol(out: &mut opaque sexpr::Output, name: *[u8]) { |
| 59 | write(out, "$"); |
| 60 | if needsQuoting(name) { |
| 61 | write(out, "\""); |
| 62 | write(out, name); |
| 63 | write(out, "\""); |
| 64 | } else { |
| 65 | write(out, name); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | //////////////////// |
| 70 | // Type printing // |
| 71 | //////////////////// |
| 72 | |
| 73 | /// Get the string representation of a type. |
| 74 | fn typeStr(typ: super::Type) -> *[u8] { |
| 75 | match typ { |
| 76 | case super::Type::W8 => return "w8", |
| 77 | case super::Type::W16 => return "w16", |
| 78 | case super::Type::W32 => return "w32", |
| 79 | case super::Type::W64 => return "w64", |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Write a type. |
| 84 | fn writeType(out: &mut opaque sexpr::Output, typ: super::Type) { |
| 85 | write(out, typeStr(typ)); |
| 86 | } |
| 87 | |
| 88 | //////////////////////// |
| 89 | // Operation printing // |
| 90 | //////////////////////// |
| 91 | |
| 92 | /// Get the string representation of a binary ALU operation. |
| 93 | fn binOpStr(op: super::BinOp) -> *[u8] { |
| 94 | match op { |
| 95 | case super::BinOp::Add => return "add", |
| 96 | case super::BinOp::Sub => return "sub", |
| 97 | case super::BinOp::Mul => return "mul", |
| 98 | case super::BinOp::Sdiv => return "sdiv", |
| 99 | case super::BinOp::Udiv => return "udiv", |
| 100 | case super::BinOp::Srem => return "srem", |
| 101 | case super::BinOp::Urem => return "urem", |
| 102 | case super::BinOp::Eq => return "eq", |
| 103 | case super::BinOp::Ne => return "ne", |
| 104 | case super::BinOp::Slt => return "slt", |
| 105 | case super::BinOp::Sge => return "sge", |
| 106 | case super::BinOp::Ult => return "ult", |
| 107 | case super::BinOp::Uge => return "uge", |
| 108 | case super::BinOp::And => return "and", |
| 109 | case super::BinOp::Or => return "or", |
| 110 | case super::BinOp::Xor => return "xor", |
| 111 | case super::BinOp::Shl => return "shl", |
| 112 | case super::BinOp::Sshr => return "sshr", |
| 113 | case super::BinOp::Ushr => return "ushr", |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /// Get the string representation of a unary ALU operation. |
| 118 | fn unOpStr(op: super::UnOp) -> *[u8] { |
| 119 | match op { |
| 120 | case super::UnOp::Neg => return "neg", |
| 121 | case super::UnOp::Not => return "not", |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | //////////////////// |
| 126 | // Value printing // |
| 127 | //////////////////// |
| 128 | |
| 129 | /// Write a value (register, immediate, symbol, or undefined). |
| 130 | fn writeVal(out: &mut opaque sexpr::Output, val: super::Val) { |
| 131 | match val { |
| 132 | case super::Val::Reg(reg) => writeReg(out, reg), |
| 133 | case super::Val::Imm(v) => writeI64(out, v), |
| 134 | case super::Val::DataSym(name) => writeSymbol(out, name), |
| 135 | case super::Val::FnAddr(name) => writeSymbol(out, name), |
| 136 | case super::Val::Undef => write(out, "undefined"), |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | /// Write a register in one operation before its stack buffer leaves scope. |
| 141 | fn writeReg(out: &mut opaque sexpr::Output, reg: super::Reg) { |
| 142 | let mut buffer: [u8; 11] = [0; 11]; |
| 143 | let start = fmt::formatU32(reg.n, &mut buffer[1..]); |
| 144 | set buffer[start] = '%'; |
| 145 | write(out, &buffer[start..]); |
| 146 | } |
| 147 | |
| 148 | /// Write a comma-separated argument list in parentheses. |
| 149 | fn writeArgs(out: &mut opaque sexpr::Output, args: &[super::Val]) { |
| 150 | write(out, "("); |
| 151 | for arg, i in args { |
| 152 | if i > 0 { |
| 153 | write(out, ", "); |
| 154 | } |
| 155 | writeVal(out, arg); |
| 156 | } |
| 157 | write(out, ")"); |
| 158 | } |
| 159 | |
| 160 | /// Write a typed parameter (e.g., `w32 %0`). |
| 161 | fn writeParam(out: &mut opaque sexpr::Output, param: super::Param) { |
| 162 | writeType(out, param.type); |
| 163 | write(out, " "); |
| 164 | writeReg(out, param.value); |
| 165 | } |
| 166 | |
| 167 | /// Write a comma-separated parameter list. |
| 168 | fn writeParams(out: &mut opaque sexpr::Output, params: &[super::Param]) { |
| 169 | for param, i in params { |
| 170 | if i > 0 { |
| 171 | write(out, ", "); |
| 172 | } |
| 173 | writeParam(out, param); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | ////////////////////////// |
| 178 | // Instruction printing // |
| 179 | ////////////////////////// |
| 180 | |
| 181 | /// Write an instruction. |
| 182 | unsafe fn writeInstr(out: &mut opaque sexpr::Output, blocks: &[super::Block], inst: super::Instr) { |
| 183 | match inst { |
| 184 | case super::Instr::Call { args, .. } => writeOperandInstr(out, inst, args), |
| 185 | |
| 186 | case super::Instr::Jmp { target, args } => { |
| 187 | write(out, "jmp "); |
| 188 | writeTarget(out, blocks, target, args); |
| 189 | } |
| 190 | case super::Instr::Br { thenArgs, elseArgs, .. } => |
| 191 | writeBranch(out, blocks, inst, thenArgs, elseArgs), |
| 192 | case super::Instr::Switch { val, defaultTarget, defaultArgs, cases } => { |
| 193 | write(out, "switch "); |
| 194 | writeVal(out, val); |
| 195 | for c in cases { |
| 196 | write(out, " ("); |
| 197 | writeI64(out, c.value); |
| 198 | write(out, " "); |
| 199 | writeTarget(out, blocks, c.target, c.args); |
| 200 | write(out, ")"); |
| 201 | } |
| 202 | write(out, " "); |
| 203 | writeTarget(out, blocks, defaultTarget, defaultArgs); |
| 204 | } |
| 205 | else => writeOperandInstr(out, inst, &[]), |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | /// Write a comparison and both of its control-flow edges. |
| 210 | fn writeBranch( |
| 211 | out: &mut opaque sexpr::Output, |
| 212 | blocks: &[super::Block], |
| 213 | inst: super::Instr, |
| 214 | thenArgs: &[super::Val], |
| 215 | elseArgs: &[super::Val], |
| 216 | ) { |
| 217 | let case super::Instr::Br { op, typ, a: va, b: vb, thenTarget, elseTarget, .. } = inst |
| 218 | else panic "writeBranch: expected branch"; |
| 219 | write(out, "br."); |
| 220 | match op { |
| 221 | case super::CmpOp::Eq => write(out, "eq"), |
| 222 | case super::CmpOp::Ne => write(out, "ne"), |
| 223 | case super::CmpOp::Slt => write(out, "slt"), |
| 224 | case super::CmpOp::Ult => write(out, "ult"), |
| 225 | } |
| 226 | write(out, " "); |
| 227 | writeType(out, typ); |
| 228 | write(out, " "); |
| 229 | writeVal(out, va); |
| 230 | write(out, " "); |
| 231 | writeVal(out, vb); |
| 232 | write(out, " "); |
| 233 | writeTarget(out, blocks, thenTarget, thenArgs); |
| 234 | write(out, " "); |
| 235 | writeTarget(out, blocks, elseTarget, elseArgs); |
| 236 | } |
| 237 | |
| 238 | /// Write a target label and its optional block arguments. |
| 239 | fn writeTarget(out: &mut opaque sexpr::Output, blocks: &[super::Block], target: u32, args: &[super::Val]) { |
| 240 | write(out, "@"); |
| 241 | write(out, blocks[target].label); |
| 242 | if args.len > 0 { |
| 243 | writeArgs(out, args); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | /// Write fixed operands and borrowed call arguments. |
| 248 | fn writeOperandInstr(out: &mut opaque sexpr::Output, inst: super::Instr, args: &[super::Val]) { |
| 249 | match inst { |
| 250 | // Call. |
| 251 | case super::Instr::Call { dst, retTy, func, .. } => { |
| 252 | write(out, "call "); |
| 253 | writeType(out, retTy); |
| 254 | write(out, " "); |
| 255 | if let d = dst { |
| 256 | writeReg(out, d); |
| 257 | write(out, " "); |
| 258 | } |
| 259 | writeVal(out, func); |
| 260 | writeArgs(out, args); |
| 261 | } |
| 262 | |
| 263 | // Memory operations. |
| 264 | case super::Instr::Reserve { dst, size, alignment } => { |
| 265 | write(out, "reserve "); |
| 266 | writeReg(out, dst); |
| 267 | write(out, " "); |
| 268 | writeVal(out, size); |
| 269 | write(out, " "); |
| 270 | writeU32(out, alignment); |
| 271 | } |
| 272 | case super::Instr::Load { dst, typ, src, offset } => { |
| 273 | write(out, "load "); |
| 274 | writeType(out, typ); |
| 275 | write(out, " "); |
| 276 | writeReg(out, dst); |
| 277 | write(out, " "); |
| 278 | writeReg(out, src); |
| 279 | write(out, " "); |
| 280 | writeI32(out, offset); |
| 281 | } |
| 282 | case super::Instr::Sload { dst, typ, src, offset } => { |
| 283 | write(out, "sload "); |
| 284 | writeType(out, typ); |
| 285 | write(out, " "); |
| 286 | writeReg(out, dst); |
| 287 | write(out, " "); |
| 288 | writeReg(out, src); |
| 289 | write(out, " "); |
| 290 | writeI32(out, offset); |
| 291 | } |
| 292 | case super::Instr::Store { typ, src, dst, offset } => { |
| 293 | write(out, "store "); |
| 294 | writeType(out, typ); |
| 295 | write(out, " "); |
| 296 | writeVal(out, src); |
| 297 | write(out, " "); |
| 298 | writeReg(out, dst); |
| 299 | write(out, " "); |
| 300 | writeI32(out, offset); |
| 301 | } |
| 302 | case super::Instr::Blit { dst, src, size } => { |
| 303 | write(out, "blit "); |
| 304 | writeReg(out, dst); |
| 305 | write(out, " "); |
| 306 | writeReg(out, src); |
| 307 | write(out, " "); |
| 308 | writeVal(out, size); |
| 309 | } |
| 310 | case super::Instr::Copy { dst, val } => { |
| 311 | write(out, "copy "); |
| 312 | writeReg(out, dst); |
| 313 | write(out, " "); |
| 314 | writeVal(out, val); |
| 315 | } |
| 316 | |
| 317 | // ALU operations. |
| 318 | case super::Instr::BinOp { op, dst, typ, a: va, b } => |
| 319 | writeTypedBinOp(out, binOpStr(op), typ, dst, va, b), |
| 320 | case super::Instr::UnOp { op, dst, typ, a: va } => |
| 321 | writeTypedUnaryOp(out, unOpStr(op), typ, dst, va), |
| 322 | |
| 323 | // Conversion operations. |
| 324 | case super::Instr::Zext { dst, typ, val } => |
| 325 | writeTypedUnaryOp(out, "zext", typ, dst, val), |
| 326 | case super::Instr::Sext { dst, typ, val } => |
| 327 | writeTypedUnaryOp(out, "sext", typ, dst, val), |
| 328 | |
| 329 | // Terminators. |
| 330 | case super::Instr::Ret { val } => { |
| 331 | write(out, "ret"); |
| 332 | if let v = val { |
| 333 | write(out, " "); |
| 334 | writeVal(out, v); |
| 335 | } |
| 336 | } |
| 337 | case super::Instr::Unreachable => { |
| 338 | write(out, "unreachable"); |
| 339 | } |
| 340 | |
| 341 | // Intrinsics. |
| 342 | case super::Instr::Ecall { dst, num, a0, a1, a2, a3 } => { |
| 343 | write(out, "ecall "); |
| 344 | writeReg(out, dst); |
| 345 | write(out, " "); |
| 346 | writeVal(out, num); |
| 347 | write(out, " "); |
| 348 | writeVal(out, a0); |
| 349 | write(out, " "); |
| 350 | writeVal(out, a1); |
| 351 | write(out, " "); |
| 352 | writeVal(out, a2); |
| 353 | write(out, " "); |
| 354 | writeVal(out, a3); |
| 355 | } |
| 356 | case super::Instr::DeviceRead { typ, dst, handle, offset } => { |
| 357 | write(out, "device-read "); write(out, typeStr(typ)); write(out, " "); |
| 358 | writeReg(out, dst); write(out, " "); writeVal(out, handle); write(out, " "); writeVal(out, offset); |
| 359 | } |
| 360 | case super::Instr::DeviceWrite { typ, handle, offset, value } => { |
| 361 | write(out, "device-write "); write(out, typeStr(typ)); write(out, " "); |
| 362 | writeVal(out, handle); write(out, " "); writeVal(out, offset); write(out, " "); writeVal(out, value); |
| 363 | } |
| 364 | case super::Instr::Ebreak => { |
| 365 | write(out, "ebreak"); |
| 366 | } |
| 367 | case super::Instr::MemoryFence => { |
| 368 | write(out, "memory-fence"); |
| 369 | } |
| 370 | case super::Instr::Jmp { .. }, |
| 371 | super::Instr::Br { .. }, super::Instr::Switch { .. } => |
| 372 | panic "writeOperandInstr: expected value operands", |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | /// Write a typed binary operation: `op type %dst %a %b`. |
| 377 | fn writeTypedBinOp( |
| 378 | out: &mut opaque sexpr::Output, |
| 379 | name: *[u8], |
| 380 | typ: super::Type, |
| 381 | dst: super::Reg, |
| 382 | va: super::Val, |
| 383 | vb: super::Val |
| 384 | ) { |
| 385 | write(out, name); |
| 386 | write(out, " "); |
| 387 | writeType(out, typ); |
| 388 | write(out, " "); |
| 389 | writeReg(out, dst); |
| 390 | write(out, " "); |
| 391 | writeVal(out, va); |
| 392 | write(out, " "); |
| 393 | writeVal(out, vb); |
| 394 | } |
| 395 | |
| 396 | /// Write a typed unary operation: `op type %dst %val`. |
| 397 | fn writeTypedUnaryOp( |
| 398 | out: &mut opaque sexpr::Output, |
| 399 | name: *[u8], |
| 400 | typ: super::Type, |
| 401 | dst: super::Reg, |
| 402 | val: super::Val |
| 403 | ) { |
| 404 | write(out, name); |
| 405 | write(out, " "); |
| 406 | writeType(out, typ); |
| 407 | write(out, " "); |
| 408 | writeReg(out, dst); |
| 409 | write(out, " "); |
| 410 | writeVal(out, val); |
| 411 | } |
| 412 | |
| 413 | //////////////////// |
| 414 | // Block printing // |
| 415 | //////////////////// |
| 416 | |
| 417 | /// Write a basic block. |
| 418 | unsafe fn writeBlock(out: &mut opaque sexpr::Output, blocks: &[super::Block], block: &super::Block) { |
| 419 | // Block label. |
| 420 | write(out, " @"); |
| 421 | write(out, block.label); |
| 422 | |
| 423 | // Block parameters. |
| 424 | if block.params.len > 0 { |
| 425 | write(out, "("); |
| 426 | writeParams(out, block.params); |
| 427 | write(out, ")"); |
| 428 | } |
| 429 | write(out, "\n"); |
| 430 | |
| 431 | // Instructions. |
| 432 | for instr in block.instrs { |
| 433 | indent(out, 1); |
| 434 | writeInstr(out, blocks, instr); |
| 435 | write(out, ";\n"); |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | /////////////////////// |
| 440 | // Function printing // |
| 441 | /////////////////////// |
| 442 | |
| 443 | /// Write a function. |
| 444 | unsafe fn writeFn(out: &mut opaque sexpr::Output, f: &super::Fn) { |
| 445 | // Function signature. |
| 446 | if f.isExtern { |
| 447 | write(out, "extern "); |
| 448 | } |
| 449 | write(out, "fn "); |
| 450 | writeType(out, f.returnType); |
| 451 | write(out, " "); |
| 452 | writeSymbol(out, f.name); |
| 453 | write(out, "("); |
| 454 | writeParams(out, f.params); |
| 455 | write(out, ")"); |
| 456 | |
| 457 | // Extern functions have no body. |
| 458 | if f.isExtern { |
| 459 | write(out, ";\n"); |
| 460 | return; |
| 461 | } |
| 462 | write(out, " {\n"); |
| 463 | |
| 464 | // Blocks. |
| 465 | for i in 0..f.blocks.len { |
| 466 | if f.blocks[i].instrs.len > 0 { |
| 467 | writeBlock(out, f.blocks, &f.blocks[i]); |
| 468 | } |
| 469 | } |
| 470 | write(out, "}\n"); |
| 471 | } |
| 472 | |
| 473 | /////////////////// |
| 474 | // Data printing // |
| 475 | /////////////////// |
| 476 | |
| 477 | /// Write a data item. |
| 478 | fn writeDataItem(out: &mut opaque sexpr::Output, item: super::DataItem) { |
| 479 | match item { |
| 480 | case super::DataItem::Val { typ, val } => { |
| 481 | writeType(out, typ); |
| 482 | write(out, " "); |
| 483 | writeI64(out, val); |
| 484 | } |
| 485 | case super::DataItem::Sym(name) => { |
| 486 | write(out, "sym "); |
| 487 | writeSymbol(out, name); |
| 488 | } |
| 489 | case super::DataItem::Fn(name) => { |
| 490 | write(out, "fn "); |
| 491 | writeSymbol(out, name); |
| 492 | } |
| 493 | case super::DataItem::Str(s) => { |
| 494 | write(out, "str "); |
| 495 | sexpr::printStringTo(out, s); |
| 496 | } |
| 497 | case super::DataItem::Undef => { |
| 498 | write(out, "undef"); |
| 499 | } |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | /// Write a data value (item with optional repeat count). |
| 504 | fn writeDataValue(out: &mut opaque sexpr::Output, value: super::DataValue) { |
| 505 | writeDataItem(out, value.item); |
| 506 | if value.count > 1 { |
| 507 | write(out, " * "); |
| 508 | writeU32(out, value.count); |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | /// Write global data. |
| 513 | fn writeData(out: &mut opaque sexpr::Output, d: super::Data) { |
| 514 | write(out, "data "); |
| 515 | if not d.readOnly { |
| 516 | write(out, "mut "); |
| 517 | } |
| 518 | writeSymbol(out, d.name); |
| 519 | write(out, " align "); |
| 520 | writeU32(out, d.alignment); |
| 521 | write(out, " {\n"); |
| 522 | |
| 523 | for v in d.values { |
| 524 | indent(out, 1); |
| 525 | writeDataValue(out, v); |
| 526 | write(out, ";\n"); |
| 527 | } |
| 528 | write(out, "}\n"); |
| 529 | } |
| 530 | |
| 531 | ////////////////////// |
| 532 | // Program printing // |
| 533 | ////////////////////// |
| 534 | |
| 535 | /// Print a program. |
| 536 | export unsafe fn printProgram(out: &mut opaque sexpr::Output, program: &super::Program) { |
| 537 | // Data declarations. |
| 538 | for data, i in program.data { |
| 539 | writeData(out, data); |
| 540 | if i < program.data.len - 1 or program.fns.len > 0 { |
| 541 | write(out, "\n"); |
| 542 | } |
| 543 | } |
| 544 | // Functions. |
| 545 | for func, i in program.fns { |
| 546 | writeFn(out, func); |
| 547 | if i < program.fns.len - 1 { |
| 548 | write(out, "\n"); |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | /// Print a program to a buffer, returning the written slice. |
| 554 | export unsafe fn printProgramToBuffer( |
| 555 | program: &super::Program, |
| 556 | buf: *mut [u8] |
| 557 | ) -> *[u8] { |
| 558 | let mut pos: u32 = 0; |
| 559 | let bytes: 'output = &mut buf[..] in { |
| 560 | let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 }; |
| 561 | printProgram(&mut out, program); |
| 562 | set pos = out.pos; |
| 563 | } |
| 564 | |
| 565 | return &buf[..pos]; |
| 566 | } |