Skip to content

Instantly share code, notes, and snippets.

@metaleap
Created January 18, 2020 20:37
Show Gist options
  • Save metaleap/6b0341ca3c34039091e48dd61ab95332 to your computer and use it in GitHub Desktop.
Save metaleap/6b0341ca3c34039091e48dd61ab95332 to your computer and use it in GitHub Desktop.
const std = @import("std");
pub fn main() !void {
var mem = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer mem.deinit();
const list = try listFromStr(&mem.allocator, "Hail Zig =)");
std.debug.warn("{}", .{try listToStr(&mem.allocator, list)});
}
fn listFromStr(mem: *std.mem.Allocator, from: []const u8) !Expr {
var ret = Expr{ .FuncRef = @enumToInt(StdFunc.Nil) };
var i = from.len;
while (i > 0) {
i -= 1;
ret = Expr{
.Call = try enHeap(mem, ExprCall{
.Callee = Expr{ .FuncRef = @enumToInt(StdFunc.Cons) },
.Args = try std.mem.dupe(mem, Expr, &[_]Expr{ ret, Expr{ .NumInt = from[i] } }),
}),
};
}
return ret;
}
fn listToStr(mem: *std.mem.Allocator, expr: Expr) !?[]const u8 {
const maybenumlist = try maybeList(mem, expr);
return if (maybenumlist) |it| listToBytes(mem, it) else null;
}
fn maybeList(mem: *std.mem.Allocator, self: Expr) !?[]const Expr {
var list = try std.ArrayList(Expr).initCapacity(mem, 1024);
errdefer list.deinit();
var next = self;
while (true) {
switch (next) {
.FuncRef => |f| if (f == @enumToInt(StdFunc.Nil))
return list.toOwnedSlice(),
.Call => |c| if (c.Args.len == 2) switch (c.Callee) {
.FuncRef => |f| if (f == @enumToInt(StdFunc.Cons)) {
try list.append(c.Args[1]);
next = c.Args[0];
continue;
},
else => {},
},
else => {},
}
break;
}
list.deinit();
return null;
}
pub fn listToBytes(mem: *std.mem.Allocator, maybeNumList: []const Expr) !?[]const u8 {
var ok = false;
const bytes = try mem.alloc(u8, maybeNumList.len);
defer if (!ok) mem.free(bytes);
for (maybeNumList) |expr, i| switch (expr) {
.NumInt => |n| if (n < 0 or n > 255) return null else bytes[i] = @intCast(u8, n),
else => return null,
};
ok = true;
return bytes;
}
pub inline fn enHeap(mem: *std.mem.Allocator, it: var) !*@TypeOf(it) {
var ret = try mem.create(@TypeOf(it));
ret.* = it;
return ret;
}
pub const ExprCall = struct {
Callee: Expr,
Args: []Expr,
};
const Expr = union(enum) {
NumInt: isize,
FuncRef: isize,
Call: *const ExprCall,
};
pub const StdFunc = enum(isize) {
Nil = 3,
Cons = 4,
};
@metaleap
Copy link
Author

metaleap commented Jan 18, 2020

Above, zig runnable repro, extracted from bigger project.

With the above:

Segmentation fault at address 0x1b
/home/_/c/z/atem/tmp/repros/segfault_2020jan18/main.zig:39:41: 0x22bfbe in maybeList (run)
            .Call => |c| if (c.Args.len == 2) switch (c.Callee) {
                                        ^

To circumvent this (until fixed), from what I have found one has 2 options:

  • "manually inline" the already-auto-inlined fn enHeap into the callsite directly / by hand
  • or line 19: take out the expression assigned to .Args = , put it between line 15 & 16 to be assigned to a new local const tmpargs = ..., then use that local for the .Args = initializer

Both ways the program will work as expected, printing out the original input string from line 7. These "fixes" are mere re-arrangements / different placements without (AFAICT) any semantic differences.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment