I have a struct “Context” that has a StringHashMap as one of its fields. This map is supposed to hold a rule name as key and the corresponding rule as its value. (Rule is another struct). When I put the data in the map it works initially, but the data is somehow corrupted (I am assuming this has to do with strings being slices, so the pointers are somehow getting corrupted but I’m not an expert since Zig is my initiation into memory management), so retrieving the values is not possible since the map damaged the keys somehow. I will post the relevant code, or you can view the project at GitHub - Clinton-Nmereole/Zoq: Miniature proof helper similar to Coq and inspired by Noq but written in Zig
pub const Expression = union(enum) {
symbol: struct { str: []const u8 },
function: struct { name: []const u8, args: []const Expression },
}
pub const Rule = struct {
expression: Expression,
equivalent: Expression,
}
pub const TokenType = union(enum) {
//keywords
Quit,
Shape,
Apply,
Done,
Rule,
//symbols
identifier,
number,
//special characters
comma,
equals,
open_paren,
close_paren,
colon,
//terminators
eof,
err: Errer,
pub const Errer = union(enum) {
unexpected_byte: u8,
};
pub fn iseql(a: TokenType, b: TokenType) bool {
return if (std.meta.activeTag(a) == std.meta.activeTag(b)) true else false;
}
};
pub fn keyword(name: []const u8) ?TokenType {
const case = enum {
quit,
shape,
apply,
done,
rule,
};
const cmd = std.meta.stringToEnum(case, name) orelse return null;
switch (cmd) {
.quit => return TokenType{ .Quit = {} },
.shape => return TokenType{ .Shape = {} },
.apply => return TokenType{ .Apply = {} },
.done => return TokenType{ .Done = {} },
.rule => return TokenType{ .Rule = {} },
}
}
pub const Token = struct {
token_type: TokenType,
value: []const u8,
};
pub const Lexer = struct {
const Self = @This();
buffer: []const u8,
pos: usize,
read_pos: usize,
ch: u8,
};
pub const Context = struct {
rules_table: std.StringHashMap(Rule),
current_expr: ?Expression,
alloctor: std.mem.Allocator,
pub fn init(alloctor: std.mem.Allocator) Context {
return .{ .rules_table = std.StringHashMap(Rule).init(alloctor), .current_expr = null, .alloctor = alloctor };
}
pub fn get_rules_table(self: Context) std.StringHashMap(Rule) {
return self.rules_table;
}
pub fn put_rule(self: *Context, key: []const u8, value: Rule) !void {
var dupe_key = try allocator.dupe(u8, key[0..]);
try self.rules_table.put(dupe_key, value);
}
pub fn get_rule(self: Context, key: []const u8) ?Rule {
return self.rules_table.get(key);
}
pub fn deinit(self: *Context) void {
self.rules_table.deinit();
}
pub fn get_current_expr(self: *Context) ?Expression {
return self.current_expr;
}
pub fn set_current_expr(self: *Context, expr: ?Expression) void {
self.current_expr = expr;
}
pub fn show_rules(self: Context) void {
var table = self.get_rules_table();
var it = table.iterator();
while (it.next()) |kv| {
std.debug.print("{s}: {any} = {any}\n", .{ kv.key_ptr.*, kv.value_ptr.expression, kv.value_ptr.* });
}
}
//FIX: This function has errors in both the rule and apply switches. The StringHashMap contaminates the data stored and fails to retrieve it. This is probably due to pointer magic.
pub fn process_command(self: *Context, lexer: *Lexer) !void {
var peeked = lexer.next();
switch (peeked.token_type) {
.Rule => {
var rule_name = lexer.nextIf(.identifier);
std.debug.print("rule?: {any}\n", .{rule_name});
if (self.get_rule(rule_name.?.value) != null) {
return error.DuplicateRule;
}
//var rule = try parseRule(lexer);
var head = try parseexpr(lexer);
_ = lexer.nextIf(.equals);
var body = try parseexpr(lexer);
var rule = Rule{ .expression = head, .equivalent = body };
std.debug.print("defined rule: {any}\n", .{&rule});
try self.put_rule(rule_name.?.value, rule);
},
.Shape => {
if (self.get_current_expr() != null) {
return error.AlreadyShapingExpression;
}
var expr = try parseexpr(lexer);
std.debug.print("Shaping: {any}\n", .{expr});
self.set_current_expr(expr);
},
.Apply => {
var expr = &self.current_expr;
if (expr.* == null) {
return error.NoShapingInProgress;
}
var name = lexer.nextIf(.identifier);
var rule_name: []const u8 = name.?.value;
std.debug.print("applying rule: {s}\n", .{rule_name});
var rule = self.get_rule(rule_name);
std.debug.print("got rule: {any}\n", .{rule});
self.show_rules();
if (rule == null) {
return error.RuleNotFound;
}
var new_expr = try rule.?.apply(expr.*.?);
std.debug.print("new expression: {any}\n", .{new_expr});
self.set_current_expr(new_expr);
},
.Done => {
std.debug.print("current expression: {any}\n", .{self.current_expr});
if (self.get_current_expr() != null) {
std.debug.print("done shaping: {any}\n", .{self.current_expr});
self.set_current_expr(null);
} else {
return error.NoShapingInProgress;
}
},
else => {
std.debug.print("unexpected token: {any}, expected token in set: {any}\n", .{ peeked, keywordset });
},
}
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var prompt: []const u8 = "Zoq::> ";
var r = std.io.getStdIn().reader();
var w = std.io.getStdOut().writer();
_ = w;
var buf: [4096]u8 = undefined;
var command: []const u8 = undefined;
var context: Context = Context.init(allocator);
defer context.deinit();
while (true) {
std.debug.print("{s}\n", .{prompt});
var temp = try r.readUntilDelimiterOrEof(&buf, '\n');
command = temp.?;
var lexer = Lexer.init(command);
var result = try context.process_command(&lexer);
_ = result;
//std.debug.print("{any}\n", .{context.rules_table});
}
context.show_rules();
}
The error is as follows:
Zoq::>
rule add add(a,b) = add(b,a)
rule?: tokenizer.Token{ .token_type = tokenizer.TokenType{ .identifier = void }, .value = { 97, 100, 100 } }
defined rule: add(a, b) ≡ add(b, a)
Zoq::>
shape add(x,y)
Shaping: add(x, y)
Zoq::>
apply add
applying rule: add
got rule:
x,(), b) ≡ add(b, a)
add:
x,(), b) =
x,(), b) ≡ add(b, a)
error: NO_MATCH
/home/clinton/Developer/Zig_projects/Zoq/src/expression.zig:305:9: 0x22908a in apply (main)
return NoMatch.NO_MATCH;
^
/home/clinton/Developer/Zig_projects/Zoq/src/exprparser.zig:200:32: 0x22a1e5 in process_command (main)
var new_expr = try rule.?.apply(expr.*.?);
^
/home/clinton/Developer/Zig_projects/Zoq/src/main.zig:35:22: 0x22b0ad in main (main)
var result = try context.process_command(&lexer);
^
[Process exited 1]
Thanks in advance.
EDIT: The issue was with the Expression union that made up the Rule Struct. It contained strings. So after tokenization and parsing, those strings got damaged because they were not allocated on the heap. They didn’t persist and the data I was getting was just fragments of the reshaped memory. So the solution was when parsing an expression instead of creating it with the token value, make sure to use a duplicate to the token value. This way the string isn’t invalidated later. Thank you to all the kind people who helped me with this problem. I’ll keep working on my project and would return if I need any more guidance.