mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
package reflect; fix substring type bug; fix scoping rules for using on procedure parameter
This commit is contained in:
@@ -0,0 +1,285 @@
|
|||||||
|
package reflect
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
|
||||||
|
Type_Kind :: enum {
|
||||||
|
Invalid,
|
||||||
|
|
||||||
|
Named,
|
||||||
|
Integer,
|
||||||
|
Rune,
|
||||||
|
Float,
|
||||||
|
Complex,
|
||||||
|
String,
|
||||||
|
Boolean,
|
||||||
|
Any,
|
||||||
|
Type_Id,
|
||||||
|
Pointer,
|
||||||
|
Procedure,
|
||||||
|
Array,
|
||||||
|
Dynamic_Array,
|
||||||
|
Slice,
|
||||||
|
Tuple,
|
||||||
|
Struct,
|
||||||
|
Union,
|
||||||
|
Enum,
|
||||||
|
Map,
|
||||||
|
Bit_Field,
|
||||||
|
Bit_Set,
|
||||||
|
Opaque,
|
||||||
|
Simd_Vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
|
ti := type_info_of(T);
|
||||||
|
if ti != nil {
|
||||||
|
#complete switch _ in ti.variant {
|
||||||
|
case runtime.Type_Info_Named: return .Named;
|
||||||
|
case runtime.Type_Info_Integer: return .Integer;
|
||||||
|
case runtime.Type_Info_Rune: return .Rune;
|
||||||
|
case runtime.Type_Info_Float: return .Float;
|
||||||
|
case runtime.Type_Info_Complex: return .Complex;
|
||||||
|
case runtime.Type_Info_String: return .String;
|
||||||
|
case runtime.Type_Info_Boolean: return .Boolean;
|
||||||
|
case runtime.Type_Info_Any: return .Any;
|
||||||
|
case runtime.Type_Info_Type_Id: return .Type_Id;
|
||||||
|
case runtime.Type_Info_Pointer: return .Pointer;
|
||||||
|
case runtime.Type_Info_Procedure: return .Procedure;
|
||||||
|
case runtime.Type_Info_Array: return .Array;
|
||||||
|
case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array;
|
||||||
|
case runtime.Type_Info_Slice: return .Slice;
|
||||||
|
case runtime.Type_Info_Tuple: return .Tuple;
|
||||||
|
case runtime.Type_Info_Struct: return .Struct;
|
||||||
|
case runtime.Type_Info_Union: return .Union;
|
||||||
|
case runtime.Type_Info_Enum: return .Enum;
|
||||||
|
case runtime.Type_Info_Map: return .Map;
|
||||||
|
case runtime.Type_Info_Bit_Field: return .Bit_Field;
|
||||||
|
case runtime.Type_Info_Bit_Set: return .Bit_Set;
|
||||||
|
case runtime.Type_Info_Opaque: return .Opaque;
|
||||||
|
case runtime.Type_Info_Simd_Vector: return .Simd_Vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return .Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bill): Better name
|
||||||
|
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
|
return type_kind(runtime.typeid_base(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bill): Better name
|
||||||
|
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
|
return type_kind(runtime.typeid_core(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size_of_typeid :: proc(T: typeid) -> int {
|
||||||
|
if ti := type_info_of(T); ti != nil {
|
||||||
|
return ti.size;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
align_of_typeid :: proc(T: typeid) -> int {
|
||||||
|
if ti := type_info_of(T); ti != nil {
|
||||||
|
return ti.align;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_bytes :: proc(v: any) -> []byte {
|
||||||
|
if v != nil {
|
||||||
|
sz := size_of_typeid(v.id);
|
||||||
|
return mem.slice_ptr((^byte)(v.data), sz);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
|
||||||
|
return v.data, v.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_nil :: proc(v: any) -> bool {
|
||||||
|
data := to_bytes(v);
|
||||||
|
if data != nil {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for v in data do if v != 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
index :: proc(v: any, i: int, loc := #caller_location) -> any {
|
||||||
|
if v == nil do return nil;
|
||||||
|
|
||||||
|
v := v;
|
||||||
|
v.id = runtime.typeid_base(v.id);
|
||||||
|
switch a in v {
|
||||||
|
case runtime.Type_Info_Array:
|
||||||
|
runtime.bounds_check_error_loc(loc, i, a.count);
|
||||||
|
offset := uintptr(a.elem.size * i);
|
||||||
|
data := rawptr(uintptr(v.data) + offset);
|
||||||
|
return any{data, a.elem.id};
|
||||||
|
|
||||||
|
case runtime.Type_Info_Slice:
|
||||||
|
raw := (^mem.Raw_Slice)(v.data);
|
||||||
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||||
|
offset := uintptr(a.elem.size * i);
|
||||||
|
data := rawptr(uintptr(raw.data) + offset);
|
||||||
|
return any{data, a.elem.id};
|
||||||
|
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
raw := (^mem.Raw_Dynamic_Array)(v.data);
|
||||||
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||||
|
offset := uintptr(a.elem.size * i);
|
||||||
|
data := rawptr(uintptr(raw.data) + offset);
|
||||||
|
return any{data, a.elem.id};
|
||||||
|
|
||||||
|
case runtime.Type_Info_String:
|
||||||
|
if a.is_cstring do return nil;
|
||||||
|
|
||||||
|
raw := (^mem.Raw_String)(v.data);
|
||||||
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||||
|
offset := uintptr(size_of(u8) * i);
|
||||||
|
data := rawptr(uintptr(raw.data) + offset);
|
||||||
|
return any{data, typeid_of(u8)};
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Struct_Tag :: distinct string;
|
||||||
|
|
||||||
|
Struct_Field :: struct {
|
||||||
|
name: string,
|
||||||
|
type: typeid,
|
||||||
|
tag: Struct_Tag,
|
||||||
|
offset: uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
if 0 <= i && i < len(s.names) {
|
||||||
|
field.name = s.names[i];
|
||||||
|
field.type = s.types[i].id;
|
||||||
|
field.tag = Struct_Tag(s.tags[i]);
|
||||||
|
field.offset = s.offsets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
for fname, i in s.names {
|
||||||
|
if fname == name {
|
||||||
|
field.name = s.names[i];
|
||||||
|
field.type = s.types[i].id;
|
||||||
|
field.tag = Struct_Tag(s.tags[i]);
|
||||||
|
field.offset = s.offsets[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct_field_names :: proc(T: typeid) -> []string {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
return s.names;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
return s.types;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
return transmute([]Struct_Tag)s.tags;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||||
|
ti := runtime.type_info_base(type_info_of(T));
|
||||||
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||||
|
return s.offsets;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
|
||||||
|
value, _ = struct_tag_lookup(tag, key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
|
||||||
|
for tag := tag; tag != ""; /**/ {
|
||||||
|
i := 0;
|
||||||
|
for i < len(tag) && tag[i] == ' ' { // Skip whitespace
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
tag = tag[i:];
|
||||||
|
if len(tag) == 0 do break;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
loop: for i < len(tag) {
|
||||||
|
switch tag[i] {
|
||||||
|
case ':', '"':
|
||||||
|
break loop;
|
||||||
|
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 do break;
|
||||||
|
if i+1 >= len(tag) do break;
|
||||||
|
|
||||||
|
if tag[i] != ':' || tag[i+1] != '"' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
name := string(tag[:i]);
|
||||||
|
tag = tag[i+1:];
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
for i < len(tag) && tag[i] != '"' { // find closing quote
|
||||||
|
if tag[i] == '\\' do i += 1; // Skip escaped characters
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(tag) do break;
|
||||||
|
|
||||||
|
val := string(tag[:i+1]);
|
||||||
|
tag = tag[i+1:];
|
||||||
|
|
||||||
|
if key == name {
|
||||||
|
return val[1:i], true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -283,7 +283,7 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||||
if info == nil do return nil;
|
if info == nil do return nil;
|
||||||
|
|
||||||
base := info;
|
base := info;
|
||||||
@@ -291,11 +291,13 @@ type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_In
|
|||||||
switch i in base.variant {
|
switch i in base.variant {
|
||||||
case Type_Info_Named: base = i.base;
|
case Type_Info_Named: base = i.base;
|
||||||
case Type_Info_Enum: base = i.base;
|
case Type_Info_Enum: base = i.base;
|
||||||
|
case Type_Info_Opaque: base = i.elem;
|
||||||
case: break loop;
|
case: break loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
type_info_base_without_enum :: type_info_core;
|
||||||
|
|
||||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
|
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
|
||||||
data := transmute(Typeid_Bit_Field)id;
|
data := transmute(Typeid_Bit_Field)id;
|
||||||
@@ -311,10 +313,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
|||||||
ti = type_info_base(ti);
|
ti = type_info_base(ti);
|
||||||
return ti.id;
|
return ti.id;
|
||||||
}
|
}
|
||||||
typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid {
|
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
||||||
ti := type_info_base_without_enum(type_info_of(id));
|
ti := type_info_base_without_enum(type_info_of(id));
|
||||||
return ti.id;
|
return ti.id;
|
||||||
}
|
}
|
||||||
|
typeid_base_without_enum :: typeid_core;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+24
-10
@@ -4,6 +4,7 @@ import "core:fmt"
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
when os.OS == "windows" {
|
when os.OS == "windows" {
|
||||||
import "core:thread"
|
import "core:thread"
|
||||||
@@ -946,26 +947,39 @@ deferred_procedure_associations :: proc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct_field_tags :: proc() {
|
reflection :: proc() {
|
||||||
fmt.println("# struct_field_tags");
|
fmt.println("# reflection");
|
||||||
|
|
||||||
Foo :: struct {
|
Foo :: struct {
|
||||||
x: int `tag1`,
|
x: int `tag1`,
|
||||||
y: string `tag2`,
|
y: string `json:"y_field"`,
|
||||||
z: bool, // no tag
|
z: bool, // no tag
|
||||||
}
|
}
|
||||||
|
|
||||||
ti := runtime.type_info_base(type_info_of(Foo));
|
id := typeid_of(Foo);
|
||||||
s := ti.variant.(runtime.Type_Info_Struct);
|
names := reflect.struct_field_names(id);
|
||||||
|
types := reflect.struct_field_types(id);
|
||||||
|
tags := reflect.struct_field_tags(id);
|
||||||
|
|
||||||
|
assert(len(names) == len(types) && len(names) == len(tags));
|
||||||
|
|
||||||
fmt.println("Foo :: struct {");
|
fmt.println("Foo :: struct {");
|
||||||
for _, i in s.names {
|
for tag, i in tags {
|
||||||
if tag := s.tags[i]; tag != "" {
|
name, type := names[i], types[i];
|
||||||
fmt.printf("\t%s: %T `%s`,\n", s.names[i], s.types[i], tag);
|
if tag != "" {
|
||||||
|
fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
|
||||||
} else {
|
} else {
|
||||||
fmt.printf("\t%s: %T,\n", s.names[i], s.types[i]);
|
fmt.printf("\t%s: %T,\n", name, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.println("}");
|
fmt.println("}");
|
||||||
|
|
||||||
|
|
||||||
|
for tag, i in tags {
|
||||||
|
if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
|
||||||
|
fmt.printf("json: %s -> %s\n", names[i], val);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
@@ -986,6 +1000,6 @@ main :: proc() {
|
|||||||
bit_set_type();
|
bit_set_type();
|
||||||
diverging_procedures();
|
diverging_procedures();
|
||||||
deferred_procedure_associations();
|
deferred_procedure_associations();
|
||||||
struct_field_tags();
|
reflection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-6
@@ -1074,6 +1074,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ProcUsingVar {
|
||||||
|
Entity *e;
|
||||||
|
Entity *uvar;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
|
void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
|
||||||
if (body == nullptr) {
|
if (body == nullptr) {
|
||||||
@@ -1098,6 +1103,13 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
|||||||
ctx->curr_proc_decl = decl;
|
ctx->curr_proc_decl = decl;
|
||||||
ctx->curr_proc_sig = type;
|
ctx->curr_proc_sig = type;
|
||||||
|
|
||||||
|
ast_node(bs, BlockStmt, body);
|
||||||
|
|
||||||
|
Array<ProcUsingVar> using_entities = {};
|
||||||
|
using_entities.allocator = heap_allocator();
|
||||||
|
defer (array_free(&using_entities));
|
||||||
|
|
||||||
|
{
|
||||||
GB_ASSERT(type->kind == Type_Proc);
|
GB_ASSERT(type->kind == Type_Proc);
|
||||||
if (type->Proc.param_count > 0) {
|
if (type->Proc.param_count > 0) {
|
||||||
TypeTuple *params = &type->Proc.params->Tuple;
|
TypeTuple *params = &type->Proc.params->Tuple;
|
||||||
@@ -1126,11 +1138,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
|||||||
uvar->Variable.is_immutable = is_immutable;
|
uvar->Variable.is_immutable = is_immutable;
|
||||||
if (is_value) uvar->flags |= EntityFlag_Value;
|
if (is_value) uvar->flags |= EntityFlag_Value;
|
||||||
|
|
||||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
|
||||||
if (prev != nullptr) {
|
ProcUsingVar puv = {e, uvar};
|
||||||
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string));
|
array_add(&using_entities, puv);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1139,10 +1150,30 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for_array(i, using_entities) {
|
||||||
|
Entity *e = using_entities[i].e;
|
||||||
|
Entity *uvar = using_entities[i].uvar;
|
||||||
|
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||||
|
if (prev != nullptr) {
|
||||||
|
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ast_node(bs, BlockStmt, body);
|
|
||||||
check_open_scope(ctx, body);
|
check_open_scope(ctx, body);
|
||||||
|
{
|
||||||
|
for_array(i, using_entities) {
|
||||||
|
Entity *e = using_entities[i].e;
|
||||||
|
Entity *uvar = using_entities[i].uvar;
|
||||||
|
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||||
|
// NOTE(bill): Don't err here
|
||||||
|
}
|
||||||
|
|
||||||
check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
|
check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
|
||||||
|
|
||||||
if (type->Proc.result_count > 0) {
|
if (type->Proc.result_count > 0) {
|
||||||
if (!check_is_terminating(body)) {
|
if (!check_is_terminating(body)) {
|
||||||
if (token.kind == Token_Ident) {
|
if (token.kind == Token_Ident) {
|
||||||
@@ -1153,6 +1184,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
check_close_scope(ctx);
|
check_close_scope(ctx);
|
||||||
|
|
||||||
check_scope_usage(ctx->checker, ctx->scope);
|
check_scope_usage(ctx->checker, ctx->scope);
|
||||||
|
|||||||
+1
-1
@@ -7164,7 +7164,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
if (o->mode == Addressing_Constant) {
|
if (o->mode == Addressing_Constant) {
|
||||||
max_count = o->value.value_string.len;
|
max_count = o->value.value_string.len;
|
||||||
}
|
}
|
||||||
o->type = t_string;
|
o->type = type_deref(o->type);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user