| 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. |
| 4 | module parser |
| 5 | |
| 6 | import v.ast |
| 7 | import v.token |
| 8 | |
| 9 | fn is_inferred_fixed_array_size_expr(expr ast.Expr) bool { |
| 10 | return expr is ast.RangeExpr && !expr.has_low && !expr.has_high |
| 11 | } |
| 12 | |
| 13 | fn 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 | |
| 18 | fn (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 | |
| 41 | fn (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 | |
| 63 | fn (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 | |
| 132 | fn (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 | |
| 150 | fn (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 |
| 392 | fn (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 | |
| 447 | fn (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 | |
| 465 | fn (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 | |