vxx / vlib / v3 / parser / parser.v
5736 lines · 5524 sloc · 122.71 KB
Raw
1module parser
2
3import os
4import strings
5import v3.flat
6import v3.pref
7import v3.scanner
8import v3.token
9
10// C.open declares the C open symbol used by parser.
11fn C.open(charptr, int, int) int
12
13// C.read declares the C read symbol used by parser.
14fn C.read(int, voidptr, int) int
15
16// C.close declares the C close symbol used by parser.
17fn C.close(int) int
18
19// C.malloc declares the C malloc symbol used by parser.
20fn C.malloc(int) &u8
21
22const max_source_file_size = 8388608
23
24// Parser represents parser data used by parser.
25pub struct Parser {
26 prefs &pref.Preferences
27mut:
28 s scanner.Scanner
29 tok token.Token
30 lit string
31 tok_pos int
32 prev_tok token.Token
33 peek_tok token.Token = .eof
34 peek_lit string
35 peek_pos int
36 has_peek bool
37 cur_file string
38 cur_module string
39 cur_fn string
40 pending_flag bool
41 pending_params bool
42 pending_export string
43 skip_next_decl bool
44 disable_fn_body bool
45 in_for_container bool
46pub mut:
47 a &flat.FlatAst = unsafe { nil }
48 parsed_v_files int
49}
50
51// new creates a Parser value for parser.
52pub fn Parser.new(prefs &pref.Preferences) &Parser {
53 return &Parser{
54 prefs: unsafe { prefs }
55 s: scanner.new_scanner(prefs, .normal)
56 a: &flat.FlatAst{
57 nodes: []flat.Node{cap: 256}
58 children: []flat.NodeId{cap: 512}
59 disabled_fns: map[string]bool{}
60 export_fn_names: map[string]string{}
61 }
62 }
63}
64
65// parse_file reads parse file input for parser.
66pub fn (mut p Parser) parse_file(path string) &flat.FlatAst {
67 p.parse_into(path)
68 return p.a
69}
70
71// parse_files reads parse files input for parser.
72pub fn (mut p Parser) parse_files(paths []string) &flat.FlatAst {
73 for path in paths {
74 p.parse_into(path)
75 }
76 return p.a
77}
78
79// parse_into reads parse into input for parser.
80pub fn (mut p Parser) parse_into(path string) {
81 p.cur_file = path
82 p.cur_module = ''
83 p.cur_fn = ''
84 p.pending_flag = false
85 p.pending_params = false
86 p.pending_export = ''
87 p.skip_next_decl = false
88 p.disable_fn_body = false
89 p.in_for_container = false
90 // File marker before content so import resolver can track source files
91 p.a.add_node(flat.Node{
92 kind: .file
93 value: path
94 })
95 src := read_source_file_raw(path)
96 if src.len == 0 {
97 // An empty but existing `.v` file is valid (declares nothing); only a
98 // genuine read failure (missing file) is an error worth reporting.
99 if !os.exists(path) {
100 eprintln('error reading ${path}')
101 }
102 return
103 }
104 if path.ends_with('.v') {
105 p.parsed_v_files++
106 }
107 mut file_set := token.FileSet.new()
108 file := file_set.add_file(path, -1, src.len)
109 p.s.init(file, src)
110 p.next()
111
112 mut ids := []flat.NodeId{}
113 for p.tok != .eof {
114 if p.tok == .semicolon {
115 p.next()
116 continue
117 }
118 if p.tok == .key_module {
119 p.next()
120 p.cur_module = p.lit
121 mod_id := p.a.add_node(flat.Node{
122 kind: .module_decl
123 value: p.lit
124 })
125 ids << mod_id
126 p.next()
127 if p.tok == .semicolon || p.lit == '' {
128 p.next()
129 }
130 continue
131 }
132 id := p.top_level_stmt()
133 if int(id) >= 0 {
134 ids << id
135 }
136 }
137 start := p.add_children(ids)
138 p.a.add_node(flat.Node{
139 kind: .file
140 value: path
141 children_start: start
142 children_count: flat.child_count(ids.len)
143 })
144}
145
146// read_source_file_raw reads read source file raw input for parser.
147fn read_source_file_raw(path string) string {
148 cpath := cstring_from_vstring(path)
149 fd := C.open(cpath, 0, 0)
150 if fd < 0 {
151 return ''
152 }
153 size := os.file_size(path)
154 if size == 0 {
155 C.close(fd)
156 return ''
157 }
158 expected := if size > max_source_file_size { max_source_file_size } else { int(size) }
159 buf := C.malloc(expected + 1)
160 mut total := 0
161 for total < expected {
162 nread := C.read(fd, unsafe { buf + total }, expected - total)
163 if nread <= 0 {
164 break
165 }
166 total += nread
167 }
168 C.close(fd)
169 if total <= 0 {
170 return ''
171 }
172 unsafe {
173 return tos(buf, total)
174 }
175}
176
177// cstring_from_vstring converts cstring from vstring data for parser.
178fn cstring_from_vstring(s string) &u8 {
179 buf := C.malloc(s.len + 1)
180 unsafe {
181 for i in 0 .. s.len {
182 buf[i] = s[i]
183 }
184 buf[s.len] = 0
185 }
186 return buf
187}
188
189// vmod_root_for_file supports vmod root for file handling for parser.
190fn vmod_root_for_file(path string) string {
191 mut dir := if path.len > 0 { os.dir(path) } else { os.getwd() }
192 if dir.len == 0 {
193 dir = os.getwd()
194 }
195 original_dir := dir
196 for {
197 if os.exists(os.join_path(dir, 'v.mod')) {
198 return dir
199 }
200 parent := os.dir(dir)
201 if parent == dir || parent.len == 0 {
202 return original_dir
203 }
204 dir = parent
205 }
206 return dir
207}
208
209// next supports next handling for Parser.
210fn (mut p Parser) next() {
211 p.prev_tok = p.tok
212 if p.has_peek {
213 p.tok = p.peek_tok
214 p.lit = p.peek_lit
215 p.tok_pos = p.peek_pos
216 p.has_peek = false
217 return
218 }
219 p.tok = p.s.scan()
220 p.lit = p.s.lit
221 p.tok_pos = p.s.pos
222 for p.tok == .comment {
223 p.tok = p.s.scan()
224 p.lit = p.s.lit
225 p.tok_pos = p.s.pos
226 }
227}
228
229// peek supports peek handling for Parser.
230fn (mut p Parser) peek() token.Token {
231 if !p.has_peek {
232 p.peek_tok = p.s.scan()
233 p.peek_lit = p.s.lit
234 p.peek_pos = p.s.pos
235 for p.peek_tok == .comment {
236 p.peek_tok = p.s.scan()
237 p.peek_lit = p.s.lit
238 p.peek_pos = p.s.pos
239 }
240 p.has_peek = true
241 }
242 return p.peek_tok
243}
244
245// peek_is supports peek is handling for Parser.
246fn (mut p Parser) peek_is(tok token.Token) bool {
247 return p.peek() == tok
248}
249
250fn (mut p Parser) check(expected token.Token) {
251 if p.tok == expected {
252 p.next()
253 }
254}
255
256fn (mut p Parser) expect(expected token.Token) string {
257 lit := p.lit
258 if p.tok != expected {
259 eprintln('expected ${expected}, got ${p.tok} "${p.lit}"')
260 }
261 p.next()
262 return lit
263}
264
265fn (mut p Parser) expect_name() string {
266 name := p.lit
267 if p.tok != .name && p.lit.len == 0 {
268 eprintln('expected name, got ${p.tok} "${p.lit}"')
269 }
270 p.next()
271 return name
272}
273
274fn (mut p Parser) expect_name_or_keyword() string {
275 name := p.lit
276 p.next()
277 return name
278}
279
280fn token_id_is_infix(tv int) bool {
281 return tv == 0 || tv == 1 || tv == 3 || tv == 13 || tv == 20 || tv == 21 || tv == 22 || tv == 44
282 || tv == 46 || tv == 74 || tv == 75 || tv == 77 || tv == 80 || tv == 81 || tv == 83
283 || tv == 85 || tv == 88 || tv == 90 || tv == 91 || tv == 94 || tv == 95 || tv == 99
284 || tv == 101 || tv == 109
285}
286
287fn token_is_infix(tok token.Token) bool {
288 return token_id_is_infix(int(tok))
289}
290
291fn token_id_is_postfix(tv int) bool {
292 return tv == 11 || tv == 24
293}
294
295fn token_is_postfix(tok token.Token) bool {
296 return token_id_is_postfix(int(tok))
297}
298
299fn token_id_is_assignment(tv int) bool {
300 return tv == 2 || tv == 4 || tv == 12 || tv == 14 || tv == 76 || tv == 82 || tv == 84
301 || tv == 86 || tv == 93 || tv == 96 || tv == 100 || tv == 102 || tv == 110
302}
303
304fn token_is_assignment(tok token.Token) bool {
305 return token_id_is_assignment(int(tok))
306}
307
308fn token_id_left_binding_power(tv int) token.BindingPower {
309 if tv == 77 {
310 return token.BindingPower.logical_or
311 }
312 if tv == 1 {
313 return token.BindingPower.logical_and
314 }
315 if tv == 20 || tv == 88 || tv == 80 || tv == 74 || tv == 22 || tv == 21 || tv == 44 || tv == 90
316 || tv == 46 || tv == 91 {
317 return token.BindingPower.compare
318 }
319 if tv == 94 {
320 return token.BindingPower.bit_or
321 }
322 if tv == 109 {
323 return token.BindingPower.bit_xor
324 }
325 if tv == 75 || tv == 99 || tv == 101 {
326 return token.BindingPower.shift
327 }
328 if tv == 95 || tv == 81 {
329 return token.BindingPower.add
330 }
331 if tv == 85 || tv == 13 || tv == 83 || tv == 0 {
332 return token.BindingPower.product
333 }
334 return token.BindingPower.lowest
335}
336
337fn token_left_binding_power(tok token.Token) token.BindingPower {
338 return token_id_left_binding_power(int(tok))
339}
340
341fn token_id_right_binding_power(tv int) token.BindingPower {
342 bp := token_id_left_binding_power(tv)
343 if bp == .logical_or {
344 return token.BindingPower.logical_and
345 }
346 if bp == .logical_and {
347 return token.BindingPower.compare
348 }
349 if bp == .compare {
350 return token.BindingPower.bit_or
351 }
352 if bp == .bit_or {
353 return token.BindingPower.bit_xor
354 }
355 if bp == .bit_xor {
356 return token.BindingPower.shift
357 }
358 if bp == .shift {
359 return token.BindingPower.add
360 }
361 if bp == .add {
362 return token.BindingPower.product
363 }
364 if bp == .product {
365 return token.BindingPower.highest
366 }
367 return token.BindingPower.lowest
368}
369
370fn token_right_binding_power(tok token.Token) token.BindingPower {
371 return token_id_right_binding_power(int(tok))
372}
373
374fn token_id_to_op(tv int) flat.Op {
375 if tv == 95 {
376 return flat.Op.plus
377 }
378 if tv == 81 {
379 return flat.Op.minus
380 }
381 if tv == 85 {
382 return flat.Op.mul
383 }
384 if tv == 13 {
385 return flat.Op.div
386 }
387 if tv == 83 {
388 return flat.Op.mod
389 }
390 if tv == 20 {
391 return flat.Op.eq
392 }
393 if tv == 88 {
394 return flat.Op.ne
395 }
396 if tv == 80 {
397 return flat.Op.lt
398 }
399 if tv == 22 {
400 return flat.Op.gt
401 }
402 if tv == 74 {
403 return flat.Op.le
404 }
405 if tv == 21 {
406 return flat.Op.ge
407 }
408 if tv == 0 {
409 return flat.Op.amp
410 }
411 if tv == 94 {
412 return flat.Op.pipe
413 }
414 if tv == 109 {
415 return flat.Op.xor
416 }
417 if tv == 75 {
418 return flat.Op.left_shift
419 }
420 if tv == 99 {
421 return flat.Op.right_shift
422 }
423 if tv == 101 {
424 return flat.Op.right_shift_unsigned
425 }
426 if tv == 1 {
427 return flat.Op.logical_and
428 }
429 if tv == 77 {
430 return flat.Op.logical_or
431 }
432 if tv == 89 {
433 return flat.Op.not
434 }
435 if tv == 6 {
436 return flat.Op.bit_not
437 }
438 if tv == 4 {
439 return flat.Op.assign
440 }
441 if tv == 96 {
442 return flat.Op.plus_assign
443 }
444 if tv == 82 {
445 return flat.Op.minus_assign
446 }
447 if tv == 86 {
448 return flat.Op.mul_assign
449 }
450 if tv == 14 {
451 return flat.Op.div_assign
452 }
453 if tv == 84 {
454 return flat.Op.mod_assign
455 }
456 if tv == 2 {
457 return flat.Op.amp_assign
458 }
459 if tv == 93 {
460 return flat.Op.pipe_assign
461 }
462 if tv == 110 {
463 return flat.Op.xor_assign
464 }
465 if tv == 76 {
466 return flat.Op.left_shift_assign
467 }
468 if tv == 100 {
469 return flat.Op.right_shift_assign
470 }
471 if tv == 102 {
472 return flat.Op.right_shift_unsigned_assign
473 }
474 if tv == 24 {
475 return flat.Op.inc
476 }
477 if tv == 11 {
478 return flat.Op.dec
479 }
480 if tv == 16 {
481 return flat.Op.dot
482 }
483 if tv == 3 {
484 return flat.Op.arrow
485 }
486 return flat.Op.none
487}
488
489fn (p &Parser) tok_can_be_decl_name() bool {
490 return p.tok == .name || (int(p.tok) >= int(token.Token.key_as)
491 && int(p.tok) <= int(token.Token.key_unsafe))
492}
493
494fn (mut p Parser) add_children(ids []flat.NodeId) int {
495 start := p.a.children.len
496 for id in ids {
497 p.a.children << id
498 }
499 return start
500}
501
502fn (mut p Parser) add_child(id flat.NodeId) int {
503 start := p.a.children.len
504 p.a.children << id
505 return start
506}
507
508fn (mut p Parser) add_children2(a flat.NodeId, b flat.NodeId) int {
509 start := p.a.children.len
510 p.a.children << a
511 p.a.children << b
512 return start
513}
514
515// ==================== top-level ====================
516
517fn (mut p Parser) top_level_stmt() flat.NodeId {
518 match p.tok {
519 .key_fn {
520 return p.fn_decl()
521 }
522 .key_pub {
523 p.next()
524 return p.top_level_stmt()
525 }
526 .key_struct, .key_union {
527 return p.struct_decl()
528 }
529 .key_global {
530 return p.global_decl()
531 }
532 .key_const {
533 return p.const_decl()
534 }
535 .key_enum {
536 return p.enum_decl()
537 }
538 .key_type {
539 return p.type_decl()
540 }
541 .key_interface {
542 return p.interface_decl()
543 }
544 .key_import {
545 return p.import_stmt()
546 }
547 .key_module {
548 return p.module_stmt()
549 }
550 .attribute {
551 p.skip_attrs()
552 return p.parse_decl_after_attrs()
553 }
554 .lsbr {
555 p.skip_attrs()
556 return p.parse_decl_after_attrs()
557 }
558 .dollar {
559 return p.parse_top_level_comptime_if()
560 }
561 .hash {
562 return p.directive()
563 }
564 .key_assert {
565 return p.assert_stmt()
566 }
567 .semicolon {
568 p.next()
569 return flat.empty_node
570 }
571 else {
572 return p.stmt()
573 }
574 }
575}
576
577// parse_decl_after_attrs parses the declaration that follows an attribute group.
578// A newline after `@[...]` yields an auto-inserted semicolon, which is consumed
579// here so the declaration itself is parsed next. When the attribute group is a
580// disabled `@[if flag ?]` (skip_next_decl set), functions are emitted as no-op
581// stubs (empty body): the signature must remain so call sites still type-check
582// and link, but the body is elided. Other disabled declarations are skipped.
583fn (mut p Parser) parse_decl_after_attrs() flat.NodeId {
584 p.consume_decl_prefix_after_attrs()
585 if p.skip_next_decl {
586 p.pending_params = false
587 p.skip_next_decl = false
588 if p.cur_decl_is_fn() {
589 p.disable_fn_body = true
590 res := p.top_level_stmt()
591 p.disable_fn_body = false
592 p.skip_next_decl = false
593 return res
594 }
595 p.skip_top_level_stmt()
596 p.skip_next_decl = false
597 p.pending_export = ''
598 return flat.empty_node
599 }
600 res := p.top_level_stmt()
601 p.pending_params = false
602 p.pending_export = ''
603 return res
604}
605
606fn (mut p Parser) consume_decl_prefix_after_attrs() {
607 for p.tok == .semicolon || p.tok == .attribute || p.tok == .lsbr {
608 if p.tok == .semicolon {
609 p.next()
610 continue
611 }
612 p.skip_attrs()
613 }
614}
615
616fn (mut p Parser) cur_decl_is_fn() bool {
617 if p.tok == .key_fn {
618 return true
619 }
620 return p.tok == .key_pub && p.peek() == .key_fn
621}
622
623fn (mut p Parser) fn_decl() flat.NodeId {
624 p.check(.key_fn)
625 mut name := ''
626 mut receiver_name := ''
627 mut receiver_type := ''
628 mut receiver_is_mut := false
629 mut is_method := false
630
631 // method receiver: fn (mut r Type) name()
632 if p.tok == .lpar {
633 is_method = true
634 p.next()
635 mut is_mut := false
636 if p.tok == .key_mut || p.tok == .key_shared {
637 is_mut = p.tok == .key_mut
638 receiver_is_mut = is_mut
639 p.next()
640 }
641 receiver_name = p.expect_name()
642 receiver_type = p.parse_type_name()
643 if is_mut {
644 receiver_type = '&' + receiver_type
645 }
646 p.check(.rpar)
647
648 // operator overload: fn (r Type) + (other Type) RetType { }
649 if p.tok != .name && p.tok != .eof {
650 if p.tok.is_overloadable() {
651 op_name := overload_token_name(p.tok)
652 p.next()
653 return p.fn_operator_overload(receiver_name, receiver_type, receiver_is_mut,
654 op_name)
655 }
656 }
657 }
658
659 // function name
660 if p.tok_can_be_decl_name() {
661 name = p.expect_name_or_keyword()
662 if p.tok == .dot {
663 p.next()
664 if name == 'C' || name == 'JS' {
665 // C.func or JS.func
666 name = p.expect_name_or_keyword()
667 for p.tok == .dot {
668 p.next()
669 name += '.' + p.expect_name_or_keyword()
670 }
671 if is_method {
672 clean_type := receiver_type.trim_left('&')
673 name = '${clean_type}.${name}'
674 }
675 return p.fn_decl_body(name, receiver_name, receiver_type, receiver_is_mut,
676 is_method, true)
677 }
678 // module.func or Type.static_method
679 second := p.expect_name_or_keyword()
680 if is_method {
681 // This shouldn't happen for methods
682 name = name + '.' + second
683 } else {
684 // Static method: Type.name
685 receiver_type = name
686 name = second
687 is_method = true
688 }
689 }
690 }
691
692 if is_method && receiver_type.len > 0 {
693 clean_type := receiver_type.trim_left('&')
694 name = '${clean_type}.${name}'
695 }
696
697 return p.fn_decl_body(name, receiver_name, receiver_type, receiver_is_mut, is_method, false)
698}
699
700fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type string, receiver_is_mut bool, op_name string) flat.NodeId {
701 disable_body := p.disable_fn_body
702 p.disable_fn_body = false
703 // parse parameter
704 p.check(.lpar)
705 mut param_ids := []flat.NodeId{}
706 // receiver
707 param_ids << p.a.add_node(flat.Node{
708 kind: .param
709 value: receiver_name
710 typ: receiver_type
711 op: if receiver_is_mut { .amp } else { .none }
712 })
713 // operator param
714 if p.tok == .key_mut {
715 p.next()
716 }
717 for p.tok != .rpar && p.tok != .eof {
718 start_offset := p.s.offset
719 param_ids << p.parse_param_group(false)
720 if p.s.offset == start_offset && p.tok != .rpar && p.tok != .eof {
721 p.next()
722 }
723 }
724 p.check(.rpar)
725
726 mut ret_type := 'void'
727 if p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
728 ret_type = p.parse_type_name()
729 }
730
731 clean_type := receiver_type.trim_left('&')
732 name := '${clean_type}.${op_name}'
733
734 mut body_ids := []flat.NodeId{}
735 if p.tok == .lcbr {
736 prev_fn := p.cur_fn
737 p.cur_fn = name
738 if disable_body {
739 p.mark_disabled_fn(name)
740 p.skip_block()
741 } else {
742 p.check(.lcbr)
743 for p.tok != .rcbr && p.tok != .eof {
744 id := p.stmt()
745 if int(id) >= 0 {
746 body_ids << id
747 }
748 }
749 p.check(.rcbr)
750 }
751 p.cur_fn = prev_fn
752 }
753
754 mut all_ids := []flat.NodeId{cap: param_ids.len + body_ids.len}
755 for id in param_ids {
756 all_ids << id
757 }
758 for id in body_ids {
759 all_ids << id
760 }
761 start := p.add_children(all_ids)
762 id := p.a.add_node(flat.Node{
763 kind: .fn_decl
764 value: name
765 typ: ret_type
766 children_start: start
767 children_count: flat.child_count(all_ids.len)
768 })
769 p.register_pending_export(name)
770 return id
771}
772
773fn (mut p Parser) fn_decl_body(name string, receiver_name string, receiver_type string, receiver_is_mut bool, is_method bool, is_c_decl bool) flat.NodeId {
774 // Capture & clear here so it applies only to this function (not nested closures
775 // or a following declaration), and is cleared even on the extern/no-body path.
776 disable_body := p.disable_fn_body
777 p.disable_fn_body = false
778 // generic params — skip
779 mut generic_params := []string{}
780 if p.tok == .lsbr {
781 generic_params = p.parse_generic_param_names()
782 }
783
784 // params
785 p.check(.lpar)
786 mut param_ids := []flat.NodeId{}
787 if is_method && receiver_name.len > 0 {
788 param_ids << p.a.add_node(flat.Node{
789 kind: .param
790 value: receiver_name
791 typ: receiver_type
792 op: if receiver_is_mut { .amp } else { .none }
793 })
794 }
795 for p.tok != .rpar && p.tok != .eof {
796 start_offset := p.s.offset
797 param_ids << p.parse_param_group(is_c_decl)
798 if p.s.offset == start_offset && p.tok != .rpar && p.tok != .eof {
799 p.next()
800 }
801 }
802 p.check(.rpar)
803
804 // return type
805 mut ret_type := 'void'
806 if p.tok == .name || p.tok == .amp || p.tok == .question || p.tok == .not || p.tok == .lsbr
807 || p.tok == .lpar || p.tok == .key_fn || p.tok == .ellipsis {
808 ret_type = p.parse_type_name()
809 }
810 // no body — extern/C declaration
811 if p.tok != .lcbr {
812 for p.tok == .semicolon {
813 p.next()
814 }
815 start := p.add_children(param_ids)
816 id := p.a.add_node(flat.Node{
817 kind: .c_fn_decl
818 value: name
819 typ: ret_type
820 generic_params: generic_params
821 children_start: start
822 children_count: flat.child_count(param_ids.len)
823 })
824 p.pending_export = ''
825 return id
826 }
827
828 // body
829 mut body_ids := []flat.NodeId{}
830 prev_fn := p.cur_fn
831 p.cur_fn = name
832 // A disabled `@[if flag ?]` function keeps its signature but gets an empty body
833 // (a no-op stub), so callers still resolve while the body is compiled out.
834 if disable_body {
835 p.mark_disabled_fn(name)
836 p.skip_block()
837 } else {
838 p.check(.lcbr)
839 for p.tok != .rcbr && p.tok != .eof {
840 id := p.stmt()
841 if int(id) >= 0 {
842 body_ids << id
843 }
844 }
845 p.check(.rcbr)
846 }
847 p.cur_fn = prev_fn
848
849 mut all_ids := []flat.NodeId{cap: param_ids.len + body_ids.len}
850 for id in param_ids {
851 all_ids << id
852 }
853 for id in body_ids {
854 all_ids << id
855 }
856 start := p.add_children(all_ids)
857 id := p.a.add_node(flat.Node{
858 kind: .fn_decl
859 value: name
860 typ: ret_type
861 generic_params: generic_params
862 children_start: start
863 children_count: flat.child_count(all_ids.len)
864 })
865 p.register_pending_export(name)
866 return id
867}
868
869fn (mut p Parser) mark_disabled_fn(name string) {
870 if name.len == 0 {
871 return
872 }
873 if p.cur_module.len > 0 && p.cur_module != 'main' && p.cur_module != 'builtin' {
874 qname := if name.starts_with('${p.cur_module}.') { name } else { '${p.cur_module}.${name}' }
875 p.a.disabled_fns[qname] = true
876 }
877 p.a.disabled_fns[name] = true
878}
879
880fn (mut p Parser) register_pending_export(name string) {
881 if p.pending_export.len == 0 || name.len == 0 {
882 p.pending_export = ''
883 return
884 }
885 qname := if p.cur_module.len > 0 && p.cur_module != 'main' && p.cur_module != 'builtin'
886 && !name.starts_with('${p.cur_module}.') {
887 '${p.cur_module}.${name}'
888 } else {
889 name
890 }
891 if name in p.a.disabled_fns || qname in p.a.disabled_fns {
892 p.pending_export = ''
893 return
894 }
895 p.a.export_fn_names[qname] = p.pending_export
896 p.pending_export = ''
897}
898
899fn (mut p Parser) parse_param_group(is_c_decl bool) []flat.NodeId {
900 mut ids := []flat.NodeId{}
901 mut names := []string{}
902 mut is_mut := false
903 if p.tok == .key_mut {
904 is_mut = true
905 p.next()
906 }
907 if p.tok == .key_shared {
908 p.next()
909 }
910 // variadic ...Type (no param name)
911 if p.tok == .ellipsis {
912 typ := p.parse_type_name()
913 ids << p.a.add_node(flat.Node{
914 kind: .param
915 value: ''
916 typ: typ
917 op: if is_mut { .amp } else { .none }
918 })
919 if p.tok == .comma {
920 p.next()
921 }
922 return ids
923 }
924 if is_c_decl && p.c_anon_param_starts_type() {
925 mut typ := p.parse_type_name()
926 if is_mut && !typ.starts_with('&') {
927 typ = '&' + typ
928 }
929 ids << p.a.add_node(flat.Node{
930 kind: .param
931 value: ''
932 typ: typ
933 op: if is_mut { .amp } else { .none }
934 })
935 if p.tok == .comma {
936 p.next()
937 }
938 return ids
939 }
940 names << p.lit
941 p.next()
942 for p.tok == .comma {
943 p.next()
944 if p.tok == .key_mut {
945 p.next()
946 }
947 if p.tok == .name {
948 names << p.lit
949 p.next()
950 }
951 }
952 mut typ := p.parse_type_name()
953 if is_mut && !typ.starts_with('&') {
954 typ = '&' + typ
955 }
956 for name in names {
957 ids << p.a.add_node(flat.Node{
958 kind: .param
959 value: name
960 typ: typ
961 op: if is_mut { .amp } else { .none }
962 })
963 }
964 if p.tok == .comma {
965 p.next()
966 }
967 return ids
968}
969
970fn (mut p Parser) c_anon_param_starts_type() bool {
971 if p.tok in [.amp, .and, .question, .not, .lsbr, .lpar, .key_fn, .key_atomic, .key_struct] {
972 return true
973 }
974 if p.tok == .name {
975 if p.lit == '&' {
976 return true
977 }
978 pk := p.peek()
979 if pk == .dot || pk == .rpar || pk == .eof {
980 return true
981 }
982 if pk == .comma && (is_builtin_type(p.lit) || is_c_anon_simple_type_name(p.lit)) {
983 return true
984 }
985 }
986 return false
987}
988
989fn (mut p Parser) struct_decl() flat.NodeId {
990 is_union := p.tok == .key_union
991 is_params := p.pending_params
992 p.pending_params = false
993 p.next() // skip 'struct' or 'union'
994 mut name := p.expect(.name)
995 if (name == 'C' || name == 'JS') && p.tok == .dot {
996 for p.tok == .dot {
997 p.next()
998 name += '.' + p.expect_name_or_keyword()
999 }
1000 }
1001 // generic params — skip
1002 mut is_generic := false
1003 mut generic_params := []string{}
1004 if p.tok == .lsbr {
1005 is_generic = true
1006 generic_params = p.parse_generic_param_names()
1007 }
1008 // implements clause
1009 if p.tok == .name && p.lit == 'implements' {
1010 p.next()
1011 p.parse_type_name()
1012 for p.tok == .comma {
1013 p.next()
1014 p.parse_type_name()
1015 }
1016 }
1017 // no body (C struct forward decl)
1018 if p.tok != .lcbr {
1019 if p.tok == .semicolon {
1020 p.next()
1021 }
1022 return p.a.add_node(flat.Node{
1023 kind: .struct_decl
1024 value: name
1025 typ: struct_decl_typ(is_union, is_generic, is_params)
1026 generic_params: generic_params
1027 })
1028 }
1029 p.check(.lcbr)
1030 mut ids := []flat.NodeId{}
1031 for p.tok != .rcbr && p.tok != .eof {
1032 // access modifiers
1033 if p.tok == .key_pub {
1034 p.next()
1035 if p.tok == .key_mut {
1036 p.next()
1037 }
1038 if p.tok == .name && p.lit == 'module_mut' {
1039 p.next()
1040 }
1041 if p.tok == .colon {
1042 p.next()
1043 }
1044 continue
1045 }
1046 if p.tok == .key_mut {
1047 p.next()
1048 if p.tok == .colon {
1049 p.next()
1050 }
1051 continue
1052 }
1053 if p.tok == .semicolon {
1054 p.next()
1055 continue
1056 }
1057 // comptime $if in struct fields
1058 if p.tok == .dollar {
1059 id := p.parse_comptime_if()
1060 if int(id) >= 0 {
1061 ids << id
1062 }
1063 continue
1064 }
1065 // attributes — skip
1066 if p.tok == .attribute {
1067 p.skip_attrs()
1068 continue
1069 }
1070 // field: name type [= default] [@[attrs]]
1071 if p.tok == .name || p.tok.is_keyword() {
1072 field_name := p.expect_name_or_keyword()
1073 if p.tok == .dot {
1074 p.next()
1075 second := p.expect_name_or_keyword()
1076 full_type := '${field_name}.${second}'
1077 field_type := full_type + p.parse_type_generic_suffix()
1078 ids << p.a.add_node(flat.Node{
1079 kind: .field_decl
1080 value: full_type
1081 typ: field_type
1082 })
1083 if p.tok == .semicolon {
1084 p.next()
1085 }
1086 continue
1087 }
1088 // embedded struct (type on its own line, followed by semicolon)
1089 if p.tok == .semicolon || p.tok == .rcbr {
1090 ids << p.a.add_node(flat.Node{
1091 kind: .field_decl
1092 value: field_name
1093 typ: field_name
1094 })
1095 if p.tok == .semicolon {
1096 p.next()
1097 }
1098 continue
1099 }
1100 // grouped fields: x, y int
1101 if p.tok == .comma {
1102 mut names := []string{}
1103 names << field_name
1104 for p.tok == .comma {
1105 p.next()
1106 names << p.expect_name_or_keyword()
1107 }
1108 field_type := p.parse_type_name()
1109 for n in names {
1110 ids << p.a.add_node(flat.Node{
1111 kind: .field_decl
1112 value: n
1113 typ: field_type
1114 })
1115 }
1116 if p.tok == .semicolon {
1117 p.next()
1118 }
1119 continue
1120 }
1121 // For embedded structs followed by access modifier or another field,
1122 // check if the next token could be a type
1123 field_type := p.parse_type_name()
1124 mut default_id := flat.empty_node
1125 // default value
1126 if p.tok == .assign {
1127 p.next()
1128 default_id = p.expr(.lowest)
1129 }
1130 mut children_start := 0
1131 mut children_count := 0
1132 if int(default_id) >= 0 {
1133 children_start = p.add_child(default_id)
1134 children_count = 1
1135 }
1136 // trailing field attributes — skip
1137 if p.tok == .attribute || p.tok == .lsbr {
1138 p.skip_attrs()
1139 }
1140 ids << p.a.add_node(flat.Node{
1141 kind: .field_decl
1142 value: field_name
1143 typ: field_type
1144 children_start: children_start
1145 children_count: flat.child_count(children_count)
1146 })
1147 if p.tok == .semicolon {
1148 p.next()
1149 }
1150 } else {
1151 p.next()
1152 }
1153 }
1154 p.check(.rcbr)
1155 p.pending_params = false
1156 start := p.add_children(ids)
1157 return p.a.add_node(flat.Node{
1158 kind: .struct_decl
1159 value: name
1160 typ: struct_decl_typ(is_union, is_generic, is_params)
1161 generic_params: generic_params
1162 children_start: start
1163 children_count: flat.child_count(ids.len)
1164 })
1165}
1166
1167fn struct_decl_typ(is_union bool, is_generic bool, is_params bool) string {
1168 mut parts := []string{}
1169 if is_union {
1170 parts << 'union'
1171 }
1172 if is_generic {
1173 parts << 'generic'
1174 }
1175 if is_params {
1176 parts << 'params'
1177 }
1178 return parts.join(',')
1179}
1180
1181fn (mut p Parser) global_decl() flat.NodeId {
1182 p.next() // skip '__global'
1183 is_grouped := p.tok == .lpar
1184 if is_grouped {
1185 p.next()
1186 }
1187 mut ids := []flat.NodeId{}
1188 for {
1189 if p.tok == .semicolon {
1190 p.next()
1191 if !is_grouped {
1192 break
1193 }
1194 if p.tok == .rpar {
1195 p.next()
1196 if p.tok == .semicolon {
1197 p.next()
1198 }
1199 break
1200 }
1201 continue
1202 }
1203 if p.tok == .key_pub {
1204 p.next()
1205 }
1206 if p.tok == .key_mut {
1207 p.next()
1208 }
1209 if p.tok == .name || p.tok.is_keyword() {
1210 gname := p.expect_name_or_keyword()
1211 // handle qualified names: C.errno
1212 mut full_name := gname
1213 for p.tok == .dot {
1214 p.next()
1215 full_name += '.' + p.expect_name_or_keyword()
1216 }
1217 if p.tok == .assign {
1218 // global with initializer: __global name = expr
1219 p.next()
1220 val_id := p.expr(.lowest)
1221 vstart := p.add_child(val_id)
1222 ids << p.a.add_node(flat.Node{
1223 kind: .field_decl
1224 value: full_name
1225 typ: ''
1226 children_start: vstart
1227 children_count: 1
1228 })
1229 } else {
1230 gtype := p.parse_type_name()
1231 ids << p.a.add_node(flat.Node{
1232 kind: .field_decl
1233 value: full_name
1234 typ: gtype
1235 })
1236 }
1237 if p.tok == .semicolon {
1238 p.next()
1239 }
1240 if !is_grouped {
1241 break
1242 }
1243 if is_grouped && p.tok == .rpar {
1244 p.next()
1245 if p.tok == .semicolon {
1246 p.next()
1247 }
1248 break
1249 }
1250 } else if p.tok == .rpar {
1251 p.next()
1252 if p.tok == .semicolon {
1253 p.next()
1254 }
1255 break
1256 } else {
1257 p.next()
1258 if !is_grouped {
1259 break
1260 }
1261 }
1262 }
1263 start := p.add_children(ids)
1264 return p.a.add_node(flat.Node{
1265 kind: .global_decl
1266 children_start: start
1267 children_count: flat.child_count(ids.len)
1268 })
1269}
1270
1271fn (mut p Parser) const_decl() flat.NodeId {
1272 p.next() // skip 'const'
1273 is_grouped := p.tok == .lpar
1274 if is_grouped {
1275 p.next()
1276 }
1277 mut ids := []flat.NodeId{}
1278 for {
1279 if p.tok == .semicolon {
1280 p.next()
1281 if !is_grouped {
1282 break
1283 }
1284 if p.tok == .rpar {
1285 p.next()
1286 if p.tok == .semicolon {
1287 p.next()
1288 }
1289 break
1290 }
1291 continue
1292 }
1293 if p.tok == .name {
1294 cname := p.expect_name()
1295 mut full_name := cname
1296 // C.NAME
1297 if full_name == 'C' && p.tok == .dot {
1298 p.next()
1299 full_name = 'C.' + p.expect_name_or_keyword()
1300 }
1301 if p.tok == .assign {
1302 p.next()
1303 val_id := p.expr(.lowest)
1304 vstart := p.add_child(val_id)
1305 ids << p.a.add_node(flat.Node{
1306 kind: .const_field
1307 value: full_name
1308 children_start: vstart
1309 children_count: 1
1310 })
1311 } else {
1312 // const with type only (header files)
1313 ctype := p.parse_type_name()
1314 ids << p.a.add_node(flat.Node{
1315 kind: .const_field
1316 value: full_name
1317 typ: ctype
1318 })
1319 }
1320 if p.tok == .semicolon {
1321 p.next()
1322 }
1323 if !is_grouped {
1324 break
1325 }
1326 if is_grouped && p.tok == .rpar {
1327 p.next()
1328 if p.tok == .semicolon {
1329 p.next()
1330 }
1331 break
1332 }
1333 } else if p.tok == .rpar {
1334 p.next()
1335 if p.tok == .semicolon {
1336 p.next()
1337 }
1338 break
1339 } else {
1340 p.next()
1341 if !is_grouped {
1342 break
1343 }
1344 }
1345 }
1346 start := p.add_children(ids)
1347 return p.a.add_node(flat.Node{
1348 kind: .const_decl
1349 children_start: start
1350 children_count: flat.child_count(ids.len)
1351 })
1352}
1353
1354fn (mut p Parser) enum_decl() flat.NodeId {
1355 p.next() // skip 'enum'
1356 name := p.expect(.name)
1357 // `as` type
1358 if p.tok == .key_as {
1359 p.next()
1360 p.parse_type_name()
1361 }
1362 p.check(.lcbr)
1363 mut ids := []flat.NodeId{}
1364 for p.tok != .rcbr && p.tok != .eof {
1365 if p.tok == .semicolon {
1366 p.next()
1367 continue
1368 }
1369 // attributes on enum fields — skip
1370 if p.tok == .attribute || p.tok == .lsbr {
1371 p.skip_attrs()
1372 continue
1373 }
1374 field_name := p.expect_name_or_keyword()
1375 if p.tok == .assign {
1376 p.next()
1377 val_id := p.expr(.lowest)
1378 vstart := p.add_child(val_id)
1379 ids << p.a.add_node(flat.Node{
1380 kind: .enum_field
1381 value: field_name
1382 children_start: vstart
1383 children_count: 1
1384 })
1385 } else {
1386 ids << p.a.add_node(flat.Node{
1387 kind: .enum_field
1388 value: field_name
1389 })
1390 }
1391 if p.tok == .semicolon {
1392 p.next()
1393 }
1394 }
1395 p.check(.rcbr)
1396 start := p.add_children(ids)
1397 mut typ := ''
1398 if p.pending_flag {
1399 typ = 'flag'
1400 p.pending_flag = false
1401 }
1402 return p.a.add_node(flat.Node{
1403 kind: .enum_decl
1404 value: name
1405 typ: typ
1406 children_start: start
1407 children_count: flat.child_count(ids.len)
1408 })
1409}
1410
1411fn (mut p Parser) type_decl() flat.NodeId {
1412 p.next() // skip 'type'
1413 // C. or JS. prefix
1414 if p.tok == .name && (p.lit == 'C' || p.lit == 'JS') {
1415 p.next()
1416 if p.tok == .dot {
1417 p.next()
1418 }
1419 }
1420 name := p.expect_name()
1421 // generic params
1422 mut generic_params := []string{}
1423 if p.tok == .lsbr {
1424 generic_params = p.parse_generic_param_names()
1425 }
1426 p.expect(.assign)
1427 first_type := p.parse_type_name()
1428 // check for sum type: type T = A | B | C
1429 // skip auto-semicolon before pipe
1430 if p.tok == .pipe || (p.tok == .semicolon && p.peek_is(token.Token.pipe)) {
1431 mut variants := []flat.NodeId{}
1432 variants << p.a.add_val(.ident, first_type)
1433 for p.tok == .pipe || (p.tok == .semicolon && p.peek_is(token.Token.pipe)) {
1434 if p.tok == .semicolon {
1435 p.next()
1436 }
1437 p.next() // skip |
1438 variant_type := p.parse_type_name()
1439 variants << p.a.add_val(.ident, variant_type)
1440 }
1441 if p.tok == .semicolon {
1442 p.next()
1443 }
1444 start := p.add_children(variants)
1445 return p.a.add_node(flat.Node{
1446 kind: .type_decl
1447 value: name
1448 generic_params: generic_params
1449 children_start: start
1450 children_count: flat.child_count(variants.len)
1451 })
1452 }
1453 if p.tok == .semicolon {
1454 p.next()
1455 }
1456 // type alias
1457 return p.a.add_node(flat.Node{
1458 kind: .type_decl
1459 value: name
1460 typ: first_type
1461 generic_params: generic_params
1462 })
1463}
1464
1465fn (mut p Parser) interface_decl() flat.NodeId {
1466 p.next() // skip 'interface'
1467 mut name := p.expect(.name)
1468 for p.tok == .dot {
1469 p.next()
1470 name += '.' + p.expect(.name)
1471 }
1472 // generic params
1473 mut generic_params := []string{}
1474 if p.tok == .lsbr {
1475 generic_params = p.parse_generic_param_names()
1476 }
1477 p.check(.lcbr)
1478 mut ids := []flat.NodeId{}
1479 for p.tok != .rcbr && p.tok != .eof {
1480 if p.tok == .key_mut {
1481 p.next()
1482 if p.tok == .colon {
1483 p.next()
1484 }
1485 continue
1486 }
1487 if p.tok == .semicolon {
1488 p.next()
1489 continue
1490 }
1491 field_name := p.expect_name_or_keyword()
1492 if p.tok == .lpar {
1493 // method: name(params) ret_type
1494 p.next() // skip (
1495 mut params := []flat.NodeId{}
1496 for p.tok != .rpar && p.tok != .eof {
1497 if p.tok == .key_mut {
1498 p.next()
1499 }
1500 // Interface method params may be named (e.g. `seed_data []u32`,
1501 // `node &ast.Node`) or type-only (`[]u32`). When the current token
1502 // is a plain identifier and the next token starts a type, that
1503 // identifier is the param name; consume it first so the array/pointer
1504 // prefix of the real type is not mis-parsed onto the name
1505 // (e.g. `seed_data[]`). `map`/`chan` are type heads, not names.
1506 if p.tok == .name && p.lit != 'map' && p.lit != 'chan' {
1507 nt := p.peek()
1508 if nt == .name || nt == .amp || nt == .question || nt == .not
1509 || nt == .key_fn || nt == .ellipsis
1510 || (nt == .lsbr && p.peek_lbr_starts_array_type()) {
1511 p.next()
1512 }
1513 }
1514 ptype := p.parse_type_name()
1515 params << p.a.add_node(flat.Node{
1516 kind: .param
1517 typ: ptype
1518 })
1519 if p.tok == .comma {
1520 p.next()
1521 }
1522 }
1523 p.check(.rpar) // skip )
1524 mut ret_type := ''
1525 if p.tok != .semicolon && p.tok != .rcbr && p.tok != .eof {
1526 ret_type = p.parse_type_name()
1527 }
1528 start := p.add_children(params)
1529 ids << p.a.add_node(flat.Node{
1530 kind: .interface_field
1531 op: .dot
1532 value: field_name
1533 typ: ret_type
1534 children_start: start
1535 children_count: flat.child_count(params.len)
1536 })
1537 } else if p.tok == .semicolon || p.tok == .rcbr {
1538 // embedded type or field without explicit type
1539 ids << p.a.add_node(flat.Node{
1540 kind: .interface_field
1541 value: field_name
1542 })
1543 } else {
1544 // field: name type
1545 ftype := p.parse_type_name()
1546 ids << p.a.add_node(flat.Node{
1547 kind: .interface_field
1548 value: field_name
1549 typ: ftype
1550 })
1551 }
1552 if p.tok == .semicolon {
1553 p.next()
1554 }
1555 }
1556 p.check(.rcbr)
1557 start := p.add_children(ids)
1558 return p.a.add_node(flat.Node{
1559 kind: .interface_decl
1560 value: name
1561 generic_params: generic_params
1562 children_start: start
1563 children_count: flat.child_count(ids.len)
1564 })
1565}
1566
1567fn (mut p Parser) import_stmt() flat.NodeId {
1568 p.next() // skip 'import'
1569 mut name := p.expect_name()
1570 mut alias := name
1571 mut selective_ids := []flat.NodeId{}
1572 for p.tok == .dot {
1573 p.next()
1574 alias = p.expect_name()
1575 name += '.' + alias
1576 }
1577 if p.tok == .key_as {
1578 p.next()
1579 alias = p.expect_name()
1580 }
1581 // selective import: import mod { sym1, sym2 }
1582 if p.tok == .lcbr {
1583 p.next()
1584 for p.tok != .rcbr && p.tok != .eof {
1585 sym := p.expect_name_or_keyword()
1586 selective_ids << p.a.add_node(flat.Node{
1587 kind: .ident
1588 value: sym
1589 })
1590 if p.tok == .comma {
1591 p.next()
1592 }
1593 }
1594 p.check(.rcbr)
1595 }
1596 if p.tok == .semicolon {
1597 p.next()
1598 }
1599 return p.a.add_node(flat.Node{
1600 kind: .import_decl
1601 value: name
1602 typ: alias
1603 children_start: p.add_children(selective_ids)
1604 children_count: flat.child_count(selective_ids.len)
1605 })
1606}
1607
1608fn (mut p Parser) module_stmt() flat.NodeId {
1609 p.next() // skip 'module'
1610 name := p.expect_name()
1611 if p.tok == .semicolon {
1612 p.next()
1613 }
1614 return p.a.add_node(flat.Node{
1615 kind: .module_decl
1616 value: name
1617 })
1618}
1619
1620fn (mut p Parser) directive() flat.NodeId {
1621 mut full := p.lit
1622 p.next() // skip '#' (lit already contains the full line)
1623 if full.starts_with('#') {
1624 full = full[1..]
1625 }
1626 mut name := full
1627 mut value := ''
1628 space_idx := full.index_u8(` `)
1629 if space_idx > 0 {
1630 name = full[..space_idx]
1631 value = full[space_idx + 1..].trim_space()
1632 }
1633 if p.tok == .semicolon {
1634 p.next()
1635 }
1636 return p.a.add_node(flat.Node{
1637 kind: .directive
1638 value: name
1639 typ: value
1640 })
1641}
1642
1643fn (mut p Parser) skip_attrs() {
1644 if p.tok == .attribute {
1645 p.next()
1646 if p.tok == .key_if {
1647 p.next()
1648 mut cond := strings.new_builder(32)
1649 for p.tok != .rsbr && p.tok != .eof {
1650 tok_str := p.comptime_cond_token_text()
1651 if cond.len > 0 && tok_str != '?' {
1652 cond.write_string(' ')
1653 }
1654 cond.write_string(tok_str)
1655 p.next()
1656 }
1657 if !eval_comptime_cond(p.prefs, cond.str()) {
1658 p.skip_next_decl = true
1659 }
1660 }
1661 for p.tok != .rsbr && p.tok != .eof {
1662 if p.tok == .name {
1663 if p.lit == 'flag' {
1664 p.pending_flag = true
1665 } else if p.lit == 'params' {
1666 p.pending_params = true
1667 } else if p.lit == 'export' {
1668 p.try_parse_export_attr()
1669 continue
1670 }
1671 }
1672 p.next()
1673 }
1674 p.check(.rsbr)
1675 return
1676 }
1677 if p.tok == .lsbr {
1678 mut depth := 1
1679 p.next()
1680 for depth > 0 && p.tok != .eof {
1681 if depth == 1 && p.tok == .name {
1682 if p.lit == 'flag' {
1683 p.pending_flag = true
1684 } else if p.lit == 'params' {
1685 p.pending_params = true
1686 } else if p.lit == 'export' {
1687 p.try_parse_export_attr()
1688 continue
1689 }
1690 }
1691 if p.tok == .lsbr {
1692 depth++
1693 } else if p.tok == .rsbr {
1694 depth--
1695 }
1696 p.next()
1697 }
1698 }
1699}
1700
1701fn (mut p Parser) try_parse_export_attr() {
1702 p.next()
1703 if p.tok != .colon {
1704 return
1705 }
1706 p.next()
1707 if int(p.tok) != int(token.Token.string) {
1708 return
1709 }
1710 p.pending_export = strip_quotes(p.lit)
1711 p.next()
1712}
1713
1714fn (mut p Parser) skip_top_level_stmt() {
1715 // A newline after the attribute group (`@[if flag ?]`) yields an auto-inserted
1716 // semicolon before the declaration; skip it (and any further attributes) so the
1717 // declaration itself — not just the empty statement — is skipped.
1718 for p.tok == .attribute || p.tok == .lsbr || p.tok == .semicolon {
1719 if p.tok == .semicolon {
1720 p.next()
1721 continue
1722 }
1723 p.skip_attrs()
1724 }
1725 if p.tok == .key_pub {
1726 p.next()
1727 }
1728 match p.tok {
1729 .key_fn {
1730 p.next()
1731 for p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
1732 p.next()
1733 }
1734 if p.tok == .lcbr {
1735 p.skip_block()
1736 } else if p.tok == .semicolon {
1737 p.next()
1738 }
1739 }
1740 .key_struct, .key_union, .key_enum, .key_interface, .key_const, .key_global {
1741 for p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
1742 p.next()
1743 }
1744 if p.tok == .lcbr {
1745 p.skip_block()
1746 } else if p.tok == .semicolon {
1747 p.next()
1748 }
1749 }
1750 else {
1751 for p.tok != .semicolon && p.tok != .eof {
1752 p.next()
1753 }
1754 if p.tok == .semicolon {
1755 p.next()
1756 }
1757 }
1758 }
1759}
1760
1761fn (mut p Parser) parse_comptime_if() flat.NodeId {
1762 p.next() // skip $
1763 if p.tok != .key_if {
1764 // $for or other comptime — skip
1765 if p.tok == .key_for {
1766 p.next()
1767 for p.tok != .lcbr && p.tok != .eof {
1768 p.next()
1769 }
1770 p.skip_block()
1771 } else {
1772 for p.tok != .semicolon && p.tok != .eof {
1773 p.next()
1774 }
1775 if p.tok == .semicolon {
1776 p.next()
1777 }
1778 }
1779 return flat.empty_node
1780 }
1781 p.next() // skip 'if'
1782 cond := p.parse_comptime_cond()
1783 if comptime_cond_has_type_test(cond) {
1784 then_block := p.block_stmt()
1785 else_block := p.parse_comptime_else()
1786 return p.comptime_if_node(cond, then_block, else_block)
1787 }
1788 taken := eval_comptime_cond(p.prefs, cond)
1789 if taken {
1790 result := p.block_stmt()
1791 p.skip_comptime_else()
1792 return result
1793 } else {
1794 p.skip_block()
1795 return p.parse_comptime_else()
1796 }
1797}
1798
1799fn (mut p Parser) parse_top_level_comptime_if() flat.NodeId {
1800 p.next() // skip $
1801 if p.tok != .key_if {
1802 // $for or other comptime - skip
1803 if p.tok == .key_for {
1804 p.next()
1805 for p.tok != .lcbr && p.tok != .eof {
1806 p.next()
1807 }
1808 p.skip_block()
1809 } else {
1810 for p.tok != .semicolon && p.tok != .eof {
1811 p.next()
1812 }
1813 if p.tok == .semicolon {
1814 p.next()
1815 }
1816 }
1817 return flat.empty_node
1818 }
1819 p.next() // skip 'if'
1820 cond := p.parse_comptime_cond()
1821 if comptime_cond_has_type_test(cond) {
1822 then_block := p.top_level_block_stmt()
1823 else_block := p.parse_top_level_comptime_else()
1824 return p.comptime_if_node(cond, then_block, else_block)
1825 }
1826 taken := eval_comptime_cond(p.prefs, cond)
1827 if taken {
1828 result := p.top_level_block_stmt()
1829 p.skip_comptime_else()
1830 return result
1831 }
1832 p.skip_block()
1833 return p.parse_top_level_comptime_else()
1834}
1835
1836fn (mut p Parser) comptime_if_node(cond string, then_block flat.NodeId, else_block flat.NodeId) flat.NodeId {
1837 mut ids := []flat.NodeId{cap: 2}
1838 if int(then_block) >= 0 {
1839 ids << then_block
1840 }
1841 if int(else_block) >= 0 {
1842 ids << else_block
1843 }
1844 start := p.add_children(ids)
1845 return p.a.add_node(flat.Node{
1846 kind: .comptime_if
1847 value: cond
1848 children_start: start
1849 children_count: flat.child_count(ids.len)
1850 })
1851}
1852
1853fn (mut p Parser) top_level_block_stmt() flat.NodeId {
1854 ids := p.parse_top_level_block_body()
1855 start := p.add_children(ids)
1856 return p.a.add_node(flat.Node{
1857 kind: .block
1858 children_start: start
1859 children_count: flat.child_count(ids.len)
1860 })
1861}
1862
1863fn (mut p Parser) parse_top_level_block_body() []flat.NodeId {
1864 p.check(.lcbr)
1865 mut ids := []flat.NodeId{}
1866 for p.tok != .rcbr && p.tok != .eof {
1867 if p.tok == .semicolon {
1868 p.next()
1869 continue
1870 }
1871 id := p.top_level_stmt()
1872 if int(id) >= 0 {
1873 ids << id
1874 }
1875 }
1876 p.check(.rcbr)
1877 return ids
1878}
1879
1880fn (mut p Parser) parse_comptime_cond() string {
1881 mut cond := strings.new_builder(64)
1882 mut prev_tok_str := ''
1883 for p.tok != .lcbr && p.tok != .eof {
1884 tok_str := p.comptime_cond_token_text()
1885 if cond.len > 0 && comptime_cond_needs_space(prev_tok_str, tok_str) {
1886 cond.write_string(' ')
1887 }
1888 cond.write_string(tok_str)
1889 prev_tok_str = tok_str
1890 p.next()
1891 }
1892 return cond.str()
1893}
1894
1895fn (p &Parser) comptime_cond_token_text() string {
1896 if p.s.pos >= 0 && p.s.pos < p.s.src.len {
1897 c := p.s.src[p.s.pos]
1898 if c == `&` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `&` {
1899 return '&&'
1900 }
1901 if c == `|` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `|` {
1902 return '||'
1903 }
1904 if c == `!` && p.s.pos + 2 < p.s.src.len && p.s.src[p.s.pos + 1] == `i`
1905 && p.s.src[p.s.pos + 2] == `s` {
1906 return '!is'
1907 }
1908 if c == `!` {
1909 return '!'
1910 }
1911 if c == `?` {
1912 return '?'
1913 }
1914 }
1915 tok := p.tok
1916 if tok == .and {
1917 return '&&'
1918 }
1919 if tok == .logical_or {
1920 return '||'
1921 }
1922 if tok == .not {
1923 return '!'
1924 }
1925 if tok == .lpar {
1926 return '('
1927 }
1928 if tok == .rpar {
1929 return ')'
1930 }
1931 if tok == .key_is {
1932 return 'is'
1933 }
1934 if tok == .not_is {
1935 return '!is'
1936 }
1937 if tok == .dollar {
1938 return '$'
1939 }
1940 if tok == .lsbr {
1941 return '['
1942 }
1943 if tok == .rsbr {
1944 return ']'
1945 }
1946 if tok == .dot {
1947 return '.'
1948 }
1949 if tok == .comma {
1950 return ','
1951 }
1952 if tok == .question {
1953 return '?'
1954 }
1955 if tok == .key_true {
1956 return 'true'
1957 }
1958 if tok == .key_false {
1959 return 'false'
1960 }
1961 if p.lit.len > 0 {
1962 return p.lit
1963 }
1964 return ''
1965}
1966
1967fn comptime_cond_needs_space(prev string, cur string) bool {
1968 if prev == 'is' || prev == '!is' {
1969 return true
1970 }
1971 if prev == '?' || prev == '!' {
1972 return false
1973 }
1974 if cur == '?' || cur == ']' || cur == '[' || cur == '.' || cur == ',' {
1975 return false
1976 }
1977 if prev == '$' || prev == '[' || prev == ']' || prev == '.' {
1978 return false
1979 }
1980 return true
1981}
1982
1983fn comptime_cond_has_type_test(cond string) bool {
1984 return cond.contains(' is ') || cond.contains(' !is ')
1985}
1986
1987fn (mut p Parser) skip_comptime_else() {
1988 for {
1989 if p.tok == .semicolon {
1990 p.next()
1991 }
1992 if p.tok != .dollar || p.peek() != .key_else {
1993 return
1994 }
1995 p.next() // skip $
1996 p.next() // skip else
1997 if p.tok == .semicolon {
1998 p.next()
1999 }
2000 if p.tok != .dollar || p.peek() != .key_if {
2001 p.skip_block()
2002 return
2003 }
2004 p.next() // skip $
2005 p.next() // skip if
2006 for p.tok != .lcbr && p.tok != .eof {
2007 p.next()
2008 }
2009 p.skip_block()
2010 }
2011}
2012
2013fn (mut p Parser) parse_comptime_else() flat.NodeId {
2014 // Skip auto-semicolons before $else
2015 if p.tok == .semicolon && p.peek() == .dollar {
2016 p.next()
2017 }
2018 if p.tok != .dollar {
2019 return flat.empty_node
2020 }
2021 if p.peek() != .key_else {
2022 return flat.empty_node
2023 }
2024 p.next() // skip $
2025 p.next() // skip else
2026 // $else $if — recurse
2027 if p.tok == .dollar || (p.tok == .semicolon && p.peek() == .dollar) {
2028 if p.tok == .semicolon {
2029 p.next()
2030 }
2031 return p.parse_comptime_if()
2032 }
2033 return p.block_stmt()
2034}
2035
2036fn (mut p Parser) parse_top_level_comptime_else() flat.NodeId {
2037 // Skip auto-semicolons before $else
2038 if p.tok == .semicolon && p.peek() == .dollar {
2039 p.next()
2040 }
2041 if p.tok != .dollar {
2042 return flat.empty_node
2043 }
2044 if p.peek() != .key_else {
2045 return flat.empty_node
2046 }
2047 p.next() // skip $
2048 p.next() // skip else
2049 // $else $if - recurse
2050 if p.tok == .dollar || (p.tok == .semicolon && p.peek() == .dollar) {
2051 if p.tok == .semicolon {
2052 p.next()
2053 }
2054 return p.parse_top_level_comptime_if()
2055 }
2056 return p.top_level_block_stmt()
2057}
2058
2059fn eval_comptime_cond(prefs &pref.Preferences, cond string) bool {
2060 c := comptime_cond_strip_outer_parens(cond.trim_space())
2061 left_or, right_or, has_or := comptime_cond_split_top_level(c, '||')
2062 if has_or {
2063 return eval_comptime_cond(prefs, left_or) || eval_comptime_cond(prefs, right_or)
2064 }
2065 left_and, right_and, has_and := comptime_cond_split_top_level(c, '&&')
2066 if has_and {
2067 return eval_comptime_cond(prefs, left_and) && eval_comptime_cond(prefs, right_and)
2068 }
2069 if c.starts_with('!') {
2070 return !eval_comptime_cond(prefs, c[1..])
2071 }
2072 if c == 'true' {
2073 return true
2074 }
2075 if c == 'false' {
2076 return false
2077 }
2078 if c.starts_with('pkgconfig') {
2079 return eval_pkgconfig_cond(c)
2080 }
2081 flag := c.trim_space().trim_right('? ')
2082 return pref.comptime_flag_value(prefs, flag)
2083}
2084
2085fn comptime_cond_strip_outer_parens(cond string) string {
2086 mut c := cond
2087 for c.len >= 2 && c[0] == `(` && c[c.len - 1] == `)` {
2088 mut depth := 0
2089 mut quote := u8(0)
2090 mut wraps := true
2091 for i in 0 .. c.len {
2092 ch := c[i]
2093 if quote != 0 {
2094 if ch == quote {
2095 quote = 0
2096 }
2097 continue
2098 }
2099 if ch == `'` || ch == `"` {
2100 quote = ch
2101 continue
2102 }
2103 if ch == `(` {
2104 depth++
2105 } else if ch == `)` {
2106 depth--
2107 if depth == 0 && i != c.len - 1 {
2108 wraps = false
2109 break
2110 }
2111 }
2112 }
2113 if !wraps {
2114 break
2115 }
2116 c = c[1..c.len - 1].trim_space()
2117 }
2118 return c
2119}
2120
2121fn comptime_cond_split_top_level(cond string, op string) (string, string, bool) {
2122 mut depth := 0
2123 mut quote := u8(0)
2124 mut i := 0
2125 for i < cond.len {
2126 ch := cond[i]
2127 if quote != 0 {
2128 if ch == quote {
2129 quote = 0
2130 }
2131 i++
2132 continue
2133 }
2134 if ch == `'` || ch == `"` {
2135 quote = ch
2136 i++
2137 continue
2138 }
2139 if ch == `(` {
2140 depth++
2141 } else if ch == `)` && depth > 0 {
2142 depth--
2143 }
2144 if depth == 0 && i + op.len <= cond.len && cond[i..i + op.len] == op {
2145 return cond[..i], cond[i + op.len..], true
2146 }
2147 i++
2148 }
2149 return '', '', false
2150}
2151
2152fn eval_pkgconfig_cond(cond string) bool {
2153 name := pkgconfig_name_from_cond(cond)
2154 if !is_safe_pkgconfig_name(name) {
2155 return false
2156 }
2157 return os.system('pkg-config --exists ${name}') == 0
2158}
2159
2160fn is_safe_pkgconfig_name(name string) bool {
2161 if name.len == 0 || !pkgconfig_name_char_is_alnum(name[0]) {
2162 return false
2163 }
2164 for ch in name {
2165 if pkgconfig_name_char_is_alnum(ch) || ch == `_` || ch == `-` || ch == `.` || ch == `+` {
2166 continue
2167 }
2168 return false
2169 }
2170 return true
2171}
2172
2173fn pkgconfig_name_char_is_alnum(ch u8) bool {
2174 return (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`) || (ch >= `0` && ch <= `9`)
2175}
2176
2177fn pkgconfig_name_from_cond(cond string) string {
2178 quote1 := cond.index_u8(`'`)
2179 if quote1 >= 0 {
2180 rest := cond[quote1 + 1..]
2181 end := rest.index_u8(`'`)
2182 if end >= 0 {
2183 return rest[..end]
2184 }
2185 }
2186 quote2 := cond.index_u8(`"`)
2187 if quote2 >= 0 {
2188 rest := cond[quote2 + 1..]
2189 end := rest.index_u8(`"`)
2190 if end >= 0 {
2191 return rest[..end]
2192 }
2193 }
2194 open := cond.index_u8(`(`)
2195 close := cond.last_index_u8(`)`)
2196 if open < 0 || close < 0 {
2197 return ''
2198 }
2199 if close <= open {
2200 return ''
2201 }
2202 return cond[open + 1..close].trim_space().trim('\'"')
2203}
2204
2205fn (mut p Parser) skip_block() {
2206 if p.tok != .lcbr {
2207 return
2208 }
2209 mut depth := 1
2210 p.next()
2211 for depth > 0 && p.tok != .eof {
2212 if p.tok == .lcbr {
2213 depth++
2214 } else if p.tok == .rcbr {
2215 depth--
2216 }
2217 p.next()
2218 }
2219}
2220
2221fn (mut p Parser) skip_brackets() {
2222 if p.tok != .lsbr {
2223 return
2224 }
2225 mut depth := 1
2226 p.next()
2227 for depth > 0 && p.tok != .eof {
2228 if p.tok == .lsbr {
2229 depth++
2230 } else if p.tok == .rsbr {
2231 depth--
2232 }
2233 p.next()
2234 }
2235}
2236
2237fn (mut p Parser) parse_generic_param_names() []string {
2238 mut names := []string{}
2239 if p.tok != .lsbr {
2240 return names
2241 }
2242 mut depth := 1
2243 mut expect_name := true
2244 p.next()
2245 for depth > 0 && p.tok != .eof {
2246 if p.tok == .lsbr {
2247 depth++
2248 expect_name = false
2249 } else if p.tok == .rsbr {
2250 depth--
2251 if depth == 0 {
2252 p.next()
2253 break
2254 }
2255 } else if depth == 1 {
2256 if p.tok == .comma {
2257 expect_name = true
2258 } else if expect_name && p.tok == .name {
2259 names << p.lit
2260 expect_name = false
2261 } else {
2262 expect_name = false
2263 }
2264 }
2265 p.next()
2266 }
2267 return names
2268}
2269
2270fn (mut p Parser) skip_parens() {
2271 if p.tok != .lpar {
2272 return
2273 }
2274 mut depth := 1
2275 p.next()
2276 for depth > 0 && p.tok != .eof {
2277 if p.tok == .lpar {
2278 depth++
2279 } else if p.tok == .rpar {
2280 depth--
2281 }
2282 p.next()
2283 }
2284}
2285
2286fn (mut p Parser) parse_comptime_expr() flat.NodeId {
2287 if p.peek() == .key_for {
2288 return p.parse_comptime_if()
2289 }
2290 p.next() // skip $
2291 if p.tok == .key_if || (p.tok == .name && p.lit == 'if') {
2292 return p.parse_comptime_if_expr_after_if()
2293 }
2294 if p.tok == .name && p.lit == 'd' {
2295 p.next()
2296 if p.tok != .lpar {
2297 return flat.empty_node
2298 }
2299 p.next()
2300 // First argument is the compile-time define name. v3 currently only
2301 // uses the default expression, so consume the name expression.
2302 if p.tok != .comma && p.tok != .rpar && p.tok != .eof {
2303 p.expr(.lowest)
2304 }
2305 if p.tok == .comma {
2306 p.next()
2307 default_expr := p.expr(.lowest)
2308 for p.tok != .rpar && p.tok != .eof {
2309 p.next()
2310 }
2311 p.check(.rpar)
2312 return default_expr
2313 }
2314 p.check(.rpar)
2315 return flat.empty_node
2316 }
2317 if p.tok == .name && p.lit == 'res' {
2318 p.next()
2319 if p.tok == .lpar {
2320 p.skip_parens()
2321 }
2322 return p.a.add_val_id(3, 'false')
2323 }
2324 if p.tok == .name && p.lit == 'env' {
2325 // $env('NAME') evaluates an environment variable at compile time and
2326 // becomes a plain string literal (empty when the variable is unset).
2327 p.next() // skip 'env'
2328 mut env_val := ''
2329 if p.tok == .lpar {
2330 p.next() // skip (
2331 if p.tok == .string {
2332 env_val = os.getenv(strip_quotes(p.lit))
2333 p.next()
2334 }
2335 for p.tok != .rpar && p.tok != .eof {
2336 p.next()
2337 }
2338 p.check(.rpar)
2339 }
2340 return p.a.add_val_id(5, env_val)
2341 }
2342 for p.tok != .semicolon && p.tok != .eof {
2343 p.next()
2344 }
2345 // Unknown comptime expression: return an empty string literal rather than
2346 // `empty_node`, so consumers (e.g. const initializers) never store an
2347 // invalid (-1) child node.
2348 return p.a.add_val_id(5, '')
2349}
2350
2351fn (mut p Parser) parse_comptime_if_expr() flat.NodeId {
2352 p.next() // skip $
2353 if p.tok != .key_if && !(p.tok == .name && p.lit == 'if') {
2354 return flat.empty_node
2355 }
2356 return p.parse_comptime_if_expr_after_if()
2357}
2358
2359fn (mut p Parser) parse_comptime_if_expr_after_if() flat.NodeId {
2360 p.next() // skip if
2361 cond := p.parse_comptime_cond()
2362 taken := eval_comptime_cond(p.prefs, cond)
2363 if taken {
2364 result := p.parse_comptime_expr_block()
2365 p.skip_comptime_else()
2366 return result
2367 }
2368 p.skip_block()
2369 return p.parse_comptime_else_expr()
2370}
2371
2372fn (mut p Parser) parse_comptime_else_expr() flat.NodeId {
2373 if p.tok == .semicolon && p.peek() == .dollar {
2374 p.next()
2375 }
2376 if p.tok != .dollar || p.peek() != .key_else {
2377 return flat.empty_node
2378 }
2379 p.next() // skip $
2380 p.next() // skip else
2381 if p.tok == .semicolon && p.peek() == .dollar {
2382 p.next()
2383 }
2384 if p.tok == .dollar {
2385 return p.parse_comptime_if_expr()
2386 }
2387 return p.parse_comptime_expr_block()
2388}
2389
2390fn (mut p Parser) parse_comptime_expr_block() flat.NodeId {
2391 if p.tok != .lcbr {
2392 return flat.empty_node
2393 }
2394 p.next()
2395 if p.tok == .rcbr {
2396 p.next()
2397 return flat.empty_node
2398 }
2399 result := p.expr(.lowest)
2400 for p.tok != .rcbr && p.tok != .eof {
2401 p.next()
2402 }
2403 if p.tok == .rcbr {
2404 p.next()
2405 }
2406 return result
2407}
2408
2409// ==================== statements ====================
2410
2411fn (mut p Parser) stmt() flat.NodeId {
2412 match p.tok {
2413 .key_return {
2414 return p.return_stmt()
2415 }
2416 .key_if {
2417 return p.if_stmt()
2418 }
2419 .key_for {
2420 return p.for_stmt()
2421 }
2422 .key_fn {
2423 return p.fn_decl()
2424 }
2425 .key_match {
2426 return p.match_stmt()
2427 }
2428 .key_break {
2429 p.next()
2430 mut label := ''
2431 if p.tok == .name {
2432 label = p.lit
2433 p.next()
2434 }
2435 if p.tok == .semicolon {
2436 p.next()
2437 }
2438 return p.a.add_val(.break_stmt, label)
2439 }
2440 .key_continue {
2441 p.next()
2442 mut label := ''
2443 if p.tok == .name {
2444 label = p.lit
2445 p.next()
2446 }
2447 if p.tok == .semicolon {
2448 p.next()
2449 }
2450 return p.a.add_val(.continue_stmt, label)
2451 }
2452 .key_mut {
2453 p.next()
2454 if p.tok == .key_static {
2455 return p.static_decl_stmt()
2456 }
2457 return p.assign_or_expr_stmt()
2458 }
2459 .key_static {
2460 return p.static_decl_stmt()
2461 }
2462 .key_pub {
2463 p.next()
2464 if p.tok == .key_fn {
2465 return p.fn_decl()
2466 }
2467 return p.assign_or_expr_stmt()
2468 }
2469 .attribute {
2470 p.skip_attrs()
2471 return p.stmt()
2472 }
2473 .key_unsafe {
2474 p.next()
2475 return p.block_stmt()
2476 }
2477 .key_defer {
2478 return p.defer_stmt()
2479 }
2480 .key_assert {
2481 return p.assert_stmt()
2482 }
2483 .key_goto {
2484 return p.goto_stmt()
2485 }
2486 .key_go, .key_spawn {
2487 p.next()
2488 spawn_expr := p.expr(.lowest)
2489 if p.tok == .semicolon {
2490 p.next()
2491 }
2492 sstart := p.add_child(spawn_expr)
2493 return p.a.add_node(flat.Node{
2494 kind: .expr_stmt
2495 children_start: sstart
2496 children_count: 1
2497 })
2498 }
2499 .key_asm {
2500 return p.asm_stmt()
2501 }
2502 .dollar {
2503 return p.parse_comptime_if()
2504 }
2505 .hash {
2506 return p.directive()
2507 }
2508 .lcbr {
2509 return p.block_stmt()
2510 }
2511 .semicolon {
2512 p.next()
2513 return flat.empty_node
2514 }
2515 .name {
2516 // label: name followed by ':'
2517 if p.peek() == .colon {
2518 label_name := p.lit
2519 p.next() // skip name
2520 p.next() // skip :
2521 if p.tok == .semicolon {
2522 p.next()
2523 }
2524 return p.a.add_val(.label_stmt, label_name)
2525 }
2526 return p.assign_or_expr_stmt()
2527 }
2528 else {
2529 return p.assign_or_expr_stmt()
2530 }
2531 }
2532}
2533
2534fn (mut p Parser) static_decl_stmt() flat.NodeId {
2535 p.next() // skip `static`
2536 if p.tok == .key_mut {
2537 p.next()
2538 }
2539 lhs := p.expr(.lowest)
2540 if p.tok == .decl_assign {
2541 p.next()
2542 rhs := p.expr(.lowest)
2543 if p.tok == .semicolon {
2544 p.next()
2545 }
2546 istart := p.add_children2(lhs, rhs)
2547 return p.a.add_node(flat.Node{
2548 kind: .decl_assign
2549 op: .assign
2550 value: 'static'
2551 children_start: istart
2552 children_count: 2
2553 })
2554 }
2555 if p.tok == .semicolon {
2556 p.next()
2557 }
2558 estart := p.add_child(lhs)
2559 return p.a.add_node(flat.Node{
2560 kind: .expr_stmt
2561 children_start: estart
2562 children_count: 1
2563 })
2564}
2565
2566fn (mut p Parser) return_stmt() flat.NodeId {
2567 p.next() // skip 'return'
2568 mut ids := []flat.NodeId{}
2569 if p.tok != .semicolon && p.tok != .rcbr && p.tok != .eof {
2570 ids << p.expr(.lowest)
2571 for p.tok == .comma {
2572 p.next()
2573 ids << p.expr(.lowest)
2574 }
2575 }
2576 if p.tok == .semicolon {
2577 p.next()
2578 }
2579 start := p.add_children(ids)
2580 return p.a.add_node(flat.Node{
2581 kind: .return_stmt
2582 children_start: start
2583 children_count: flat.child_count(ids.len)
2584 })
2585}
2586
2587fn (mut p Parser) if_stmt() flat.NodeId {
2588 p.next() // skip 'if'
2589 cond := p.expr(.lowest)
2590
2591 // if-guard: if a, b := expr { ... } or if val := expr { ... }
2592 mut guard_cond := cond
2593 if p.tok == .comma || p.tok == .decl_assign {
2594 // Simple if-guard; treat as regular condition for flat AST
2595 if p.tok == .decl_assign {
2596 p.next()
2597 rhs := p.expr(.lowest)
2598 istart := p.add_children2(guard_cond, rhs)
2599 guard_cond = p.a.add_node(flat.Node{
2600 kind: .decl_assign
2601 op: .assign
2602 children_start: istart
2603 children_count: 2
2604 })
2605 } else {
2606 // comma case: if a, b := expr
2607 mut lhs_ids := []flat.NodeId{}
2608 lhs_ids << guard_cond
2609 for p.tok == .comma {
2610 p.next()
2611 lhs_ids << p.expr(.lowest)
2612 }
2613 if p.tok == .decl_assign {
2614 p.next()
2615 rhs := p.expr(.lowest)
2616 mut all_ids := []flat.NodeId{cap: lhs_ids.len + 1}
2617 all_ids << lhs_ids[0]
2618 all_ids << rhs
2619 for i in 1 .. lhs_ids.len {
2620 all_ids << lhs_ids[i]
2621 }
2622 istart := p.add_children(all_ids)
2623 guard_cond = p.a.add_node(flat.Node{
2624 kind: .decl_assign
2625 op: .assign
2626 children_start: istart
2627 children_count: flat.child_count(all_ids.len)
2628 })
2629 }
2630 }
2631 }
2632
2633 // skip auto-semicolon before {
2634 if p.tok == .semicolon && p.peek() == .lcbr {
2635 p.next()
2636 }
2637 body := p.block_stmt()
2638 mut ids := []flat.NodeId{}
2639 ids << guard_cond
2640 ids << body
2641
2642 // skip auto-semicolon between } and else
2643 if p.tok == .semicolon && p.peek() == .key_else {
2644 p.next()
2645 }
2646 if p.tok == .key_else {
2647 p.next()
2648 if p.tok == .key_if {
2649 ids << p.if_stmt()
2650 } else {
2651 ids << p.block_stmt()
2652 }
2653 }
2654 start := p.add_children(ids)
2655 return p.a.add_node(flat.Node{
2656 kind: .if_expr
2657 children_start: start
2658 children_count: flat.child_count(ids.len)
2659 })
2660}
2661
2662fn (mut p Parser) for_stmt() flat.NodeId {
2663 p.next() // skip 'for'
2664 if p.tok == .lcbr {
2665 // infinite loop: for { ... }
2666 body_ids := p.parse_block_body()
2667 empty1 := p.a.add(flat.NodeKind.empty)
2668 empty2 := p.a.add(flat.NodeKind.empty)
2669 empty3 := p.a.add(flat.NodeKind.empty)
2670 mut ids := []flat.NodeId{}
2671 ids << empty1
2672 ids << empty2
2673 ids << empty3
2674 for id in body_ids {
2675 ids << id
2676 }
2677 start := p.add_children(ids)
2678 return p.a.add_node(flat.Node{
2679 kind: .for_stmt
2680 children_start: start
2681 children_count: flat.child_count(ids.len)
2682 })
2683 }
2684
2685 // Check for for-in: `for x in ...` or `for i, x in ...` or `for mut x in ...`
2686 if p.tok == .name && (p.peek() == .key_in || p.peek() == .comma) {
2687 first_expr := p.expr(.bit_or)
2688 return p.for_in(first_expr, false)
2689 }
2690 if p.tok == .key_mut {
2691 p.next()
2692 mut first_expr := flat.empty_node
2693 if p.tok == .name {
2694 ident := p.a.add_val(.ident, p.expect_name())
2695 if p.tok == .key_in || p.tok == .comma {
2696 first_expr = ident
2697 } else {
2698 first_expr = p.expr_with_lhs(ident, .lowest)
2699 }
2700 } else {
2701 first_expr = p.expr(.lowest)
2702 }
2703 if p.tok == .key_in || p.tok == .comma {
2704 return p.for_in(first_expr, true)
2705 }
2706 if p.tok == .lcbr {
2707 body_ids := p.parse_block_body()
2708 init_empty := p.a.add(flat.NodeKind.empty)
2709 post_empty := p.a.add(flat.NodeKind.empty)
2710 mut ids := []flat.NodeId{}
2711 ids << init_empty
2712 ids << first_expr
2713 ids << post_empty
2714 for id in body_ids {
2715 ids << id
2716 }
2717 start := p.add_children(ids)
2718 return p.a.add_node(flat.Node{
2719 kind: .for_stmt
2720 children_start: start
2721 children_count: flat.child_count(ids.len)
2722 })
2723 }
2724 }
2725
2726 if p.tok == .semicolon {
2727 p.next()
2728 cond := if p.tok == .semicolon {
2729 p.a.add(flat.NodeKind.empty)
2730 } else {
2731 p.expr(.lowest)
2732 }
2733 if p.tok == .semicolon {
2734 p.next()
2735 }
2736 post := if p.tok != .lcbr && p.tok != .eof {
2737 p.assign_or_expr_inline()
2738 } else {
2739 p.a.add(flat.NodeKind.empty)
2740 }
2741 body_ids := p.parse_block_body()
2742 mut ids := []flat.NodeId{}
2743 ids << p.a.add(flat.NodeKind.empty)
2744 ids << cond
2745 ids << post
2746 for id in body_ids {
2747 ids << id
2748 }
2749 start := p.add_children(ids)
2750 return p.a.add_node(flat.Node{
2751 kind: .for_stmt
2752 children_start: start
2753 children_count: flat.child_count(ids.len)
2754 })
2755 }
2756
2757 first_expr := p.expr(.lowest)
2758
2759 // for-in: `for x in expr` or `for i, x in expr`
2760 if p.tok == .key_in || p.tok == .comma {
2761 return p.for_in(first_expr, false)
2762 }
2763
2764 // C-style: `for i := 0; ...`
2765 if p.tok == .decl_assign || token_is_assignment(p.tok) {
2766 return p.for_c_style(first_expr)
2767 }
2768
2769 if p.tok == .semicolon {
2770 // could be C-style with expression init, or just a separator
2771 // peek to check
2772 pk := p.peek()
2773 if pk == .semicolon || pk == .lcbr {
2774 // condition-only: `for cond { }`
2775 // fall through
2776 } else if pk != .rcbr && pk != .eof {
2777 // might be C-style: `for expr; cond; post`
2778 p.next() // skip ;
2779 cond := p.expr(.lowest)
2780 if p.tok == .semicolon {
2781 p.next()
2782 }
2783 post := if p.tok != .lcbr && p.tok != .eof {
2784 p.assign_or_expr_inline()
2785 } else {
2786 p.a.add(flat.NodeKind.empty)
2787 }
2788 body_ids := p.parse_block_body()
2789 // first_expr becomes init as expr_stmt
2790 init_start := p.add_child(first_expr)
2791 init_id := p.a.add_node(flat.Node{
2792 kind: .expr_stmt
2793 children_start: init_start
2794 children_count: 1
2795 })
2796 mut ids := []flat.NodeId{}
2797 ids << init_id
2798 ids << cond
2799 ids << post
2800 for id in body_ids {
2801 ids << id
2802 }
2803 start := p.add_children(ids)
2804 return p.a.add_node(flat.Node{
2805 kind: .for_stmt
2806 children_start: start
2807 children_count: flat.child_count(ids.len)
2808 })
2809 }
2810 }
2811
2812 // condition-only: `for cond { ... }`
2813 if p.tok == .lcbr {
2814 body_ids := p.parse_block_body()
2815 init_empty := p.a.add(flat.NodeKind.empty)
2816 post_empty := p.a.add(flat.NodeKind.empty)
2817 mut ids := []flat.NodeId{}
2818 ids << init_empty
2819 ids << first_expr
2820 ids << post_empty
2821 for id in body_ids {
2822 ids << id
2823 }
2824 start := p.add_children(ids)
2825 return p.a.add_node(flat.Node{
2826 kind: .for_stmt
2827 children_start: start
2828 children_count: flat.child_count(ids.len)
2829 })
2830 }
2831
2832 return flat.empty_node
2833}
2834
2835fn (mut p Parser) for_c_style(lhs_expr flat.NodeId) flat.NodeId {
2836 op_id := int(p.tok)
2837 p.next()
2838 rhs := p.expr(.lowest)
2839
2840 mut init_id := flat.empty_node
2841 if op_id == 12 {
2842 istart := p.add_children2(lhs_expr, rhs)
2843 init_id = p.a.add_node(flat.Node{
2844 kind: .decl_assign
2845 op: .assign
2846 children_start: istart
2847 children_count: 2
2848 })
2849 } else {
2850 istart := p.add_children2(lhs_expr, rhs)
2851 init_id = p.a.add_node(flat.Node{
2852 kind: .assign
2853 op: token_id_to_op(op_id)
2854 children_start: istart
2855 children_count: 2
2856 })
2857 }
2858
2859 if p.tok == .semicolon {
2860 p.next()
2861 }
2862
2863 cond := p.expr(.lowest)
2864 if p.tok == .semicolon {
2865 p.next()
2866 }
2867
2868 post := if p.tok != .lcbr && p.tok != .eof {
2869 p.assign_or_expr_inline()
2870 } else {
2871 p.a.add(flat.NodeKind.empty)
2872 }
2873
2874 body_ids := p.parse_block_body()
2875
2876 mut ids := []flat.NodeId{}
2877 ids << init_id
2878 ids << cond
2879 ids << post
2880 for id in body_ids {
2881 ids << id
2882 }
2883 start := p.add_children(ids)
2884 return p.a.add_node(flat.Node{
2885 kind: .for_stmt
2886 children_start: start
2887 children_count: flat.child_count(ids.len)
2888 })
2889}
2890
2891fn (mut p Parser) for_in(first_expr flat.NodeId, first_is_mut bool) flat.NodeId {
2892 // first_expr is either the key var or the only var
2893 mut key_id := first_expr
2894 mut val_id := flat.empty_node
2895 mut value_is_mut := first_is_mut
2896
2897 if p.tok == .comma {
2898 p.next()
2899 // second variable
2900 if p.tok == .key_mut {
2901 p.next()
2902 value_is_mut = true
2903 }
2904 val_id = p.a.add_val(.ident, p.expect_name())
2905 }
2906
2907 p.check(.key_in)
2908 was_in_for_container := p.in_for_container
2909 p.in_for_container = true
2910 container := p.expr(.lowest)
2911 p.in_for_container = was_in_for_container
2912
2913 // optional range: `for i in 0 .. n`
2914 mut range_end := flat.empty_node
2915 if p.tok == .dotdot {
2916 p.next()
2917 range_end = p.expr(.lowest)
2918 }
2919
2920 body_ids := p.parse_block_body()
2921
2922 mut ids := []flat.NodeId{cap: 4 + body_ids.len}
2923 ids << key_id
2924 ids << val_id
2925 ids << container
2926 if int(range_end) >= 0 {
2927 ids << range_end
2928 }
2929 for id in body_ids {
2930 ids << id
2931 }
2932 start := p.add_children(ids)
2933 return p.a.add_node(flat.Node{
2934 kind: .for_in_stmt
2935 children_start: start
2936 children_count: flat.child_count(ids.len)
2937 // value field stores the count of header elements (key, val, container, [range_end])
2938 // so gen knows where body starts
2939 value: if int(range_end) >= 0 { '4' } else { '3' }
2940 op: if value_is_mut { .amp } else { .none }
2941 })
2942}
2943
2944fn (mut p Parser) match_stmt() flat.NodeId {
2945 p.next() // skip 'match'
2946 match_expr := p.expr(.lowest)
2947 p.check(.lcbr)
2948
2949 mut ids := []flat.NodeId{cap: 8}
2950 ids << match_expr
2951
2952 for p.tok != .rcbr && p.tok != .eof {
2953 if p.tok == .semicolon {
2954 p.next()
2955 continue
2956 }
2957 ids << p.match_branch()
2958 }
2959 p.check(.rcbr)
2960
2961 start := p.add_children(ids)
2962 return p.a.add_node(flat.Node{
2963 kind: .match_stmt
2964 children_start: start
2965 children_count: flat.child_count(ids.len)
2966 })
2967}
2968
2969fn (mut p Parser) match_branch_cond() flat.NodeId {
2970 if p.tok == .lsbr && p.peek() == .rsbr {
2971 typ := p.parse_type_name()
2972 elem_type := if typ.starts_with('[]') { typ[2..] } else { typ }
2973 return p.a.add_node(flat.Node{
2974 kind: .array_init
2975 value: elem_type
2976 typ: typ
2977 })
2978 }
2979 if p.tok == .name && p.peek() == .dot {
2980 mod_name := p.lit
2981 p.next()
2982 p.next()
2983 if p.tok == .name && p.lit.len > 0 && p.lit[0] >= `A` && p.lit[0] <= `Z` {
2984 type_name := p.lit
2985 p.next()
2986 mod_id := p.a.add_val(.ident, mod_name)
2987 start := p.add_child(mod_id)
2988 return p.a.add_node(flat.Node{
2989 kind: .selector
2990 value: type_name
2991 children_start: start
2992 children_count: 1
2993 })
2994 }
2995 base_id := p.a.add_val(.ident, mod_name)
2996 start := p.add_child(base_id)
2997 sel := p.a.add_node(flat.Node{
2998 kind: .selector
2999 value: p.lit
3000 children_start: start
3001 children_count: 1
3002 })
3003 if p.tok == .name {
3004 p.next()
3005 }
3006 if p.tok != .lcbr && p.tok != .comma {
3007 return p.expr_with_lhs(sel, .lowest)
3008 }
3009 return sel
3010 }
3011 if p.tok == .name && p.lit.len > 0 && p.lit[0] >= `A` && p.lit[0] <= `Z` {
3012 name := p.lit
3013 p.next()
3014 return p.a.add_val(.ident, name)
3015 }
3016 if p.tok == .name && is_builtin_type(p.lit) && p.peek() == .lcbr {
3017 name := p.lit
3018 p.next()
3019 return p.a.add_val(.ident, name)
3020 }
3021 cond := p.expr(.lowest)
3022 if p.tok == .ellipsis {
3023 p.next()
3024 rhs := p.expr(.lowest)
3025 rstart := p.add_children2(cond, rhs)
3026 return p.a.add_node(flat.Node{
3027 kind: .range
3028 children_start: rstart
3029 children_count: 2
3030 })
3031 }
3032 return cond
3033}
3034
3035fn (mut p Parser) match_branch() flat.NodeId {
3036 mut branch_ids := []flat.NodeId{}
3037 mut is_else := false
3038 mut n_conds := 0
3039
3040 if p.tok == .key_else {
3041 is_else = true
3042 p.next()
3043 } else {
3044 branch_ids << p.match_branch_cond()
3045 n_conds = 1
3046 for p.tok == .comma {
3047 p.next()
3048 branch_ids << p.match_branch_cond()
3049 n_conds++
3050 }
3051 }
3052
3053 p.check(.lcbr)
3054 for p.tok != .rcbr && p.tok != .eof {
3055 if p.looks_like_match_branch_start() {
3056 break
3057 }
3058 id := p.stmt()
3059 if int(id) >= 0 {
3060 branch_ids << id
3061 }
3062 }
3063 p.check(.rcbr)
3064
3065 bstart := p.add_children(branch_ids)
3066 return p.a.add_node(flat.Node{
3067 kind: .match_branch
3068 value: if is_else { 'else' } else { '${n_conds}' }
3069 children_start: bstart
3070 children_count: flat.child_count(branch_ids.len)
3071 })
3072}
3073
3074fn (p &Parser) looks_like_match_branch_start() bool {
3075 if p.tok == .key_else {
3076 return true
3077 }
3078 return false
3079}
3080
3081fn (mut p Parser) block_stmt() flat.NodeId {
3082 ids := p.parse_block_body()
3083 start := p.add_children(ids)
3084 return p.a.add_node(flat.Node{
3085 kind: .block
3086 children_start: start
3087 children_count: flat.child_count(ids.len)
3088 })
3089}
3090
3091fn (mut p Parser) parse_block_body() []flat.NodeId {
3092 p.check(.lcbr)
3093 mut ids := []flat.NodeId{}
3094 for p.tok != .rcbr && p.tok != .eof {
3095 id := p.stmt()
3096 if int(id) >= 0 {
3097 ids << id
3098 }
3099 }
3100 p.check(.rcbr)
3101 return ids
3102}
3103
3104fn (mut p Parser) assign_or_expr_stmt() flat.NodeId {
3105 lhs := p.expr(.lowest)
3106
3107 // multi-assign: a, b := expr1, expr2
3108 if p.tok == .comma {
3109 mut lhs_ids := []flat.NodeId{}
3110 lhs_ids << lhs
3111 for p.tok == .comma {
3112 p.next()
3113 if p.tok == .key_mut {
3114 p.next()
3115 }
3116 lhs_ids << p.expr(.lowest)
3117 }
3118 if p.tok == .decl_assign || token_is_assignment(p.tok) {
3119 op_id := int(p.tok)
3120 p.next()
3121 mut rhs_ids := []flat.NodeId{}
3122 rhs_ids << p.expr(.lowest)
3123 for p.tok == .comma {
3124 p.next()
3125 rhs_ids << p.expr(.lowest)
3126 }
3127 if p.tok == .semicolon {
3128 p.next()
3129 }
3130 mut all_ids := []flat.NodeId{cap: lhs_ids.len * 2}
3131 for i in 0 .. lhs_ids.len {
3132 all_ids << lhs_ids[i]
3133 if i < rhs_ids.len {
3134 all_ids << rhs_ids[i]
3135 }
3136 }
3137 istart := p.add_children(all_ids)
3138 return p.a.add_node(flat.Node{
3139 kind: if op_id == 12 {
3140 flat.NodeKind.decl_assign
3141 } else {
3142 flat.NodeKind.assign
3143 }
3144 op: token_id_to_op(op_id)
3145 children_start: istart
3146 children_count: flat.child_count(all_ids.len)
3147 })
3148 }
3149 if p.tok == .semicolon {
3150 p.next()
3151 }
3152 mut stmt_ids := []flat.NodeId{cap: lhs_ids.len}
3153 for expr_id in lhs_ids {
3154 estart := p.add_child(expr_id)
3155 stmt_ids << p.a.add_node(flat.Node{
3156 kind: .expr_stmt
3157 children_start: estart
3158 children_count: 1
3159 })
3160 }
3161 bstart := p.add_children(stmt_ids)
3162 return p.a.add_node(flat.Node{
3163 kind: .block
3164 children_start: bstart
3165 children_count: flat.child_count(stmt_ids.len)
3166 })
3167 }
3168
3169 if p.tok == .decl_assign {
3170 p.next()
3171 rhs := p.expr(.lowest)
3172 if p.tok == .semicolon {
3173 p.next()
3174 }
3175 istart := p.add_children2(lhs, rhs)
3176 return p.a.add_node(flat.Node{
3177 kind: .decl_assign
3178 op: .assign
3179 children_start: istart
3180 children_count: 2
3181 })
3182 }
3183
3184 if token_is_assignment(p.tok) {
3185 op_id := int(p.tok)
3186 p.next()
3187 rhs := p.expr(.lowest)
3188 if p.tok == .semicolon {
3189 p.next()
3190 }
3191 lhs_node := p.a.nodes[int(lhs)]
3192 kind := if lhs_node.kind == .selector {
3193 flat.NodeKind.selector_assign
3194 } else if lhs_node.kind == .index {
3195 flat.NodeKind.index_assign
3196 } else {
3197 flat.NodeKind.assign
3198 }
3199 istart := p.add_children2(lhs, rhs)
3200 return p.a.add_node(flat.Node{
3201 kind: kind
3202 op: token_id_to_op(op_id)
3203 children_start: istart
3204 children_count: 2
3205 })
3206 }
3207
3208 if p.tok == .semicolon {
3209 p.next()
3210 }
3211
3212 estart := p.add_child(lhs)
3213 return p.a.add_node(flat.Node{
3214 kind: .expr_stmt
3215 children_start: estart
3216 children_count: 1
3217 })
3218}
3219
3220fn (mut p Parser) assign_or_expr_inline() flat.NodeId {
3221 lhs := p.expr(.lowest)
3222
3223 if token_is_assignment(p.tok) {
3224 op_id := int(p.tok)
3225 p.next()
3226 rhs := p.expr(.lowest)
3227 lhs_node := p.a.nodes[int(lhs)]
3228 kind := if lhs_node.kind == .selector {
3229 flat.NodeKind.selector_assign
3230 } else if lhs_node.kind == .index {
3231 flat.NodeKind.index_assign
3232 } else {
3233 flat.NodeKind.assign
3234 }
3235 istart := p.add_children2(lhs, rhs)
3236 return p.a.add_node(flat.Node{
3237 kind: kind
3238 op: token_id_to_op(op_id)
3239 children_start: istart
3240 children_count: 2
3241 })
3242 }
3243
3244 estart := p.add_child(lhs)
3245 return p.a.add_node(flat.Node{
3246 kind: .expr_stmt
3247 children_start: estart
3248 children_count: 1
3249 })
3250}
3251
3252fn (mut p Parser) defer_stmt() flat.NodeId {
3253 p.next() // skip 'defer'
3254 mut mode := ''
3255 if p.tok == .lpar {
3256 p.next()
3257 if p.tok == .key_fn {
3258 mode = 'function'
3259 p.next()
3260 }
3261 p.check(.rpar)
3262 }
3263 body := p.block_stmt()
3264 dstart := p.add_child(body)
3265 return p.a.add_node(flat.Node{
3266 kind: .defer_stmt
3267 children_start: dstart
3268 children_count: 1
3269 value: mode
3270 })
3271}
3272
3273fn (mut p Parser) assert_stmt() flat.NodeId {
3274 p.next() // skip 'assert'
3275 cond := p.expr(.lowest)
3276 mut ids := []flat.NodeId{}
3277 ids << cond
3278 // optional message: assert cond, 'message'
3279 if p.tok == .comma {
3280 p.next()
3281 ids << p.expr(.lowest)
3282 }
3283 if p.tok == .semicolon {
3284 p.next()
3285 }
3286 astart := p.add_children(ids)
3287 return p.a.add_node(flat.Node{
3288 kind: .assert_stmt
3289 children_start: astart
3290 children_count: flat.child_count(ids.len)
3291 })
3292}
3293
3294fn (mut p Parser) goto_stmt() flat.NodeId {
3295 p.next() // skip 'goto'
3296 label := p.expect_name()
3297 if p.tok == .semicolon {
3298 p.next()
3299 }
3300 return p.a.add_val(.goto_stmt, label)
3301}
3302
3303fn (mut p Parser) asm_stmt() flat.NodeId {
3304 p.next() // skip 'asm'
3305 // consume optional volatile keyword
3306 if p.tok == .name && p.lit == 'volatile' {
3307 p.next()
3308 }
3309 // consume the asm block
3310 if p.tok == .lcbr {
3311 p.skip_block()
3312 }
3313 if p.tok == .semicolon {
3314 p.next()
3315 }
3316 return p.a.add(flat.NodeKind.asm_stmt)
3317}
3318
3319// ==================== expressions (Pratt parser) ====================
3320
3321fn (mut p Parser) expr(min_bp token.BindingPower) flat.NodeId {
3322 lhs := p.prefix_expr()
3323 return p.expr_with_lhs(lhs, min_bp)
3324}
3325
3326fn (mut p Parser) expr_with_lhs(first flat.NodeId, min_bp token.BindingPower) flat.NodeId {
3327 mut lhs := first
3328 for {
3329 // selector / method call
3330 if p.tok == .dot {
3331 lhs = p.selector_or_method(lhs)
3332 continue
3333 }
3334 // module-qualified struct init: module.Type{} or module.Type{field: val, ...}
3335 if p.tok == .lcbr {
3336 lhs_node := p.a.nodes[int(lhs)]
3337 if lhs_node.kind == .index {
3338 if full_name := p.generic_struct_init_type_name(lhs) {
3339 lhs = p.struct_init(full_name)
3340 continue
3341 }
3342 }
3343 if lhs_node.kind == .selector && lhs_node.value.len > 0 {
3344 base := p.a.child_node(&lhs_node, 0)
3345 is_c_struct := base.kind == .ident && base.value == 'C'
3346 && !is_all_upper_ident(lhs_node.value)
3347 && (p.peek() == .rcbr || p.peek() == .name
3348 || p.peek() == .ellipsis)
3349 is_v_struct := base.kind == .ident && base.value != 'C' && lhs_node.value[0] >= `A`
3350 && lhs_node.value[0] <= `Z`
3351 if is_c_struct || is_v_struct {
3352 full_name := '${base.value}.${lhs_node.value}'
3353 lhs = p.struct_init(full_name)
3354 continue
3355 }
3356 }
3357 }
3358 // function call
3359 if p.tok == .lpar {
3360 lhs_node := p.a.nodes[int(lhs)]
3361 if lhs_node.kind == .selector && lhs_node.children_count > 0 && lhs_node.value.len > 0
3362 && lhs_node.value[0] >= `A` && lhs_node.value[0] <= `Z` {
3363 base := p.a.child_node(&lhs_node, 0)
3364 if base.kind == .ident && base.value != 'C' {
3365 full_name := '${base.value}.${lhs_node.value}'
3366 p.next() // skip (
3367 inner := p.expr(.lowest)
3368 p.check(.rpar)
3369 cstart := p.add_child(inner)
3370 lhs = p.a.add_node(flat.Node{
3371 kind: .cast_expr
3372 value: full_name
3373 children_start: cstart
3374 children_count: 1
3375 })
3376 continue
3377 }
3378 }
3379 lhs = p.call_args(lhs)
3380 continue
3381 }
3382 // index / generic
3383 if p.tok == .lsbr {
3384 lhs = p.index_expr(lhs)
3385 continue
3386 }
3387 // postfix: ++ -- ? !
3388 if token_is_postfix(p.tok) {
3389 op_id := int(p.tok)
3390 p.next()
3391 pstart := p.add_child(lhs)
3392 lhs = p.a.add_node(flat.Node{
3393 kind: .postfix
3394 op: token_id_to_op(op_id)
3395 children_start: pstart
3396 children_count: 1
3397 })
3398 continue
3399 }
3400 // postfix `!` error propagation: expr!
3401 if p.tok == .not {
3402 p.next()
3403 ostart := p.add_children2(lhs, p.a.add(flat.NodeKind.empty))
3404 lhs = p.a.add_node(flat.Node{
3405 kind: .or_expr
3406 value: '!'
3407 children_start: ostart
3408 children_count: 2
3409 })
3410 continue
3411 }
3412 // postfix `?` optional propagation: expr?
3413 if p.tok == .question {
3414 p.next()
3415 ostart := p.add_children2(lhs, p.a.add(flat.NodeKind.empty))
3416 lhs = p.a.add_node(flat.Node{
3417 kind: .or_expr
3418 value: '?'
3419 children_start: ostart
3420 children_count: 2
3421 })
3422 continue
3423 }
3424 // `as` cast: expr as Type
3425 if p.tok == .key_as {
3426 p.next()
3427 type_name := p.parse_type_name()
3428 astart := p.add_child(lhs)
3429 lhs = p.a.add_node(flat.Node{
3430 kind: .as_expr
3431 value: type_name
3432 children_start: astart
3433 children_count: 1
3434 })
3435 continue
3436 }
3437 // `or` block: expr or { ... }
3438 if p.tok == .key_or {
3439 p.next()
3440 or_body := p.block_stmt()
3441 ostart := p.add_children2(lhs, or_body)
3442 lhs = p.a.add_node(flat.Node{
3443 kind: .or_expr
3444 children_start: ostart
3445 children_count: 2
3446 })
3447 continue
3448 }
3449 // range: expr .. expr
3450 if p.tok == .dotdot {
3451 if int(min_bp) > int(token.BindingPower.lowest) {
3452 break
3453 }
3454 p.next()
3455 rhs := p.expr(.lowest)
3456 rstart := p.add_children2(lhs, rhs)
3457 lhs = p.a.add_node(flat.Node{
3458 kind: .range
3459 children_start: rstart
3460 children_count: 2
3461 })
3462 continue
3463 }
3464 // `is` / `!is` / `not_is` type check
3465 if p.tok == .key_is || p.tok == .not_is {
3466 bp := token.BindingPower.compare
3467 if int(bp) < int(min_bp) {
3468 break
3469 }
3470 is_negated := p.tok == .not_is
3471 p.next()
3472 type_name := p.parse_type_name()
3473 istart := p.add_child(lhs)
3474 is_node := p.a.add_node(flat.Node{
3475 kind: .is_expr
3476 value: type_name
3477 children_start: istart
3478 children_count: 1
3479 })
3480 if is_negated {
3481 nstart := p.add_child(is_node)
3482 lhs = p.a.add_node(flat.Node{
3483 kind: .prefix
3484 op: .not
3485 children_start: nstart
3486 children_count: 1
3487 })
3488 } else {
3489 lhs = is_node
3490 }
3491 continue
3492 }
3493 if p.tok == .not && p.peek() == .key_is {
3494 bp := token.BindingPower.compare
3495 if int(bp) < int(min_bp) {
3496 break
3497 }
3498 p.next() // skip !
3499 p.next() // skip is
3500 type_name := p.parse_type_name()
3501 istart := p.add_child(lhs)
3502 is_node := p.a.add_node(flat.Node{
3503 kind: .is_expr
3504 value: type_name
3505 children_start: istart
3506 children_count: 1
3507 })
3508 nstart := p.add_child(is_node)
3509 lhs = p.a.add_node(flat.Node{
3510 kind: .prefix
3511 op: .not
3512 children_start: nstart
3513 children_count: 1
3514 })
3515 continue
3516 }
3517 // `in` / `!in` / `not_in`
3518 if p.tok == .key_in || p.tok == .not_in {
3519 is_negated := p.tok == .not_in
3520 bp := token.BindingPower.compare
3521 if int(bp) < int(min_bp) {
3522 break
3523 }
3524 p.next()
3525 mut rhs := p.expr(token.BindingPower.bit_or)
3526 if p.tok == .dotdot {
3527 p.next()
3528 range_rhs := p.expr(.lowest)
3529 rstart := p.add_children2(rhs, range_rhs)
3530 rhs = p.a.add_node(flat.Node{
3531 kind: .range
3532 children_start: rstart
3533 children_count: 2
3534 })
3535 }
3536 istart := p.add_children2(lhs, rhs)
3537 in_node := p.a.add_node(flat.Node{
3538 kind: .in_expr
3539 children_start: istart
3540 children_count: 2
3541 })
3542 if is_negated {
3543 nstart := p.add_child(in_node)
3544 lhs = p.a.add_node(flat.Node{
3545 kind: .prefix
3546 op: .not
3547 children_start: nstart
3548 children_count: 1
3549 })
3550 } else {
3551 lhs = in_node
3552 }
3553 continue
3554 }
3555 if p.tok == .not && p.peek() == .key_in {
3556 bp := token.BindingPower.compare
3557 if int(bp) < int(min_bp) {
3558 break
3559 }
3560 p.next() // skip !
3561 p.next() // skip in
3562 mut rhs := p.expr(token.BindingPower.bit_or)
3563 if p.tok == .dotdot {
3564 p.next()
3565 range_rhs := p.expr(.lowest)
3566 rstart := p.add_children2(rhs, range_rhs)
3567 rhs = p.a.add_node(flat.Node{
3568 kind: .range
3569 children_start: rstart
3570 children_count: 2
3571 })
3572 }
3573 istart := p.add_children2(lhs, rhs)
3574 in_node := p.a.add_node(flat.Node{
3575 kind: .in_expr
3576 children_start: istart
3577 children_count: 2
3578 })
3579 nstart := p.add_child(in_node)
3580 lhs = p.a.add_node(flat.Node{
3581 kind: .prefix
3582 op: .not
3583 children_start: nstart
3584 children_count: 1
3585 })
3586 continue
3587 }
3588 // skip auto-semicolons before infix operators (multi-line expressions)
3589 if p.tok == .semicolon {
3590 peek_tok := p.peek()
3591 if token_is_infix(peek_tok) && int(peek_tok) != 85 && int(peek_tok) != 0 {
3592 p.next()
3593 }
3594 }
3595 if token_is_assignment(p.tok) {
3596 break
3597 }
3598 // infix operators
3599 if !token_is_infix(p.tok) {
3600 break
3601 }
3602 op_id := int(p.tok)
3603 bp := token_id_left_binding_power(op_id)
3604 if int(bp) < int(min_bp) {
3605 break
3606 }
3607 p.next()
3608 rhs := p.expr(token_id_right_binding_power(op_id))
3609 istart := p.add_children2(lhs, rhs)
3610 lhs = p.a.add_node(flat.Node{
3611 kind: .infix
3612 op: token_id_to_op(op_id)
3613 children_start: istart
3614 children_count: 2
3615 })
3616 }
3617
3618 return lhs
3619}
3620
3621fn (mut p Parser) sql_expr() flat.NodeId {
3622 db_expr := if p.tok != .lcbr && p.tok != .eof {
3623 p.expr(.lowest)
3624 } else {
3625 flat.empty_node
3626 }
3627 tokens := p.sql_block_tokens()
3628 typ := sql_result_type(tokens)
3629 mut children_start := 0
3630 mut children_count := 0
3631 if int(db_expr) >= 0 {
3632 children_start = p.add_child(db_expr)
3633 children_count = 1
3634 }
3635 return p.a.add_node(flat.Node{
3636 kind: .sql_expr
3637 value: tokens.join(' ')
3638 typ: typ
3639 children_start: children_start
3640 children_count: flat.child_count(children_count)
3641 })
3642}
3643
3644fn (mut p Parser) starts_sql_expr() bool {
3645 if p.tok != .name {
3646 return false
3647 }
3648 saved_s := p.s
3649 saved_tok := p.tok
3650 saved_lit := p.lit
3651 saved_tok_pos := p.tok_pos
3652 saved_prev_tok := p.prev_tok
3653 saved_peek_tok := p.peek_tok
3654 saved_peek_lit := p.peek_lit
3655 saved_peek_pos := p.peek_pos
3656 saved_has_peek := p.has_peek
3657 p.next()
3658 for p.tok == .dot {
3659 p.next()
3660 if p.tok != .name && !p.tok.is_keyword() {
3661 break
3662 }
3663 p.next()
3664 }
3665 ok := p.tok == .lcbr
3666 p.s = saved_s
3667 p.tok = saved_tok
3668 p.lit = saved_lit
3669 p.tok_pos = saved_tok_pos
3670 p.prev_tok = saved_prev_tok
3671 p.peek_tok = saved_peek_tok
3672 p.peek_lit = saved_peek_lit
3673 p.peek_pos = saved_peek_pos
3674 p.has_peek = saved_has_peek
3675 return ok
3676}
3677
3678fn (mut p Parser) sql_block_tokens() []string {
3679 mut tokens := []string{}
3680 if p.tok != .lcbr {
3681 return tokens
3682 }
3683 mut depth := 1
3684 p.next()
3685 for depth > 0 && p.tok != .eof {
3686 if p.tok == .lcbr {
3687 depth++
3688 } else if p.tok == .rcbr {
3689 depth--
3690 if depth == 0 {
3691 p.next()
3692 break
3693 }
3694 }
3695 if depth > 0 {
3696 text := p.sql_token_text()
3697 if text.len > 0 {
3698 tokens << text
3699 }
3700 p.next()
3701 }
3702 }
3703 return tokens
3704}
3705
3706fn (p &Parser) sql_token_text() string {
3707 if p.lit.len > 0 {
3708 return p.lit
3709 }
3710 return match p.tok {
3711 .key_select { 'select' }
3712 .key_in { 'in' }
3713 .key_or { 'or' }
3714 .key_as { 'as' }
3715 .key_true { 'true' }
3716 .key_false { 'false' }
3717 else { '' }
3718 }
3719}
3720
3721fn sql_result_type(tokens []string) string {
3722 if tokens.len == 0 {
3723 return '!void'
3724 }
3725 if tokens[0] != 'select' {
3726 return '!void'
3727 }
3728 if tokens.len > 1 && tokens[1] == 'count' {
3729 return '!int'
3730 }
3731 for i, tok in tokens {
3732 if tok == 'from' && i + 1 < tokens.len {
3733 table := sql_type_name(tokens[i + 1])
3734 if table.len > 0 {
3735 return '![]${table}'
3736 }
3737 }
3738 }
3739 return '![]int'
3740}
3741
3742fn sql_type_name(raw string) string {
3743 mut out := strings.new_builder(raw.len)
3744 for ch in raw {
3745 if (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`)
3746 || (ch >= `0` && ch <= `9`) || ch == `_` || ch == `.` {
3747 out.write_u8(ch)
3748 } else {
3749 break
3750 }
3751 }
3752 return out.str()
3753}
3754
3755fn is_float_number_literal(val string) bool {
3756 if val.len > 2 && val[0] == `0` {
3757 second := val[1]
3758 if second == `x` || second == `X` || second == `b` || second == `B` || second == `o`
3759 || second == `O` {
3760 return false
3761 }
3762 }
3763 return val.contains('.') || val.contains('e') || val.contains('E')
3764}
3765
3766fn (mut p Parser) prefix_expr() flat.NodeId {
3767 tok_id := int(p.tok)
3768 if tok_id == 92 {
3769 val := p.lit
3770 p.next()
3771 kind_id := if is_float_number_literal(val) {
3772 2
3773 } else {
3774 1
3775 }
3776 return p.a.add_val_id(kind_id, val)
3777 }
3778 if tok_id == 107 {
3779 return p.string_literal()
3780 }
3781 if tok_id == 7 {
3782 val := p.lit
3783 p.next()
3784 return p.a.add_val_id(4, val)
3785 }
3786 if tok_id == 66 {
3787 p.next()
3788 return p.a.add_val_id(3, 'true')
3789 }
3790 if tok_id == 36 {
3791 p.next()
3792 return p.a.add_val_id(3, 'false')
3793 }
3794 if tok_id == 53 {
3795 p.next()
3796 return p.a.add_id(28)
3797 }
3798 if tok_id == 54 {
3799 p.next()
3800 return p.a.add_id(29)
3801 }
3802 if tok_id == 3 {
3803 p.next()
3804 inner := p.expr(.highest)
3805 return p.a.add_node(flat.Node{
3806 kind: .prefix
3807 op: .arrow
3808 children_start: p.add_child(inner)
3809 children_count: 1
3810 })
3811 }
3812 if tok_id == 6 || tok_id == 81 || tok_id == 85 || tok_id == 89 {
3813 p.next()
3814 operand := p.expr(.highest)
3815 pstart := p.add_child(operand)
3816 return p.a.add_node(flat.Node{
3817 kind: .prefix
3818 op: token_id_to_op(tok_id)
3819 children_start: pstart
3820 children_count: 1
3821 })
3822 }
3823 match p.tok {
3824 .number {
3825 val := p.lit
3826 p.next()
3827 kind_id := if is_float_number_literal(val) {
3828 2
3829 } else {
3830 1
3831 }
3832 return p.a.add_val_id(kind_id, val)
3833 }
3834 .string {
3835 return p.string_literal()
3836 }
3837 .char {
3838 val := p.lit
3839 p.next()
3840 return p.a.add_val_id(4, val)
3841 }
3842 .key_true {
3843 p.next()
3844 return p.a.add_val_id(3, 'true')
3845 }
3846 .key_false {
3847 p.next()
3848 return p.a.add_val_id(3, 'false')
3849 }
3850 .key_nil {
3851 p.next()
3852 return p.a.add_id(28)
3853 }
3854 .key_none {
3855 p.next()
3856 return p.a.add_id(29)
3857 }
3858 .arrow {
3859 p.next()
3860 inner := p.expr(.highest)
3861 return p.a.add_node(flat.Node{
3862 kind: .prefix
3863 op: .arrow
3864 children_start: p.add_child(inner)
3865 children_count: 1
3866 })
3867 }
3868 .key_mut, .key_shared {
3869 p.next()
3870 return p.prefix_expr()
3871 }
3872 .name, .key_module {
3873 name := p.lit
3874 p.next()
3875 if name == 'sql' && p.starts_sql_expr() {
3876 return p.sql_expr()
3877 }
3878 if name == '@FILE' {
3879 return p.a.add_val_id(5, p.cur_file)
3880 }
3881 if name == '@VMODROOT' {
3882 return p.a.add_val_id(5, vmod_root_for_file(p.cur_file))
3883 }
3884 if name == '@VEXEROOT' {
3885 return p.a.add_val_id(5, p.prefs.vroot)
3886 }
3887 if name == '@VEXE' {
3888 return p.a.add_val_id(5, p.prefs.vroot + '/v')
3889 }
3890 if name == '@LINE' {
3891 return p.a.add_val_id(1, '0')
3892 }
3893 if name == '@FN' || name == '@METHOD' {
3894 return p.a.add_val_id(5, p.cur_fn)
3895 }
3896 if name == '@LOCATION' {
3897 return p.a.add_val_id(5, '${p.cur_file}:0: ${p.cur_fn}')
3898 }
3899 if name == '@VCURRENTHASH' || name == '@VHASH' {
3900 return p.a.add_val_id(5, '')
3901 }
3902 if name == 'chan' && p.can_start_type_name() {
3903 elem_type := p.parse_type_name()
3904 chan_type := 'chan ${elem_type}'
3905 if p.tok == .lcbr {
3906 return p.struct_init(chan_type)
3907 }
3908 return p.a.add_val(.ident, chan_type)
3909 }
3910 // map init: map[K]V{} or map[K]V{k1: v1, ...}
3911 if name == 'map' && p.tok == .lsbr {
3912 p.next() // skip [
3913 key_type := p.parse_type_name()
3914 p.check(.rsbr)
3915 val_type := p.parse_type_name()
3916 map_type := 'map[${key_type}]${val_type}'
3917 if p.tok == .lcbr {
3918 p.next() // skip {
3919 mut ids := []flat.NodeId{}
3920 for p.tok != .rcbr && p.tok != .eof {
3921 if p.tok == .semicolon {
3922 p.next()
3923 continue
3924 }
3925 k := p.expr(.lowest)
3926 p.check(.colon)
3927 v := p.expr(.lowest)
3928 ids << k
3929 ids << v
3930 if p.tok == .comma || p.tok == .semicolon {
3931 p.next()
3932 }
3933 }
3934 p.check(.rcbr)
3935 if ids.len > 0 {
3936 istart := p.add_children(ids)
3937 return p.a.add_node(flat.Node{
3938 kind: .map_init
3939 value: map_type
3940 children_start: istart
3941 children_count: flat.child_count(ids.len)
3942 })
3943 }
3944 }
3945 return p.a.add_val(.map_init, map_type)
3946 }
3947 // struct init: Name{...}; vlib/builtin also uses concrete lowercase
3948 // runtime structs like array{} and string{}.
3949 if !p.in_for_container && p.tok == .lcbr && name.len > 0
3950 && ((name[0] >= `A` && name[0] <= `Z`)
3951 || name in ['array', 'string', 'map', 'mapnode', '_result', '_option']) {
3952 return p.struct_init(name)
3953 }
3954 // type cast: TypeName(expr) or builtin_type(expr)
3955 if p.tok == .lpar && name.len > 0 && ((name[0] >= `A` && name[0] <= `Z`)
3956 || is_builtin_type(name)) {
3957 p.next() // skip (
3958 inner := p.expr(.lowest)
3959 p.check(.rpar)
3960 cstart := p.add_child(inner)
3961 return p.a.add_node(flat.Node{
3962 kind: .cast_expr
3963 value: name
3964 children_start: cstart
3965 children_count: 1
3966 })
3967 }
3968 return p.a.add_val(.ident, name)
3969 }
3970 .lpar {
3971 p.next()
3972 inner := p.expr(.lowest)
3973 p.check(.rpar)
3974 pstart := p.add_child(inner)
3975 return p.a.add_node(flat.Node{
3976 kind: .paren
3977 children_start: pstart
3978 children_count: 1
3979 })
3980 }
3981 .lcbr {
3982 p.next()
3983 mut ids := []flat.NodeId{}
3984 for p.tok != .rcbr && p.tok != .eof {
3985 if p.tok == .semicolon || p.tok == .comma {
3986 p.next()
3987 continue
3988 }
3989 k := p.expr(.lowest)
3990 p.check(.colon)
3991 v := p.expr(.lowest)
3992 ids << k
3993 ids << v
3994 if p.tok == .comma || p.tok == .semicolon {
3995 p.next()
3996 }
3997 }
3998 p.check(.rcbr)
3999 start := p.add_children(ids)
4000 return p.a.add_node(flat.Node{
4001 kind: .map_init
4002 children_start: start
4003 children_count: flat.child_count(ids.len)
4004 })
4005 }
4006 .amp {
4007 p.next()
4008 if p.tok == .amp {
4009 p.next()
4010 if p.tok == .name && is_builtin_type(p.lit) {
4011 type_name := '&&${p.lit}'
4012 p.next()
4013 if p.tok == .lpar {
4014 p.next()
4015 inner := p.expr(.lowest)
4016 p.check(.rpar)
4017 cstart := p.add_child(inner)
4018 return p.a.add_node(flat.Node{
4019 kind: .cast_expr
4020 value: type_name
4021 children_start: cstart
4022 children_count: 1
4023 })
4024 }
4025 id := p.a.add_val(.ident, type_name[2..])
4026 first := p.a.add_node(flat.Node{
4027 kind: .prefix
4028 op: .amp
4029 children_start: p.add_child(id)
4030 children_count: 1
4031 })
4032 return p.a.add_node(flat.Node{
4033 kind: .prefix
4034 op: .amp
4035 children_start: p.add_child(first)
4036 children_count: 1
4037 })
4038 }
4039 } else if p.tok == .name && is_builtin_type(p.lit) {
4040 name := p.lit
4041 type_name := '&${name}'
4042 p.next()
4043 if p.tok == .lpar {
4044 p.next()
4045 inner := p.expr(.lowest)
4046 p.check(.rpar)
4047 cstart := p.add_child(inner)
4048 return p.a.add_node(flat.Node{
4049 kind: .cast_expr
4050 value: type_name
4051 children_start: cstart
4052 children_count: 1
4053 })
4054 }
4055 if p.tok == .lcbr {
4056 inner := p.struct_init(name)
4057 return p.a.add_node(flat.Node{
4058 kind: .prefix
4059 op: .amp
4060 children_start: p.add_child(inner)
4061 children_count: 1
4062 })
4063 }
4064 id := p.a.add_val(.ident, name)
4065 return p.a.add_node(flat.Node{
4066 kind: .prefix
4067 op: .amp
4068 children_start: p.add_child(id)
4069 children_count: 1
4070 })
4071 } else if p.tok == .lsbr && p.peek() == .rsbr {
4072 type_name := '&' + p.parse_type_name()
4073 if p.tok == .lpar {
4074 p.next()
4075 inner := p.expr(.lowest)
4076 p.check(.rpar)
4077 cstart := p.add_child(inner)
4078 return p.a.add_node(flat.Node{
4079 kind: .cast_expr
4080 value: type_name
4081 children_start: cstart
4082 children_count: 1
4083 })
4084 }
4085 }
4086 operand := p.expr(.highest)
4087 pstart := p.add_child(operand)
4088 return p.a.add_node(flat.Node{
4089 kind: .prefix
4090 op: .amp
4091 children_start: pstart
4092 children_count: 1
4093 })
4094 }
4095 .and {
4096 p.next()
4097 if p.tok == .name && is_builtin_type(p.lit) {
4098 type_name := '&&${p.lit}'
4099 p.next()
4100 if p.tok == .lpar {
4101 p.next()
4102 inner := p.expr(.lowest)
4103 p.check(.rpar)
4104 cstart := p.add_child(inner)
4105 return p.a.add_node(flat.Node{
4106 kind: .cast_expr
4107 value: type_name
4108 children_start: cstart
4109 children_count: 1
4110 })
4111 }
4112 }
4113 return p.a.add(.empty)
4114 }
4115 .minus, .not, .bit_not, .mul {
4116 op_id := int(p.tok)
4117 p.next()
4118 operand := p.expr(.highest)
4119 pstart := p.add_child(operand)
4120 return p.a.add_node(flat.Node{
4121 kind: .prefix
4122 op: token_id_to_op(op_id)
4123 children_start: pstart
4124 children_count: 1
4125 })
4126 }
4127 .question {
4128 p.next()
4129 inner_type := p.parse_type_name()
4130 type_name := if inner_type.len > 0 { '?${inner_type}' } else { '?' }
4131 if p.tok == .lpar {
4132 p.next()
4133 inner := p.expr(.lowest)
4134 p.check(.rpar)
4135 cstart := p.add_child(inner)
4136 return p.a.add_node(flat.Node{
4137 kind: .cast_expr
4138 value: type_name
4139 children_start: cstart
4140 children_count: 1
4141 })
4142 }
4143 return p.a.add(flat.NodeKind.empty)
4144 }
4145 .key_if {
4146 return p.if_stmt()
4147 }
4148 .key_match {
4149 return p.match_stmt()
4150 }
4151 .key_fn {
4152 return p.fn_literal()
4153 }
4154 .key_go, .key_spawn {
4155 p.next()
4156 inner := p.expr(.lowest)
4157 sstart := p.add_child(inner)
4158 return p.a.add_node(flat.Node{
4159 kind: .spawn_expr
4160 children_start: sstart
4161 children_count: 1
4162 })
4163 }
4164 .key_lock, .key_rlock {
4165 return p.lock_expr()
4166 }
4167 .key_select {
4168 if p.peek() == .lpar {
4169 p.next()
4170 return p.a.add_val(.ident, 'select')
4171 }
4172 return p.select_expr()
4173 }
4174 .key_sizeof {
4175 return p.sizeof_expr()
4176 }
4177 .key_typeof {
4178 return p.typeof_expr()
4179 }
4180 .key_offsetof {
4181 return p.offsetof_expr()
4182 }
4183 .key_dump {
4184 return p.dump_expr()
4185 }
4186 .key_likely, .key_unlikely {
4187 p.next()
4188 p.check(.lpar)
4189 inner := p.expr(.lowest)
4190 p.check(.rpar)
4191 return inner
4192 }
4193 .key_isreftype {
4194 p.next()
4195 p.check(.lpar)
4196 p.parse_type_name()
4197 p.check(.rpar)
4198 return p.a.add_val_id(3, 'false')
4199 }
4200 .dot {
4201 // enum value: .member
4202 p.next()
4203 member := p.expect_name_or_keyword()
4204 return p.a.add_val(.enum_val, member)
4205 }
4206 .lsbr {
4207 return p.array_literal()
4208 }
4209 .key_unsafe {
4210 p.next()
4211 return p.block_stmt()
4212 }
4213 .ellipsis {
4214 // spread: ...expr
4215 p.next()
4216 inner := p.expr(.lowest)
4217 pstart := p.add_child(inner)
4218 return p.a.add_node(flat.Node{
4219 kind: .prefix
4220 op: .none
4221 value: '...'
4222 children_start: pstart
4223 children_count: 1
4224 })
4225 }
4226 .dollar {
4227 return p.parse_comptime_expr()
4228 }
4229 else {
4230 p.next()
4231 return p.a.add(flat.NodeKind.empty)
4232 }
4233 }
4234}
4235
4236fn (mut p Parser) selector_or_method(lhs flat.NodeId) flat.NodeId {
4237 p.next() // skip '.'
4238 field_name := p.expect_name_or_keyword()
4239 sel_start := p.add_child(lhs)
4240 sel := p.a.add_node(flat.Node{
4241 kind: .selector
4242 value: field_name
4243 children_start: sel_start
4244 children_count: 1
4245 })
4246 if p.tok == .lpar {
4247 lhs_node := p.a.nodes[int(lhs)]
4248 if lhs_node.kind == .ident && lhs_node.value != 'C' && field_name.len > 0
4249 && field_name[0] >= `A` && field_name[0] <= `Z` {
4250 full_name := '${lhs_node.value}.${field_name}'
4251 p.next() // skip (
4252 inner := p.expr(.lowest)
4253 p.check(.rpar)
4254 cstart := p.add_child(inner)
4255 return p.a.add_node(flat.Node{
4256 kind: .cast_expr
4257 value: full_name
4258 children_start: cstart
4259 children_count: 1
4260 })
4261 }
4262 return p.call_args(sel)
4263 }
4264 return sel
4265}
4266
4267fn (mut p Parser) call_args(fn_expr flat.NodeId) flat.NodeId {
4268 p.check(.lpar)
4269 mut ids := []flat.NodeId{}
4270 ids << fn_expr
4271 for p.tok != .rpar && p.tok != .eof {
4272 prev_offset := p.s.offset
4273 prev_tok := p.tok
4274 if p.tok == .key_mut || p.tok == .key_shared {
4275 p.next()
4276 }
4277 // vararg spread: ...expr
4278 if p.tok == .ellipsis {
4279 p.next()
4280 inner := p.expr(.lowest)
4281 sstart := p.add_child(inner)
4282 ids << p.a.add_node(flat.Node{
4283 kind: .prefix
4284 op: .none
4285 value: '...'
4286 children_start: sstart
4287 children_count: 1
4288 })
4289 } else if p.tok == .logical_or {
4290 // lambda no args: || expr
4291 p.next()
4292 lambda_body := p.expr(.lowest)
4293 lstart := p.add_child(lambda_body)
4294 ids << p.a.add_node(flat.Node{
4295 kind: .lambda_expr
4296 children_start: lstart
4297 children_count: 1
4298 })
4299 } else if p.tok == .pipe {
4300 // lambda with args: |a, b| expr
4301 p.next()
4302 mut lambda_params := []flat.NodeId{}
4303 lambda_params << p.a.add_val(.ident, p.expect_name())
4304 for p.tok == .comma {
4305 p.next()
4306 lambda_params << p.a.add_val(.ident, p.expect_name())
4307 }
4308 p.check(.pipe)
4309 lambda_body := p.expr(.lowest)
4310 mut lids := lambda_params.clone()
4311 lids << lambda_body
4312 lstart := p.add_children(lids)
4313 ids << p.a.add_node(flat.Node{
4314 kind: .lambda_expr
4315 children_start: lstart
4316 children_count: flat.child_count(lids.len)
4317 })
4318 } else {
4319 arg := p.expr(.lowest)
4320 // struct config syntax: name: value
4321 if p.tok == .colon {
4322 p.next()
4323 val := p.expr(.lowest)
4324 arg_node := p.a.nodes[int(arg)]
4325 vstart := p.add_child(val)
4326 ids << p.a.add_node(flat.Node{
4327 kind: .field_init
4328 value: arg_node.value
4329 children_start: vstart
4330 children_count: 1
4331 })
4332 if p.tok == .semicolon {
4333 p.next()
4334 }
4335 } else {
4336 ids << arg
4337 }
4338 }
4339 if p.tok == .comma {
4340 p.next()
4341 }
4342 if p.s.offset == prev_offset && p.tok == prev_tok {
4343 p.next()
4344 }
4345 }
4346 p.check(.rpar)
4347 cstart := p.add_children(ids)
4348 return p.a.add_node(flat.Node{
4349 kind: .call
4350 children_start: cstart
4351 children_count: flat.child_count(ids.len)
4352 })
4353}
4354
4355fn (mut p Parser) index_expr(lhs flat.NodeId) flat.NodeId {
4356 p.check(.lsbr)
4357 // range index: arr[..b]
4358 if p.tok == .dotdot {
4359 p.next()
4360 mut end_id := flat.empty_node
4361 if p.tok != .rsbr {
4362 end_id = p.expr(.lowest)
4363 }
4364 p.check(.rsbr)
4365 start_id := p.a.add(flat.NodeKind.empty)
4366 mut ids := []flat.NodeId{}
4367 ids << lhs
4368 ids << start_id
4369 if int(end_id) >= 0 {
4370 ids << end_id
4371 }
4372 istart := p.add_children(ids)
4373 return p.a.add_node(flat.Node{
4374 kind: .index
4375 value: 'range'
4376 children_start: istart
4377 children_count: flat.child_count(ids.len)
4378 })
4379 }
4380 idx := p.expr(.logical_or)
4381 // fast path for the common simple index `arr[i]`: avoid the children array
4382 if p.tok == .rsbr {
4383 p.next()
4384 istart := p.add_children2(lhs, idx)
4385 return p.a.add_node(flat.Node{
4386 kind: .index
4387 children_start: istart
4388 children_count: 2
4389 })
4390 }
4391 mut ids := []flat.NodeId{}
4392 ids << lhs
4393 ids << idx
4394 for p.tok == .comma {
4395 p.next()
4396 ids << p.expr(.logical_or)
4397 }
4398 // range index: arr[a..b]
4399 if p.tok == .dotdot {
4400 p.next()
4401 mut end_id := flat.empty_node
4402 if p.tok != .rsbr {
4403 end_id = p.expr(.lowest)
4404 }
4405 p.check(.rsbr)
4406 mut range_ids := []flat.NodeId{}
4407 range_ids << lhs
4408 range_ids << idx
4409 if int(end_id) >= 0 {
4410 range_ids << end_id
4411 }
4412 istart := p.add_children(range_ids)
4413 return p.a.add_node(flat.Node{
4414 kind: .index
4415 value: 'range'
4416 children_start: istart
4417 children_count: flat.child_count(range_ids.len)
4418 })
4419 }
4420 p.check(.rsbr)
4421 istart := p.add_children(ids)
4422 return p.a.add_node(flat.Node{
4423 kind: .index
4424 children_start: istart
4425 children_count: flat.child_count(ids.len)
4426 })
4427}
4428
4429fn (p &Parser) generic_struct_init_type_name(id flat.NodeId) ?string {
4430 type_name := p.type_expr_name(id)
4431 if !generic_type_name_can_init(type_name) {
4432 return none
4433 }
4434 return type_name
4435}
4436
4437fn (p &Parser) type_expr_name(id flat.NodeId) string {
4438 if int(id) < 0 {
4439 return ''
4440 }
4441 node := p.a.nodes[int(id)]
4442 match node.kind {
4443 .ident {
4444 return node.value
4445 }
4446 .selector {
4447 if node.children_count == 0 {
4448 return node.value
4449 }
4450 base := p.type_expr_name(p.a.child(&node, 0))
4451 if base.len == 0 {
4452 return node.value
4453 }
4454 return '${base}.${node.value}'
4455 }
4456 .index {
4457 if node.children_count < 2 || node.value == 'range' {
4458 return ''
4459 }
4460 base := p.type_expr_name(p.a.child(&node, 0))
4461 if base.len == 0 {
4462 return ''
4463 }
4464 mut args := []string{}
4465 for i in 1 .. node.children_count {
4466 arg := p.type_expr_name(p.a.child(&node, i))
4467 if arg.len == 0 {
4468 return ''
4469 }
4470 args << arg
4471 }
4472 return '${base}[${args.join(', ')}]'
4473 }
4474 .array_init {
4475 if node.value.len == 0 {
4476 return ''
4477 }
4478 return '[]${node.value}'
4479 }
4480 .prefix {
4481 if node.children_count == 0 {
4482 return ''
4483 }
4484 child := p.type_expr_name(p.a.child(&node, 0))
4485 if child.len == 0 {
4486 return ''
4487 }
4488 if node.op == .amp {
4489 return '&${child}'
4490 }
4491 return child
4492 }
4493 else {
4494 return ''
4495 }
4496 }
4497}
4498
4499fn generic_type_name_can_init(type_name string) bool {
4500 if !type_name.contains('[') {
4501 return false
4502 }
4503 base := type_name.all_before('[')
4504 short := if base.contains('.') { base.all_after_last('.') } else { base }
4505 return short.len > 0 && short[0] >= `A` && short[0] <= `Z`
4506}
4507
4508fn (mut p Parser) struct_init(name string) flat.NodeId {
4509 p.check(.lcbr)
4510 mut ids := []flat.NodeId{}
4511 // assoc syntax: Type{...base, field: val}
4512 if p.tok == .ellipsis {
4513 p.next()
4514 base_expr := p.expr(.lowest)
4515 if p.tok == .comma || p.tok == .semicolon {
4516 p.next()
4517 }
4518 mut field_ids := []flat.NodeId{}
4519 field_ids << base_expr
4520 for p.tok != .rcbr && p.tok != .eof {
4521 if p.tok == .comma {
4522 p.next()
4523 }
4524 fname := p.expect_name_or_keyword()
4525 p.check(.colon)
4526 val := p.expr(.lowest)
4527 vstart := p.add_child(val)
4528 field_ids << p.a.add_node(flat.Node{
4529 kind: .field_init