scope.ha (2475B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use hare::ast; 5 use hare::types; 6 use hash; 7 use hash::fnv; 8 use strings; 9 10 // What sort of [[object]] is represented. 11 export type object_kind = enum { 12 BIND, 13 CONST, 14 DECL, 15 TYPE, 16 }; 17 18 // An object is a named object in a scope, such as a binding, type, or 19 // declaration. 20 export type object = struct { 21 kind: object_kind, 22 hash: u64, 23 24 // The fully qualified identifier 25 ident: ast::ident, 26 // Local name, if different from the fully qualified identifier 27 name: ast::ident, 28 29 _type: const *types::_type, 30 // TODO: store value for constants 31 }; 32 33 export def SCOPE_BUCKETS: size = 4096; 34 35 // What kind of [[scope]] is represented. 36 export type scope_class = enum { 37 COMPOUND, 38 ENUM, 39 FUNC, 40 LOOP, 41 MATCH, 42 SUBUNIT, 43 UNIT, 44 }; 45 46 // A scope is a member of a hierarchy of storage containers which hold named 47 // [[object]]s, such as variables or function parameters. 48 export type scope = struct { 49 class: scope_class, 50 parent: nullable *scope, 51 objects: []object, 52 hashmap: [SCOPE_BUCKETS][]*object, 53 }; 54 55 fn scope_push(ctx: *context, class: scope_class) *scope = { 56 let new = alloc(scope { 57 class = class, 58 parent = ctx.scope, 59 ... 60 }); 61 ctx.scope = new; 62 return new; 63 }; 64 65 fn scope_pop(ctx: *context) *scope = { 66 const top_scope = ctx.scope; 67 ctx.scope = ctx.scope.parent: *scope; // TODO: as *scope 68 return top_scope; 69 }; 70 71 fn ident_hash(ident: ast::ident) u64 = { 72 let hash = fnv::fnv64a(); 73 const zerobuf = [0u8]; 74 for (let i = len(ident); i > 0; i -= 1) { 75 hash::write(&hash, strings::toutf8(ident[i - 1])); 76 hash::write(&hash, zerobuf[..]); 77 }; 78 return fnv::sum64(&hash); 79 }; 80 81 fn scope_insert(ctx: *context, obj: object) *object = { 82 const scope = ctx.scope; 83 append(scope.objects, obj); 84 let obj = &scope.objects[len(scope.objects) - 1]; 85 const hash = ident_hash(obj.ident); 86 obj.hash = hash; 87 append(scope.hashmap[hash: size % SCOPE_BUCKETS], obj); 88 return obj; 89 }; 90 91 fn ctx_lookup(ctx: *context, ident: ast::ident) nullable *object = 92 scope_lookup(ctx.scope, ident); 93 94 fn scope_lookup(scp: *scope, ident: ast::ident) nullable *object = { 95 const hash = ident_hash(ident); 96 const bucket = scp.hashmap[hash: size % SCOPE_BUCKETS]; 97 for (let i = 0z; i < len(bucket); i += 1) { 98 if (ast::ident_eq(bucket[i].name, ident) 99 || ast::ident_eq(bucket[i].ident, ident)) { 100 return bucket[i]; 101 }; 102 }; 103 match (scp.parent) { 104 case null => 105 return null; 106 case let s: *scope => 107 return scope_lookup(s, ident); 108 }; 109 };