implement field access in comptime interpreter
This commit is contained in:
parent
9dbae69288
commit
497f1e1b23
@ -161,7 +161,7 @@ pub fn huntItDown(
|
|||||||
namespace: NamespaceIndex,
|
namespace: NamespaceIndex,
|
||||||
decl_name: []const u8,
|
decl_name: []const u8,
|
||||||
options: InterpretOptions,
|
options: InterpretOptions,
|
||||||
) error{IdentifierNotFound}!Decl {
|
) ?Decl {
|
||||||
_ = options;
|
_ = options;
|
||||||
|
|
||||||
var current_namespace = namespace;
|
var current_namespace = namespace;
|
||||||
@ -174,7 +174,7 @@ pub fn huntItDown(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return error.IdentifierNotFound;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Might be useful in the future
|
// Might be useful in the future
|
||||||
@ -303,15 +303,20 @@ pub fn interpret(
|
|||||||
|
|
||||||
const decl = ast.varDecl(tree, node_idx).?;
|
const decl = ast.varDecl(tree, node_idx).?;
|
||||||
|
|
||||||
if (decl.ast.init_node == 0)
|
const type_value = if (decl.ast.type_node != 0) try ((try interpreter.interpret(decl.ast.type_node, namespace, .{})).getValue()) else null;
|
||||||
return InterpretResult{ .nothing = {} };
|
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, .{
|
try decls.putNoClobber(interpreter.allocator, name, .{
|
||||||
.name = name,
|
.name = name,
|
||||||
.ty = init_type_value.ty,
|
.ty = if (type_value) |v| v.val else init_value.?.ty,
|
||||||
.val = init_type_value.val,
|
.val = if (init_value) |init| init.val else .none,
|
||||||
.alignment = 0, // TODO
|
.alignment = 0, // TODO
|
||||||
.address_space = .generic, // TODO
|
.address_space = .generic, // TODO
|
||||||
.is_pub = true, // TODO
|
.is_pub = true, // TODO
|
||||||
@ -377,7 +382,7 @@ pub fn interpret(
|
|||||||
return InterpretResult{ .nothing = {} };
|
return InterpretResult{ .nothing = {} };
|
||||||
},
|
},
|
||||||
.identifier => {
|
.identifier => {
|
||||||
const value = offsets.nodeToSlice(tree, node_idx);
|
const identifier = offsets.nodeToSlice(tree, node_idx);
|
||||||
|
|
||||||
const simples = std.ComptimeStringMap(InternPool.Simple, .{
|
const simples = std.ComptimeStringMap(InternPool.Simple, .{
|
||||||
.{ "anyerror", .anyerror },
|
.{ "anyerror", .anyerror },
|
||||||
@ -411,7 +416,7 @@ pub fn interpret(
|
|||||||
.{ "void", .void },
|
.{ "void", .void },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (simples.get(value)) |simple| {
|
if (simples.get(identifier)) |simple| {
|
||||||
return InterpretResult{ .value = Value{
|
return InterpretResult{ .value = Value{
|
||||||
.interpreter = interpreter,
|
.interpreter = interpreter,
|
||||||
.node_idx = node_idx,
|
.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{
|
return InterpretResult{ .value = Value{
|
||||||
.interpreter = interpreter,
|
.interpreter = interpreter,
|
||||||
.node_idx = node_idx,
|
.node_idx = node_idx,
|
||||||
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }),
|
||||||
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{
|
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{
|
||||||
.signedness = if (value[0] == 'u') .unsigned else .signed,
|
.signedness = if (identifier[0] == 'u') .unsigned else .signed,
|
||||||
.bits = std.fmt.parseInt(u16, value[1..], 10) catch break :blk,
|
.bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk,
|
||||||
} }),
|
} }),
|
||||||
} };
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logic to find identifiers in accessible scopes
|
// Logic to find identifiers in accessible scopes
|
||||||
const decl = interpreter.huntItDown(namespace, value, options) catch |err| switch (err) {
|
if (interpreter.huntItDown(namespace, identifier, options)) |decl| {
|
||||||
error.IdentifierNotFound => |e| {
|
return InterpretResult{ .value = Value{
|
||||||
try interpreter.recordError(
|
.interpreter = interpreter,
|
||||||
node_idx,
|
.node_idx = node_idx,
|
||||||
"undeclared_identifier",
|
.ty = decl.ty,
|
||||||
"use of undeclared identifier '{s}'",
|
.val = decl.val,
|
||||||
.{value},
|
} };
|
||||||
);
|
}
|
||||||
return e;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return InterpretResult{ .value = Value{
|
try interpreter.recordError(
|
||||||
.interpreter = interpreter,
|
node_idx,
|
||||||
.node_idx = node_idx,
|
"undeclared_identifier",
|
||||||
.ty = decl.ty,
|
"use of undeclared identifier '{s}'",
|
||||||
.val = decl.val,
|
.{identifier},
|
||||||
} };
|
);
|
||||||
|
return error.IdentifierNotFound;
|
||||||
},
|
},
|
||||||
.field_access => {
|
.field_access => {
|
||||||
if (data[node_idx].rhs == 0) return error.CriticalAstFailure;
|
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 ir = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||||
var irv = try ir.getValue();
|
var irv = try ir.getValue();
|
||||||
|
|
||||||
const lhs_namespace = interpreter.ip.indexToKey(irv.val).getNamespace();
|
const lhs = interpreter.ip.indexToKey(irv.ty);
|
||||||
|
const inner_lhs = switch (lhs) {
|
||||||
var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| switch (err) {
|
.pointer_type => |info| if (info.size == .One) interpreter.ip.indexToKey(info.elem_type) else lhs,
|
||||||
error.IdentifierNotFound => |e| {
|
else => lhs,
|
||||||
try interpreter.recordError(
|
|
||||||
node_idx,
|
|
||||||
"undeclared_identifier",
|
|
||||||
"`{}` has no member '{s}'",
|
|
||||||
.{ irv.ty.fmtType(interpreter.ip), rhs_str },
|
|
||||||
);
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return InterpretResult{ .value = Value{
|
const can_have_fields: bool = switch (inner_lhs) {
|
||||||
.interpreter = interpreter,
|
.simple => |simple| switch (simple) {
|
||||||
.node_idx = data[node_idx].rhs,
|
.type => blk: {
|
||||||
.ty = scope_sub_decl.ty,
|
const ty_key = interpreter.ip.indexToKey(irv.val);
|
||||||
.val = scope_sub_decl.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 => {
|
.grouped_expression => {
|
||||||
return try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
return try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||||
|
Loading…
Reference in New Issue
Block a user