v2 / vlib / v / parser / struct.v
1082 lines · 1065 sloc · 32.75 KB · 9f160f7679ee713a2486d9f4e2447a3a5e7e4cf6
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.errors
8import v.token
9import v.util
10
11fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
12 p.top_level_statement_start()
13 // save attributes, they will be changed later in fields
14 attrs := p.attrs
15 p.attrs = []
16 start_pos := p.tok.pos()
17 mut is_pub := p.tok.kind == .key_pub
18 mut is_shared := p.tok.kind == .key_shared
19 is_option := is_anon && p.prev_tok.kind == .question
20 if is_pub {
21 p.next()
22 }
23 if is_anon {
24 if is_shared {
25 p.register_auto_import('sync')
26 p.next()
27 }
28 is_pub = true
29 }
30 is_union := p.tok.kind == .key_union
31 if p.tok.kind == .key_struct {
32 p.next()
33 } else {
34 p.check(.key_union)
35 }
36 mut language := p.parse_language()
37 name_pos := p.tok.pos()
38 if p.inside_struct_field_decl && language == .v {
39 // anon struct/union language should keep the same language of outside
40 language = p.struct_language
41 } else {
42 old_struct_language := p.struct_language
43 p.struct_language = language
44 defer(fn) {
45 p.struct_language = old_struct_language
46 }
47 }
48 p.check_for_impure_v(language, name_pos)
49 if p.disallow_declarations_in_script_mode() {
50 return ast.StructDecl{}
51 }
52 mut name := if is_anon {
53 if is_union {
54 p.table.anon_union_counter++
55 '_VAnonUnion${p.table.anon_union_counter}'
56 } else {
57 p.table.anon_struct_counter++
58 '_VAnonStruct${p.table.anon_struct_counter}'
59 }
60 } else {
61 p.check_name()
62 }
63 if name.len == 1 && name[0].is_capital() {
64 p.error_with_pos('single letter capital names are reserved for generic template types.',
65 name_pos)
66 return ast.StructDecl{}
67 }
68 if name == 'IError' && p.mod != 'builtin' {
69 p.error_with_pos('cannot register struct `IError`, it is builtin interface type', name_pos)
70 }
71 // append module name before any type of parsing to enable recursion parsing
72 p.table.start_parsing_type(p.prepend_mod(name))
73 defer {
74 p.table.reset_parsing_type()
75 }
76 generic_types, _ := p.parse_generic_types()
77 mut pre_comments := p.eat_comments()
78 mut comments_before_key_struct := if p.pref.is_vls {
79 p.cur_comments.clone()
80 } else {
81 []
82 }
83 p.cur_comments.clear()
84 no_body := p.tok.kind != .lcbr && p.tok.kind != .key_implements
85 if language == .v && no_body {
86 p.error_with_pos('`${p.tok.lit}` lacks body', name_pos)
87 return ast.StructDecl{}
88 }
89 if name.len == 1 {
90 p.error_with_pos('struct names must have more than one character', name_pos)
91 return ast.StructDecl{}
92 }
93 if p.is_imported_symbol(name) {
94 p.error_with_pos('cannot register struct `${name}`, this type was already imported',
95 name_pos)
96 return ast.StructDecl{}
97 }
98 mut orig_name := name
99 if language == .c {
100 name = 'C.${name}'
101 orig_name = name
102 } else if language == .js {
103 name = 'JS.${name}'
104 orig_name = name
105 } else if language == .wasm {
106 name = 'WASM.${name}'
107 orig_name = name
108 } else {
109 name = p.prepend_mod(name)
110 }
111 mut ast_fields := []ast.StructField{}
112 mut fields := []ast.StructField{}
113 mut embed_types := []ast.Type{}
114 mut embeds := []ast.Embed{}
115 mut embed_field_names := []string{}
116 mut mut_pos := -1
117 mut pub_pos := -1
118 mut pub_mut_pos := -1
119 mut global_pos := -1
120 mut module_pos := -1
121 mut is_field_mut := language == .c
122 // Anonymous struct parameter fields are part of the function's call surface,
123 // so callers in other modules must be able to initialize them.
124 mut is_field_pub := language == .c || (is_anon && p.inside_fn_param)
125 mut is_field_global := false
126 mut is_implements := false
127 mut implements_types := []ast.TypeNode{cap: 3} // ast.void_type
128 mut last_line := p.prev_tok.pos().line_nr + 1
129 mut end_comments := []ast.Comment{}
130 mut has_option := false
131 if !no_body {
132 if p.tok.kind == .key_implements {
133 is_implements = true
134 for {
135 p.next()
136 type_pos := p.tok.pos()
137 implements_types << ast.TypeNode{
138 typ: p.parse_type()
139 pos: type_pos
140 }
141 if p.tok.kind != .comma {
142 break
143 }
144 }
145 }
146 p.check(.lcbr)
147 // if p.is_vls && p.tok.kind == .key_struct { // p.tok.is_key() {
148 if p.is_vls && p.tok.is_key() && !(p.tok.kind in [.key_pub, .key_mut]
149 && p.peek_tok.kind in [.colon, .key_mut]) {
150 // End parsing after `struct Foo {` in vls mode to avoid lots of junk errors
151 // If next token after { is a key, the struct wasn't finished
152 p.error('expected `}` to finish a struct definition')
153 p.should_abort = true
154 return ast.StructDecl{
155 name: name
156 }
157 }
158 pre_comments << p.eat_comments()
159 mut i := 0
160 for p.tok.kind != .rcbr {
161 mut comments := []ast.Comment{}
162 if p.tok.kind == .rcbr {
163 end_comments = p.eat_comments(same_line: true)
164 break
165 }
166 if p.tok.kind == .key_pub && p.peek_tok.kind in [.key_mut, .colon] {
167 p.next()
168 if p.tok.kind == .key_mut {
169 if pub_mut_pos != -1 {
170 p.error('redefinition of `pub mut` section')
171 return ast.StructDecl{}
172 }
173 p.next()
174 pub_mut_pos = ast_fields.len
175 is_field_pub = true
176 is_field_mut = true
177 is_field_global = false
178 } else {
179 if pub_pos != -1 {
180 p.error('redefinition of `pub` section')
181 return ast.StructDecl{}
182 }
183 pub_pos = ast_fields.len
184 is_field_pub = true
185 is_field_mut = false
186 is_field_global = false
187 }
188 p.check(.colon)
189 } else if p.tok.kind == .key_mut && p.peek_tok.kind == .colon {
190 if mut_pos != -1 {
191 p.error('redefinition of `mut` section')
192 return ast.StructDecl{}
193 }
194 p.next()
195 p.check(.colon)
196 mut_pos = ast_fields.len
197 is_field_pub = false
198 is_field_mut = true
199 is_field_global = false
200 } else if p.tok.kind == .key_mut && p.peek_tok.kind == .name
201 && p.peek_token(2).line_nr == p.tok.line_nr
202 && p.peek_token(2).kind !in [.assign, .rcbr, .semicolon] {
203 p.error_with_pos('missing `:` after `mut` in struct', p.tok.pos())
204 return ast.StructDecl{}
205 } else if p.tok.kind == .key_global && p.peek_tok.kind == .colon {
206 if global_pos != -1 {
207 p.error('redefinition of `global` section')
208 return ast.StructDecl{}
209 }
210 p.next()
211 p.check(.colon)
212 global_pos = ast_fields.len
213 is_field_pub = true
214 is_field_mut = true
215 is_field_global = true
216 } else if p.tok.kind == .key_module && p.peek_tok.kind == .colon {
217 if module_pos != -1 {
218 p.error('redefinition of `module` section')
219 return ast.StructDecl{}
220 }
221 p.next()
222 p.check(.colon)
223 module_pos = ast_fields.len
224 is_field_pub = false
225 is_field_mut = false
226 is_field_global = false
227 }
228 pre_field_comments := p.eat_comments()
229 mut next_field_comments := []ast.Comment{}
230 field_start_pos := p.tok.pos()
231 mut is_field_volatile := false
232 mut is_field_deprecated := false
233 if p.tok.kind == .key_volatile && p.peek_token(2).line_nr == p.tok.line_nr {
234 p.next()
235 is_field_volatile = true
236 }
237 is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()
238 && (p.peek_tok.line_nr != p.tok.line_nr || p.peek_tok.kind !in [.name, .amp])
239 && (p.peek_tok.kind != .lsbr || p.peek_token(2).kind != .rsbr))
240 || p.peek_tok.kind == .dot) && language == .v && p.peek_tok.kind != .key_fn
241 is_on_top := ast_fields.len == 0 && !(is_field_pub || is_field_mut || is_field_global)
242 has_prev_newline := p.has_prev_newline()
243 has_break_line := has_prev_newline || p.has_prev_line_comment_or_label()
244 mut field_name := ''
245 mut typ := ast.no_type
246 mut type_pos := token.Pos{}
247 mut field_pos := token.Pos{}
248 mut option_pos := token.Pos{}
249
250 if p.tok.kind == .rcbr {
251 if ast_fields.len > 0 {
252 ast_fields.last().next_comments << pre_field_comments
253 }
254 break
255 }
256
257 if is_embed {
258 if p.peek_tok.kind == .dot && p.peek_tok.line_nr == p.peek_token(3).line_nr
259 && p.peek_token(3).kind == .name {
260 p.error_with_pos('invalid field name', p.tok.pos())
261 return ast.StructDecl{}
262 }
263 // struct embedding
264 type_pos = p.tok.pos()
265 typ = p.parse_type()
266 comments << p.eat_comments()
267 type_pos = type_pos.extend(p.prev_tok.pos())
268 if typ.idx() == 0 {
269 // error is set in parse_type
270 return ast.StructDecl{}
271 }
272 if !is_on_top {
273 p.error_with_pos('struct embedding must be declared at the beginning of the struct body',
274 type_pos)
275 return ast.StructDecl{}
276 }
277 sym := p.table.sym(typ)
278 if typ in embed_types {
279 p.error_with_pos('cannot embed `${sym.name}` more than once', type_pos)
280 return ast.StructDecl{}
281 }
282 field_name = sym.embed_name()
283 if field_name in embed_field_names {
284 p.error_with_pos('duplicate field `${field_name}`', type_pos)
285 return ast.StructDecl{}
286 }
287 if p.tok.kind == .lsbr {
288 p.error('cannot use attributes on embedded structs')
289 }
290 embed_field_names << field_name
291 embed_types << typ
292 embeds << ast.Embed{
293 typ: typ
294 pos: type_pos
295 comments: comments
296 }
297 } else {
298 // struct field
299 field_name = p.check_name()
300 p.inside_struct_field_decl = true
301 is_anon_struct := p.tok.kind == .key_struct
302 || (p.tok.kind == .key_shared && p.peek_tok.kind == .key_struct)
303 is_anon_union := p.tok.kind == .key_union
304 || (p.tok.kind == .key_shared && p.peek_tok.kind == .key_union)
305 if is_anon_struct || is_anon_union {
306 // Anon structs
307 field_is_shared := p.tok.kind == .key_shared
308 p.anon_struct_decl = p.struct_decl(true)
309 p.anon_struct_decl.language = language
310 // Find the registered anon struct type, it was registered above in `p.struct_decl()`
311 typ = p.table.find_type_idx(p.anon_struct_decl.name)
312 if field_is_shared {
313 typ = typ.set_flag(.shared_f)
314 typ = typ.set_nr_muls(1)
315 }
316 } else {
317 start_type_pos := p.tok.pos()
318 typ = p.parse_type()
319 type_pos = start_type_pos.extend(p.prev_tok.pos())
320 }
321 p.inside_struct_field_decl = false
322 if typ.idx() == 0 {
323 // error is set in parse_type
324 return ast.StructDecl{}
325 }
326
327 // for field_name []fn, cgen will generate closure, so detect here
328 if p.file_backend_mode == .v || p.file_backend_mode == .c {
329 sym := p.table.sym(typ)
330 mut elem_kind := ast.Kind.placeholder
331 if sym.kind == .array && (sym.info is ast.Array || sym.info is ast.Alias) {
332 elem_kind = p.table.sym(sym.array_info().elem_type).kind
333 } else if sym.kind == .array_fixed
334 && (sym.info is ast.ArrayFixed || sym.info is ast.Alias) {
335 elem_kind = p.table.sym(sym.array_fixed_info().elem_type).kind
336 }
337 if elem_kind == .function {
338 p.register_auto_import('builtin.closure')
339 }
340 }
341
342 field_pos = field_start_pos.extend(p.prev_tok.pos())
343 if typ.has_option_or_result() {
344 option_pos = p.peek_token(-2).pos()
345 has_option = true
346 }
347 }
348 // Comments after type (same line)
349 prev_attrs := p.attrs
350 p.attrs = []
351 // TODO: remove once old syntax is no longer supported
352 if p.tok.kind == .lsbr {
353 p.inside_struct_attr_decl = true
354 // attrs are stored in `p.attrs`
355 p.attributes()
356 for fa in p.attrs {
357 if fa.name == 'deprecated' {
358 is_field_deprecated = true
359 }
360 }
361 p.inside_struct_attr_decl = false
362 }
363 comments << p.eat_comments(same_line: true)
364 mut default_expr := ast.empty_expr
365 mut has_default_expr := false
366 if !is_embed {
367 if p.tok.kind == .assign {
368 // Default value
369 p.next()
370 old_assign_rhs := p.inside_assign_rhs
371 p.inside_assign_rhs = true
372 default_expr = p.expr(0)
373 p.inside_assign_rhs = old_assign_rhs
374 match mut default_expr {
375 ast.EnumVal { default_expr.typ = typ }
376 // TODO: implement all types??
377 else {}
378 }
379
380 has_default_expr = true
381 comments << p.eat_comments(same_line: true)
382 }
383 if p.tok.kind == .at {
384 p.inside_struct_attr_decl = true
385 // attrs are stored in `p.attrs`
386 p.attributes()
387 for fa in p.attrs {
388 if fa.name == 'deprecated' {
389 is_field_deprecated = true
390 }
391 }
392 p.inside_struct_attr_decl = false
393 comments << p.eat_comments(same_line: true)
394 }
395 next_field_comments = p.eat_comments(follow_up: true)
396 ast_fields << ast.StructField{
397 name: field_name
398 typ: typ
399 pos: field_pos
400 type_pos: type_pos
401 option_pos: option_pos
402 pre_comments: pre_field_comments
403 comments: comments
404 next_comments: next_field_comments
405 i: i
406 default_expr: default_expr
407 has_default_expr: has_default_expr
408 has_prev_newline: has_prev_newline
409 has_break_line: has_break_line
410 attrs: p.attrs
411 is_pub: is_embed || is_field_pub
412 is_mut: is_embed || is_field_mut
413 is_global: is_field_global
414 is_volatile: is_field_volatile
415 is_deprecated: is_field_deprecated
416 anon_struct_decl: p.anon_struct_decl
417 }
418 }
419 // save embeds as table fields too, it will be used in generation phase
420 fields << ast.StructField{
421 name: field_name
422 typ: typ
423 pos: if is_embed { type_pos } else { field_pos }
424 type_pos: type_pos
425 option_pos: option_pos
426 pre_comments: pre_field_comments
427 comments: comments
428 next_comments: next_field_comments
429 i: i
430 default_expr: default_expr
431 has_default_expr: has_default_expr
432 attrs: p.attrs
433 is_pub: is_embed || is_field_pub
434 is_mut: is_embed || is_field_mut
435 is_embed: is_embed
436 is_global: is_field_global
437 is_volatile: is_field_volatile
438 is_deprecated: is_field_deprecated
439 anon_struct_decl: p.anon_struct_decl
440 }
441 p.anon_struct_decl = ast.StructDecl{}
442 p.attrs = prev_attrs
443 i++
444 }
445 p.top_level_statement_end()
446 last_line = p.tok.line_nr
447 p.check(.rcbr)
448 end_comments = p.eat_comments(same_line: true)
449 }
450 mut scoped_name := ''
451 if !is_anon && p.inside_fn && p.cur_fn_scope != unsafe { nil } {
452 scoped_name = '_${name}_${p.cur_fn_scope.start_pos}'
453 }
454 is_minify := attrs.contains('minify')
455 mut sym := ast.TypeSymbol{
456 kind: .struct
457 language: language
458 name: name
459 cname: util.no_dots(name)
460 ngname: ast.strip_generic_params(name)
461 mod: p.mod
462 info: ast.Struct{
463 scoped_name: scoped_name
464 embeds: embed_types
465 fields: fields
466 is_typedef: attrs.contains('typedef')
467 is_union: is_union
468 is_heap: attrs.contains('heap')
469 is_markused: attrs.contains('markused')
470 is_minify: is_minify
471 is_generic: generic_types.len > 0
472 generic_types: generic_types
473 attrs: attrs
474 is_anon: is_anon
475 is_shared: is_shared
476 has_option: has_option
477 name_pos: name_pos
478 }
479 is_pub: is_pub
480 is_builtin: name in ast.builtins
481 }
482 if language == .v && p.table.has_deep_child_no_ref(&sym, name) {
483 p.error_with_pos('invalid recursive struct `${orig_name}`', name_pos)
484 return ast.StructDecl{}
485 }
486 mut ret := p.table.register_sym(sym)
487 if is_anon {
488 if is_union {
489 p.table.register_anon_union(name, ret)
490 } else {
491 p.table.register_anon_struct(name, ret)
492 }
493 }
494 // allow duplicate c struct declarations
495 if ret == -1 && language != .c && !p.pref.is_fmt {
496 msg := 'cannot register struct `${name}`, another type with this name exists'
497 mut existing_sym, mut existing_idx := p.table.find_sym_and_type_idx(name)
498 if existing_idx <= 0 && name.starts_with('main.') {
499 existing_sym, existing_idx =
500 p.table.find_sym_and_type_idx(name.trim_string_left('main.'))
501 }
502 if existing_idx > 0 {
503 if existing_name_pos := existing_sym.info.get_name_pos() {
504 existing_file_path := if existing_name_pos.file_idx < 0 {
505 p.file_path
506 } else {
507 p.table.filelist[existing_name_pos.file_idx]
508 }
509 error_file_path := if name_pos.file_idx < 0 {
510 p.file_path
511 } else {
512 p.table.filelist[name_pos.file_idx]
513 }
514 p.error_with_error(errors.Error{
515 file_path: error_file_path
516 pos: name_pos
517 reporter: .parser
518 message: msg
519 details: util.formatted_error('details:',
520 'another declaration was found here', existing_file_path, existing_name_pos)
521 })
522 return ast.StructDecl{}
523 }
524 }
525 p.error_with_pos(msg, name_pos)
526 return ast.StructDecl{}
527 }
528 p.expr_mod = ''
529 struct_decl := ast.StructDecl{
530 name: name
531 scoped_name: scoped_name
532 is_pub: is_pub
533 fields: ast_fields
534 pos: start_pos.extend_with_last_line(name_pos, last_line)
535 mut_pos: mut_pos
536 pub_pos: pub_pos
537 pub_mut_pos: pub_mut_pos
538 global_pos: global_pos
539 module_pos: module_pos
540 language: language
541 is_union: is_union
542 is_option: is_option
543 is_aligned: attrs.contains('aligned')
544 attrs: if is_anon { []ast.Attr{} } else { attrs } // anon structs can't have attributes
545 pre_comments: pre_comments
546 end_comments: end_comments
547 generic_types: generic_types
548 embeds: embeds
549 is_implements: is_implements
550 implements_types: implements_types
551 }
552 if p.pref.is_vls {
553 key := 'struct_${name}'
554 mut has_decl_end_comment := false
555 if struct_decl.pre_comments.len > 0
556 && struct_decl.pre_comments[0].pos.line_nr == struct_decl.pos.line_nr {
557 // struct MyS { // MyS end_comment1
558 comments_before_key_struct << struct_decl.pre_comments[0]
559 has_decl_end_comment = true
560 }
561 val := ast.VlsInfo{
562 pos: struct_decl.pos
563 doc: p.keyword_comments_to_string(orig_name, comments_before_key_struct) +
564 p.comments_to_string(struct_decl.end_comments)
565 }
566
567 p.table.register_vls_info(key, val)
568 for i, f in ast_fields {
569 f_key := 'struct_${name}.${f.name}'
570 f_val := if i == 0 {
571 first_field_pre_comment := if has_decl_end_comment {
572 struct_decl.pre_comments[1..].clone()
573 } else {
574 struct_decl.pre_comments
575 }
576 ast.VlsInfo{
577 pos: f.pos
578 doc: p.comments_to_string(first_field_pre_comment) +
579 p.comments_to_string(f.comments)
580 }
581 } else {
582 ast.VlsInfo{
583 pos: f.pos
584 doc: p.comments_to_string(ast_fields[i - 1].next_comments) +
585 p.comments_to_string(f.comments)
586 }
587 }
588 p.table.register_vls_info(f_key, f_val)
589 }
590 }
591 return struct_decl
592}
593
594fn (mut p Parser) struct_init(typ_str string, kind ast.StructInitKind, is_option bool) ast.StructInit {
595 first_pos :=
596 (if kind == .short_syntax && p.prev_tok.kind == .lcbr { p.prev_tok } else { p.tok }).pos()
597 p.init_generic_types = []ast.Type{}
598 mut typ := if kind == .short_syntax { ast.void_type } else { p.parse_type() }
599 struct_init_generic_types := p.init_generic_types.clone()
600 if is_option {
601 typ = typ.set_flag(.option)
602 }
603 return p.struct_init_from_parts(first_pos, typ_str, typ, ast.empty_expr,
604 struct_init_generic_types, kind)
605}
606
607fn (mut p Parser) struct_init_with_type_expr(type_expr ast.Expr, kind ast.StructInitKind) ast.StructInit {
608 p.init_generic_types = []ast.Type{}
609 mut typ := ast.void_type
610 mut typ_expr := type_expr
611 match type_expr {
612 ast.TypeNode {
613 typ = type_expr.typ
614 typ_expr = ast.empty_expr
615 }
616 ast.ParExpr {
617 if type_expr.expr is ast.TypeNode {
618 typ = type_expr.expr.typ
619 typ_expr = ast.empty_expr
620 }
621 }
622 else {}
623 }
624
625 return p.struct_init_from_parts(type_expr.pos(), type_expr.str(), typ, typ_expr, []ast.Type{},
626 kind)
627}
628
629fn (mut p Parser) struct_init_from_parts(first_pos token.Pos, typ_str string, typ ast.Type, typ_expr ast.Expr, struct_init_generic_types []ast.Type, kind ast.StructInitKind) ast.StructInit {
630 p.expr_mod = ''
631 if kind != .short_syntax {
632 p.check(.lcbr)
633 }
634 pre_comments := p.eat_comments()
635 mut init_fields := []ast.StructInitField{}
636 mut i := 0
637 no_keys := p.peek_tok.kind != .colon && p.tok.kind != .rcbr && p.tok.kind != .ellipsis // `Vec{a,b,c}
638 saved_is_amp := p.is_amp
639 p.is_amp = false
640 mut update_expr := ast.empty_expr
641 mut update_expr_comments := []ast.Comment{}
642 mut has_update_expr := false
643 mut update_expr_pos := token.Pos{}
644 mut has_prev_newline := false
645 mut has_break_line := false
646 for p.tok.kind !in [.rcbr, .rpar, .eof] {
647 mut field_name := ''
648 mut expr := ast.empty_expr
649 mut field_pos := token.Pos{}
650 mut first_field_pos := token.Pos{}
651 mut prev_comments := []ast.Comment{}
652 mut end_comments := []ast.Comment{}
653 mut nline_comments := []ast.Comment{}
654 is_update_expr := init_fields.len == 0 && p.tok.kind == .ellipsis
655 if no_keys {
656 // name will be set later in checker
657 expr = p.expr(0)
658 field_pos = expr.pos()
659 first_field_pos = field_pos
660 end_comments = p.eat_comments(same_line: true)
661 } else if is_update_expr {
662 // struct updating syntax; f2 := Foo{ ...f, name: 'f2' }
663 update_expr_pos = p.tok.pos()
664 p.check(.ellipsis)
665 update_expr = p.expr(0)
666 update_expr_comments << p.eat_comments()
667 has_update_expr = true
668 } else {
669 prev_comments = p.eat_comments()
670 first_field_pos = p.tok.pos()
671 has_prev_newline = p.has_prev_newline()
672 has_break_line = has_prev_newline || p.has_prev_line_comment_or_label()
673 field_name = p.check_name()
674 if p.is_vls {
675 // In VLS mode allow unfinished struct inits without the ending }
676 // `Foo{
677 // field: name.`
678
679 if p.tok.kind != .colon {
680 unsafe {
681 goto end
682 }
683 }
684 }
685 p.check(.colon)
686 if p.tok.kind == .lcbr && typ != ast.void_type {
687 struct_sym := p.table.final_sym(p.table.unaliased_type(typ))
688 if field := struct_sym.find_field(field_name) {
689 field_sym := p.table.final_sym(p.table.unaliased_type(field.typ))
690 if field_sym.kind in [.array, .array_fixed] {
691 p.error_with_pos('cannot use `{}` for array field `${field_name}`; use `[]` instead',
692 p.tok.pos())
693 }
694 }
695 }
696 expr = p.expr(0)
697 end_comments = p.eat_comments(same_line: true)
698 last_field_pos := expr.pos()
699 field_len := if last_field_pos.len > 0 {
700 last_field_pos.pos - first_field_pos.pos + last_field_pos.len
701 } else {
702 first_field_pos.len + 1
703 }
704 field_pos = token.Pos{
705 line_nr: first_field_pos.line_nr
706 pos: first_field_pos.pos
707 len: field_len
708 col: first_field_pos.col
709 }
710 }
711 i++
712 if p.tok.kind == .comma {
713 p.next()
714 }
715 end_comments << p.eat_comments(same_line: true)
716 nline_comments << p.eat_comments(follow_up: true)
717 if !is_update_expr {
718 init_fields << ast.StructInitField{
719 name: field_name
720 expr: expr
721 pos: field_pos
722 name_pos: first_field_pos
723 pre_comments: prev_comments
724 end_comments: end_comments
725 next_comments: nline_comments
726 parent_type: typ
727 has_prev_newline: has_prev_newline
728 has_break_line: has_break_line
729 is_embed: field_name.len > 0 && field_name[0].is_capital()
730 }
731 }
732 }
733 if kind != .short_syntax {
734 p.check(.rcbr)
735 }
736 p.is_amp = saved_is_amp
737 end:
738 return ast.StructInit{
739 unresolved: typ.has_flag(.generic)
740 typ_str: typ_str
741 typ: typ
742 typ_expr: typ_expr
743 init_fields: init_fields
744 update_expr: update_expr
745 update_expr_pos: update_expr_pos
746 update_expr_comments: update_expr_comments
747 has_update_expr: has_update_expr
748 name_pos: first_pos
749 pos: first_pos.extend(if kind == .short_syntax {
750 p.tok.pos()
751 } else {
752 p.prev_tok.pos()
753 })
754 no_keys: no_keys
755 is_short_syntax: kind == .short_syntax
756 is_anon: kind == .anon
757 pre_comments: pre_comments
758 generic_types: struct_init_generic_types
759 }
760}
761
762fn (mut p Parser) interface_decl() ast.InterfaceDecl {
763 p.top_level_statement_start()
764 mut pos := p.tok.pos()
765 attrs := p.attrs
766 is_pub := p.tok.kind == .key_pub
767 if is_pub {
768 p.next()
769 }
770 p.next() // `interface`
771 language := p.parse_language()
772 name_pos := p.tok.pos()
773 mut comments_before_key_interface := if p.pref.is_vls {
774 p.cur_comments.clone()
775 } else {
776 []
777 }
778 mut pre_comment_string := ''
779 p.check_for_impure_v(language, name_pos)
780 if p.disallow_declarations_in_script_mode() {
781 return ast.InterfaceDecl{}
782 }
783 modless_name := p.check_name()
784 if modless_name.len == 1 && modless_name[0].is_capital() {
785 p.error_with_pos('single letter capital names are reserved for generic template types.',
786 name_pos)
787 return ast.InterfaceDecl{}
788 }
789 if modless_name == 'IError' && p.mod != 'builtin' {
790 p.error_with_pos('cannot register interface `IError`, it is builtin interface type',
791 name_pos)
792 }
793 mut interface_name := ''
794 if language == .js {
795 interface_name = 'JS.' + modless_name
796 } else {
797 interface_name = p.prepend_mod(modless_name)
798 }
799 generic_types, _ := p.parse_generic_types()
800 mut pre_comments := p.eat_comments()
801 p.check(.lcbr)
802 pre_comments << p.eat_comments()
803 if p.pref.is_vls {
804 pre_comment_string = if pre_comments.len > 0 && pre_comments[0].pos.line_nr == pos.line_nr {
805 // interface MyInterface { // end_comment
806 p.comments_to_string(pre_comments[1..])
807 } else {
808 p.comments_to_string(pre_comments)
809 }
810 }
811 if p.is_imported_symbol(modless_name) {
812 p.error_with_pos('cannot register interface `${interface_name}`, this type was already imported',
813 name_pos)
814 return ast.InterfaceDecl{}
815 }
816 // Declare the type
817 reg_idx := p.table.register_sym(
818 is_pub: is_pub
819 kind: .interface
820 name: interface_name
821 cname: util.no_dots(interface_name)
822 ngname: ast.strip_generic_params(interface_name)
823 mod: p.mod
824 info: ast.Interface{
825 types: []
826 is_generic: generic_types.len > 0
827 is_markused: attrs.contains('markused')
828 generic_types: generic_types
829 }
830 language: language
831 )
832 if reg_idx == -1 && !p.pref.is_fmt {
833 p.error_with_pos('cannot register interface `${interface_name}`, another type with this name exists',
834 name_pos)
835 return ast.InterfaceDecl{}
836 }
837 typ := ast.new_type(reg_idx)
838 mut ts := p.table.sym(typ)
839 mut info := ts.info as ast.Interface
840 // if methods were declared before, it's an error, ignore them
841 ts.methods = []ast.Fn{cap: 20}
842 // Parse fields or methods
843 mut fields := []ast.StructField{cap: 20}
844 mut methods := []ast.FnDecl{cap: 20}
845 mut embeds := []ast.InterfaceEmbedding{}
846 mut is_mut := false
847 mut mut_pos := -1
848 for p.tok.kind != .rcbr && p.tok.kind != .eof {
849 // check embedded interface from internal module
850 if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
851 && (p.peek_tok.line_nr != p.tok.line_nr
852 || p.peek_tok.kind !in [.name, .amp, .lsbr, .lpar]
853 || (p.peek_tok.kind == .lsbr && p.peek_tok.is_next_to(p.tok))) {
854 iface_pos := p.tok.pos()
855 mut iface_name := p.tok.lit
856 iface_type := p.parse_type()
857 if iface_name == 'JS' {
858 iface_name = p.table.sym(iface_type).name
859 }
860 comments := p.eat_comments()
861 embeds << ast.InterfaceEmbedding{
862 name: iface_name
863 typ: iface_type
864 pos: iface_pos
865 comments: comments
866 }
867 if p.tok.kind == .rcbr {
868 break
869 }
870 continue
871 }
872 // check embedded interface from external module
873 if p.tok.kind == .name && p.peek_tok.kind == .dot {
874 if p.tok.lit !in p.imports {
875 p.error_with_pos('mod `${p.tok.lit}` not imported', p.tok.pos())
876 break
877 }
878 mod_name := p.tok.lit
879 from_mod_typ := p.parse_type()
880 from_mod_name := '${mod_name}.${p.prev_tok.lit}'
881 if from_mod_name.is_lower() {
882 p.error_with_pos('the interface name need to have the pascal case',
883 p.prev_tok.pos())
884 break
885 }
886 comments := p.eat_comments()
887 embeds << ast.InterfaceEmbedding{
888 name: from_mod_name
889 typ: from_mod_typ
890 pos: p.prev_tok.pos()
891 comments: comments
892 }
893 if p.tok.kind == .rcbr {
894 break
895 }
896 continue
897 }
898
899 if p.tok.kind == .key_mut {
900 if is_mut {
901 p.error_with_pos('redefinition of `mut` section', p.tok.pos())
902 return ast.InterfaceDecl{}
903 }
904 p.next()
905 p.check(.colon)
906 is_mut = true
907 mut_pos = fields.len
908 }
909 if p.peek_tok.kind == .lsbr && p.peek_tok.is_next_to(p.tok) {
910 if generic_types.len == 0 {
911 p.error_with_pos('non-generic interface `${interface_name}` cannot define a generic method',
912 p.peek_tok.pos())
913 } else {
914 p.error_with_pos("no need to add generic type names in generic interface's method",
915 p.peek_tok.pos())
916 }
917 return ast.InterfaceDecl{}
918 }
919 mut comments := p.eat_comments()
920 if p.peek_tok.kind == .lpar {
921 // interface methods
922 method_start_pos := p.tok.pos()
923 has_prev_newline := p.has_prev_newline()
924 has_break_line := has_prev_newline || p.has_prev_line_comment_or_label()
925 line_nr := p.tok.line_nr
926 name := p.check_name()
927
928 if name in ['type_name', 'type_idx'] {
929 p.error_with_pos('cannot override built-in method `${name}`', method_start_pos)
930 return ast.InterfaceDecl{}
931 }
932 if ts.has_method(name) {
933 p.error_with_pos('duplicate method `${name}`', method_start_pos)
934 return ast.InterfaceDecl{}
935 }
936 params_t, _, is_variadic, _ :=
937 p.fn_params() // TODO: merge ast.Param and ast.Arg to avoid this
938 mut params := [
939 ast.Param{
940 name: 'x'
941 is_mut: is_mut
942 typ: typ
943 is_hidden: true
944 },
945 ]
946 params << params_t
947 mut method := ast.FnDecl{
948 name: name
949 short_name: name
950 mod: p.mod
951 params: params
952 file: p.file_path
953 return_type: ast.void_type
954 is_variadic: is_variadic
955 is_pub: true
956 pos: method_start_pos.extend(p.prev_tok.pos())
957 scope: p.scope
958 has_prev_newline: has_prev_newline
959 has_break_line: has_break_line
960 }
961 if p.tok.kind.is_start_of_type() && p.tok.line_nr == line_nr {
962 method.return_type_pos = p.tok.pos()
963 last_inside_return := p.inside_fn_return
964 p.inside_fn_return = true
965 method.return_type = p.parse_type()
966 p.inside_fn_return = last_inside_return
967 method.return_type_pos = method.return_type_pos.extend(p.tok.pos())
968 method.pos = method.pos.extend(method.return_type_pos)
969 }
970 comments << p.eat_comments(same_line: true)
971 mnext_comments := p.eat_comments(follow_up: true)
972 method.comments = comments
973 method.next_comments = mnext_comments
974 methods << method
975 tmethod := ast.Fn{
976 name: name
977 params: params
978 pos: method.pos
979 return_type: method.return_type
980 is_variadic: is_variadic
981 is_pub: true
982 is_method: true
983 receiver_type: typ
984 no_body: true
985 }
986 ts.register_method(tmethod)
987 info.methods << tmethod
988
989 if p.pref.is_vls {
990 f_key := 'fn_${p.mod}[${modless_name}]${name}'
991 f_val := ast.VlsInfo{
992 pos: method.pos
993 doc: pre_comment_string + p.comments_to_string(comments)
994 }
995 p.table.register_vls_info(f_key, f_val)
996 // use mnext_comments create next field/method's pre_comment
997 pre_comment_string = p.comments_to_string(mnext_comments)
998 }
999 } else {
1000 // interface fields
1001 field_pos := p.tok.pos()
1002 has_prev_newline := p.has_prev_newline()
1003 has_break_line := has_prev_newline || p.has_prev_line_comment_or_label()
1004 field_name := p.check_name()
1005 mut type_pos := p.tok.pos()
1006 field_typ := p.parse_type()
1007 type_pos = type_pos.extend(p.prev_tok.pos())
1008 comments << p.eat_comments(follow_up: true)
1009 fields << ast.StructField{
1010 name: field_name
1011 pos: field_pos
1012 type_pos: type_pos
1013 typ: field_typ
1014 comments: comments
1015 is_pub: true
1016 has_prev_newline: has_prev_newline
1017 has_break_line: has_break_line
1018 }
1019 info.fields << ast.StructField{
1020 name: field_name
1021 typ: field_typ
1022 is_pub: true
1023 is_mut: is_mut
1024 has_prev_newline: has_prev_newline
1025 has_break_line: has_break_line
1026 }
1027 if p.pref.is_vls {
1028 // split comments into f_end_comment and f_nxt_comment first
1029 mut f_end_comment := ast.Comment{}
1030 mut f_nxt_comment := []ast.Comment{}
1031 if comments.len > 0 && comments[0].pos.line_nr == type_pos.line_nr {
1032 f_end_comment = comments[0]
1033 f_nxt_comment = comments[1..].clone()
1034 } else {
1035 f_nxt_comment = comments.clone()
1036 }
1037 f_key := 'interface_${interface_name}.${field_name}'
1038 f_val := ast.VlsInfo{
1039 pos: field_pos
1040 doc: pre_comment_string + p.comments_to_string([f_end_comment])
1041 }
1042 p.table.register_vls_info(f_key, f_val)
1043 // use f_nxt_comment create next field/method's pre_comment
1044 pre_comment_string = p.comments_to_string(f_nxt_comment)
1045 }
1046 }
1047 }
1048 info.embeds = embeds.map(it.typ)
1049 ts.info = info
1050 p.top_level_statement_end()
1051 p.check(.rcbr)
1052 pos = pos.extend_with_last_line(p.prev_tok.pos(), p.prev_tok.line_nr)
1053 res := ast.InterfaceDecl{
1054 name: interface_name
1055 language: language
1056 typ: typ
1057 fields: fields
1058 methods: methods
1059 embeds: embeds
1060 is_pub: is_pub
1061 attrs: attrs
1062 pos: pos
1063 pre_comments: pre_comments
1064 generic_types: generic_types
1065 mut_pos: mut_pos
1066 name_pos: name_pos
1067 }
1068 p.table.register_interface(res)
1069 if p.pref.is_vls {
1070 key := 'interface_${interface_name}'
1071 if res.pre_comments.len > 0 && res.pre_comments[0].pos.line_nr == res.pos.line_nr {
1072 // interface MyInterface { // MyInterface end_comment1
1073 comments_before_key_interface << res.pre_comments[0]
1074 }
1075 val := ast.VlsInfo{
1076 pos: res.pos
1077 doc: p.keyword_comments_to_string(modless_name, comments_before_key_interface)
1078 }
1079 p.table.register_vls_info(key, val)
1080 }
1081 return res
1082}
1083