vxx2 / vlib / v / parser / containers.v
514 lines · 502 sloc · 15.29 KB · 16e9dff060e9e7db1a6fdd8d171127734d6a6849
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 mut has_update_expr := false
167 mut update_expr := ast.empty_expr
168 mut update_expr_pos := token.Pos{}
169 mut update_expr_comments := []ast.Comment{}
170 if alias_array_type == ast.void_type {
171 p.check(.lsbr)
172 if p.tok.kind == .rsbr {
173 last_pos = p.tok.pos()
174 // []typ => `[]` and `typ` must be on the same line
175 line_nr := p.tok.line_nr
176 p.next()
177 // []string
178 is_elem_type_expr := p.is_array_init_elem_type_expr()
179 if (p.tok.kind in [.name, .amp, .lsbr, .lpar, .question, .key_shared, .not]
180 || is_elem_type_expr) && p.tok.line_nr == line_nr {
181 elem_type_pos = p.tok.pos()
182 if is_elem_type_expr {
183 old_inside_array_init_type_expr := p.inside_array_init_type_expr
184 p.inside_array_init_type_expr = true
185 elem_type_expr = p.expr(0)
186 p.inside_array_init_type_expr = old_inside_array_init_type_expr
187 has_type = true
188 } else {
189 elem_type = p.parse_type()
190 // this is set here because it's a known type, others could be the
191 // result of expr so we do those in checker
192 if elem_type != 0 {
193 if elem_type.has_flag(.result) {
194 p.error_with_pos('arrays do not support storing Result values',
195 elem_type_pos)
196 }
197 idx := p.table.find_or_register_array(elem_type)
198 if elem_type.has_flag(.generic) {
199 array_type = ast.new_type(idx).set_flag(.generic)
200 } else {
201 array_type = ast.new_type(idx)
202 }
203 if is_option {
204 array_type = array_type.set_flag(.option)
205 }
206 has_type = true
207 } else {
208 last_pos = p.tok.pos()
209 }
210 }
211 }
212 last_pos = p.tok.pos()
213 } else {
214 // [1,2,3] or [const]u8
215 old_inside_array_lit := p.inside_array_lit
216 old_last_enum_name := p.last_enum_name
217 old_last_enum_mod := p.last_enum_mod
218 p.inside_array_lit = true
219 p.last_enum_name = ''
220 p.last_enum_mod = ''
221 pre_cmnts = p.eat_comments()
222 if p.tok.kind == .ellipsis {
223 // updating init `[...base_array, 3, 4]`
224 has_update_expr = true
225 p.check(.ellipsis)
226 update_expr = p.expr(0)
227 update_expr_pos = update_expr.pos()
228 // Eat comments that may sit between the spread base and the
229 // separating comma, e.g. `[...base /* keep */, 3]`.
230 update_expr_comments << p.eat_comments(same_line: true)
231 if p.tok.kind == .comma {
232 p.next()
233 }
234 update_expr_comments << p.eat_comments(same_line: true)
235 update_expr_comments << p.eat_comments()
236 }
237 for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ {
238 exprs << if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr {
239 ast.Expr(ast.RangeExpr{
240 pos: p.tok.pos()
241 low: ast.empty_expr
242 high: ast.empty_expr
243 })
244 } else {
245 p.expr(0)
246 }
247 if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr {
248 p.next()
249 }
250 ecmnts << p.eat_comments()
251 if p.tok.kind == .comma {
252 p.next()
253 }
254 ecmnts.last() << p.eat_comments()
255 }
256 p.inside_array_lit = old_inside_array_lit
257 p.last_enum_name = old_last_enum_name
258 p.last_enum_mod = old_last_enum_mod
259 line_nr := p.tok.line_nr
260 last_pos = p.tok.pos()
261 p.check(.rsbr)
262 if exprs.len == 1 && p.tok.line_nr == line_nr
263 && (p.tok.kind in [.name, .amp, .lpar, .question, .key_shared]
264 || (p.tok.kind == .lsbr && p.is_array_type())) {
265 // [100]u8{} or [100]u8[1 2 3]
266 elem_type = p.parse_fixed_array_literal_elem_type()
267 last_pos = p.tok.pos()
268 is_fixed = true
269 if p.tok.kind == .lsbr {
270 array_type = p.fixed_array_literal_type(exprs[0], elem_type)
271 return p.parse_fixed_array_literal_values(array_type, is_option)
272 } else if p.tok.kind == .lcbr {
273 if is_inferred_fixed_array_size_expr(exprs[0]) {
274 p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`',
275 first_pos.extend(last_pos))
276 return ast.ArrayInit{}
277 }
278 p.next()
279 if p.tok.kind != .rcbr {
280 pos := p.tok.pos()
281 n := p.check_name()
282 if n != 'init' {
283 if is_fixed {
284 p.error_with_pos('`len` and `cap` are invalid attributes for fixed array dimension',
285 pos)
286 } else {
287 p.error_with_pos('expected `init:`, not `${n}`', pos)
288 }
289 return ast.ArrayInit{}
290 }
291 p.check(.colon)
292 has_init = true
293 has_index = p.handle_index_variable(mut init_expr)
294 }
295 last_pos = p.tok.pos()
296 p.check(.rcbr)
297 array_type = ast.void_type
298 } else {
299 if is_inferred_fixed_array_size_expr(exprs[0]) {
300 p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`',
301 first_pos.extend(last_pos))
302 return ast.ArrayInit{}
303 }
304 array_type = ast.void_type
305 modifier := if is_option { '?' } else { '' }
306 p.warn_with_pos('use e.g. `x := ${modifier}[1]Type{}` instead of `x := ${modifier}[1]Type`',
307 first_pos.extend(last_pos))
308 }
309 } else {
310 if p.tok.kind == .not {
311 last_pos = p.tok.pos()
312 is_fixed = true
313 has_val = true
314 if exprs.len == 1 && p.tok.line_nr == line_nr && p.is_array_type() {
315 p.error('fixed arrays do not support storing Result values')
316 } else {
317 p.next()
318 }
319 }
320 if p.tok.kind == .not && p.tok.line_nr == p.prev_tok.line_nr {
321 last_pos = p.tok.pos()
322 p.error_with_pos('use e.g. `[1, 2, 3]!` instead of `[1, 2, 3]!!`', last_pos)
323 p.next()
324 }
325 }
326 }
327 if exprs.len == 0 && p.tok.kind != .lcbr && has_type && !p.inside_array_init_type_expr {
328 if !p.pref.is_fmt {
329 modifier := if is_option { '?' } else { '' }
330 p.warn_with_pos('use `x := ${modifier}[]Type{}` instead of `x := ${modifier}[]Type`',
331 first_pos.extend(last_pos))
332 }
333 }
334 } else {
335 array_type = (p.table.sym(alias_array_type).info as ast.Alias).parent_type
336 elem_type = p.table.sym(array_type).array_info().elem_type
337 has_type = true
338 p.next()
339 }
340 mut has_len := false
341 mut has_cap := false
342 mut len_expr := ast.empty_expr
343 mut cap_expr := ast.empty_expr
344 mut attr_pos := token.Pos{}
345 if p.tok.kind == .lcbr && exprs.len == 0 && has_type {
346 // `[]int{ len: 10, cap: 100}` syntax
347 p.next()
348 for p.tok.kind != .rcbr {
349 attr_pos = p.tok.pos()
350 key := p.check_name()
351 p.check(.colon)
352 if is_option {
353 p.error('Option array cannot have initializers')
354 }
355 match key {
356 'len' {
357 has_len = true
358 len_expr = p.expr(0)
359 }
360 'cap' {
361 has_cap = true
362 cap_expr = p.expr(0)
363 }
364 'init' {
365 has_init = true
366 has_index = p.handle_index_variable(mut init_expr)
367 }
368 else {
369 p.error_with_pos('wrong field `${key}`, expecting `len`, `cap`, or `init`',
370 attr_pos)
371 return ast.ArrayInit{}
372 }
373 }
374
375 if p.tok.kind != .rcbr {
376 p.check(.comma)
377 }
378 }
379 p.check(.rcbr)
380 if has_init && !has_len {
381 p.error_with_pos('cannot use `init` attribute unless `len` attribute is also provided',
382 attr_pos)
383 }
384 }
385 pos := first_pos.extend_with_last_line(last_pos, p.prev_tok.line_nr)
386 return ast.ArrayInit{
387 is_fixed: is_fixed
388 has_val: has_val
389 mod: p.mod
390 elem_type: elem_type
391 typ: array_type
392 alias_type: alias_array_type
393 exprs: exprs
394 ecmnts: ecmnts
395 pre_cmnts: pre_cmnts
396 elem_type_expr: elem_type_expr
397 pos: pos
398 elem_type_pos: elem_type_pos
399 has_len: has_len
400 len_expr: len_expr
401 has_cap: has_cap
402 has_init: has_init
403 has_index: has_index
404 cap_expr: cap_expr
405 init_expr: init_expr
406 is_option: is_option
407 has_update_expr: has_update_expr
408 update_expr: update_expr
409 update_expr_pos: update_expr_pos
410 update_expr_comments: update_expr_comments
411 }
412}
413
414// parse tokens between braces
415fn (mut p Parser) map_init() ast.MapInit {
416 old_inside_map_init := p.inside_map_init
417 p.inside_map_init = true
418 defer {
419 p.inside_map_init = old_inside_map_init
420 }
421 first_pos := p.prev_tok.pos()
422 mut keys := []ast.Expr{}
423 mut vals := []ast.Expr{}
424 mut comments := [][]ast.Comment{}
425 mut has_update_expr := false
426 mut update_expr := ast.empty_expr
427 mut update_expr_comments := []ast.Comment{}
428 mut update_expr_pos := token.Pos{}
429 pre_cmnts := p.eat_comments()
430 if p.tok.kind == .ellipsis {
431 // updating init { ...base_map, 'b': 44, 'c': 55 }
432 has_update_expr = true
433 p.check(.ellipsis)
434 update_expr = p.expr(0)
435 update_expr_pos = update_expr.pos()
436 if p.tok.kind == .comma {
437 p.next()
438 }
439 update_expr_comments << p.eat_comments(same_line: true)
440 }
441 for p.tok.kind !in [.rcbr, .eof] {
442 if p.tok.kind == .name && p.tok.lit in ['r', 'c', 'js'] {
443 key := p.string_expr()
444 keys << key
445 } else {
446 key := p.expr(0)
447 keys << key
448 }
449 p.check(.colon)
450 val := p.expr(0)
451 vals << val
452 if p.tok.kind == .comma {
453 p.next()
454 }
455 comments << p.eat_comments()
456 }
457 return ast.MapInit{
458 keys: keys
459 vals: vals
460 pos: first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
461 comments: comments
462 pre_cmnts: pre_cmnts
463 has_update_expr: has_update_expr
464 update_expr: update_expr
465 update_expr_pos: update_expr_pos
466 update_expr_comments: update_expr_comments
467 }
468}
469
470fn (mut p Parser) scope_register_index() {
471 p.scope.objects['index'] = ast.Var{ // override index variable if it already exist, else create index variable
472 name: 'index'
473 pos: p.tok.pos()
474 typ: ast.int_type
475 is_mut: false
476 is_used: false
477 is_index_var: true
478 }
479 p.scope.objects['it'] = ast.Var{ // it is now deprecated, will be removed in future stable release
480 name: 'it'
481 pos: p.tok.pos()
482 typ: ast.int_type
483 is_mut: false
484 is_used: false
485 }
486}
487
488fn (mut p Parser) handle_index_variable(mut default_expr ast.Expr) bool {
489 mut has_index := false
490 p.open_scope()
491 defer {
492 p.close_scope()
493 }
494 p.scope_register_index()
495 default_expr = p.expr(0)
496 if var := p.scope.find_var('index') {
497 mut variable := unsafe { var }
498 is_used := variable.is_used
499 variable.is_used = true
500 has_index = is_used
501 }
502 if var := p.scope.find_var('it') { // FIXME: Remove this block when `it` is forbidden
503 mut variable := unsafe { var }
504 is_used := variable.is_used
505 if is_used {
506 p.warn('variable `it` in array initialization will soon be replaced with `index`')
507 }
508 variable.is_used = true
509 if !has_index {
510 has_index = is_used
511 }
512 }
513 return has_index
514}
515