richer formatting
This commit is contained in:
@@ -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'")
|
||||
|
||||
Reference in New Issue
Block a user