v2 / vlib / v / ast / scope.v
329 lines · 300 sloc · 6.7 KB · 8cad25e0947448ebcf5a1018e850c2a21eaa9574
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. 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 ast
5
6pub const empty_scope = &Scope{
7 parent: unsafe { nil }
8}
9
10@[heap]
11pub struct Scope {
12pub mut:
13 // mut:
14 objects map[string]ScopeObject
15 struct_fields map[string]ScopeStructField
16 parent &Scope = unsafe { nil }
17 detached_from_parent bool
18 children []&Scope
19 start_pos int
20 end_pos int
21}
22
23@[unsafe]
24pub fn (s &Scope) free() {
25 if s == unsafe { nil } {
26 return
27 }
28 unsafe {
29 s.objects.free()
30 s.struct_fields.free()
31 for child in s.children {
32 child.free()
33 }
34 s.children.free()
35 }
36}
37
38/*
39pub fn new_scope(parent &Scope, start_pos int) &Scope {
40 return &Scope{
41 parent: parent
42 start_pos: start_pos
43 }
44}
45*/
46
47@[inline]
48fn (s &Scope) dont_lookup_parent() bool {
49 return s.parent == unsafe { nil } || s.detached_from_parent
50}
51
52pub fn (s &Scope) find(name string) ?ScopeObject {
53 if _unlikely_(s == unsafe { nil }) {
54 return none
55 }
56 for sc := unsafe { s }; true; sc = sc.parent {
57 if name in sc.objects {
58 return unsafe { sc.objects[name] }
59 }
60 if sc.dont_lookup_parent() {
61 break
62 }
63 }
64 return none
65}
66
67pub fn (s &Scope) find_ptr(name string) &ScopeObject {
68 if _unlikely_(s == unsafe { nil }) {
69 return 0
70 }
71 for sc := unsafe { s }; true; sc = sc.parent {
72 pobj := unsafe { &sc.objects[name] or { nil } }
73 if pobj != unsafe { nil } {
74 return pobj
75 }
76 if sc.dont_lookup_parent() {
77 break
78 }
79 }
80 return 0
81}
82
83// selector_expr: name.field_name
84pub fn (s &Scope) find_struct_field(name string, struct_type Type, field_name string) &ScopeStructField {
85 if _unlikely_(s == unsafe { nil }) {
86 return unsafe { nil }
87 }
88 k := '${name}.${field_name}'
89 for sc := unsafe { s }; true; sc = sc.parent {
90 if field := sc.struct_fields[k] {
91 if field.struct_type == struct_type {
92 return &ScopeStructField{
93 ...field
94 }
95 }
96 }
97 if sc.dont_lookup_parent() {
98 break
99 }
100 }
101 return unsafe { nil }
102}
103
104pub fn (s &Scope) find_var(name string) ?&Var {
105 obj := s.find_ptr(name)
106 if _likely_(obj != unsafe { nil }) {
107 match obj {
108 Var { return &obj }
109 else {}
110 }
111 }
112 return none
113}
114
115pub fn (s &Scope) find_global(name string) ?&GlobalField {
116 obj := s.find_ptr(name)
117 if _likely_(obj != unsafe { nil }) {
118 match obj {
119 GlobalField { return &obj }
120 else {}
121 }
122 }
123 return none
124}
125
126pub fn (s &Scope) find_const(name string) ?&ConstField {
127 obj := s.find_ptr(name)
128 if _likely_(obj != unsafe { nil }) {
129 match obj {
130 ConstField { return &obj }
131 else {}
132 }
133 }
134 return none
135}
136
137pub fn (s &Scope) known_var(name string) bool {
138 if _unlikely_(s == unsafe { nil }) {
139 return false
140 }
141 for sc := unsafe { s }; true; sc = sc.parent {
142 if name in sc.objects {
143 obj := unsafe { sc.objects[name] or { empty_scope_object } }
144 if obj is Var {
145 return true
146 }
147 }
148 if sc.dont_lookup_parent() {
149 break
150 }
151 }
152 return false
153}
154
155pub fn (s &Scope) known_global(name string) bool {
156 s.find_global(name) or { return false }
157 return true
158}
159
160pub fn (s &Scope) known_const(name string) bool {
161 s.find_const(name) or { return false }
162 return true
163}
164
165pub fn (mut s Scope) update_var_type(name string, typ Type) {
166 mut obj := unsafe { s.objects[name] }
167 if mut obj is Var {
168 if obj.typ != typ {
169 obj.typ = typ
170 }
171 }
172}
173
174pub fn (mut s Scope) update_ct_var_kind(name string, kind ComptimeVarKind) {
175 mut obj := unsafe { s.objects[name] }
176 if mut obj is Var {
177 obj.ct_type_var = kind
178 }
179}
180
181pub fn (mut s Scope) update_smartcasts(name string, typ Type, is_unwrapped bool) {
182 mut obj := unsafe { s.objects[name] }
183 if mut obj is Var {
184 obj.smartcasts = [typ]
185 obj.is_unwrapped = is_unwrapped
186 }
187}
188
189pub fn (mut s Scope) reset_smartcasts(name string) {
190 mut obj := unsafe { s.objects[name] }
191 if mut obj is Var {
192 obj.smartcasts = []
193 obj.is_unwrapped = false
194 }
195}
196
197// selector_expr: name.field_name
198pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) {
199 k := '${name}.${field.name}'
200 s.struct_fields[k] = field
201}
202
203pub fn (mut s Scope) register(obj ScopeObject) {
204 if !(obj.name == '_' || obj.name in s.objects) {
205 s.objects[obj.name] = obj
206 }
207}
208
209// returns the innermost scope containing pos
210// pub fn (s &Scope) innermost(pos int) ?&Scope {
211@[direct_array_access]
212pub fn (s &Scope) innermost(pos int) &Scope {
213 if s.contains(pos) {
214 // binary search
215 mut first := 0
216 mut last := s.children.len - 1
217 mut middle := last / 2
218 for first <= last {
219 // println('FIRST: ${first}, LAST: ${last}, LEN: ${s.children.len-1}')
220 s1 := s.children[middle]
221 if s1.end_pos < pos {
222 first = middle + 1
223 } else if s1.contains(pos) {
224 return s1.innermost(pos)
225 } else {
226 last = middle - 1
227 }
228 middle = (first + last) / 2
229 if first > last {
230 break
231 }
232 }
233 return s
234 }
235 // return none
236 return s
237}
238
239// get_all_vars extracts all current scope vars
240pub fn (s &Scope) get_all_vars() []ScopeObject {
241 if s == unsafe { nil } {
242 return []
243 }
244 mut scope_vars := []ScopeObject{}
245 for sc := unsafe { s }; true; sc = sc.parent {
246 if sc.objects.len > 0 {
247 scope_vars << sc.objects.values().filter(|it| it is Var)
248 }
249 if sc.dont_lookup_parent() {
250 break
251 }
252 }
253 return scope_vars
254}
255
256@[inline]
257pub fn (s &Scope) contains(pos int) bool {
258 return pos >= s.start_pos && pos <= s.end_pos
259}
260
261@[inline]
262pub fn (s &Scope) == (o &Scope) bool {
263 return s.start_pos == o.start_pos && s.end_pos == o.end_pos
264}
265
266pub fn (s &Scope) has_inherited_vars() bool {
267 for _, obj in s.objects {
268 if obj is Var {
269 if obj.is_inherited {
270 return true
271 }
272 }
273 }
274 return false
275}
276
277pub fn (s &Scope) is_inherited_var(var_name string) bool {
278 for _, obj in s.objects {
279 if obj is Var {
280 if obj.is_inherited && obj.name == var_name {
281 return true
282 }
283 }
284 }
285 return false
286}
287
288pub fn (sc &Scope) show(depth int, max_depth int) string {
289 mut out := ''
290 mut indent := ''
291 for _ in 0 .. depth * 4 {
292 indent += ' '
293 }
294 out += '${indent}# ${sc.start_pos} - ${sc.end_pos}\n'
295 for _, obj in sc.objects {
296 match obj {
297 ConstField { out += '${indent} * const: ${obj.name} - ${obj.typ}\n' }
298 Var { out += '${indent} * var: ${obj.name} - ${obj.typ}\n' }
299 else {}
300 }
301 }
302 for _, field in sc.struct_fields {
303 out += '${indent} * struct_field: ${field.struct_type} ${field.name} - ${field.typ}\n'
304 }
305 if max_depth == 0 || depth < max_depth - 1 {
306 for i, _ in sc.children {
307 out += sc.children[i].show(depth + 1, max_depth)
308 }
309 }
310 return out
311}
312
313pub fn (mut sc Scope) mark_var_as_used(varname string) bool {
314 mut obj := sc.find_ptr(varname)
315 if obj == unsafe { nil } {
316 return false
317 }
318 if mut obj is Var {
319 obj.is_used = true
320 return true
321 } else if obj is GlobalField {
322 return true
323 }
324 return false
325}
326
327pub fn (sc &Scope) str() string {
328 return sc.show(0, 3)
329}
330