v2 / vlib / v / parser / containers.v
491 lines · 479 sloc · 14.35 KB · 8b5f74e6a7b52c1100cb52ef2832fd0c090fb407
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 parser
5
6import v.ast
7import v.token
8
9fn is_inferred_fixed_array_size_expr(expr ast.Expr) bool {
10 return expr is ast.RangeExpr && !expr.has_low && !expr.has_high
11}
12
13fn is_array_init_type_expr_field(name string) bool {
14 return name in ['idx', 'typ', 'unaliased_typ', 'key_type', 'value_type', 'element_type',
15 'pointee_type', 'payload_type', 'variant_types', 'indirections']
16}
17
18fn (mut p Parser) parse_fixed_array_literal_elem_type() ast.Type {
19 elem_type_pos := p.tok.pos()
20 if p.tok.kind == .name && p.tok.lit == 'byte' {
21 p.error_with_pos('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`',
22 elem_type_pos)
23 }
24 old_allow_auto_fixed_array_size := p.allow_auto_fixed_array_size
25 p.allow_auto_fixed_array_size = true
26 elem_type := p.parse_type()
27 p.allow_auto_fixed_array_size = old_allow_auto_fixed_array_size
28 if elem_type != 0 {
29 s := p.table.sym(elem_type)
30 if s.name == 'byte' {
31 p.error('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`')
32 }
33 }
34 if elem_type == ast.chan_type {
35 p.chan_type_error()
36 return 0
37 }
38 return elem_type
39}
40
41fn (mut p Parser) fixed_array_literal_type(size_expr ast.Expr, elem_type ast.Type) ast.Type {
42 mut fixed_size := 0
43 mut size_unresolved := true
44 if !is_inferred_fixed_array_size_expr(size_expr) {
45 if p.pref.is_fmt {
46 fixed_size = 987654321
47 } else {
48 mut mutable_size_expr := size_expr
49 fixed_size, size_unresolved = p.eval_array_fixed_sizes(mut mutable_size_expr)
50 }
51 }
52 if fixed_size <= 0 && !size_unresolved {
53 p.error_with_pos('fixed size cannot be zero or negative', size_expr.pos())
54 }
55 idx := p.table.find_or_register_array_fixed(elem_type, fixed_size, size_expr, false)
56 mut array_type := ast.new_type(idx)
57 if elem_type.has_flag(.generic) {
58 array_type = array_type.set_flag(.generic)
59 }
60 return array_type
61}
62
63fn (mut p Parser) parse_fixed_array_literal_values(array_type ast.Type, is_option bool) ast.ArrayInit {
64 first_pos := p.tok.pos()
65 mut last_pos := first_pos
66 raw_array_type := array_type.clear_option_and_result()
67 array_info := p.table.sym(raw_array_type).array_fixed_info()
68 mut elem_type := array_info.elem_type
69 mut exprs := []ast.Expr{}
70 mut ecmnts := [][]ast.Comment{}
71 mut pre_cmnts := []ast.Comment{}
72 p.check(.lsbr)
73 old_inside_array_lit := p.inside_array_lit
74 old_last_enum_name := p.last_enum_name
75 old_last_enum_mod := p.last_enum_mod
76 p.inside_array_lit = true
77 p.last_enum_name = ''
78 p.last_enum_mod = ''
79 pre_cmnts = p.eat_comments()
80 for p.tok.kind !in [.rsbr, .eof] {
81 exprs << if p.table.final_sym(elem_type).kind == .array_fixed && p.tok.kind == .lsbr {
82 ast.Expr(p.parse_fixed_array_literal_values(elem_type, false))
83 } else {
84 p.expr(0)
85 }
86 ecmnts << p.eat_comments()
87 if p.tok.kind == .comma {
88 p.next()
89 }
90 ecmnts.last() << p.eat_comments()
91 }
92 p.inside_array_lit = old_inside_array_lit
93 p.last_enum_name = old_last_enum_name
94 p.last_enum_mod = old_last_enum_mod
95 last_pos = p.tok.pos()
96 p.check(.rsbr)
97 if exprs.len > 0 && p.table.final_sym(elem_type).kind == .array_fixed
98 && exprs[0] is ast.ArrayInit {
99 first_expr := exprs[0] as ast.ArrayInit
100 elem_type = first_expr.typ
101 }
102 mut final_array_type := raw_array_type
103 if is_inferred_fixed_array_size_expr(array_info.size_expr) && exprs.len > 0 {
104 idx := p.table.find_or_register_array_fixed(elem_type, exprs.len, ast.empty_expr,
105 array_info.is_fn_ret)
106 final_array_type = ast.new_type(idx)
107 } else if elem_type != array_info.elem_type {
108 idx := p.table.find_or_register_array_fixed(elem_type, array_info.size,
109 array_info.size_expr, array_info.is_fn_ret)
110 final_array_type = ast.new_type(idx)
111 }
112 if is_option {
113 final_array_type = final_array_type.set_flag(.option)
114 }
115 return ast.ArrayInit{
116 pos: first_pos.extend_with_last_line(last_pos, p.prev_tok.line_nr)
117 mod: p.mod
118 ecmnts: ecmnts
119 pre_cmnts: pre_cmnts
120 is_fixed: true
121 is_option: is_option
122 has_val: true
123 exprs: exprs
124 elem_type_pos: first_pos
125 elem_type: elem_type
126 typ: final_array_type
127 literal_typ: raw_array_type
128 alias_type: ast.void_type
129 }
130}
131
132fn (p &Parser) is_array_init_elem_type_expr() bool {
133 if p.tok.kind == .key_typeof {
134 return true
135 }
136 if p.tok.kind != .name {
137 return false
138 }
139 mut offset := 1
140 mut has_type_field := false
141 for p.peek_token(offset).kind == .dot && p.peek_token(offset + 1).kind == .name {
142 if is_array_init_type_expr_field(p.peek_token(offset + 1).lit) {
143 has_type_field = true
144 }
145 offset += 2
146 }
147 return has_type_field
148}
149
150fn (mut p Parser) array_init(is_option bool, alias_array_type ast.Type) ast.ArrayInit {
151 first_pos := p.tok.pos()
152 mut last_pos := p.tok.pos()
153 mut array_type := ast.void_type
154 mut elem_type := ast.void_type
155 mut elem_type_pos := first_pos
156 mut elem_type_expr := ast.empty_expr
157 mut exprs := []ast.Expr{}
158 mut ecmnts := [][]ast.Comment{}
159 mut pre_cmnts := []ast.Comment{}
160 mut is_fixed := false
161 mut has_val := false
162 mut has_type := false
163 mut has_init := false
164 mut has_index := false
165 mut init_expr := ast.empty_expr
166 if alias_array_type == ast.void_type {
167 p.check(.lsbr)
168 if p.tok.kind == .rsbr {
169 last_pos = p.tok.pos()
170 // []typ => `[]` and `typ` must be on the same line
171 line_nr := p.tok.line_nr
172 p.next()
173 // []string
174 is_elem_type_expr := p.is_array_init_elem_type_expr()
175 if (p.tok.kind in [.name, .amp, .lsbr, .lpar, .question, .key_shared, .not]
176 || is_elem_type_expr) && p.tok.line_nr == line_nr {
177 elem_type_pos = p.tok.pos()
178 if is_elem_type_expr {
179 old_inside_array_init_type_expr := p.inside_array_init_type_expr
180 p.inside_array_init_type_expr = true
181 elem_type_expr = p.expr(0)
182 p.inside_array_init_type_expr = old_inside_array_init_type_expr
183 has_type = true
184 } else {
185 elem_type = p.parse_type()
186 // this is set here because it's a known type, others could be the
187 // result of expr so we do those in checker
188 if elem_type != 0 {
189 if elem_type.has_flag(.result) {
190 p.error_with_pos('arrays do not support storing Result values',
191 elem_type_pos)
192 }
193 idx := p.table.find_or_register_array(elem_type)
194 if elem_type.has_flag(.generic) {
195 array_type = ast.new_type(idx).set_flag(.generic)
196 } else {
197 array_type = ast.new_type(idx)
198 }
199 if is_option {
200 array_type = array_type.set_flag(.option)
201 }
202 has_type = true
203 } else {
204 last_pos = p.tok.pos()
205 }
206 }
207 }
208 last_pos = p.tok.pos()
209 } else {
210 // [1,2,3] or [const]u8
211 old_inside_array_lit := p.inside_array_lit
212 old_last_enum_name := p.last_enum_name
213 old_last_enum_mod := p.last_enum_mod
214 p.inside_array_lit = true
215 p.last_enum_name = ''
216 p.last_enum_mod = ''
217 pre_cmnts = p.eat_comments()
218 for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ {
219 exprs << if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr {
220 ast.Expr(ast.RangeExpr{
221 pos: p.tok.pos()
222 low: ast.empty_expr
223 high: ast.empty_expr
224 })
225 } else {
226 p.expr(0)
227 }
228 if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr {
229 p.next()
230 }
231 ecmnts << p.eat_comments()
232 if p.tok.kind == .comma {
233 p.next()
234 }
235 ecmnts.last() << p.eat_comments()
236 }
237 p.inside_array_lit = old_inside_array_lit
238 p.last_enum_name = old_last_enum_name
239 p.last_enum_mod = old_last_enum_mod
240 line_nr := p.tok.line_nr
241 last_pos = p.tok.pos()
242 p.check(.rsbr)
243 if exprs.len == 1 && p.tok.line_nr == line_nr
244 && (p.tok.kind in [.name, .amp, .lpar, .question, .key_shared]
245 || (p.tok.kind == .lsbr && p.is_array_type())) {
246 // [100]u8{} or [100]u8[1 2 3]
247 elem_type = p.parse_fixed_array_literal_elem_type()
248 last_pos = p.tok.pos()
249 is_fixed = true
250 if p.tok.kind == .lsbr {
251 array_type = p.fixed_array_literal_type(exprs[0], elem_type)
252 return p.parse_fixed_array_literal_values(array_type, is_option)
253 } else if p.tok.kind == .lcbr {
254 if is_inferred_fixed_array_size_expr(exprs[0]) {
255 p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`',
256 first_pos.extend(last_pos))
257 return ast.ArrayInit{}
258 }
259 p.next()
260 if p.tok.kind != .rcbr {
261 pos := p.tok.pos()
262 n := p.check_name()
263 if n != 'init' {
264 if is_fixed {
265 p.error_with_pos('`len` and `cap` are invalid attributes for fixed array dimension',
266 pos)
267 } else {
268 p.error_with_pos('expected `init:`, not `${n}`', pos)
269 }
270 return ast.ArrayInit{}
271 }
272 p.check(.colon)
273 has_init = true
274 has_index = p.handle_index_variable(mut init_expr)
275 }
276 last_pos = p.tok.pos()
277 p.check(.rcbr)
278 array_type = ast.void_type
279 } else {
280 if is_inferred_fixed_array_size_expr(exprs[0]) {
281 p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`',
282 first_pos.extend(last_pos))
283 return ast.ArrayInit{}
284 }
285 array_type = ast.void_type
286 modifier := if is_option { '?' } else { '' }
287 p.warn_with_pos('use e.g. `x := ${modifier}[1]Type{}` instead of `x := ${modifier}[1]Type`',
288 first_pos.extend(last_pos))
289 }
290 } else {
291 if p.tok.kind == .not {
292 last_pos = p.tok.pos()
293 is_fixed = true
294 has_val = true
295 if exprs.len == 1 && p.tok.line_nr == line_nr && p.is_array_type() {
296 p.error('fixed arrays do not support storing Result values')
297 } else {
298 p.next()
299 }
300 }
301 if p.tok.kind == .not && p.tok.line_nr == p.prev_tok.line_nr {
302 last_pos = p.tok.pos()
303 p.error_with_pos('use e.g. `[1, 2, 3]!` instead of `[1, 2, 3]!!`', last_pos)
304 p.next()
305 }
306 }
307 }
308 if exprs.len == 0 && p.tok.kind != .lcbr && has_type && !p.inside_array_init_type_expr {
309 if !p.pref.is_fmt {
310 modifier := if is_option { '?' } else { '' }
311 p.warn_with_pos('use `x := ${modifier}[]Type{}` instead of `x := ${modifier}[]Type`',
312 first_pos.extend(last_pos))
313 }
314 }
315 } else {
316 array_type = (p.table.sym(alias_array_type).info as ast.Alias).parent_type
317 elem_type = p.table.sym(array_type).array_info().elem_type
318 has_type = true
319 p.next()
320 }
321 mut has_len := false
322 mut has_cap := false
323 mut len_expr := ast.empty_expr
324 mut cap_expr := ast.empty_expr
325 mut attr_pos := token.Pos{}
326 if p.tok.kind == .lcbr && exprs.len == 0 && has_type {
327 // `[]int{ len: 10, cap: 100}` syntax
328 p.next()
329 for p.tok.kind != .rcbr {
330 attr_pos = p.tok.pos()
331 key := p.check_name()
332 p.check(.colon)
333 if is_option {
334 p.error('Option array cannot have initializers')
335 }
336 match key {
337 'len' {
338 has_len = true
339 len_expr = p.expr(0)
340 }
341 'cap' {
342 has_cap = true
343 cap_expr = p.expr(0)
344 }
345 'init' {
346 has_init = true
347 has_index = p.handle_index_variable(mut init_expr)
348 }
349 else {
350 p.error_with_pos('wrong field `${key}`, expecting `len`, `cap`, or `init`',
351 attr_pos)
352 return ast.ArrayInit{}
353 }
354 }
355
356 if p.tok.kind != .rcbr {
357 p.check(.comma)
358 }
359 }
360 p.check(.rcbr)
361 if has_init && !has_len {
362 p.error_with_pos('cannot use `init` attribute unless `len` attribute is also provided',
363 attr_pos)
364 }
365 }
366 pos := first_pos.extend_with_last_line(last_pos, p.prev_tok.line_nr)
367 return ast.ArrayInit{
368 is_fixed: is_fixed
369 has_val: has_val
370 mod: p.mod
371 elem_type: elem_type
372 typ: array_type
373 alias_type: alias_array_type
374 exprs: exprs
375 ecmnts: ecmnts
376 pre_cmnts: pre_cmnts
377 elem_type_expr: elem_type_expr
378 pos: pos
379 elem_type_pos: elem_type_pos
380 has_len: has_len
381 len_expr: len_expr
382 has_cap: has_cap
383 has_init: has_init
384 has_index: has_index
385 cap_expr: cap_expr
386 init_expr: init_expr
387 is_option: is_option
388 }
389}
390
391// parse tokens between braces
392fn (mut p Parser) map_init() ast.MapInit {
393 old_inside_map_init := p.inside_map_init
394 p.inside_map_init = true
395 defer {
396 p.inside_map_init = old_inside_map_init
397 }
398 first_pos := p.prev_tok.pos()
399 mut keys := []ast.Expr{}
400 mut vals := []ast.Expr{}
401 mut comments := [][]ast.Comment{}
402 mut has_update_expr := false
403 mut update_expr := ast.empty_expr
404 mut update_expr_comments := []ast.Comment{}
405 mut update_expr_pos := token.Pos{}
406 pre_cmnts := p.eat_comments()
407 if p.tok.kind == .ellipsis {
408 // updating init { ...base_map, 'b': 44, 'c': 55 }
409 has_update_expr = true
410 p.check(.ellipsis)
411 update_expr = p.expr(0)
412 update_expr_pos = update_expr.pos()
413 if p.tok.kind == .comma {
414 p.next()
415 }
416 update_expr_comments << p.eat_comments(same_line: true)
417 }
418 for p.tok.kind !in [.rcbr, .eof] {
419 if p.tok.kind == .name && p.tok.lit in ['r', 'c', 'js'] {
420 key := p.string_expr()
421 keys << key
422 } else {
423 key := p.expr(0)
424 keys << key
425 }
426 p.check(.colon)
427 val := p.expr(0)
428 vals << val
429 if p.tok.kind == .comma {
430 p.next()
431 }
432 comments << p.eat_comments()
433 }
434 return ast.MapInit{
435 keys: keys
436 vals: vals
437 pos: first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
438 comments: comments
439 pre_cmnts: pre_cmnts
440 has_update_expr: has_update_expr
441 update_expr: update_expr
442 update_expr_pos: update_expr_pos
443 update_expr_comments: update_expr_comments
444 }
445}
446
447fn (mut p Parser) scope_register_index() {
448 p.scope.objects['index'] = ast.Var{ // override index variable if it already exist, else create index variable
449 name: 'index'
450 pos: p.tok.pos()
451 typ: ast.int_type
452 is_mut: false
453 is_used: false
454 is_index_var: true
455 }
456 p.scope.objects['it'] = ast.Var{ // it is now deprecated, will be removed in future stable release
457 name: 'it'
458 pos: p.tok.pos()
459 typ: ast.int_type
460 is_mut: false
461 is_used: false
462 }
463}
464
465fn (mut p Parser) handle_index_variable(mut default_expr ast.Expr) bool {
466 mut has_index := false
467 p.open_scope()
468 defer {
469 p.close_scope()
470 }
471 p.scope_register_index()
472 default_expr = p.expr(0)
473 if var := p.scope.find_var('index') {
474 mut variable := unsafe { var }
475 is_used := variable.is_used
476 variable.is_used = true
477 has_index = is_used
478 }
479 if var := p.scope.find_var('it') { // FIXME: Remove this block when `it` is forbidden
480 mut variable := unsafe { var }
481 is_used := variable.is_used
482 if is_used {
483 p.warn('variable `it` in array initialization will soon be replaced with `index`')
484 }
485 variable.is_used = true
486 if !has_index {
487 has_index = is_used
488 }
489 }
490 return has_index
491}
492