richer formatting

This commit is contained in:
2026-07-15 20:38:54 +02:00
parent 1165cfb7c0
commit c4fa8e930f
9 changed files with 807 additions and 99 deletions
+215 -7
View File
@@ -8,6 +8,7 @@ import "../types"
import "base:intrinsics"
import "core:fmt"
import "core:hash"
import "core:math"
import "core:mem"
import "core:strings"
@@ -18,14 +19,18 @@ Comptime_Value_Kind :: enum u8 {
Integer,
Type,
String,
Static,
}
Comptime_Value :: struct {
name: symbol.Id,
type: types.Type,
value: i128,
text: string,
kind: Comptime_Value_Kind,
name: symbol.Id,
type: types.Type,
value: i128,
text: string,
static_value: Ct_Value_Id,
key: string,
fingerprint: u64,
kind: Comptime_Value_Kind,
}
Constant_Kind :: enum {
@@ -85,7 +90,8 @@ comptime_values_equal :: proc(left, right: []Comptime_Value) -> bool {
other := right[index]
if value.name != other.name || value.kind != other.kind || !types.equal(value.type, other.type) ||
(value.kind == .Integer && value.value != other.value) ||
(value.kind == .String && value.text != other.text) {
(value.kind == .String && value.text != other.text) ||
(value.kind == .Static && (value.fingerprint != other.fingerprint || value.key != other.key)) {
return false
}
}
@@ -362,6 +368,9 @@ ct_state_make :: proc(
}
id := ct_add_value(&state, Ct_Value{kind=.String, type=value.type, index=string_id})
ct_bind_value(&state, value.name, value.type, id, false)
} else if value.kind == .Static {
id := ct_clone_graph(&state, &checker.static_state, value.static_value)
ct_bind_value(&state, value.name, value.type, id, false)
}
}
for binding in checker.static_bindings {
@@ -2312,9 +2321,10 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
typeinfo_type := std_named_type(checker, "@std/meta", "TypeInfo")
fieldinfo_type := std_named_type(checker, "@std/meta", "FieldInfo")
recordinfo_type := std_named_type(checker, "@std/meta", "RecordInfo")
enuminfo_type := std_named_type(checker, "@std/meta", "EnumInfo")
layout_type := std_named_type(checker, "@std/meta", "Layout")
if !types.is_valid(typeinfo_type) || !types.is_valid(fieldinfo_type) ||
!types.is_valid(recordinfo_type) || !types.is_valid(layout_type) {
!types.is_valid(recordinfo_type) || !types.is_valid(enuminfo_type) || !types.is_valid(layout_type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, span,
"typeinfo! requires importing @std/meta",
@@ -2352,6 +2362,31 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
if !variant_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta TypeInfo is malformed")
}
if tag == "enum" {
members := types.enum_members_for(store, resolved)
field_values := make([]Ct_Value_Id, len(members), checker.allocator)
defer delete(field_values, checker.allocator)
for member, index in members {
name_value := ct_reflection_string(state, symbol_text(checker, symbol.Id(member.name)))
type_value := ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)})
index_value := ct_add_value(state, Ct_Value{kind=.Integer, type=types.USIZE, integer=i128(index)})
field_values[index] = ct_struct_value(
state, fieldinfo_type, []Ct_Value_Id{name_value, type_value, index_value},
)
}
fields_start := u32(len(state.children))
append(&state.children, ..field_values)
fields_type := types.array(store, fieldinfo_type, u64(len(field_values)), false)
fields_value := ct_add_value(state, Ct_Value{
kind=.Array, type=fields_type, start=fields_start, count=u32(len(field_values)),
})
enum_value := ct_struct_value(state, enuminfo_type, []Ct_Value_Id{fields_value})
payload_start := u32(len(state.children))
append(&state.children, enum_value)
return ct_add_value(state, Ct_Value{
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
}), ct_flow(.Normal), true
}
if tag != "record" {
start := u32(len(state.children))
return ct_add_value(state, Ct_Value{
@@ -2437,6 +2472,22 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
if len(expr.args) != 2 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "field! expects 2 arguments, got %d", len(expr.args))
}
if enum_type, ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file);
ok && types.is_enum(enum_type, &checker.module.types) {
name_value, name_flow, name_ok := ct_eval_expr(state, expr.args[1], types.INVALID, depth+1)
if !name_ok || name_flow.kind != .Normal {
return INVALID_CT_VALUE, name_flow, name_ok
}
name, text_ok := ct_value_bytes(state, name_value)
if !text_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "field! name must be a comptime immutable byte string")
}
member, member_ok := find_enum_member(checker, enum_type, symbol.intern(checker.symbols, name))
if !member_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unknown enum member '%s'", name)
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=enum_type, integer=member.value}), ct_flow(.Normal), true
}
base, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
@@ -2753,6 +2804,163 @@ store_static_binding :: proc(checker: ^Checker, source: ^Ct_State, id: Ct_Value_
return Static_Binding{name=name, type=value_type, value=value}
}
ct_write_comptime_key :: proc(state: ^Ct_State, id: Ct_Value_Id, builder: ^strings.Builder, depth := 0) -> bool {
if depth > 256 || id == INVALID_CT_VALUE || int(id) >= len(state.values) {
return false
}
value := state.values[id]
fmt.sbprintf(builder, "t%d:", value.type)
#partial switch value.kind {
case .Integer:
fmt.sbprintf(builder, "i%d;", value.integer)
return true
case .Float:
if types.bits(value.type, state.checker.target) == 32 {
fmt.sbprintf(builder, "f%08x;", transmute(u32)f32(value.float))
} else {
fmt.sbprintf(builder, "f%016x;", transmute(u64)value.float)
}
return true
case .Bool:
fmt.sbprintf(builder, "b%d;", value.integer)
return true
case .Type:
fmt.sbprintf(builder, "y%d;", value.index)
return true
case .String:
if value.index >= u64(len(state.checker.ast_module.strings)) {
return false
}
text := state.checker.ast_module.strings[value.index]
fmt.sbprintf(builder, "s%d:", len(text))
hex := "0123456789abcdef"
for byte in transmute([]byte)text {
strings.write_byte(builder, hex[byte>>4])
strings.write_byte(builder, hex[byte&0xf])
}
strings.write_byte(builder, ';')
return true
case .Slice:
item, item_ok := types.container(value.type, &state.checker.module.types)
if !item_ok || item.kind != .Slice || item.mutable || item.child != types.U8 {
return false
}
text, text_ok := ct_value_bytes(state, id)
if !text_ok {
return false
}
fmt.sbprintf(builder, "s%d:", len(text))
hex := "0123456789abcdef"
for byte in transmute([]byte)text {
strings.write_byte(builder, hex[byte>>4])
strings.write_byte(builder, hex[byte&0xf])
}
strings.write_byte(builder, ';')
return true
case .Array:
children := ct_child_slice(state, value)
fmt.sbprintf(builder, "a%d[", len(children))
for child in children {
if !ct_write_comptime_key(state, child, builder, depth+1) {
return false
}
}
strings.write_string(builder, "];")
return true
case .Struct:
if types.is_union(value.type, &state.checker.module.types) &&
!types.is_tagged_union(value.type, &state.checker.module.types) {
return false
}
children := ct_child_slice(state, value)
fmt.sbprintf(builder, "r%d:%d[", value.active, len(children))
for child in children {
if child != INVALID_CT_VALUE && !ct_write_comptime_key(state, child, builder, depth+1) {
return false
}
}
strings.write_string(builder, "];")
return true
case .None:
strings.write_string(builder, "n;")
return true
case .Optional_Some:
children := ct_child_slice(state, value)
if len(children) != 1 || !ct_write_comptime_key(state, children[0], builder, depth+1) {
return false
}
strings.write_string(builder, "o;")
return true
case .Invalid, .Void, .Undefined, .Range, .Pointer, .Function, .Fallible:
return false
}
return false
}
eval_static_comptime_value :: proc(
checker: ^Checker,
name: symbol.Id,
expr: ast.Expr_Id,
declared: types.Type,
pkg: ast.Package_Id,
file: ast.File_Id,
values: []Comptime_Value = nil,
diagnose := false,
) -> (Comptime_Value, bool) {
state := ct_state_make(checker, pkg, file, values=values, diagnose=false)
defer ct_state_destroy(&state)
id, flow, ok := ct_eval_expr(&state, expr, declared, 0)
if ok && flow.kind == .Normal {
id, ok = ct_coerce_value(&state, id, declared, checker.ast_module.exprs[expr].span)
}
if !ok || flow.kind != .Normal || ct_value_contains_undefined(&state, id) {
if diagnose {
source.add(
checker.diagnostics,
checker.ast_module.exprs[expr].span,
"comptime argument has no stable comptime identity",
)
}
return {}, false
}
builder := strings.builder_make(checker.allocator)
defer strings.builder_destroy(&builder)
if !ct_write_comptime_key(&state, id, &builder) {
if diagnose {
source.add(
checker.diagnostics,
checker.ast_module.exprs[expr].span,
"comptime argument has no stable comptime identity",
)
}
return {}, false
}
key_view := strings.to_string(builder)
static_value := INVALID_CT_VALUE
key := ""
for existing, index in checker.comptime_keys {
if existing == key_view {
key = existing
static_value = checker.comptime_static_values[index]
break
}
}
if static_value == INVALID_CT_VALUE {
key = strings.clone(key_view, checker.allocator)
static_value = ct_clone_graph(&checker.static_state, &state, id)
append(&checker.comptime_keys, key)
append(&checker.comptime_static_values, static_value)
}
return Comptime_Value{
name=name,
type=declared,
static_value=static_value,
key=key,
fingerprint=hash.fnv64a(transmute([]byte)key),
kind=.Static,
}, true
}
ct_eval_try_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
if state.defer_depth > 0 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "cannot 'try' inside a 'defer'")