v / vlib / v2 / types / scope.v
250 lines · 227 sloc · 5.73 KB · 4a24806c9e1c60bd649846722997af8a5a0d1088
Raw
1// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module types
5
6pub type Object = Const | Fn | Global | Module | SmartCastSelector | Type | TypeObject
7
8// pub type Object = Const | Fn | Global | Module |
9// Alias | Array | Enum | Map | Pointer | Primitive | String | Struct | SumType
10
11struct SmartCastSelector {
12 origin Type
13 field string
14 cast_type Type
15}
16
17pub struct TypeObject {
18pub:
19 typ Type
20}
21
22@[heap]
23pub struct Scope {
24pub:
25 parent &Scope = unsafe { nil }
26pub mut:
27 objects map[string]Object
28 types map[string]Type
29 // TODO: try implement using original concept
30 field_smartcasts map[string]Type
31 // smartcasts map[string]Type
32 // TODO: it may be more efficient looking up local vars using an ID
33 // even if we had to store them in two different places. investigate.
34 // variables []Object
35 start int
36 end int
37}
38
39pub fn new_scope(parent &Scope) &Scope {
40 unsafe {
41 return &Scope{
42 parent: parent
43 }
44 }
45}
46
47// same_scope_ptr compares scope identity by address instead of structural equality.
48pub fn same_scope_ptr(a &Scope, b &Scope) bool {
49 return voidptr(a) == voidptr(b)
50}
51
52// TODO: try implement the alternate method I was experimenting with (SmartCastSelector)
53// i'm not sure if it is actually possible though. need to explore it.
54pub fn (s &Scope) lookup_field_smartcast(name string) ?Type {
55 if !scope_lookup_string_is_valid(name) {
56 return none
57 }
58 if name in s.field_smartcasts {
59 return s.field_smartcasts[name] or { return none }
60 }
61 if s.parent != unsafe { nil } {
62 return s.parent.lookup_field_smartcast(name)
63 }
64 return none
65}
66
67pub fn (s &Scope) lookup(name string) ?Object {
68 if !scope_lookup_string_is_valid(name) {
69 return none
70 }
71 if name in s.objects {
72 return s.objects[name] or { return none }
73 }
74 return none
75}
76
77pub fn (s &Scope) lookup_parent(name string, pos int) ?Object {
78 if obj := s.lookup(name) {
79 return obj
80 // if !pos.is_valid() || cmpPos(obj.scopePos(), pos) <= 0 {
81 // return s, obj
82 // }
83 }
84 if s.parent != unsafe { nil } {
85 if parent_obj := s.parent.lookup_parent(name, pos) {
86 return parent_obj
87 }
88 }
89 // println('lookup_parent: NOT FOUND: ${name}')
90 return none
91}
92
93pub fn (s &Scope) lookup_type(name string) ?Type {
94 if !scope_lookup_string_is_valid(name) {
95 return none
96 }
97 if name in s.types {
98 return s.types[name] or { return none }
99 }
100 return none
101}
102
103pub fn (s &Scope) lookup_type_parent(name string, pos int) ?Type {
104 if typ := s.lookup_type(name) {
105 return typ
106 // if !pos.is_valid() || cmpPos(obj.scopePos(), pos) <= 0 {
107 // return s, obj
108 // }
109 }
110 if s.parent != unsafe { nil } {
111 if parent_typ := s.parent.lookup_type_parent(name, pos) {
112 return parent_typ
113 }
114 }
115 return none
116}
117
118// lookup_var_type looks up a variable by name and returns its type.
119// Walks up the scope chain to find the variable.
120pub fn (s &Scope) lookup_var_type(name string) ?Type {
121 if obj := s.lookup_parent(name, 0) {
122 return obj.typ()
123 }
124 return none
125}
126
127pub fn (s &Scope) lookup_parent_with_scope(name string, pos int) ?(&Scope, Object) {
128 if obj := s.lookup(name) {
129 return s, obj
130 // if !pos.is_valid() || cmpPos(obj.scopePos(), pos) <= 0 {
131 // return s, obj
132 // }
133 }
134 if s.parent != unsafe { nil } {
135 if parent_scope, parent_obj := s.parent.lookup_parent_with_scope(name, pos) {
136 return parent_scope, parent_obj
137 }
138 }
139 // println('lookup_parent: NOT FOUND: ${name}')
140 return none
141}
142
143pub fn (mut s Scope) insert(name string, obj Object) {
144 if !scope_lookup_string_is_valid(name) {
145 return
146 }
147 trace_scope_fixed_array_object('insert_in', name, obj)
148 if typ := object_decl_type(obj) {
149 s.types[name] = typ
150 }
151 if name in s.objects {
152 existing := s.objects[name] or { return }
153 // Module scopes pre-register a self-module placeholder so code can
154 // reference `mod_name.CONST` from inside the same module. A real symbol
155 // with the same name should override that placeholder.
156 if existing is Module && obj !is Module {
157 s.objects[name] = obj
158 }
159 return
160 }
161 s.objects[name] = obj
162 if stored := s.objects[name] {
163 trace_scope_fixed_array_object('insert_out', name, stored)
164 }
165}
166
167// insert_or_update always overwrites an existing entry. Used for fn_root_scope
168// where variables from nested scopes must be updated when re-declared.
169pub fn (mut s Scope) insert_or_update(name string, obj Object) {
170 if !scope_lookup_string_is_valid(name) {
171 return
172 }
173 trace_scope_fixed_array_object('insert_update_in', name, obj)
174 if typ := object_decl_type(obj) {
175 s.types[name] = typ
176 }
177 s.objects[name] = obj
178 if stored := s.objects[name] {
179 trace_scope_fixed_array_object('insert_update_out', name, stored)
180 }
181}
182
183fn trace_scope_fixed_array_object(_label string, _name string, _obj Object) {
184}
185
186pub fn (mut s Scope) insert_type(name string, typ Type) {
187 if !scope_lookup_string_is_valid(name) {
188 return
189 }
190 s.types[name] = typ
191}
192
193fn object_decl_type(obj Object) ?Type {
194 match obj {
195 Type {
196 return obj
197 }
198 else {}
199 }
200
201 return none
202}
203
204fn scope_lookup_string_is_valid(s string) bool {
205 if s.len <= 0 || s.len > 512 {
206 return false
207 }
208 ptr := unsafe { u64(voidptr(s.str)) }
209 return ptr > 4096
210}
211
212pub fn (s &Scope) print(recurse_parents bool) {
213 println('# SCOPE:')
214 for name, obj in s.objects {
215 println(' * ${name}: ${obj.type_name()}')
216 // if obj is Type {
217 // println(' - ${name}: ${obj.type_name()}')
218 // }
219 }
220 if recurse_parents && s.parent != unsafe { nil } {
221 s.parent.print(recurse_parents)
222 }
223}
224
225pub fn (obj &Object) typ() Type {
226 match obj {
227 Const {
228 return obj.typ
229 }
230 Fn {
231 return obj.typ
232 }
233 Global {
234 return obj.typ
235 }
236 Module {
237 // TODO: modules don't have a type, return a placeholder
238 return Type(u16_)
239 }
240 SmartCastSelector {
241 return obj.origin
242 }
243 Type {
244 return obj
245 }
246 TypeObject {
247 return obj.typ
248 }
249 }
250}
251