implement field access in comptime interpreter
This commit is contained in:
parent
9dbae69288
commit
497f1e1b23
@ -161,7 +161,7 @@ pub fn huntItDown(
|
||||
namespace: NamespaceIndex,
|
||||
decl_name: []const u8,
|
||||
options: InterpretOptions,
|
||||
) error{IdentifierNotFound}!Decl {
|
||||
) ?Decl {
|
||||
_ = options;
|
||||
|
||||
var current_namespace = namespace;
|
||||
@ -174,7 +174,7 @@ pub fn huntItDown(
|
||||
}
|
||||
}
|
||||
|
||||
return error.IdentifierNotFound;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Might be useful in the future
|
||||
@ -303,15 +303,20 @@ pub fn interpret(
|
||||
|
||||
const decl = ast.varDecl(tree, node_idx).?;
|
||||
|
||||
if (decl.ast.init_node == 0)
|
||||
return InterpretResult{ .nothing = {} };
|
||||
const type_value = if (decl.ast.type_node != 0) try ((try interpreter.interpret(decl.ast.type_node, namespace, .{})).getValue()) else null;
|
||||
const init_value = if (decl.ast.init_node != 0) try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue()) else null;
|
||||
|
||||
const init_type_value = try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue());
|
||||
if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} };
|
||||
|
||||
if (type_value) |v| {
|
||||
const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
|
||||
if (v.ty != type_type) return InterpretResult{ .nothing = {} };
|
||||
}
|
||||
|
||||
try decls.putNoClobber(interpreter.allocator, name, .{
|
||||
.name = name,
|
||||
.ty = init_type_value.ty,
|
||||
.val = init_type_value.val,
|
||||
.ty = if (type_value) |v| v.val else init_value.?.ty,
|
||||
.val = if (init_value) |init| init.val else .none,
|
||||
.alignment = 0, // TODO
|
||||
.address_space = .generic, // TODO
|
||||
.is_pub = true, // TODO
|
||||
@ -377,7 +382,7 @@ pub fn interpret(
|
||||
return InterpretResult{ .nothing = {} };
|
||||
},
|
||||
.identifier => {
|
||||
const value = offsets.nodeToSlice(tree, node_idx);
|
||||
const identifier = offsets.nodeToSlice(tree, node_idx);
|
||||
|
||||
const simples = std.ComptimeStringMap(InternPool.Simple, .{
|
||||
.{ "anyerror", .anyerror },
|
||||
@ -411,7 +416,7 @@ pub fn interpret(
|
||||
.{ "void", .void },
|
||||
});
|
||||
|
||||
if (simples.get(value)) |simple| {
|
||||
if (simples.get(identifier)) |simple| {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
@ -420,65 +425,209 @@ pub fn interpret(
|
||||
} };
|
||||
}
|
||||
|
||||
if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) blk: {
|
||||
if (identifier.len >= 2 and (identifier[0] == 'u' or identifier[0] == 'i')) blk: {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
||||
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{
|
||||
.signedness = if (value[0] == 'u') .unsigned else .signed,
|
||||
.bits = std.fmt.parseInt(u16, value[1..], 10) catch break :blk,
|
||||
.signedness = if (identifier[0] == 'u') .unsigned else .signed,
|
||||
.bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk,
|
||||
} }),
|
||||
} };
|
||||
}
|
||||
|
||||
// Logic to find identifiers in accessible scopes
|
||||
const decl = interpreter.huntItDown(namespace, value, options) catch |err| switch (err) {
|
||||
error.IdentifierNotFound => |e| {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"undeclared_identifier",
|
||||
"use of undeclared identifier '{s}'",
|
||||
.{value},
|
||||
);
|
||||
return e;
|
||||
},
|
||||
};
|
||||
if (interpreter.huntItDown(namespace, identifier, options)) |decl| {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
} };
|
||||
}
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
} };
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"undeclared_identifier",
|
||||
"use of undeclared identifier '{s}'",
|
||||
.{identifier},
|
||||
);
|
||||
return error.IdentifierNotFound;
|
||||
},
|
||||
.field_access => {
|
||||
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
|
||||
const rhs_str = tree.tokenSlice(data[node_idx].rhs);
|
||||
const field_name = tree.tokenSlice(data[node_idx].rhs);
|
||||
|
||||
var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
var irv = try ir.getValue();
|
||||
|
||||
const lhs_namespace = interpreter.ip.indexToKey(irv.val).getNamespace();
|
||||
|
||||
var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| switch (err) {
|
||||
error.IdentifierNotFound => |e| {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"undeclared_identifier",
|
||||
"`{}` has no member '{s}'",
|
||||
.{ irv.ty.fmtType(interpreter.ip), rhs_str },
|
||||
);
|
||||
return e;
|
||||
},
|
||||
const lhs = interpreter.ip.indexToKey(irv.ty);
|
||||
const inner_lhs = switch (lhs) {
|
||||
.pointer_type => |info| if (info.size == .One) interpreter.ip.indexToKey(info.elem_type) else lhs,
|
||||
else => lhs,
|
||||
};
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = scope_sub_decl.ty,
|
||||
.val = scope_sub_decl.val,
|
||||
} };
|
||||
const can_have_fields: bool = switch (inner_lhs) {
|
||||
.simple => |simple| switch (simple) {
|
||||
.type => blk: {
|
||||
const ty_key = interpreter.ip.indexToKey(irv.val);
|
||||
if (interpreter.huntItDown(ty_key.getNamespace(), field_name, options)) |decl| {
|
||||
std.debug.print("here {s}: {}\n", .{field_name, decl.ty.fmtType(interpreter.ip)});
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
} };
|
||||
}
|
||||
|
||||
switch (ty_key) {
|
||||
.error_set_type => |error_set_info| { // TODO
|
||||
_ = error_set_info;
|
||||
},
|
||||
.union_type => {}, // TODO
|
||||
.enum_type => |enum_info| { // TODO
|
||||
if (interpreter.ip.contains(IPKey{ .bytes = field_name })) |field_name_index| {
|
||||
for (enum_info.fields) |field| {
|
||||
if (field.name != field_name_index) continue;
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = irv.val,
|
||||
.val = .none, // TODO resolve enum value
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
else => break :blk false,
|
||||
}
|
||||
break :blk true;
|
||||
},
|
||||
else => false,
|
||||
},
|
||||
.pointer_type => |pointer_info| blk: {
|
||||
if (pointer_info.size == .Slice) {
|
||||
if (std.mem.eql(u8, field_name, "ptr")) {
|
||||
var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info };
|
||||
many_ptr_info.pointer_type.size = .Many;
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, many_ptr_info),
|
||||
.val = .none, // TODO resolve ptr of Slice
|
||||
},
|
||||
};
|
||||
} else if (std.mem.eql(u8, field_name, "len")) {
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }),
|
||||
.val = .none, // TODO resolve length of Slice
|
||||
},
|
||||
};
|
||||
}
|
||||
} else if (interpreter.ip.indexToKey(pointer_info.elem_type) == .array_type) {
|
||||
if (std.mem.eql(u8, field_name, "len")) {
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }),
|
||||
.val = .none, // TODO resolve length of Slice
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
break :blk true;
|
||||
},
|
||||
.array_type => |array_info| blk: {
|
||||
const len_value = try interpreter.ip.get(interpreter.allocator, .{ .int_u64_value = array_info.len });
|
||||
|
||||
if (std.mem.eql(u8, field_name, "len")) {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .comptime_int }),
|
||||
.val = len_value,
|
||||
} };
|
||||
}
|
||||
break :blk true;
|
||||
},
|
||||
.optional_type => |optional_info| blk: {
|
||||
if (!std.mem.eql(u8, field_name, "?")) break :blk false;
|
||||
const null_value = try interpreter.ip.get(interpreter.allocator, .{ .simple = .null_value });
|
||||
if (irv.val == null_value) {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"null_unwrap",
|
||||
"tried to unwrap optional of type `{}` which was null",
|
||||
.{irv.ty.fmtType(interpreter.ip)},
|
||||
);
|
||||
return error.InvalidOperation;
|
||||
} else {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = optional_info.payload_type,
|
||||
.val = irv.val,
|
||||
} };
|
||||
}
|
||||
},
|
||||
.struct_type => |struct_info| blk: {
|
||||
// if the intern pool does not contain the field name, it is impossible that there is a field with the given name
|
||||
const field_name_index = interpreter.ip.contains(IPKey{ .bytes = field_name }) orelse break :blk true;
|
||||
|
||||
for (struct_info.fields) |field, i| {
|
||||
std.debug.print("field {} {}\n", .{field.ty, field.ty.fmtType(interpreter.ip)});
|
||||
if (field.name != field_name_index) continue;
|
||||
const val = found_val: {
|
||||
if (irv.val == .none) break :found_val .none;
|
||||
const val_key = interpreter.ip.indexToKey(irv.val);
|
||||
if (val_key != .aggregate) break :found_val .none;
|
||||
break :found_val val_key.aggregate[i];
|
||||
};
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = field.ty,
|
||||
.val = val,
|
||||
} };
|
||||
}
|
||||
break :blk true;
|
||||
},
|
||||
.enum_type => |enum_info| blk: { // TODO
|
||||
_ = enum_info;
|
||||
break :blk true;
|
||||
},
|
||||
.union_type => |union_info| blk: { // TODO
|
||||
_ = union_info;
|
||||
break :blk true;
|
||||
},
|
||||
else => false,
|
||||
};
|
||||
|
||||
if (can_have_fields) {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"undeclared_identifier",
|
||||
"`{}` has no member '{s}'",
|
||||
.{ irv.ty.fmtType(interpreter.ip), field_name },
|
||||
);
|
||||
} else {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"invalid_field_access",
|
||||
"`{}` does not support field access",
|
||||
.{irv.ty.fmtType(interpreter.ip)},
|
||||
);
|
||||
}
|
||||
return error.InvalidOperation;
|
||||
},
|
||||
.grouped_expression => {
|
||||
return try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
|
Loading…
Reference in New Issue
Block a user