summaryrefslogtreecommitdiff
path: root/src/zisp/io
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2026-06-20 22:53:50 +0200
committerTaylan Kammer <taylan.kammer@gmail.com>2026-06-20 22:53:50 +0200
commitb84ed4f563b3536365f7d3cc4d068407e98685b3 (patch)
tree9ab7b18d712db1329b6230cb45520e7c85dc46fd /src/zisp/io
parentbfaa74b19fc81dbe071d55566a78a8e329237eff (diff)
It's a revolution baby.HEADmaster
Diffstat (limited to 'src/zisp/io')
-rw-r--r--src/zisp/io/Decoder.zig1
-rw-r--r--src/zisp/io/Encoder.zig1
-rw-r--r--src/zisp/io/Parser.zig195
-rw-r--r--src/zisp/io/Printer.zig255
4 files changed, 292 insertions, 160 deletions
diff --git a/src/zisp/io/Decoder.zig b/src/zisp/io/Decoder.zig
new file mode 100644
index 0000000..95a0b68
--- /dev/null
+++ b/src/zisp/io/Decoder.zig
@@ -0,0 +1 @@
+const std = @import("std");
diff --git a/src/zisp/io/Encoder.zig b/src/zisp/io/Encoder.zig
new file mode 100644
index 0000000..eb27e20
--- /dev/null
+++ b/src/zisp/io/Encoder.zig
@@ -0,0 +1 @@
+// wip
diff --git a/src/zisp/io/Parser.zig b/src/zisp/io/Parser.zig
index d4a0a68..f768468 100644
--- a/src/zisp/io/Parser.zig
+++ b/src/zisp/io/Parser.zig
@@ -1,7 +1,7 @@
//!
//! === Syntax ===
//!
-//! See doc/c1/1-parse.md to understand the implemented syntax.
+//! See /doc/0/1-parse.md to understand the implemented syntax.
//!
//!
//! === Trampolining strategy ===
@@ -43,8 +43,8 @@ const gc = @import("../gc.zig");
const lib = @import("../lib.zig");
const value = @import("../value.zig");
+const ListPool = gc.ListPool;
const IstrSet = gc.IstrSet;
-const PairPool = gc.PairPool;
const Value = value.Value;
const Parser = @This();
@@ -81,21 +81,22 @@ pub const Error = enum {
};
pub const Context = struct {
- // What to do next.
+ /// What to do next.
next: ?Fn = undefined,
- // For storing a context value, like datum to join in join syntax.
+ /// For storing a context value, like datum to join in join syntax.
val: Value = undefined,
- // For storing a context char, like list opening bracket.
+ /// For storing a context char, like list opening bracket.
char: u8 = undefined,
- // Count of list elements on current parse level.
- list_len: usize = undefined,
+ /// Start index of list elements on current parse level, within the global
+ /// list element accumulation array.
+ list_start: usize = undefined,
};
-alloc: Alloc,
+list_pool: ?*ListPool,
istr_set: ?*IstrSet,
-pair_pool: *PairPool,
+alloc: Alloc,
-input: *Reader = undefined,
+reader: *Reader = undefined,
context: Context = .{},
ctx_stack: List(Context) = undefined,
@@ -107,23 +108,23 @@ unread_char: ?u8 = null,
err_msg: []const u8 = undefined,
pub fn init(alloc: Alloc) !Parser {
+ const list_pool = gc.mainListPool();
const istr_set = gc.mainIstrSet();
- const pair_pool = gc.mainPairPool();
- return initCustom(alloc, 32, 2048, 32, istr_set, pair_pool);
+ return initCustom(list_pool, istr_set, alloc, 32, 2048, 32);
}
pub fn initCustom(
+ list_pool: ?*ListPool,
+ istr_set: ?*IstrSet,
alloc: Alloc,
init_ctx_stack_cap: usize,
init_str_chars_cap: usize,
init_list_elts_cap: usize,
- istr_set: ?*IstrSet,
- pair_pool: *PairPool,
) !Parser {
var p: Parser = .{
- .alloc = alloc,
+ .list_pool = list_pool,
.istr_set = istr_set,
- .pair_pool = pair_pool,
+ .alloc = alloc,
};
p.ctx_stack = try .initCapacity(alloc, init_ctx_stack_cap);
p.str_chars = try .initCapacity(alloc, init_str_chars_cap);
@@ -148,7 +149,7 @@ fn read(p: *Parser) !?u8 {
.{p.unread_char.?},
);
}
- const c = p.input.takeByte() catch |e| switch (e) {
+ const c = p.reader.takeByte() catch |e| switch (e) {
error.EndOfStream => return null,
else => return p.err(.ReadError, "???"),
};
@@ -158,6 +159,13 @@ fn read(p: *Parser) !?u8 {
return c;
}
+fn readIntoSlice(p: *Parser, slice: []u8) !void {
+ p.reader.readSliceAll(slice) catch |e| return switch (e) {
+ error.EndOfStream => p.err(.UnexpectedEof, "reading into slice"),
+ else => p.err(.ReadError, "???"),
+ };
+}
+
fn readNoEof(p: *Parser, comptime emsg: []const u8) !u8 {
return try p.read() orelse p.err(.UnexpectedEof, emsg);
}
@@ -222,25 +230,22 @@ fn getCharsAsRune(p: *Parser) Value {
}
//
-// Pair consing & list creation
+// List creation
//
-fn cons(p: *Parser, car: Value, cdr: Value) !Value {
- return value.pair.consInPool(p.pair_pool, car, cdr);
-}
-
fn addListElt(p: *Parser, elt: Value) !void {
try p.list_elts.append(p.alloc, elt);
- p.context.list_len += 1;
}
-fn getList(p: *Parser, tail: Value) !Value {
- var list = tail;
- for (0..p.context.list_len) |_| {
- const elt = p.list_elts.pop() orelse unreachable;
- list = try p.cons(elt, list);
- }
- return list;
+fn getList(p: *Parser) !Value {
+ if (p.list_elts.items.len == p.context.list_start) return value.nil;
+ defer p.list_elts.items.len = p.context.list_start;
+ const vals = p.list_elts.items[p.context.list_start..];
+ return value.list.new(p.alloc, p.list_pool, vals);
+}
+
+fn makeList(p: *Parser, vals: []const Value) !Value {
+ return value.list.new(p.alloc, p.list_pool, vals);
}
//
@@ -259,8 +264,6 @@ const Fn = enum {
endRuneDatum,
endLabelDatum,
continueList,
- endImproperList,
- closeImproperList,
endQuoteExpr,
};
@@ -277,14 +280,12 @@ inline fn call(p: *Parser, f: Fn) !void {
.endRuneDatum => p.endRuneDatum(),
.endLabelDatum => p.endLabelDatum(),
.continueList => p.continueList(),
- .endImproperList => p.endImproperList(),
- .closeImproperList => p.closeImproperList(),
.endQuoteExpr => p.endQuoteExpr(),
};
}
-pub fn run(p: *Parser, input: *Reader) !Value {
- p.input = input;
+pub fn run(p: *Parser, reader: *Reader) !Value {
+ p.reader = reader;
p.context.next = .parseUnit;
while (p.context.next) |next| {
if (detailed_debug) p.printStack();
@@ -336,7 +337,7 @@ fn pushContext(p: *Parser, next: Fn) !void {
.next = next,
.val = p.context.val,
.char = p.context.char,
- .list_len = p.context.list_len,
+ .list_start = p.context.list_start,
});
}
@@ -455,7 +456,7 @@ fn endJoinDatum(p: *Parser) !void {
':' => COLON,
else => unreachable,
};
- const joined = try p.cons(rune, try p.cons(prev, p.result));
+ const joined = try p.makeList(&.{ rune, prev, p.result });
return p.jump(.parseJoin, joined);
}
@@ -511,21 +512,44 @@ fn getString(p: *Parser, comptime close: u8) !Value {
};
const s = try p.getCharsAsString();
return switch (close) {
- '|' => try p.cons(PQSTR, s),
- '"' => try p.cons(DQSTR, s),
+ '|' => try p.makeList(&.{ PQSTR, s }),
+ '"' => try p.makeList(&.{ DQSTR, s }),
else => unreachable,
};
}
fn getAtString(p: *Parser) !Value {
const stop = try p.readNoEof("at-string");
+ return if (stop == 255) p.getAtLenStr() else p.getAtSentinelStr(stop);
+}
+
+fn getAtLenStr(p: *Parser) !Value {
+ var len: u48 = 0;
+ inline for (0..6) |_| {
+ len <<= 8;
+ len += try p.readNoEof("at-length-string");
+ }
+ const AH = value.array.ArrayHeader;
+ const aln: std.mem.Alignment = @enumFromInt(@alignOf(AH));
+ const mem = try p.alloc.alignedAlloc(u8, aln, @sizeOf(AH) + len);
+ const arr: value.array.ArrayPtr = @ptrCast(mem);
+ arr.* = .{
+ .len_or_ptr = len,
+ .type = .str,
+ .info = .{ .str = .{} },
+ };
+ try p.readIntoSlice(arr.bytes());
+ return p.makeList(&.{ ATSTR, value.ptr.pack(.array, arr) });
+}
+
+fn getAtSentinelStr(p: *Parser, stop: u8) !Value {
while (try p.readNoEofOpt("at-string")) |c| {
if (c == stop) break;
try p.addChar(c);
}
const str = try p.getCharsAsString();
const byte = value.fixnum.pack(stop);
- return try p.cons(ATSTR, try p.cons(byte, str));
+ return p.makeList(&.{ ATSTR, byte, str });
}
fn skipStringLfEscape(p: *Parser) !u8 {
@@ -591,8 +615,9 @@ fn parseHashExpr(p: *Parser, next: Fn) !void {
},
'\\' => {
const c1 = try p.readNoEof("hash-backslash");
- const bs = try p.getBareString(c1);
- return p.jump(next, try p.cons(HASH, bs));
+ const str = try p.getBareString(c1);
+ const val = try p.makeList(&.{ HASH, str });
+ return p.jump(next, val);
},
'!' => return p.parseHashBang(next),
'%' => return p.parseLabel(next),
@@ -611,7 +636,7 @@ fn endHashDatum(p: *Parser) !void {
if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "hash datum");
}
- return p.retval(try p.cons(HASH, p.result));
+ return p.retval(try p.makeList(&.{ HASH, p.result }));
}
fn getRune(p: *Parser, c1: u8) !Value {
@@ -635,11 +660,25 @@ fn parseRuneEnd(p: *Parser, r: Value, next: Fn) !void {
switch (c) {
'\\' => {
const c1 = try p.readNoEof("rune-backslash");
- return p.jump(next, try p.cons(r, try p.getBareString(c1)));
+ const str = try p.getBareString(c1);
+ const val = try p.makeList(&.{ r, str });
+ return p.jump(next, val);
+ },
+ '"' => {
+ const str = try p.getString('"');
+ const val = try p.makeList(&.{ r, str });
+ return p.jump(next, val);
+ },
+ '|' => {
+ const str = try p.getString('|');
+ const val = try p.makeList(&.{ r, str });
+ return p.jump(next, val);
+ },
+ '@' => {
+ const str = try p.getAtString();
+ const val = try p.makeList(&.{ r, str });
+ return p.jump(next, val);
},
- '"' => return p.jump(next, try p.cons(r, try p.getString('"'))),
- '|' => return p.jump(next, try p.cons(r, try p.getString('|'))),
- '@' => return p.jump(next, try p.cons(r, try p.getAtString())),
'#', '(', '[', '{', '\'', '`', ',' => {
p.unread(c);
try p.push(next);
@@ -654,31 +693,31 @@ fn parseRuneEnd(p: *Parser, r: Value, next: Fn) !void {
}
fn endRuneDatum(p: *Parser) !void {
- return p.retval(try p.cons(p.context.val, p.result));
+ return p.retval(try p.makeList(&.{ p.context.val, p.result }));
}
fn parseHashBang(p: *Parser, next: Fn) !void {
- const val = try p.getHashBangValue();
- return p.jump(next, try p.cons(SHBANG, val));
+ const interp, const arg_line = try p.getHashBangValue();
+ if (arg_line) |args| {
+ return p.jump(next, try p.makeList(&.{ SHBANG, interp, args }));
+ } else {
+ return p.jump(next, try p.makeList(&.{ SHBANG, interp }));
+ }
}
-fn getHashBangValue(p: *Parser) !Value {
+fn getHashBangValue(p: *Parser) !struct { Value, ?Value } {
while (try p.readNoEofOpt("hash-bang")) |c| switch (c) {
' ', '\t' => continue,
'\n' => return p.err(.InvalidCharacter, "hash-bang"),
else => {
try p.addChar(c);
while (try p.read()) |c2| switch (c2) {
- '\n' => return p.getCharsAsString(),
+ '\n' => return .{ try p.getCharsAsString(), null },
' ', '\t' => break,
else => try p.addChar(c2),
};
const interp = try p.getCharsAsString();
- if (try p.getHashBangArgLine()) |arg_line| {
- return try p.cons(interp, arg_line);
- } else {
- return interp;
- }
+ return .{ interp, try p.getHashBangArgLine() };
},
};
unreachable;
@@ -704,7 +743,7 @@ fn parseLabel(p: *Parser, next: Fn) !void {
const n = try p.parseHex(u48, "datum label");
const l = value.fixnum.pack(n);
switch (p.getUnread() orelse try p.readNoEof("datum label")) {
- '%' => return p.jump(next, try p.cons(LABEL, l)),
+ '%' => return p.jump(next, try p.makeList(&.{ LABEL, l })),
'=' => {
try p.push(next);
p.context.val = l;
@@ -718,7 +757,7 @@ fn endLabelDatum(p: *Parser) !void {
if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "label datum");
}
- return p.retval(try p.cons(LABEL, try p.cons(p.context.val, p.result)));
+ return p.retval(try p.makeList(&.{ LABEL, p.context.val, p.result }));
}
fn parseList(p: *Parser, open: u8, next: Fn) !void {
@@ -729,7 +768,7 @@ fn parseList(p: *Parser, open: u8, next: Fn) !void {
'{' => '}',
else => unreachable,
};
- p.context.list_len = 0;
+ p.context.list_start = p.list_elts.items.len;
switch (open) {
'(' => {},
'[' => try p.addListElt(SQUARE),
@@ -750,9 +789,6 @@ fn continueList(p: *Parser) !void {
if (c == close) {
return p.endList();
}
- if (c == '&') {
- return p.subr(.parseUnit, .endImproperList);
- }
return p.err(.InvalidCharacter, "list");
}
@@ -762,32 +798,7 @@ fn continueList(p: *Parser) !void {
}
fn endList(p: *Parser) !void {
- return p.retval(try p.getList(value.nil));
-}
-
-fn endImproperList(p: *Parser) !void {
- if (p.result.eq(value.none)) {
- return p.err(.InvalidCharacter, "list tail");
- }
- p.context.val = try p.getList(p.result);
- return p.closeImproperList();
-}
-
-fn closeImproperList(p: *Parser) !void {
- const result = p.context.val;
- const close = p.context.char;
- var c1 = p.getUnread() orelse try p.read();
- while (c1) |c| : (c1 = try p.read()) {
- if (c == close) {
- return p.retval(result);
- }
- switch (try p.checkBlank(c)) {
- .yes => {},
- .skip_unit => return p.subr(.parseUnit, .closeImproperList),
- .no => return p.err(.InvalidCharacter, "after list tail"),
- }
- }
- return p.err(.UnexpectedEof, "after list tail");
+ return p.retval(try p.getList());
}
fn parseQuoteExpr(p: *Parser, c1: u8, next: Fn) !void {
@@ -808,7 +819,7 @@ fn endQuoteExpr(p: *Parser) !void {
if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "quote expression datum");
}
- return p.retval(try p.cons(p.context.val, p.result));
+ return p.retval(try p.makeList(&.{ p.context.val, p.result }));
}
// Helpers
@@ -836,7 +847,7 @@ pub fn isSpecialBareChar(c: u8) bool {
pub fn isBareChar(c: u8) bool {
return switch (c) {
// zig fmt: off
- 'a'...'z' , 'A'...'Z' , '0'...'9' , '!' , '$' , '%' , '*' ,
+ 'a'...'z' , 'A'...'Z' , '0'...'9' , '!' , '$' , '%' , '&' , '*' ,
'+' , '-' , '/' , '<' , '=' , '>' , '?' , '^' , '_' , '~' , => true,
// zig fmt: on
else => false,
diff --git a/src/zisp/io/Printer.zig b/src/zisp/io/Printer.zig
index e6b3d5b..4b06005 100644
--- a/src/zisp/io/Printer.zig
+++ b/src/zisp/io/Printer.zig
@@ -7,7 +7,6 @@ const value = @import("../value.zig");
const Parser = io.Parser;
const Value = value.Value;
-const PairPtr = value.pair.PairPtr;
const IstrPtr = value.istr.IstrPtr;
const ArrayPtr = value.array.ArrayPtr;
@@ -19,17 +18,21 @@ pub fn init(writer: *Writer) Printer {
return .{ .writer = writer };
}
-fn write(p: *Printer, str: []const u8) !void {
- _ = try p.writer.write(str);
+fn write(p: *Printer, bytes: []const u8) !void {
+ _ = try p.writer.write(bytes);
+}
+
+fn writeByte(p: *Printer, c: u8) !void {
+ _ = try p.writer.writeByte(c);
}
pub fn print(p: *Printer, v: Value) anyerror!void {
if (v.isSstr()) return p.printBareStr(value.sstr.unpack(&v));
if (v.isRune()) return p.printRune(v);
if (v.isMisc()) return p.printMisc(v);
- if (value.istr.check(v)) |str| return p.printBareStr(str.str());
- if (value.array.check(.str, v)) |str| return p.printBareStr(str.str());
- if (value.pair.check(v)) |pair| return p.printPair(pair);
+ if (value.list.check(v)) return p.printList(v);
+ if (value.istr.check(v)) return p.printIstr(v);
+ if (value.array.check(.str, v)) |str| return p.printBareStr(str.bytes());
@panic("Unsupported type to print.");
}
@@ -38,15 +41,20 @@ pub fn printBareStr(p: *Printer, s: []const u8) !void {
const no_joins = Parser.isSpecialBareChar(s[0]);
for (s) |c| {
if (Parser.isBareChar(c)) {
- try p.writer.writeByte(c);
+ try p.writeByte(c);
} else if (no_joins and (c == '.' or c == ':')) {
- try p.writer.writeByte(c);
+ try p.writeByte(c);
} else {
@panic("String needs quoting.");
}
}
}
+pub fn printIstr(p: *Printer, v: Value) !void {
+ const istr = value.istr.unpack(v);
+ try p.printBareStr(istr.bytes());
+}
+
pub fn printRune(p: *Printer, v: Value) !void {
try p.write("#");
try p.write(value.rune.unpack(&v));
@@ -58,90 +66,201 @@ pub fn printMisc(p: *Printer, v: Value) !void {
value.t.bits => p.write("#t"),
value.nil.bits => p.write("()"),
value.eof.bits => p.write("#EOF"),
- value.none.bits => p.write("#NONE"),
- value.undef.bits => p.write("#UNDEF"),
else => @panic("Unsupported misc value to print."),
};
}
-pub fn printPair(p: *Printer, pair: PairPtr) !void {
- try switch (pair.car.bits) {
- Parser.PQSTR.bits => p.printQuotString(pair.cdr, '|'),
- Parser.DQSTR.bits => p.printQuotString(pair.cdr, '"'),
- Parser.ATSTR.bits => p.printAtString(pair.cdr),
- Parser.LABEL.bits => p.printLabel(pair.cdr),
- else => p.printList(pair),
+pub fn printList(p: *Printer, list: Value) !void {
+ const len = value.list.getLenTag(list);
+ const ptr = value.list.getValPtr(list);
+ try switch (len) {
+ 2 => switch (ptr[0].bits) {
+ Parser.SHBANG.bits => p.printShebang(ptr[1]),
+ Parser.LABEL.bits => p.printLabel(ptr[1]),
+ Parser.HASH.bits => p.printHashDatum(ptr[1]),
+ Parser.PQSTR.bits => p.printQuotStr(ptr[1], '|'),
+ Parser.DQSTR.bits => p.printQuotStr(ptr[1], '"'),
+ Parser.ATSTR.bits => p.printAtLenStr(ptr[1]),
+ Parser.QUOTE.bits => p.printQuoteWithChar('\'', ptr[1]),
+ Parser.GRAVE.bits => p.printQuoteWithChar('`', ptr[1]),
+ Parser.COMMA.bits => p.printQuoteWithChar(',', ptr[1]),
+ Parser.SQUARE.bits => p.printListDirect(len, ptr),
+ Parser.BRACE.bits => p.printListDirect(len, ptr),
+ else => if (ptr[0].isRune()) {
+ return p.printRuneDatum(ptr[0], ptr[1]);
+ } else {
+ return p.printListDirect(len, ptr);
+ },
+ },
+ 3 => switch (ptr[0].bits) {
+ Parser.DOT.bits => p.printJoinWithChar('.', ptr[1], ptr[2]),
+ Parser.COLON.bits => p.printJoinWithChar(':', ptr[1], ptr[2]),
+ Parser.JOIN.bits => p.printJoin(ptr[1], ptr[2]),
+ Parser.SHBANG.bits => p.printShebangWithArgs(ptr[1], ptr[2]),
+ Parser.LABEL.bits => p.printLabelDatum(ptr[1], ptr[2]),
+ Parser.ATSTR.bits => p.printAtStr(ptr[1], ptr[2]),
+ else => p.printListDirect(len, ptr),
+ },
+ else => p.printListDirect(len, ptr),
};
}
-fn printQuotString(p: *Printer, s: Value, comptime qchar: u8) !void {
- try p.writer.writeByte(qchar);
- const str = try value.string.getString(&s);
- for (str) |c| switch (c) {
+fn printJoinWithChar(p: *Printer, c: u8, d1: Value, d2: Value) !void {
+ try p.print(d1);
+ try p.writeByte(c);
+ try p.print(d2);
+}
+
+fn printJoin(p: *Printer, d1: Value, d2: Value) !void {
+ try p.print(d1);
+ try p.print(d2);
+}
+
+fn printShebang(p: *Printer, str: Value) !void {
+ try p.write("#!");
+ try p.write(value.string.getBytes(&str));
+ try p.write("\n");
+}
+
+fn printShebangWithArgs(p: *Printer, str: Value, str2: Value) !void {
+ try p.write("#!");
+ try p.write(value.string.getBytes(&str));
+ try p.write(" ");
+ try p.write(value.string.getBytes(&str2));
+ try p.write("\n");
+}
+
+fn printLabel(p: *Printer, num: Value) !void {
+ try p.printLabelNum(num);
+ try p.write("%");
+}
+
+fn printLabelDatum(p: *Printer, num: Value, dat: Value) !void {
+ try p.printLabelNum(num);
+ try p.write("=");
+ try p.print(dat);
+}
+
+fn printLabelNum(p: *Printer, num: Value) !void {
+ const x = value.fixnum.unpack(num);
+ std.debug.assert(x >= 0);
+ std.debug.assert(x <= std.math.maxInt(u48));
+
+ var buf: [12]u8 = undefined;
+ const end = std.fmt.printInt(&buf, x, 16, .lower, .{});
+
+ try p.write("#%");
+ try p.write(buf[0..end]);
+}
+
+fn printHashDatum(p: *Printer, d: Value) !void {
+ try p.write("#");
+ if (value.string.isString(d)) try p.write("\\");
+ try p.print(d);
+}
+
+fn printRuneDatum(p: *Printer, r: Value, d: Value) !void {
+ try p.printRune(r);
+ if (value.string.isString(d)) try p.write("\\");
+ try p.print(d);
+}
+
+fn printQuotStr(p: *Printer, str: Value, comptime qchar: u8) !void {
+ try p.writeByte(qchar);
+ for (value.string.getBytes(&str)) |c| switch (c) {
qchar => {
- try p.writer.writeByte('\\');
- try p.writer.writeByte(qchar);
+ try p.writeByte('\\');
+ try p.writeByte(qchar);
},
'\\' => {
- try p.writer.writeByte('\\');
- try p.writer.writeByte('\\');
+ try p.writeByte('\\');
+ try p.writeByte('\\');
},
else => {
- try p.writer.writeByte(c);
+ try p.writeByte(c);
},
};
- try p.writer.writeByte(qchar);
+ try p.writeByte(qchar);
}
-pub fn printAtString(p: *Printer, at_str_pair: Value) !void {
- const pair = value.pair.assert(at_str_pair);
- const byte = value.fixnum.unpack(pair.car);
- std.debug.assert(byte <= 255);
- const str = try value.string.getString(&pair.cdr);
+fn printAtLenStr(p: *Printer, str: Value) !void {
+ const bytes = value.string.getBytes(&str);
+
try p.write("@");
- try p.writer.writeByte(@intCast(byte));
- try p.write(str);
- try p.writer.writeByte(@intCast(byte));
-}
+ try p.writeByte(255);
-pub fn printLabel(p: *Printer, v: Value) !void {
- var num: Value = undefined;
- var dat: ?Value = undefined;
- if (value.pair.check(v)) |pair| {
- num = pair.car;
- dat = pair.cdr;
- } else {
- num = v;
- dat = null;
+ var buf: [6]u8 = undefined;
+ var len = bytes.len;
+ inline for (0..6) |i| {
+ buf[5 - i] = @truncate(len);
+ len >>= 8;
}
- const x = value.fixnum.unpack(num);
- std.debug.assert(x >= 0);
- std.debug.assert(x <= std.math.maxInt(u48));
- var buf: [12]u8 = undefined;
- const end = std.fmt.printInt(&buf, x, 16, .lower, .{});
+ try p.write(buf[0..6]);
+ try p.write(bytes);
+}
- try p.write("#%");
- try p.write(buf[0..end]);
- if (dat) |d| {
- try p.write("=");
- try p.print(d);
- } else {
- try p.write("%");
+fn printAtStr(p: *Printer, sentinel: Value, str: Value) !void {
+ const sentinel_num = value.fixnum.unpack(sentinel);
+ if (sentinel_num > 254) {
+ @panic("At-string sentinel must be <= 254.");
}
+
+ const byte: u8 = @intCast(sentinel_num);
+ const str_bytes = value.string.getBytes(&str);
+ for (str_bytes) |c| {
+ if (c == byte) @panic("String contains sentinel byte.");
+ }
+ try p.write("@");
+ try p.writeByte(byte);
+ try p.write(str_bytes);
+ try p.writeByte(byte);
+}
+
+fn printQuoteWithChar(p: *Printer, c: u8, d: Value) !void {
+ try p.writeByte(c);
+ try p.print(d);
}
-pub fn printList(p: *Printer, pair: PairPtr) !void {
- try p.write("(");
- try p.print(pair.car);
- var cdr = pair.cdr;
- while (value.pair.check(cdr)) |next| : (cdr = next.cdr) {
- try p.write(" ");
- try p.print(next.car);
+fn printListDirect(p: *Printer, len: u3, vals: [*]Value) !void {
+ var close = ")";
+ var i: usize = 0;
+
+ switch (vals[0].bits) {
+ Parser.SQUARE.bits => {
+ try p.write("[");
+ close = "]";
+ i = 1;
+ },
+ Parser.BRACE.bits => {
+ try p.write("{");
+ close = "}";
+ i = 1;
+ },
+ else => {
+ if (vals[0].isRune()) {
+ try p.printRune(vals[0]);
+ i = 1;
+ }
+ try p.write("(");
+ },
}
- if (!value.nil.eq(cdr)) {
- try p.write(" & ");
- try p.print(cdr);
+
+ if (len != 0) {
+ var first = true;
+ for (i..len) |j| {
+ if (!first) try p.write(" ");
+ first = false;
+ try p.print(vals[j]);
+ }
+ } else {
+ try p.print(vals[i]);
+ i += 1;
+ while (vals[i].bits != value.none.bits) : (i += 1) {
+ try p.write(" ");
+ try p.print(vals[i]);
+ }
}
- try p.write(")");
+
+ try p.write(close);
}