1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-17 17:50:24 +03:00
mal/impls/zig/printer.zig
Joel Martin 8a19f60386 Move implementations into impls/ dir
- Reorder README to have implementation list after "learning tool"
  bullet.

- This also moves tests/ and libs/ into impls. It would be preferrable
  to have these directories at the top level.  However, this causes
  difficulties with the wasm implementations which need pre-open
  directories and have trouble with paths starting with "../../". So
  in lieu of that, symlink those directories to the top-level.

- Move the run_argv_test.sh script into the tests directory for
  general hygiene.
2020-02-10 23:50:16 -06:00

232 lines
7.4 KiB
Zig

const io = @import("std").io;
const fmt = @import("std").fmt;
const warn = @import("std").debug.warn;
const mem = @import("std").mem;
const math = @import("std").math;
const Allocator = @import("std").heap.c_allocator;
const MalType = @import("types.zig").MalType;
const MalTypeValue = @import("types.zig").MalTypeValue;
const MalLinkedList = @import("linked_list.zig").MalLinkedList;
const MalError = @import("error.zig").MalError;
const ResizeBuffer = struct {
buffer: ?[]u8,
pos: usize,
len: usize,
};
// TODO fix emacs highlighting, remove this
const backslash =
\\\
;
fn appendToBuffer(resize_buffer: *ResizeBuffer, buffer: []const u8) MalError!void {
const n: usize = buffer.len;
if(n + resize_buffer.pos > resize_buffer.len or resize_buffer.buffer == null) {
const new_len = math.max(math.max(2*resize_buffer.len, 10), n+resize_buffer.pos);
var bigger_buffer: [] u8 = Allocator.alloc(u8, new_len) catch return MalError.SystemError;
if(resize_buffer.buffer) |old_buffer| {
var i: usize = 0;
while(i < resize_buffer.len) {
bigger_buffer[i] = old_buffer[i];
i += 1;
}
Allocator.free(old_buffer);
}
resize_buffer.buffer = bigger_buffer;
resize_buffer.len = new_len;
}
if(resize_buffer.buffer) |n_buffer| {
var i: usize = 0;
while(i < n) {
n_buffer[resize_buffer.pos] = buffer[i];
i += 1;
resize_buffer.pos += 1;
}
}
}
fn print_mal_to_buffer(mal: *const MalType, readable: bool) MalError!ResizeBuffer {
var rb = ResizeBuffer{
.buffer = null,
.pos = 0,
.len = 0,
};
try print_to_buffer(mal, &rb, readable);
return rb;
}
pub fn print_str(optional_mal: ?*const MalType) MalError![] const u8 {
const stdout_file = io.getStdOut() catch return MalError.SystemError;
if(optional_mal == null) {
var return_string: [] u8 = Allocator.alloc(u8, 3) catch return MalError.SystemError;
return_string[0] = 'E'; //TODO: memcpy
return_string[1] = 'O';
return_string[2] = 'F';
return return_string; // TODO: is this right?
//stdout_file.write("EOF\n") catch return MalError.SystemError;
}
const mal = optional_mal orelse return "";
var rb = try print_mal_to_buffer(mal, true);
if(rb.buffer) |buffer| {
//stdout_file.write(buffer[0..rb.pos]) catch return MalError.SystemError;
//stdout_file.write("\n") catch return MalError.SystemError;
var return_string: [] u8 = Allocator.alloc(u8, rb.pos) catch return MalError.SystemError;
var i: usize = 0; // TODO: replace with memcpy (and elsewhere)
while(i < rb.pos) {
return_string[i] = buffer[i];
i += 1;
}
Allocator.free(buffer);
return return_string;
}
return MalError.SystemError;
}
pub fn print_mal_to_string(args: MalLinkedList, readable: bool, sep: bool) MalError![] u8 {
// TODO: handle empty string
var rb = ResizeBuffer{
.buffer = null,
.pos = 0,
.len = 0,
};
var iterator = args.iterator();
var first: bool = true;
while(iterator.next()) |node| {
if(!first and sep) {
try appendToBuffer(&rb, " ");
}
try print_to_buffer(node, &rb, readable);
first = false;
}
// TODO: is this the right exception?
if(rb.buffer) |buffer| {
const len = rb.pos;
var return_string: [] u8 = Allocator.alloc(u8, len) catch return MalError.SystemError;
var i: usize = 0;
while(i < len) {
return_string[i] = buffer[i];
i += 1;
}
Allocator.free(buffer);
return return_string;
}
const s: []u8 = "";
return s;
}
fn print_to_buffer(mal: *const MalType, rb: *ResizeBuffer, readable: bool) MalError!void {
switch(mal.data) {
.String => |string| {
if(readable) {
try appendToBuffer(rb, "\"");
}
// TODO: optimize this
var i: usize = 0;
var n: usize = string.len;
while(i < n){
const this_char = string[i];
if(readable and (this_char == '"' or this_char==92)) {
try appendToBuffer(rb, backslash);
}
if(readable and (this_char == '\n')) {
try appendToBuffer(rb, "\\n");
}
else {
try appendToBuffer(rb, string[i..i+1]);
}
i += 1;
}
if(readable) {
try appendToBuffer(rb, "\"");
}
},
.Keyword => |kwd| {
try appendToBuffer(rb, ":");
try appendToBuffer(rb, kwd[1..kwd.len]);
},
.Int => |val| {
try fmt.format(rb, MalError, appendToBuffer, "{0}", val);
},
.Nil => {
try appendToBuffer(rb, "nil");
},
.True => {
try appendToBuffer(rb, "true");
},
.False => {
try appendToBuffer(rb, "false");
},
.List => |l| {
try appendToBuffer(rb, "(");
var iterator = l.iterator();
var first_iteration = true;
while(iterator.next()) |next_mal| {
if(!first_iteration) {
try appendToBuffer(rb, " ");
}
try print_to_buffer(next_mal, rb, readable);
first_iteration = false;
}
try appendToBuffer(rb, ")");
},
.Vector => |v| {
try appendToBuffer(rb, "[");
var iterator = v.iterator();
var first_iteration = true;
while(iterator.next()) |next_mal| {
if(!first_iteration) {
try appendToBuffer(rb, " ");
}
try print_to_buffer(next_mal, rb, readable);
first_iteration = false;
}
try appendToBuffer(rb, "]");
},
.Atom => |atom_value| {
try appendToBuffer(rb, "(atom ");
try print_to_buffer(atom_value.*, rb, readable);
try appendToBuffer(rb, ")");
},
.Func, .Fn0, .Fn1, .Fn2, .Fn3, .Fn4, .FVar => {
try appendToBuffer(rb, "#<function>");
},
.Generic => |value| {
try appendToBuffer(rb, value);
},
.HashMap => |h| {
try appendToBuffer(rb, "{");
var iterator = h.iterator();
var first = true;
while(true) {
const optional_pair = iterator.next();
const pair = optional_pair orelse break;
if(!first) {
try appendToBuffer(rb, " ");
}
if(pair.key.len > 1 and pair.key[0] == 255) {
try appendToBuffer(rb, ":");
try appendToBuffer(rb, pair.key[1..pair.key.len]);
}
else {
try appendToBuffer(rb, "\"");
try appendToBuffer(rb, pair.key);
try appendToBuffer(rb, "\"");
}
try appendToBuffer(rb, " ");
try print_to_buffer(pair.value, rb, readable);
first = false;
}
try appendToBuffer(rb, "}");
},
}
}