v4 / vlib / v3 / parser / parser.v
5515 lines · 5310 sloc · 117.59 KB · eecdfebe02e3f976e0a993721335b50a9c2a56f4
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 is_method := false
629
630 // method receiver: fn (mut r Type) name()
631 if p.tok == .lpar {
632 is_method = true
633 p.next()
634 mut is_mut := false
635 if p.tok == .key_mut || p.tok == .key_shared {
636 is_mut = p.tok == .key_mut
637 p.next()
638 }
639 receiver_name = p.expect_name()
640 receiver_type = p.parse_type_name()
641 if is_mut {
642 receiver_type = '&' + receiver_type
643 }
644 p.check(.rpar)
645
646 // operator overload: fn (r Type) + (other Type) RetType { }
647 if p.tok != .name && p.tok != .eof {
648 if p.tok.is_overloadable() {
649 op_name := overload_token_name(p.tok)
650 p.next()
651 return p.fn_operator_overload(receiver_name, receiver_type, op_name)
652 }
653 }
654 }
655
656 // function name
657 if p.tok_can_be_decl_name() {
658 name = p.expect_name_or_keyword()
659 if p.tok == .dot {
660 p.next()
661 if name == 'C' || name == 'JS' {
662 // C.func or JS.func
663 name = p.expect_name_or_keyword()
664 for p.tok == .dot {
665 p.next()
666 name += '.' + p.expect_name_or_keyword()
667 }
668 if is_method {
669 clean_type := receiver_type.trim_left('&')
670 name = '${clean_type}.${name}'
671 }
672 return p.fn_decl_body(name, receiver_name, receiver_type, is_method, true)
673 }
674 // module.func or Type.static_method
675 second := p.expect_name_or_keyword()
676 if is_method {
677 // This shouldn't happen for methods
678 name = name + '.' + second
679 } else {
680 // Static method: Type.name
681 receiver_type = name
682 name = second
683 is_method = true
684 }
685 }
686 }
687
688 if is_method && receiver_type.len > 0 {
689 clean_type := receiver_type.trim_left('&')
690 name = '${clean_type}.${name}'
691 }
692
693 return p.fn_decl_body(name, receiver_name, receiver_type, is_method, false)
694}
695
696fn (mut p Parser) fn_operator_overload(receiver_name string, receiver_type string, op_name string) flat.NodeId {
697 disable_body := p.disable_fn_body
698 p.disable_fn_body = false
699 // parse parameter
700 p.check(.lpar)
701 mut param_ids := []flat.NodeId{}
702 // receiver
703 param_ids << p.a.add_node(flat.Node{
704 kind: .param
705 value: receiver_name
706 typ: receiver_type
707 })
708 // operator param
709 if p.tok == .key_mut {
710 p.next()
711 }
712 for p.tok != .rpar && p.tok != .eof {
713 start_offset := p.s.offset
714 param_ids << p.parse_param_group(false)
715 if p.s.offset == start_offset && p.tok != .rpar && p.tok != .eof {
716 p.next()
717 }
718 }
719 p.check(.rpar)
720
721 mut ret_type := 'void'
722 if p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
723 ret_type = p.parse_type_name()
724 }
725
726 clean_type := receiver_type.trim_left('&')
727 name := '${clean_type}.${op_name}'
728
729 mut body_ids := []flat.NodeId{}
730 if p.tok == .lcbr {
731 prev_fn := p.cur_fn
732 p.cur_fn = name
733 if disable_body {
734 p.mark_disabled_fn(name)
735 p.skip_block()
736 } else {
737 p.check(.lcbr)
738 for p.tok != .rcbr && p.tok != .eof {
739 id := p.stmt()
740 if int(id) >= 0 {
741 body_ids << id
742 }
743 }
744 p.check(.rcbr)
745 }
746 p.cur_fn = prev_fn
747 }
748
749 mut all_ids := []flat.NodeId{cap: param_ids.len + body_ids.len}
750 for id in param_ids {
751 all_ids << id
752 }
753 for id in body_ids {
754 all_ids << id
755 }
756 start := p.add_children(all_ids)
757 id := p.a.add_node(flat.Node{
758 kind: .fn_decl
759 value: name
760 typ: ret_type
761 children_start: start
762 children_count: flat.child_count(all_ids.len)
763 })
764 p.register_pending_export(name)
765 return id
766}
767
768fn (mut p Parser) fn_decl_body(name string, receiver_name string, receiver_type string, is_method bool, is_c_decl bool) flat.NodeId {
769 // Capture & clear here so it applies only to this function (not nested closures
770 // or a following declaration), and is cleared even on the extern/no-body path.
771 disable_body := p.disable_fn_body
772 p.disable_fn_body = false
773 // generic params — skip
774 mut generic_params := []string{}
775 if p.tok == .lsbr {
776 generic_params = p.parse_generic_param_names()
777 }
778
779 // params
780 p.check(.lpar)
781 mut param_ids := []flat.NodeId{}
782 if is_method && receiver_name.len > 0 {
783 param_ids << p.a.add_node(flat.Node{
784 kind: .param
785 value: receiver_name
786 typ: receiver_type
787 })
788 }
789 for p.tok != .rpar && p.tok != .eof {
790 start_offset := p.s.offset
791 param_ids << p.parse_param_group(is_c_decl)
792 if p.s.offset == start_offset && p.tok != .rpar && p.tok != .eof {
793 p.next()
794 }
795 }
796 p.check(.rpar)
797
798 // return type
799 mut ret_type := 'void'
800 if p.tok == .name || p.tok == .amp || p.tok == .question || p.tok == .not || p.tok == .lsbr
801 || p.tok == .lpar || p.tok == .key_fn || p.tok == .ellipsis {
802 ret_type = p.parse_type_name()
803 }
804 // no body — extern/C declaration
805 if p.tok != .lcbr {
806 for p.tok == .semicolon {
807 p.next()
808 }
809 start := p.add_children(param_ids)
810 id := p.a.add_node(flat.Node{
811 kind: .c_fn_decl
812 value: name
813 typ: ret_type
814 generic_params: generic_params
815 children_start: start
816 children_count: flat.child_count(param_ids.len)
817 })
818 p.pending_export = ''
819 return id
820 }
821
822 // body
823 mut body_ids := []flat.NodeId{}
824 prev_fn := p.cur_fn
825 p.cur_fn = name
826 // A disabled `@[if flag ?]` function keeps its signature but gets an empty body
827 // (a no-op stub), so callers still resolve while the body is compiled out.
828 if disable_body {
829 p.mark_disabled_fn(name)
830 p.skip_block()
831 } else {
832 p.check(.lcbr)
833 for p.tok != .rcbr && p.tok != .eof {
834 id := p.stmt()
835 if int(id) >= 0 {
836 body_ids << id
837 }
838 }
839 p.check(.rcbr)
840 }
841 p.cur_fn = prev_fn
842
843 mut all_ids := []flat.NodeId{cap: param_ids.len + body_ids.len}
844 for id in param_ids {
845 all_ids << id
846 }
847 for id in body_ids {
848 all_ids << id
849 }
850 start := p.add_children(all_ids)
851 id := p.a.add_node(flat.Node{
852 kind: .fn_decl
853 value: name
854 typ: ret_type
855 generic_params: generic_params
856 children_start: start
857 children_count: flat.child_count(all_ids.len)
858 })
859 p.register_pending_export(name)
860 return id
861}
862
863fn (mut p Parser) mark_disabled_fn(name string) {
864 if name.len == 0 {
865 return
866 }
867 if p.cur_module.len > 0 && p.cur_module != 'main' && p.cur_module != 'builtin' {
868 qname := if name.starts_with('${p.cur_module}.') { name } else { '${p.cur_module}.${name}' }
869 p.a.disabled_fns[qname] = true
870 }
871 p.a.disabled_fns[name] = true
872}
873
874fn (mut p Parser) register_pending_export(name string) {
875 if p.pending_export.len == 0 || name.len == 0 {
876 p.pending_export = ''
877 return
878 }
879 qname := if p.cur_module.len > 0 && p.cur_module != 'main' && p.cur_module != 'builtin'
880 && !name.starts_with('${p.cur_module}.') {
881 '${p.cur_module}.${name}'
882 } else {
883 name
884 }
885 if name in p.a.disabled_fns || qname in p.a.disabled_fns {
886 p.pending_export = ''
887 return
888 }
889 p.a.export_fn_names[qname] = p.pending_export
890 p.pending_export = ''
891}
892
893fn (mut p Parser) parse_param_group(is_c_decl bool) []flat.NodeId {
894 mut ids := []flat.NodeId{}
895 mut names := []string{}
896 mut is_mut := false
897 if p.tok == .key_mut {
898 is_mut = true
899 p.next()
900 }
901 if p.tok == .key_shared {
902 p.next()
903 }
904 // variadic ...Type (no param name)
905 if p.tok == .ellipsis {
906 typ := p.parse_type_name()
907 ids << p.a.add_node(flat.Node{
908 kind: .param
909 value: ''
910 typ: typ
911 })
912 if p.tok == .comma {
913 p.next()
914 }
915 return ids
916 }
917 if is_c_decl && p.c_anon_param_starts_type() {
918 mut typ := p.parse_type_name()
919 if is_mut && !typ.starts_with('&') {
920 typ = '&' + typ
921 }
922 ids << p.a.add_node(flat.Node{
923 kind: .param
924 value: ''
925 typ: typ
926 })
927 if p.tok == .comma {
928 p.next()
929 }
930 return ids
931 }
932 names << p.lit
933 p.next()
934 for p.tok == .comma {
935 p.next()
936 if p.tok == .key_mut {
937 p.next()
938 }
939 if p.tok == .name {
940 names << p.lit
941 p.next()
942 }
943 }
944 mut typ := p.parse_type_name()
945 if is_mut && !typ.starts_with('&') {
946 typ = '&' + typ
947 }
948 for name in names {
949 ids << p.a.add_node(flat.Node{
950 kind: .param
951 value: name
952 typ: typ
953 })
954 }
955 if p.tok == .comma {
956 p.next()
957 }
958 return ids
959}
960
961fn (mut p Parser) c_anon_param_starts_type() bool {
962 if p.tok in [.amp, .and, .question, .not, .lsbr, .lpar, .key_fn, .key_atomic, .key_struct] {
963 return true
964 }
965 if p.tok == .name {
966 if p.lit == '&' {
967 return true
968 }
969 pk := p.peek()
970 if pk == .dot || pk == .rpar || pk == .eof {
971 return true
972 }
973 if pk == .comma && (is_builtin_type(p.lit) || is_c_anon_simple_type_name(p.lit)) {
974 return true
975 }
976 }
977 return false
978}
979
980fn (mut p Parser) struct_decl() flat.NodeId {
981 is_union := p.tok == .key_union
982 is_params := p.pending_params
983 p.pending_params = false
984 p.next() // skip 'struct' or 'union'
985 mut name := p.expect(.name)
986 if (name == 'C' || name == 'JS') && p.tok == .dot {
987 for p.tok == .dot {
988 p.next()
989 name += '.' + p.expect_name_or_keyword()
990 }
991 }
992 // generic params — skip
993 mut is_generic := false
994 mut generic_params := []string{}
995 if p.tok == .lsbr {
996 is_generic = true
997 generic_params = p.parse_generic_param_names()
998 }
999 // implements clause
1000 if p.tok == .name && p.lit == 'implements' {
1001 p.next()
1002 p.parse_type_name()
1003 for p.tok == .comma {
1004 p.next()
1005 p.parse_type_name()
1006 }
1007 }
1008 // no body (C struct forward decl)
1009 if p.tok != .lcbr {
1010 if p.tok == .semicolon {
1011 p.next()
1012 }
1013 return p.a.add_node(flat.Node{
1014 kind: .struct_decl
1015 value: name
1016 typ: struct_decl_typ(is_union, is_generic, is_params)
1017 generic_params: generic_params
1018 })
1019 }
1020 p.check(.lcbr)
1021 mut ids := []flat.NodeId{}
1022 for p.tok != .rcbr && p.tok != .eof {
1023 // access modifiers
1024 if p.tok == .key_pub {
1025 p.next()
1026 if p.tok == .key_mut {
1027 p.next()
1028 }
1029 if p.tok == .name && p.lit == 'module_mut' {
1030 p.next()
1031 }
1032 if p.tok == .colon {
1033 p.next()
1034 }
1035 continue
1036 }
1037 if p.tok == .key_mut {
1038 p.next()
1039 if p.tok == .colon {
1040 p.next()
1041 }
1042 continue
1043 }
1044 if p.tok == .semicolon {
1045 p.next()
1046 continue
1047 }
1048 // comptime $if in struct fields
1049 if p.tok == .dollar {
1050 id := p.parse_comptime_if()
1051 if int(id) >= 0 {
1052 ids << id
1053 }
1054 continue
1055 }
1056 // attributes — skip
1057 if p.tok == .attribute {
1058 p.skip_attrs()
1059 continue
1060 }
1061 // field: name type [= default] [@[attrs]]
1062 if p.tok == .name || p.tok.is_keyword() {
1063 field_name := p.expect_name_or_keyword()
1064 if p.tok == .dot {
1065 p.next()
1066 second := p.expect_name_or_keyword()
1067 full_type := '${field_name}.${second}'
1068 field_type := full_type + p.parse_type_generic_suffix()
1069 ids << p.a.add_node(flat.Node{
1070 kind: .field_decl
1071 value: full_type
1072 typ: field_type
1073 })
1074 if p.tok == .semicolon {
1075 p.next()
1076 }
1077 continue
1078 }
1079 // embedded struct (type on its own line, followed by semicolon)
1080 if p.tok == .semicolon || p.tok == .rcbr {
1081 ids << p.a.add_node(flat.Node{
1082 kind: .field_decl
1083 value: field_name
1084 typ: field_name
1085 })
1086 if p.tok == .semicolon {
1087 p.next()
1088 }
1089 continue
1090 }
1091 // grouped fields: x, y int
1092 if p.tok == .comma {
1093 mut names := []string{}
1094 names << field_name
1095 for p.tok == .comma {
1096 p.next()
1097 names << p.expect_name_or_keyword()
1098 }
1099 field_type := p.parse_type_name()
1100 for n in names {
1101 ids << p.a.add_node(flat.Node{
1102 kind: .field_decl
1103 value: n
1104 typ: field_type
1105 })
1106 }
1107 if p.tok == .semicolon {
1108 p.next()
1109 }
1110 continue
1111 }
1112 // For embedded structs followed by access modifier or another field,
1113 // check if the next token could be a type
1114 field_type := p.parse_type_name()
1115 mut default_id := flat.empty_node
1116 // default value
1117 if p.tok == .assign {
1118 p.next()
1119 default_id = p.expr(.lowest)
1120 }
1121 mut children_start := 0
1122 mut children_count := 0
1123 if int(default_id) >= 0 {
1124 children_start = p.add_child(default_id)
1125 children_count = 1
1126 }
1127 // trailing field attributes — skip
1128 if p.tok == .attribute || p.tok == .lsbr {
1129 p.skip_attrs()
1130 }
1131 ids << p.a.add_node(flat.Node{
1132 kind: .field_decl
1133 value: field_name
1134 typ: field_type
1135 children_start: children_start
1136 children_count: flat.child_count(children_count)
1137 })
1138 if p.tok == .semicolon {
1139 p.next()
1140 }
1141 } else {
1142 p.next()
1143 }
1144 }
1145 p.check(.rcbr)
1146 p.pending_params = false
1147 start := p.add_children(ids)
1148 return p.a.add_node(flat.Node{
1149 kind: .struct_decl
1150 value: name
1151 typ: struct_decl_typ(is_union, is_generic, is_params)
1152 generic_params: generic_params
1153 children_start: start
1154 children_count: flat.child_count(ids.len)
1155 })
1156}
1157
1158fn struct_decl_typ(is_union bool, is_generic bool, is_params bool) string {
1159 mut parts := []string{}
1160 if is_union {
1161 parts << 'union'
1162 }
1163 if is_generic {
1164 parts << 'generic'
1165 }
1166 if is_params {
1167 parts << 'params'
1168 }
1169 return parts.join(',')
1170}
1171
1172fn (mut p Parser) global_decl() flat.NodeId {
1173 p.next() // skip '__global'
1174 is_grouped := p.tok == .lpar
1175 if is_grouped {
1176 p.next()
1177 }
1178 mut ids := []flat.NodeId{}
1179 for {
1180 if p.tok == .semicolon {
1181 p.next()
1182 if !is_grouped {
1183 break
1184 }
1185 if p.tok == .rpar {
1186 p.next()
1187 if p.tok == .semicolon {
1188 p.next()
1189 }
1190 break
1191 }
1192 continue
1193 }
1194 if p.tok == .key_pub {
1195 p.next()
1196 }
1197 if p.tok == .key_mut {
1198 p.next()
1199 }
1200 if p.tok == .name || p.tok.is_keyword() {
1201 gname := p.expect_name_or_keyword()
1202 // handle qualified names: C.errno
1203 mut full_name := gname
1204 for p.tok == .dot {
1205 p.next()
1206 full_name += '.' + p.expect_name_or_keyword()
1207 }
1208 if p.tok == .assign {
1209 // global with initializer: __global name = expr
1210 p.next()
1211 val_id := p.expr(.lowest)
1212 vstart := p.add_child(val_id)
1213 ids << p.a.add_node(flat.Node{
1214 kind: .field_decl
1215 value: full_name
1216 typ: ''
1217 children_start: vstart
1218 children_count: 1
1219 })
1220 } else {
1221 gtype := p.parse_type_name()
1222 ids << p.a.add_node(flat.Node{
1223 kind: .field_decl
1224 value: full_name
1225 typ: gtype
1226 })
1227 }
1228 if p.tok == .semicolon {
1229 p.next()
1230 }
1231 if !is_grouped {
1232 break
1233 }
1234 if is_grouped && p.tok == .rpar {
1235 p.next()
1236 if p.tok == .semicolon {
1237 p.next()
1238 }
1239 break
1240 }
1241 } else if p.tok == .rpar {
1242 p.next()
1243 if p.tok == .semicolon {
1244 p.next()
1245 }
1246 break
1247 } else {
1248 p.next()
1249 if !is_grouped {
1250 break
1251 }
1252 }
1253 }
1254 start := p.add_children(ids)
1255 return p.a.add_node(flat.Node{
1256 kind: .global_decl
1257 children_start: start
1258 children_count: flat.child_count(ids.len)
1259 })
1260}
1261
1262fn (mut p Parser) const_decl() flat.NodeId {
1263 p.next() // skip 'const'
1264 is_grouped := p.tok == .lpar
1265 if is_grouped {
1266 p.next()
1267 }
1268 mut ids := []flat.NodeId{}
1269 for {
1270 if p.tok == .semicolon {
1271 p.next()
1272 if !is_grouped {
1273 break
1274 }
1275 if p.tok == .rpar {
1276 p.next()
1277 if p.tok == .semicolon {
1278 p.next()
1279 }
1280 break
1281 }
1282 continue
1283 }
1284 if p.tok == .name {
1285 cname := p.expect_name()
1286 mut full_name := cname
1287 // C.NAME
1288 if full_name == 'C' && p.tok == .dot {
1289 p.next()
1290 full_name = 'C.' + p.expect_name_or_keyword()
1291 }
1292 if p.tok == .assign {
1293 p.next()
1294 val_id := p.expr(.lowest)
1295 vstart := p.add_child(val_id)
1296 ids << p.a.add_node(flat.Node{
1297 kind: .const_field
1298 value: full_name
1299 children_start: vstart
1300 children_count: 1
1301 })
1302 } else {
1303 // const with type only (header files)
1304 ctype := p.parse_type_name()
1305 ids << p.a.add_node(flat.Node{
1306 kind: .const_field
1307 value: full_name
1308 typ: ctype
1309 })
1310 }
1311 if p.tok == .semicolon {
1312 p.next()
1313 }
1314 if !is_grouped {
1315 break
1316 }
1317 if is_grouped && p.tok == .rpar {
1318 p.next()
1319 if p.tok == .semicolon {
1320 p.next()
1321 }
1322 break
1323 }
1324 } else if p.tok == .rpar {
1325 p.next()
1326 if p.tok == .semicolon {
1327 p.next()
1328 }
1329 break
1330 } else {
1331 p.next()
1332 if !is_grouped {
1333 break
1334 }
1335 }
1336 }
1337 start := p.add_children(ids)
1338 return p.a.add_node(flat.Node{
1339 kind: .const_decl
1340 children_start: start
1341 children_count: flat.child_count(ids.len)
1342 })
1343}
1344
1345fn (mut p Parser) enum_decl() flat.NodeId {
1346 p.next() // skip 'enum'
1347 name := p.expect(.name)
1348 // `as` type
1349 if p.tok == .key_as {
1350 p.next()
1351 p.parse_type_name()
1352 }
1353 p.check(.lcbr)
1354 mut ids := []flat.NodeId{}
1355 for p.tok != .rcbr && p.tok != .eof {
1356 if p.tok == .semicolon {
1357 p.next()
1358 continue
1359 }
1360 // attributes on enum fields — skip
1361 if p.tok == .attribute || p.tok == .lsbr {
1362 p.skip_attrs()
1363 continue
1364 }
1365 field_name := p.expect_name_or_keyword()
1366 if p.tok == .assign {
1367 p.next()
1368 val_id := p.expr(.lowest)
1369 vstart := p.add_child(val_id)
1370 ids << p.a.add_node(flat.Node{
1371 kind: .enum_field
1372 value: field_name
1373 children_start: vstart
1374 children_count: 1
1375 })
1376 } else {
1377 ids << p.a.add_node(flat.Node{
1378 kind: .enum_field
1379 value: field_name
1380 })
1381 }
1382 if p.tok == .semicolon {
1383 p.next()
1384 }
1385 }
1386 p.check(.rcbr)
1387 start := p.add_children(ids)
1388 mut typ := ''
1389 if p.pending_flag {
1390 typ = 'flag'
1391 p.pending_flag = false
1392 }
1393 return p.a.add_node(flat.Node{
1394 kind: .enum_decl
1395 value: name
1396 typ: typ
1397 children_start: start
1398 children_count: flat.child_count(ids.len)
1399 })
1400}
1401
1402fn (mut p Parser) type_decl() flat.NodeId {
1403 p.next() // skip 'type'
1404 // C. or JS. prefix
1405 if p.tok == .name && (p.lit == 'C' || p.lit == 'JS') {
1406 p.next()
1407 if p.tok == .dot {
1408 p.next()
1409 }
1410 }
1411 name := p.expect_name()
1412 // generic params
1413 mut generic_params := []string{}
1414 if p.tok == .lsbr {
1415 generic_params = p.parse_generic_param_names()
1416 }
1417 p.expect(.assign)
1418 first_type := p.parse_type_name()
1419 // check for sum type: type T = A | B | C
1420 // skip auto-semicolon before pipe
1421 if p.tok == .pipe || (p.tok == .semicolon && p.peek_is(token.Token.pipe)) {
1422 mut variants := []flat.NodeId{}
1423 variants << p.a.add_val(.ident, first_type)
1424 for p.tok == .pipe || (p.tok == .semicolon && p.peek_is(token.Token.pipe)) {
1425 if p.tok == .semicolon {
1426 p.next()
1427 }
1428 p.next() // skip |
1429 variant_type := p.parse_type_name()
1430 variants << p.a.add_val(.ident, variant_type)
1431 }
1432 if p.tok == .semicolon {
1433 p.next()
1434 }
1435 start := p.add_children(variants)
1436 return p.a.add_node(flat.Node{
1437 kind: .type_decl
1438 value: name
1439 generic_params: generic_params
1440 children_start: start
1441 children_count: flat.child_count(variants.len)
1442 })
1443 }
1444 if p.tok == .semicolon {
1445 p.next()
1446 }
1447 // type alias
1448 return p.a.add_node(flat.Node{
1449 kind: .type_decl
1450 value: name
1451 typ: first_type
1452 generic_params: generic_params
1453 })
1454}
1455
1456fn (mut p Parser) interface_decl() flat.NodeId {
1457 p.next() // skip 'interface'
1458 mut name := p.expect(.name)
1459 for p.tok == .dot {
1460 p.next()
1461 name += '.' + p.expect(.name)
1462 }
1463 // generic params
1464 mut generic_params := []string{}
1465 if p.tok == .lsbr {
1466 generic_params = p.parse_generic_param_names()
1467 }
1468 p.check(.lcbr)
1469 mut ids := []flat.NodeId{}
1470 for p.tok != .rcbr && p.tok != .eof {
1471 if p.tok == .key_mut {
1472 p.next()
1473 if p.tok == .colon {
1474 p.next()
1475 }
1476 continue
1477 }
1478 if p.tok == .semicolon {
1479 p.next()
1480 continue
1481 }
1482 field_name := p.expect_name_or_keyword()
1483 if p.tok == .lpar {
1484 // method: name(params) ret_type
1485 p.next() // skip (
1486 mut params := []flat.NodeId{}
1487 for p.tok != .rpar && p.tok != .eof {
1488 if p.tok == .key_mut {
1489 p.next()
1490 }
1491 // Interface method params may be named (e.g. `seed_data []u32`,
1492 // `node &ast.Node`) or type-only (`[]u32`). When the current token
1493 // is a plain identifier and the next token starts a type, that
1494 // identifier is the param name; consume it first so the array/pointer
1495 // prefix of the real type is not mis-parsed onto the name
1496 // (e.g. `seed_data[]`). `map`/`chan` are type heads, not names.
1497 if p.tok == .name && p.lit != 'map' && p.lit != 'chan' {
1498 nt := p.peek()
1499 if nt == .name || nt == .amp || nt == .question || nt == .not
1500 || nt == .key_fn || nt == .ellipsis
1501 || (nt == .lsbr && p.peek_lbr_starts_array_type()) {
1502 p.next()
1503 }
1504 }
1505 ptype := p.parse_type_name()
1506 params << p.a.add_node(flat.Node{
1507 kind: .param
1508 typ: ptype
1509 })
1510 if p.tok == .comma {
1511 p.next()
1512 }
1513 }
1514 p.check(.rpar) // skip )
1515 mut ret_type := ''
1516 if p.tok != .semicolon && p.tok != .rcbr && p.tok != .eof {
1517 ret_type = p.parse_type_name()
1518 }
1519 start := p.add_children(params)
1520 ids << p.a.add_node(flat.Node{
1521 kind: .interface_field
1522 op: .dot
1523 value: field_name
1524 typ: ret_type
1525 children_start: start
1526 children_count: flat.child_count(params.len)
1527 })
1528 } else if p.tok == .semicolon || p.tok == .rcbr {
1529 // embedded type or field without explicit type
1530 ids << p.a.add_node(flat.Node{
1531 kind: .interface_field
1532 value: field_name
1533 })
1534 } else {
1535 // field: name type
1536 ftype := p.parse_type_name()
1537 ids << p.a.add_node(flat.Node{
1538 kind: .interface_field
1539 value: field_name
1540 typ: ftype
1541 })
1542 }
1543 if p.tok == .semicolon {
1544 p.next()
1545 }
1546 }
1547 p.check(.rcbr)
1548 start := p.add_children(ids)
1549 return p.a.add_node(flat.Node{
1550 kind: .interface_decl
1551 value: name
1552 generic_params: generic_params
1553 children_start: start
1554 children_count: flat.child_count(ids.len)
1555 })
1556}
1557
1558fn (mut p Parser) import_stmt() flat.NodeId {
1559 p.next() // skip 'import'
1560 mut name := p.expect_name()
1561 mut alias := name
1562 mut selective_ids := []flat.NodeId{}
1563 for p.tok == .dot {
1564 p.next()
1565 alias = p.expect_name()
1566 name += '.' + alias
1567 }
1568 if p.tok == .key_as {
1569 p.next()
1570 alias = p.expect_name()
1571 }
1572 // selective import: import mod { sym1, sym2 }
1573 if p.tok == .lcbr {
1574 p.next()
1575 for p.tok != .rcbr && p.tok != .eof {
1576 sym := p.expect_name_or_keyword()
1577 selective_ids << p.a.add_node(flat.Node{
1578 kind: .ident
1579 value: sym
1580 })
1581 if p.tok == .comma {
1582 p.next()
1583 }
1584 }
1585 p.check(.rcbr)
1586 }
1587 if p.tok == .semicolon {
1588 p.next()
1589 }
1590 return p.a.add_node(flat.Node{
1591 kind: .import_decl
1592 value: name
1593 typ: alias
1594 children_start: p.add_children(selective_ids)
1595 children_count: flat.child_count(selective_ids.len)
1596 })
1597}
1598
1599fn (mut p Parser) module_stmt() flat.NodeId {
1600 p.next() // skip 'module'
1601 name := p.expect_name()
1602 if p.tok == .semicolon {
1603 p.next()
1604 }
1605 return p.a.add_node(flat.Node{
1606 kind: .module_decl
1607 value: name
1608 })
1609}
1610
1611fn (mut p Parser) directive() flat.NodeId {
1612 mut full := p.lit
1613 p.next() // skip '#' (lit already contains the full line)
1614 if full.starts_with('#') {
1615 full = full[1..]
1616 }
1617 mut name := full
1618 mut value := ''
1619 space_idx := full.index_u8(` `)
1620 if space_idx > 0 {
1621 name = full[..space_idx]
1622 value = full[space_idx + 1..].trim_space()
1623 }
1624 if p.tok == .semicolon {
1625 p.next()
1626 }
1627 return p.a.add_node(flat.Node{
1628 kind: .directive
1629 value: name
1630 typ: value
1631 })
1632}
1633
1634fn (mut p Parser) skip_attrs() {
1635 if p.tok == .attribute {
1636 p.next()
1637 if p.tok == .key_if {
1638 p.next()
1639 mut cond := strings.new_builder(32)
1640 for p.tok != .rsbr && p.tok != .eof {
1641 tok_str := p.comptime_cond_token_text()
1642 if cond.len > 0 && tok_str != '?' {
1643 cond.write_string(' ')
1644 }
1645 cond.write_string(tok_str)
1646 p.next()
1647 }
1648 if !eval_comptime_cond(p.prefs, cond.str()) {
1649 p.skip_next_decl = true
1650 }
1651 }
1652 for p.tok != .rsbr && p.tok != .eof {
1653 if p.tok == .name {
1654 if p.lit == 'flag' {
1655 p.pending_flag = true
1656 } else if p.lit == 'params' {
1657 p.pending_params = true
1658 } else if p.lit == 'export' {
1659 p.try_parse_export_attr()
1660 continue
1661 }
1662 }
1663 p.next()
1664 }
1665 p.check(.rsbr)
1666 return
1667 }
1668 if p.tok == .lsbr {
1669 mut depth := 1
1670 p.next()
1671 for depth > 0 && p.tok != .eof {
1672 if depth == 1 && p.tok == .name {
1673 if p.lit == 'flag' {
1674 p.pending_flag = true
1675 } else if p.lit == 'params' {
1676 p.pending_params = true
1677 } else if p.lit == 'export' {
1678 p.try_parse_export_attr()
1679 continue
1680 }
1681 }
1682 if p.tok == .lsbr {
1683 depth++
1684 } else if p.tok == .rsbr {
1685 depth--
1686 }
1687 p.next()
1688 }
1689 }
1690}
1691
1692fn (mut p Parser) try_parse_export_attr() {
1693 p.next()
1694 if p.tok != .colon {
1695 return
1696 }
1697 p.next()
1698 if int(p.tok) != int(token.Token.string) {
1699 return
1700 }
1701 p.pending_export = strip_quotes(p.lit)
1702 p.next()
1703}
1704
1705fn (mut p Parser) skip_top_level_stmt() {
1706 // A newline after the attribute group (`@[if flag ?]`) yields an auto-inserted
1707 // semicolon before the declaration; skip it (and any further attributes) so the
1708 // declaration itself — not just the empty statement — is skipped.
1709 for p.tok == .attribute || p.tok == .lsbr || p.tok == .semicolon {
1710 if p.tok == .semicolon {
1711 p.next()
1712 continue
1713 }
1714 p.skip_attrs()
1715 }
1716 if p.tok == .key_pub {
1717 p.next()
1718 }
1719 match p.tok {
1720 .key_fn {
1721 p.next()
1722 for p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
1723 p.next()
1724 }
1725 if p.tok == .lcbr {
1726 p.skip_block()
1727 } else if p.tok == .semicolon {
1728 p.next()
1729 }
1730 }
1731 .key_struct, .key_union, .key_enum, .key_interface, .key_const, .key_global {
1732 for p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
1733 p.next()
1734 }
1735 if p.tok == .lcbr {
1736 p.skip_block()
1737 } else if p.tok == .semicolon {
1738 p.next()
1739 }
1740 }
1741 else {
1742 for p.tok != .semicolon && p.tok != .eof {
1743 p.next()
1744 }
1745 if p.tok == .semicolon {
1746 p.next()
1747 }
1748 }
1749 }
1750}
1751
1752fn (mut p Parser) parse_comptime_if() flat.NodeId {
1753 p.next() // skip $
1754 if p.tok != .key_if {
1755 // $for or other comptime — skip
1756 if p.tok == .key_for {
1757 p.next()
1758 for p.tok != .lcbr && p.tok != .eof {
1759 p.next()
1760 }
1761 p.skip_block()
1762 } else {
1763 for p.tok != .semicolon && p.tok != .eof {
1764 p.next()
1765 }
1766 if p.tok == .semicolon {
1767 p.next()
1768 }
1769 }
1770 return flat.empty_node
1771 }
1772 p.next() // skip 'if'
1773 cond := p.parse_comptime_cond()
1774 if comptime_cond_has_type_test(cond) {
1775 p.skip_block()
1776 p.skip_comptime_else()
1777 return flat.empty_node
1778 }
1779 taken := eval_comptime_cond(p.prefs, cond)
1780 if taken {
1781 result := p.block_stmt()
1782 p.skip_comptime_else()
1783 return result
1784 } else {
1785 p.skip_block()
1786 return p.parse_comptime_else()
1787 }
1788}
1789
1790fn (mut p Parser) parse_top_level_comptime_if() flat.NodeId {
1791 p.next() // skip $
1792 if p.tok != .key_if {
1793 // $for or other comptime - skip
1794 if p.tok == .key_for {
1795 p.next()
1796 for p.tok != .lcbr && p.tok != .eof {
1797 p.next()
1798 }
1799 p.skip_block()
1800 } else {
1801 for p.tok != .semicolon && p.tok != .eof {
1802 p.next()
1803 }
1804 if p.tok == .semicolon {
1805 p.next()
1806 }
1807 }
1808 return flat.empty_node
1809 }
1810 p.next() // skip 'if'
1811 cond := p.parse_comptime_cond()
1812 if comptime_cond_has_type_test(cond) {
1813 p.skip_block()
1814 p.skip_comptime_else()
1815 return flat.empty_node
1816 }
1817 taken := eval_comptime_cond(p.prefs, cond)
1818 if taken {
1819 result := p.top_level_block_stmt()
1820 p.skip_comptime_else()
1821 return result
1822 }
1823 p.skip_block()
1824 return p.parse_top_level_comptime_else()
1825}
1826
1827fn (mut p Parser) top_level_block_stmt() flat.NodeId {
1828 ids := p.parse_top_level_block_body()
1829 start := p.add_children(ids)
1830 return p.a.add_node(flat.Node{
1831 kind: .block
1832 children_start: start
1833 children_count: flat.child_count(ids.len)
1834 })
1835}
1836
1837fn (mut p Parser) parse_top_level_block_body() []flat.NodeId {
1838 p.check(.lcbr)
1839 mut ids := []flat.NodeId{}
1840 for p.tok != .rcbr && p.tok != .eof {
1841 if p.tok == .semicolon {
1842 p.next()
1843 continue
1844 }
1845 id := p.top_level_stmt()
1846 if int(id) >= 0 {
1847 ids << id
1848 }
1849 }
1850 p.check(.rcbr)
1851 return ids
1852}
1853
1854fn (mut p Parser) parse_comptime_cond() string {
1855 mut cond := strings.new_builder(64)
1856 for p.tok != .lcbr && p.tok != .eof {
1857 tok_str := p.comptime_cond_token_text()
1858 if cond.len > 0 && tok_str != '?' {
1859 cond.write_string(' ')
1860 }
1861 cond.write_string(tok_str)
1862 p.next()
1863 }
1864 return cond.str()
1865}
1866
1867fn (p &Parser) comptime_cond_token_text() string {
1868 if p.lit.len > 0 {
1869 return p.lit
1870 }
1871 if p.s.pos >= 0 && p.s.pos < p.s.src.len {
1872 c := p.s.src[p.s.pos]
1873 if c == `&` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `&` {
1874 return '&&'
1875 }
1876 if c == `|` && p.s.pos + 1 < p.s.src.len && p.s.src[p.s.pos + 1] == `|` {
1877 return '||'
1878 }
1879 if c == `!` {
1880 return '!'
1881 }
1882 if c == `?` {
1883 return '?'
1884 }
1885 }
1886 tok := p.tok
1887 if tok == .and {
1888 return '&&'
1889 }
1890 if tok == .logical_or {
1891 return '||'
1892 }
1893 if tok == .not {
1894 return '!'
1895 }
1896 if tok == .question {
1897 return '?'
1898 }
1899 if tok == .key_true {
1900 return 'true'
1901 }
1902 if tok == .key_false {
1903 return 'false'
1904 }
1905 return ''
1906}
1907
1908fn comptime_cond_has_type_test(cond string) bool {
1909 return cond.contains(' is ') || cond.contains(' !is ')
1910}
1911
1912fn (mut p Parser) skip_comptime_else() {
1913 for {
1914 if p.tok == .semicolon {
1915 p.next()
1916 }
1917 if p.tok != .dollar || p.peek() != .key_else {
1918 return
1919 }
1920 p.next() // skip $
1921 p.next() // skip else
1922 if p.tok == .semicolon {
1923 p.next()
1924 }
1925 if p.tok != .dollar || p.peek() != .key_if {
1926 p.skip_block()
1927 return
1928 }
1929 p.next() // skip $
1930 p.next() // skip if
1931 for p.tok != .lcbr && p.tok != .eof {
1932 p.next()
1933 }
1934 p.skip_block()
1935 }
1936}
1937
1938fn (mut p Parser) parse_comptime_else() flat.NodeId {
1939 // Skip auto-semicolons before $else
1940 if p.tok == .semicolon && p.peek() == .dollar {
1941 p.next()
1942 }
1943 if p.tok != .dollar {
1944 return flat.empty_node
1945 }
1946 if p.peek() != .key_else {
1947 return flat.empty_node
1948 }
1949 p.next() // skip $
1950 p.next() // skip else
1951 // $else $if — recurse
1952 if p.tok == .dollar || (p.tok == .semicolon && p.peek() == .dollar) {
1953 if p.tok == .semicolon {
1954 p.next()
1955 }
1956 return p.parse_comptime_if()
1957 }
1958 return p.block_stmt()
1959}
1960
1961fn (mut p Parser) parse_top_level_comptime_else() flat.NodeId {
1962 // Skip auto-semicolons before $else
1963 if p.tok == .semicolon && p.peek() == .dollar {
1964 p.next()
1965 }
1966 if p.tok != .dollar {
1967 return flat.empty_node
1968 }
1969 if p.peek() != .key_else {
1970 return flat.empty_node
1971 }
1972 p.next() // skip $
1973 p.next() // skip else
1974 // $else $if - recurse
1975 if p.tok == .dollar || (p.tok == .semicolon && p.peek() == .dollar) {
1976 if p.tok == .semicolon {
1977 p.next()
1978 }
1979 return p.parse_top_level_comptime_if()
1980 }
1981 return p.top_level_block_stmt()
1982}
1983
1984fn eval_comptime_cond(prefs &pref.Preferences, cond string) bool {
1985 c := comptime_cond_strip_outer_parens(cond.trim_space())
1986 left_or, right_or, has_or := comptime_cond_split_top_level(c, '||')
1987 if has_or {
1988 return eval_comptime_cond(prefs, left_or) || eval_comptime_cond(prefs, right_or)
1989 }
1990 left_and, right_and, has_and := comptime_cond_split_top_level(c, '&&')
1991 if has_and {
1992 return eval_comptime_cond(prefs, left_and) && eval_comptime_cond(prefs, right_and)
1993 }
1994 if c.starts_with('!') {
1995 return !eval_comptime_cond(prefs, c[1..])
1996 }
1997 if c == 'true' {
1998 return true
1999 }
2000 if c == 'false' {
2001 return false
2002 }
2003 if c.starts_with('pkgconfig') {
2004 return eval_pkgconfig_cond(c)
2005 }
2006 flag := c.trim_space().trim_right('? ')
2007 return pref.comptime_flag_value(prefs, flag)
2008}
2009
2010fn comptime_cond_strip_outer_parens(cond string) string {
2011 mut c := cond
2012 for c.len >= 2 && c[0] == `(` && c[c.len - 1] == `)` {
2013 mut depth := 0
2014 mut quote := u8(0)
2015 mut wraps := true
2016 for i in 0 .. c.len {
2017 ch := c[i]
2018 if quote != 0 {
2019 if ch == quote {
2020 quote = 0
2021 }
2022 continue
2023 }
2024 if ch == `'` || ch == `"` {
2025 quote = ch
2026 continue
2027 }
2028 if ch == `(` {
2029 depth++
2030 } else if ch == `)` {
2031 depth--
2032 if depth == 0 && i != c.len - 1 {
2033 wraps = false
2034 break
2035 }
2036 }
2037 }
2038 if !wraps {
2039 break
2040 }
2041 c = c[1..c.len - 1].trim_space()
2042 }
2043 return c
2044}
2045
2046fn comptime_cond_split_top_level(cond string, op string) (string, string, bool) {
2047 mut depth := 0
2048 mut quote := u8(0)
2049 mut i := 0
2050 for i < cond.len {
2051 ch := cond[i]
2052 if quote != 0 {
2053 if ch == quote {
2054 quote = 0
2055 }
2056 i++
2057 continue
2058 }
2059 if ch == `'` || ch == `"` {
2060 quote = ch
2061 i++
2062 continue
2063 }
2064 if ch == `(` {
2065 depth++
2066 } else if ch == `)` && depth > 0 {
2067 depth--
2068 }
2069 if depth == 0 && i + op.len <= cond.len && cond[i..i + op.len] == op {
2070 return cond[..i], cond[i + op.len..], true
2071 }
2072 i++
2073 }
2074 return '', '', false
2075}
2076
2077fn eval_pkgconfig_cond(cond string) bool {
2078 name := pkgconfig_name_from_cond(cond)
2079 if !is_safe_pkgconfig_name(name) {
2080 return false
2081 }
2082 return os.system('pkg-config --exists ${name}') == 0
2083}
2084
2085fn is_safe_pkgconfig_name(name string) bool {
2086 if name.len == 0 || !pkgconfig_name_char_is_alnum(name[0]) {
2087 return false
2088 }
2089 for ch in name {
2090 if pkgconfig_name_char_is_alnum(ch) || ch == `_` || ch == `-` || ch == `.` || ch == `+` {
2091 continue
2092 }
2093 return false
2094 }
2095 return true
2096}
2097
2098fn pkgconfig_name_char_is_alnum(ch u8) bool {
2099 return (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`) || (ch >= `0` && ch <= `9`)
2100}
2101
2102fn pkgconfig_name_from_cond(cond string) string {
2103 quote1 := cond.index_u8(`'`)
2104 if quote1 >= 0 {
2105 rest := cond[quote1 + 1..]
2106 end := rest.index_u8(`'`)
2107 if end >= 0 {
2108 return rest[..end]
2109 }
2110 }
2111 quote2 := cond.index_u8(`"`)
2112 if quote2 >= 0 {
2113 rest := cond[quote2 + 1..]
2114 end := rest.index_u8(`"`)
2115 if end >= 0 {
2116 return rest[..end]
2117 }
2118 }
2119 open := cond.index_u8(`(`)
2120 close := cond.last_index_u8(`)`)
2121 if open < 0 || close < 0 {
2122 return ''
2123 }
2124 if close <= open {
2125 return ''
2126 }
2127 return cond[open + 1..close].trim_space().trim('\'"')
2128}
2129
2130fn (mut p Parser) skip_block() {
2131 if p.tok != .lcbr {
2132 return
2133 }
2134 mut depth := 1
2135 p.next()
2136 for depth > 0 && p.tok != .eof {
2137 if p.tok == .lcbr {
2138 depth++
2139 } else if p.tok == .rcbr {
2140 depth--
2141 }
2142 p.next()
2143 }
2144}
2145
2146fn (mut p Parser) skip_brackets() {
2147 if p.tok != .lsbr {
2148 return
2149 }
2150 mut depth := 1
2151 p.next()
2152 for depth > 0 && p.tok != .eof {
2153 if p.tok == .lsbr {
2154 depth++
2155 } else if p.tok == .rsbr {
2156 depth--
2157 }
2158 p.next()
2159 }
2160}
2161
2162fn (mut p Parser) parse_generic_param_names() []string {
2163 mut names := []string{}
2164 if p.tok != .lsbr {
2165 return names
2166 }
2167 mut depth := 1
2168 mut expect_name := true
2169 p.next()
2170 for depth > 0 && p.tok != .eof {
2171 if p.tok == .lsbr {
2172 depth++
2173 expect_name = false
2174 } else if p.tok == .rsbr {
2175 depth--
2176 if depth == 0 {
2177 p.next()
2178 break
2179 }
2180 } else if depth == 1 {
2181 if p.tok == .comma {
2182 expect_name = true
2183 } else if expect_name && p.tok == .name {
2184 names << p.lit
2185 expect_name = false
2186 } else {
2187 expect_name = false
2188 }
2189 }
2190 p.next()
2191 }
2192 return names
2193}
2194
2195fn (mut p Parser) skip_parens() {
2196 if p.tok != .lpar {
2197 return
2198 }
2199 mut depth := 1
2200 p.next()
2201 for depth > 0 && p.tok != .eof {
2202 if p.tok == .lpar {
2203 depth++
2204 } else if p.tok == .rpar {
2205 depth--
2206 }
2207 p.next()
2208 }
2209}
2210
2211fn (mut p Parser) parse_comptime_expr() flat.NodeId {
2212 if p.peek() == .key_for {
2213 return p.parse_comptime_if()
2214 }
2215 p.next() // skip $
2216 if p.tok == .key_if || (p.tok == .name && p.lit == 'if') {
2217 return p.parse_comptime_if_expr_after_if()
2218 }
2219 if p.tok == .name && p.lit == 'd' {
2220 p.next()
2221 if p.tok != .lpar {
2222 return flat.empty_node
2223 }
2224 p.next()
2225 // First argument is the compile-time define name. v3 currently only
2226 // uses the default expression, so consume the name expression.
2227 if p.tok != .comma && p.tok != .rpar && p.tok != .eof {
2228 p.expr(.lowest)
2229 }
2230 if p.tok == .comma {
2231 p.next()
2232 default_expr := p.expr(.lowest)
2233 for p.tok != .rpar && p.tok != .eof {
2234 p.next()
2235 }
2236 p.check(.rpar)
2237 return default_expr
2238 }
2239 p.check(.rpar)
2240 return flat.empty_node
2241 }
2242 if p.tok == .name && p.lit == 'res' {
2243 p.next()
2244 if p.tok == .lpar {
2245 p.skip_parens()
2246 }
2247 return p.a.add_val_id(3, 'false')
2248 }
2249 if p.tok == .name && p.lit == 'env' {
2250 // $env('NAME') evaluates an environment variable at compile time and
2251 // becomes a plain string literal (empty when the variable is unset).
2252 p.next() // skip 'env'
2253 mut env_val := ''
2254 if p.tok == .lpar {
2255 p.next() // skip (
2256 if p.tok == .string {
2257 env_val = os.getenv(strip_quotes(p.lit))
2258 p.next()
2259 }
2260 for p.tok != .rpar && p.tok != .eof {
2261 p.next()
2262 }
2263 p.check(.rpar)
2264 }
2265 return p.a.add_val_id(5, env_val)
2266 }
2267 for p.tok != .semicolon && p.tok != .eof {
2268 p.next()
2269 }
2270 // Unknown comptime expression: return an empty string literal rather than
2271 // `empty_node`, so consumers (e.g. const initializers) never store an
2272 // invalid (-1) child node.
2273 return p.a.add_val_id(5, '')
2274}
2275
2276fn (mut p Parser) parse_comptime_if_expr() flat.NodeId {
2277 p.next() // skip $
2278 if p.tok != .key_if && !(p.tok == .name && p.lit == 'if') {
2279 return flat.empty_node
2280 }
2281 return p.parse_comptime_if_expr_after_if()
2282}
2283
2284fn (mut p Parser) parse_comptime_if_expr_after_if() flat.NodeId {
2285 p.next() // skip if
2286 cond := p.parse_comptime_cond()
2287 taken := eval_comptime_cond(p.prefs, cond)
2288 if taken {
2289 result := p.parse_comptime_expr_block()
2290 p.skip_comptime_else()
2291 return result
2292 }
2293 p.skip_block()
2294 return p.parse_comptime_else_expr()
2295}
2296
2297fn (mut p Parser) parse_comptime_else_expr() flat.NodeId {
2298 if p.tok == .semicolon && p.peek() == .dollar {
2299 p.next()
2300 }
2301 if p.tok != .dollar || p.peek() != .key_else {
2302 return flat.empty_node
2303 }
2304 p.next() // skip $
2305 p.next() // skip else
2306 if p.tok == .semicolon && p.peek() == .dollar {
2307 p.next()
2308 }
2309 if p.tok == .dollar {
2310 return p.parse_comptime_if_expr()
2311 }
2312 return p.parse_comptime_expr_block()
2313}
2314
2315fn (mut p Parser) parse_comptime_expr_block() flat.NodeId {
2316 if p.tok != .lcbr {
2317 return flat.empty_node
2318 }
2319 p.next()
2320 if p.tok == .rcbr {
2321 p.next()
2322 return flat.empty_node
2323 }
2324 result := p.expr(.lowest)
2325 for p.tok != .rcbr && p.tok != .eof {
2326 p.next()
2327 }
2328 if p.tok == .rcbr {
2329 p.next()
2330 }
2331 return result
2332}
2333
2334// ==================== statements ====================
2335
2336fn (mut p Parser) stmt() flat.NodeId {
2337 match p.tok {
2338 .key_return {
2339 return p.return_stmt()
2340 }
2341 .key_if {
2342 return p.if_stmt()
2343 }
2344 .key_for {
2345 return p.for_stmt()
2346 }
2347 .key_fn {
2348 return p.fn_decl()
2349 }
2350 .key_match {
2351 return p.match_stmt()
2352 }
2353 .key_break {
2354 p.next()
2355 mut label := ''
2356 if p.tok == .name {
2357 label = p.lit
2358 p.next()
2359 }
2360 if p.tok == .semicolon {
2361 p.next()
2362 }
2363 return p.a.add_val(.break_stmt, label)
2364 }
2365 .key_continue {
2366 p.next()
2367 mut label := ''
2368 if p.tok == .name {
2369 label = p.lit
2370 p.next()
2371 }
2372 if p.tok == .semicolon {
2373 p.next()
2374 }
2375 return p.a.add_val(.continue_stmt, label)
2376 }
2377 .key_mut {
2378 p.next()
2379 if p.tok == .key_static {
2380 return p.static_decl_stmt()
2381 }
2382 return p.assign_or_expr_stmt()
2383 }
2384 .key_static {
2385 return p.static_decl_stmt()
2386 }
2387 .key_pub {
2388 p.next()
2389 if p.tok == .key_fn {
2390 return p.fn_decl()
2391 }
2392 return p.assign_or_expr_stmt()
2393 }
2394 .attribute {
2395 p.skip_attrs()
2396 return p.stmt()
2397 }
2398 .key_unsafe {
2399 p.next()
2400 return p.block_stmt()
2401 }
2402 .key_defer {
2403 return p.defer_stmt()
2404 }
2405 .key_assert {
2406 return p.assert_stmt()
2407 }
2408 .key_goto {
2409 return p.goto_stmt()
2410 }
2411 .key_go, .key_spawn {
2412 p.next()
2413 spawn_expr := p.expr(.lowest)
2414 if p.tok == .semicolon {
2415 p.next()
2416 }
2417 sstart := p.add_child(spawn_expr)
2418 return p.a.add_node(flat.Node{
2419 kind: .expr_stmt
2420 children_start: sstart
2421 children_count: 1
2422 })
2423 }
2424 .key_asm {
2425 return p.asm_stmt()
2426 }
2427 .dollar {
2428 return p.parse_comptime_if()
2429 }
2430 .hash {
2431 return p.directive()
2432 }
2433 .lcbr {
2434 return p.block_stmt()
2435 }
2436 .semicolon {
2437 p.next()
2438 return flat.empty_node
2439 }
2440 .name {
2441 // label: name followed by ':'
2442 if p.peek() == .colon {
2443 label_name := p.lit
2444 p.next() // skip name
2445 p.next() // skip :
2446 if p.tok == .semicolon {
2447 p.next()
2448 }
2449 return p.a.add_val(.label_stmt, label_name)
2450 }
2451 return p.assign_or_expr_stmt()
2452 }
2453 else {
2454 return p.assign_or_expr_stmt()
2455 }
2456 }
2457}
2458
2459fn (mut p Parser) static_decl_stmt() flat.NodeId {
2460 p.next() // skip `static`
2461 if p.tok == .key_mut {
2462 p.next()
2463 }
2464 lhs := p.expr(.lowest)
2465 if p.tok == .decl_assign {
2466 p.next()
2467 rhs := p.expr(.lowest)
2468 if p.tok == .semicolon {
2469 p.next()
2470 }
2471 istart := p.add_children2(lhs, rhs)
2472 return p.a.add_node(flat.Node{
2473 kind: .decl_assign
2474 op: .assign
2475 value: 'static'
2476 children_start: istart
2477 children_count: 2
2478 })
2479 }
2480 if p.tok == .semicolon {
2481 p.next()
2482 }
2483 estart := p.add_child(lhs)
2484 return p.a.add_node(flat.Node{
2485 kind: .expr_stmt
2486 children_start: estart
2487 children_count: 1
2488 })
2489}
2490
2491fn (mut p Parser) return_stmt() flat.NodeId {
2492 p.next() // skip 'return'
2493 mut ids := []flat.NodeId{}
2494 if p.tok != .semicolon && p.tok != .rcbr && p.tok != .eof {
2495 ids << p.expr(.lowest)
2496 for p.tok == .comma {
2497 p.next()
2498 ids << p.expr(.lowest)
2499 }
2500 }
2501 if p.tok == .semicolon {
2502 p.next()
2503 }
2504 start := p.add_children(ids)
2505 return p.a.add_node(flat.Node{
2506 kind: .return_stmt
2507 children_start: start
2508 children_count: flat.child_count(ids.len)
2509 })
2510}
2511
2512fn (mut p Parser) if_stmt() flat.NodeId {
2513 p.next() // skip 'if'
2514 cond := p.expr(.lowest)
2515
2516 // if-guard: if a, b := expr { ... } or if val := expr { ... }
2517 mut guard_cond := cond
2518 if p.tok == .comma || p.tok == .decl_assign {
2519 // Simple if-guard; treat as regular condition for flat AST
2520 if p.tok == .decl_assign {
2521 p.next()
2522 rhs := p.expr(.lowest)
2523 istart := p.add_children2(guard_cond, rhs)
2524 guard_cond = p.a.add_node(flat.Node{
2525 kind: .decl_assign
2526 op: .assign
2527 children_start: istart
2528 children_count: 2
2529 })
2530 } else {
2531 // comma case: if a, b := expr
2532 mut lhs_ids := []flat.NodeId{}
2533 lhs_ids << guard_cond
2534 for p.tok == .comma {
2535 p.next()
2536 lhs_ids << p.expr(.lowest)
2537 }
2538 if p.tok == .decl_assign {
2539 p.next()
2540 rhs := p.expr(.lowest)
2541 mut all_ids := []flat.NodeId{cap: lhs_ids.len + 1}
2542 all_ids << lhs_ids[0]
2543 all_ids << rhs
2544 for i in 1 .. lhs_ids.len {
2545 all_ids << lhs_ids[i]
2546 }
2547 istart := p.add_children(all_ids)
2548 guard_cond = p.a.add_node(flat.Node{
2549 kind: .decl_assign
2550 op: .assign
2551 children_start: istart
2552 children_count: flat.child_count(all_ids.len)
2553 })
2554 }
2555 }
2556 }
2557
2558 // skip auto-semicolon before {
2559 if p.tok == .semicolon && p.peek() == .lcbr {
2560 p.next()
2561 }
2562 body := p.block_stmt()
2563 mut ids := []flat.NodeId{}
2564 ids << guard_cond
2565 ids << body
2566
2567 // skip auto-semicolon between } and else
2568 if p.tok == .semicolon && p.peek() == .key_else {
2569 p.next()
2570 }
2571 if p.tok == .key_else {
2572 p.next()
2573 if p.tok == .key_if {
2574 ids << p.if_stmt()
2575 } else {
2576 ids << p.block_stmt()
2577 }
2578 }
2579 start := p.add_children(ids)
2580 return p.a.add_node(flat.Node{
2581 kind: .if_expr
2582 children_start: start
2583 children_count: flat.child_count(ids.len)
2584 })
2585}
2586
2587fn (mut p Parser) for_stmt() flat.NodeId {
2588 p.next() // skip 'for'
2589 if p.tok == .lcbr {
2590 // infinite loop: for { ... }
2591 body_ids := p.parse_block_body()
2592 empty1 := p.a.add(flat.NodeKind.empty)
2593 empty2 := p.a.add(flat.NodeKind.empty)
2594 empty3 := p.a.add(flat.NodeKind.empty)
2595 mut ids := []flat.NodeId{}
2596 ids << empty1
2597 ids << empty2
2598 ids << empty3
2599 for id in body_ids {
2600 ids << id
2601 }
2602 start := p.add_children(ids)
2603 return p.a.add_node(flat.Node{
2604 kind: .for_stmt
2605 children_start: start
2606 children_count: flat.child_count(ids.len)
2607 })
2608 }
2609
2610 // Check for for-in: `for x in ...` or `for i, x in ...` or `for mut x in ...`
2611 if p.tok == .name && (p.peek() == .key_in || p.peek() == .comma) {
2612 first_expr := p.expr(.bit_or)
2613 return p.for_in(first_expr, false)
2614 }
2615 if p.tok == .key_mut {
2616 p.next()
2617 mut first_expr := flat.empty_node
2618 if p.tok == .name {
2619 ident := p.a.add_val(.ident, p.expect_name())
2620 if p.tok == .key_in || p.tok == .comma {
2621 first_expr = ident
2622 } else {
2623 first_expr = p.expr_with_lhs(ident, .lowest)
2624 }
2625 } else {
2626 first_expr = p.expr(.lowest)
2627 }
2628 if p.tok == .key_in || p.tok == .comma {
2629 return p.for_in(first_expr, true)
2630 }
2631 if p.tok == .lcbr {
2632 body_ids := p.parse_block_body()
2633 init_empty := p.a.add(flat.NodeKind.empty)
2634 post_empty := p.a.add(flat.NodeKind.empty)
2635 mut ids := []flat.NodeId{}
2636 ids << init_empty
2637 ids << first_expr
2638 ids << post_empty
2639 for id in body_ids {
2640 ids << id
2641 }
2642 start := p.add_children(ids)
2643 return p.a.add_node(flat.Node{
2644 kind: .for_stmt
2645 children_start: start
2646 children_count: flat.child_count(ids.len)
2647 })
2648 }
2649 }
2650
2651 if p.tok == .semicolon {
2652 p.next()
2653 cond := if p.tok == .semicolon {
2654 p.a.add(flat.NodeKind.empty)
2655 } else {
2656 p.expr(.lowest)
2657 }
2658 if p.tok == .semicolon {
2659 p.next()
2660 }
2661 post := if p.tok != .lcbr && p.tok != .eof {
2662 p.assign_or_expr_inline()
2663 } else {
2664 p.a.add(flat.NodeKind.empty)
2665 }
2666 body_ids := p.parse_block_body()
2667 mut ids := []flat.NodeId{}
2668 ids << p.a.add(flat.NodeKind.empty)
2669 ids << cond
2670 ids << post
2671 for id in body_ids {
2672 ids << id
2673 }
2674 start := p.add_children(ids)
2675 return p.a.add_node(flat.Node{
2676 kind: .for_stmt
2677 children_start: start
2678 children_count: flat.child_count(ids.len)
2679 })
2680 }
2681
2682 first_expr := p.expr(.lowest)
2683
2684 // for-in: `for x in expr` or `for i, x in expr`
2685 if p.tok == .key_in || p.tok == .comma {
2686 return p.for_in(first_expr, false)
2687 }
2688
2689 // C-style: `for i := 0; ...`
2690 if p.tok == .decl_assign || token_is_assignment(p.tok) {
2691 return p.for_c_style(first_expr)
2692 }
2693
2694 if p.tok == .semicolon {
2695 // could be C-style with expression init, or just a separator
2696 // peek to check
2697 pk := p.peek()
2698 if pk == .semicolon || pk == .lcbr {
2699 // condition-only: `for cond { }`
2700 // fall through
2701 } else if pk != .rcbr && pk != .eof {
2702 // might be C-style: `for expr; cond; post`
2703 p.next() // skip ;
2704 cond := p.expr(.lowest)
2705 if p.tok == .semicolon {
2706 p.next()
2707 }
2708 post := if p.tok != .lcbr && p.tok != .eof {
2709 p.assign_or_expr_inline()
2710 } else {
2711 p.a.add(flat.NodeKind.empty)
2712 }
2713 body_ids := p.parse_block_body()
2714 // first_expr becomes init as expr_stmt
2715 init_start := p.add_child(first_expr)
2716 init_id := p.a.add_node(flat.Node{
2717 kind: .expr_stmt
2718 children_start: init_start
2719 children_count: 1
2720 })
2721 mut ids := []flat.NodeId{}
2722 ids << init_id
2723 ids << cond
2724 ids << post
2725 for id in body_ids {
2726 ids << id
2727 }
2728 start := p.add_children(ids)
2729 return p.a.add_node(flat.Node{
2730 kind: .for_stmt
2731 children_start: start
2732 children_count: flat.child_count(ids.len)
2733 })
2734 }
2735 }
2736
2737 // condition-only: `for cond { ... }`
2738 if p.tok == .lcbr {
2739 body_ids := p.parse_block_body()
2740 init_empty := p.a.add(flat.NodeKind.empty)
2741 post_empty := p.a.add(flat.NodeKind.empty)
2742 mut ids := []flat.NodeId{}
2743 ids << init_empty
2744 ids << first_expr
2745 ids << post_empty
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 return flat.empty_node
2758}
2759
2760fn (mut p Parser) for_c_style(lhs_expr flat.NodeId) flat.NodeId {
2761 op_id := int(p.tok)
2762 p.next()
2763 rhs := p.expr(.lowest)
2764
2765 mut init_id := flat.empty_node
2766 if op_id == 12 {
2767 istart := p.add_children2(lhs_expr, rhs)
2768 init_id = p.a.add_node(flat.Node{
2769 kind: .decl_assign
2770 op: .assign
2771 children_start: istart
2772 children_count: 2
2773 })
2774 } else {
2775 istart := p.add_children2(lhs_expr, rhs)
2776 init_id = p.a.add_node(flat.Node{
2777 kind: .assign
2778 op: token_id_to_op(op_id)
2779 children_start: istart
2780 children_count: 2
2781 })
2782 }
2783
2784 if p.tok == .semicolon {
2785 p.next()
2786 }
2787
2788 cond := p.expr(.lowest)
2789 if p.tok == .semicolon {
2790 p.next()
2791 }
2792
2793 post := if p.tok != .lcbr && p.tok != .eof {
2794 p.assign_or_expr_inline()
2795 } else {
2796 p.a.add(flat.NodeKind.empty)
2797 }
2798
2799 body_ids := p.parse_block_body()
2800
2801 mut ids := []flat.NodeId{}
2802 ids << init_id
2803 ids << cond
2804 ids << post
2805 for id in body_ids {
2806 ids << id
2807 }
2808 start := p.add_children(ids)
2809 return p.a.add_node(flat.Node{
2810 kind: .for_stmt
2811 children_start: start
2812 children_count: flat.child_count(ids.len)
2813 })
2814}
2815
2816fn (mut p Parser) for_in(first_expr flat.NodeId, first_is_mut bool) flat.NodeId {
2817 // first_expr is either the key var or the only var
2818 mut key_id := first_expr
2819 mut val_id := flat.empty_node
2820 mut value_is_mut := first_is_mut
2821
2822 if p.tok == .comma {
2823 p.next()
2824 // second variable
2825 if p.tok == .key_mut {
2826 p.next()
2827 value_is_mut = true
2828 }
2829 val_id = p.a.add_val(.ident, p.expect_name())
2830 }
2831
2832 p.check(.key_in)
2833 was_in_for_container := p.in_for_container
2834 p.in_for_container = true
2835 container := p.expr(.lowest)
2836 p.in_for_container = was_in_for_container
2837
2838 // optional range: `for i in 0 .. n`
2839 mut range_end := flat.empty_node
2840 if p.tok == .dotdot {
2841 p.next()
2842 range_end = p.expr(.lowest)
2843 }
2844
2845 body_ids := p.parse_block_body()
2846
2847 mut ids := []flat.NodeId{cap: 4 + body_ids.len}
2848 ids << key_id
2849 ids << val_id
2850 ids << container
2851 if int(range_end) >= 0 {
2852 ids << range_end
2853 }
2854 for id in body_ids {
2855 ids << id
2856 }
2857 start := p.add_children(ids)
2858 return p.a.add_node(flat.Node{
2859 kind: .for_in_stmt
2860 children_start: start
2861 children_count: flat.child_count(ids.len)
2862 // value field stores the count of header elements (key, val, container, [range_end])
2863 // so gen knows where body starts
2864 value: if int(range_end) >= 0 { '4' } else { '3' }
2865 op: if value_is_mut { .amp } else { .none }
2866 })
2867}
2868
2869fn (mut p Parser) match_stmt() flat.NodeId {
2870 p.next() // skip 'match'
2871 match_expr := p.expr(.lowest)
2872 p.check(.lcbr)
2873
2874 mut ids := []flat.NodeId{cap: 8}
2875 ids << match_expr
2876
2877 for p.tok != .rcbr && p.tok != .eof {
2878 if p.tok == .semicolon {
2879 p.next()
2880 continue
2881 }
2882 ids << p.match_branch()
2883 }
2884 p.check(.rcbr)
2885
2886 start := p.add_children(ids)
2887 return p.a.add_node(flat.Node{
2888 kind: .match_stmt
2889 children_start: start
2890 children_count: flat.child_count(ids.len)
2891 })
2892}
2893
2894fn (mut p Parser) match_branch_cond() flat.NodeId {
2895 if p.tok == .lsbr && p.peek() == .rsbr {
2896 typ := p.parse_type_name()
2897 elem_type := if typ.starts_with('[]') { typ[2..] } else { typ }
2898 return p.a.add_node(flat.Node{
2899 kind: .array_init
2900 value: elem_type
2901 typ: typ
2902 })
2903 }
2904 if p.tok == .name && p.peek() == .dot {
2905 mod_name := p.lit
2906 p.next()
2907 p.next()
2908 if p.tok == .name && p.lit.len > 0 && p.lit[0] >= `A` && p.lit[0] <= `Z` {
2909 type_name := p.lit
2910 p.next()
2911 mod_id := p.a.add_val(.ident, mod_name)
2912 start := p.add_child(mod_id)
2913 return p.a.add_node(flat.Node{
2914 kind: .selector
2915 value: type_name
2916 children_start: start
2917 children_count: 1
2918 })
2919 }
2920 base_id := p.a.add_val(.ident, mod_name)
2921 start := p.add_child(base_id)
2922 sel := p.a.add_node(flat.Node{
2923 kind: .selector
2924 value: p.lit
2925 children_start: start
2926 children_count: 1
2927 })
2928 if p.tok == .name {
2929 p.next()
2930 }
2931 if p.tok != .lcbr && p.tok != .comma {
2932 return p.expr_with_lhs(sel, .lowest)
2933 }
2934 return sel
2935 }
2936 if p.tok == .name && p.lit.len > 0 && p.lit[0] >= `A` && p.lit[0] <= `Z` {
2937 name := p.lit
2938 p.next()
2939 return p.a.add_val(.ident, name)
2940 }
2941 if p.tok == .name && is_builtin_type(p.lit) && p.peek() == .lcbr {
2942 name := p.lit
2943 p.next()
2944 return p.a.add_val(.ident, name)
2945 }
2946 cond := p.expr(.lowest)
2947 if p.tok == .ellipsis {
2948 p.next()
2949 rhs := p.expr(.lowest)
2950 rstart := p.add_children2(cond, rhs)
2951 return p.a.add_node(flat.Node{
2952 kind: .range
2953 children_start: rstart
2954 children_count: 2
2955 })
2956 }
2957 return cond
2958}
2959
2960fn (mut p Parser) match_branch() flat.NodeId {
2961 mut branch_ids := []flat.NodeId{}
2962 mut is_else := false
2963 mut n_conds := 0
2964
2965 if p.tok == .key_else {
2966 is_else = true
2967 p.next()
2968 } else {
2969 branch_ids << p.match_branch_cond()
2970 n_conds = 1
2971 for p.tok == .comma {
2972 p.next()
2973 branch_ids << p.match_branch_cond()
2974 n_conds++
2975 }
2976 }
2977
2978 p.check(.lcbr)
2979 for p.tok != .rcbr && p.tok != .eof {
2980 if p.looks_like_match_branch_start() {
2981 break
2982 }
2983 id := p.stmt()
2984 if int(id) >= 0 {
2985 branch_ids << id
2986 }
2987 }
2988 p.check(.rcbr)
2989
2990 bstart := p.add_children(branch_ids)
2991 return p.a.add_node(flat.Node{
2992 kind: .match_branch
2993 value: if is_else { 'else' } else { '${n_conds}' }
2994 children_start: bstart
2995 children_count: flat.child_count(branch_ids.len)
2996 })
2997}
2998
2999fn (p &Parser) looks_like_match_branch_start() bool {
3000 if p.tok == .key_else {
3001 return true
3002 }
3003 return false
3004}
3005
3006fn (mut p Parser) block_stmt() flat.NodeId {
3007 ids := p.parse_block_body()
3008 start := p.add_children(ids)
3009 return p.a.add_node(flat.Node{
3010 kind: .block
3011 children_start: start
3012 children_count: flat.child_count(ids.len)
3013 })
3014}
3015
3016fn (mut p Parser) parse_block_body() []flat.NodeId {
3017 p.check(.lcbr)
3018 mut ids := []flat.NodeId{}
3019 for p.tok != .rcbr && p.tok != .eof {
3020 id := p.stmt()
3021 if int(id) >= 0 {
3022 ids << id
3023 }
3024 }
3025 p.check(.rcbr)
3026 return ids
3027}
3028
3029fn (mut p Parser) assign_or_expr_stmt() flat.NodeId {
3030 lhs := p.expr(.lowest)
3031
3032 // multi-assign: a, b := expr1, expr2
3033 if p.tok == .comma {
3034 mut lhs_ids := []flat.NodeId{}
3035 lhs_ids << lhs
3036 for p.tok == .comma {
3037 p.next()
3038 if p.tok == .key_mut {
3039 p.next()
3040 }
3041 lhs_ids << p.expr(.lowest)
3042 }
3043 if p.tok == .decl_assign || token_is_assignment(p.tok) {
3044 op_id := int(p.tok)
3045 p.next()
3046 mut rhs_ids := []flat.NodeId{}
3047 rhs_ids << p.expr(.lowest)
3048 for p.tok == .comma {
3049 p.next()
3050 rhs_ids << p.expr(.lowest)
3051 }
3052 if p.tok == .semicolon {
3053 p.next()
3054 }
3055 mut all_ids := []flat.NodeId{cap: lhs_ids.len * 2}
3056 for i in 0 .. lhs_ids.len {
3057 all_ids << lhs_ids[i]
3058 if i < rhs_ids.len {
3059 all_ids << rhs_ids[i]
3060 }
3061 }
3062 istart := p.add_children(all_ids)
3063 return p.a.add_node(flat.Node{
3064 kind: if op_id == 12 {
3065 flat.NodeKind.decl_assign
3066 } else {
3067 flat.NodeKind.assign
3068 }
3069 op: token_id_to_op(op_id)
3070 children_start: istart
3071 children_count: flat.child_count(all_ids.len)
3072 })
3073 }
3074 if p.tok == .semicolon {
3075 p.next()
3076 }
3077 mut stmt_ids := []flat.NodeId{cap: lhs_ids.len}
3078 for expr_id in lhs_ids {
3079 estart := p.add_child(expr_id)
3080 stmt_ids << p.a.add_node(flat.Node{
3081 kind: .expr_stmt
3082 children_start: estart
3083 children_count: 1
3084 })
3085 }
3086 bstart := p.add_children(stmt_ids)
3087 return p.a.add_node(flat.Node{
3088 kind: .block
3089 children_start: bstart
3090 children_count: flat.child_count(stmt_ids.len)
3091 })
3092 }
3093
3094 if p.tok == .decl_assign {
3095 p.next()
3096 rhs := p.expr(.lowest)
3097 if p.tok == .semicolon {
3098 p.next()
3099 }
3100 istart := p.add_children2(lhs, rhs)
3101 return p.a.add_node(flat.Node{
3102 kind: .decl_assign
3103 op: .assign
3104 children_start: istart
3105 children_count: 2
3106 })
3107 }
3108
3109 if token_is_assignment(p.tok) {
3110 op_id := int(p.tok)
3111 p.next()
3112 rhs := p.expr(.lowest)
3113 if p.tok == .semicolon {
3114 p.next()
3115 }
3116 lhs_node := p.a.nodes[int(lhs)]
3117 kind := if lhs_node.kind == .selector {
3118 flat.NodeKind.selector_assign
3119 } else if lhs_node.kind == .index {
3120 flat.NodeKind.index_assign
3121 } else {
3122 flat.NodeKind.assign
3123 }
3124 istart := p.add_children2(lhs, rhs)
3125 return p.a.add_node(flat.Node{
3126 kind: kind
3127 op: token_id_to_op(op_id)
3128 children_start: istart
3129 children_count: 2
3130 })
3131 }
3132
3133 if p.tok == .semicolon {
3134 p.next()
3135 }
3136
3137 estart := p.add_child(lhs)
3138 return p.a.add_node(flat.Node{
3139 kind: .expr_stmt
3140 children_start: estart
3141 children_count: 1
3142 })
3143}
3144
3145fn (mut p Parser) assign_or_expr_inline() flat.NodeId {
3146 lhs := p.expr(.lowest)
3147
3148 if token_is_assignment(p.tok) {
3149 op_id := int(p.tok)
3150 p.next()
3151 rhs := p.expr(.lowest)
3152 lhs_node := p.a.nodes[int(lhs)]
3153 kind := if lhs_node.kind == .selector {
3154 flat.NodeKind.selector_assign
3155 } else if lhs_node.kind == .index {
3156 flat.NodeKind.index_assign
3157 } else {
3158 flat.NodeKind.assign
3159 }
3160 istart := p.add_children2(lhs, rhs)
3161 return p.a.add_node(flat.Node{
3162 kind: kind
3163 op: token_id_to_op(op_id)
3164 children_start: istart
3165 children_count: 2
3166 })
3167 }
3168
3169 estart := p.add_child(lhs)
3170 return p.a.add_node(flat.Node{
3171 kind: .expr_stmt
3172 children_start: estart
3173 children_count: 1
3174 })
3175}
3176
3177fn (mut p Parser) defer_stmt() flat.NodeId {
3178 p.next() // skip 'defer'
3179 mut mode := ''
3180 if p.tok == .lpar {
3181 p.next()
3182 if p.tok == .key_fn {
3183 mode = 'function'
3184 p.next()
3185 }
3186 p.check(.rpar)
3187 }
3188 body := p.block_stmt()
3189 dstart := p.add_child(body)
3190 return p.a.add_node(flat.Node{
3191 kind: .defer_stmt
3192 children_start: dstart
3193 children_count: 1
3194 value: mode
3195 })
3196}
3197
3198fn (mut p Parser) assert_stmt() flat.NodeId {
3199 p.next() // skip 'assert'
3200 cond := p.expr(.lowest)
3201 mut ids := []flat.NodeId{}
3202 ids << cond
3203 // optional message: assert cond, 'message'
3204 if p.tok == .comma {
3205 p.next()
3206 ids << p.expr(.lowest)
3207 }
3208 if p.tok == .semicolon {
3209 p.next()
3210 }
3211 astart := p.add_children(ids)
3212 return p.a.add_node(flat.Node{
3213 kind: .assert_stmt
3214 children_start: astart
3215 children_count: flat.child_count(ids.len)
3216 })
3217}
3218
3219fn (mut p Parser) goto_stmt() flat.NodeId {
3220 p.next() // skip 'goto'
3221 label := p.expect_name()
3222 if p.tok == .semicolon {
3223 p.next()
3224 }
3225 return p.a.add_val(.goto_stmt, label)
3226}
3227
3228fn (mut p Parser) asm_stmt() flat.NodeId {
3229 p.next() // skip 'asm'
3230 // consume optional volatile keyword
3231 if p.tok == .name && p.lit == 'volatile' {
3232 p.next()
3233 }
3234 // consume the asm block
3235 if p.tok == .lcbr {
3236 p.skip_block()
3237 }
3238 if p.tok == .semicolon {
3239 p.next()
3240 }
3241 return p.a.add(flat.NodeKind.asm_stmt)
3242}
3243
3244// ==================== expressions (Pratt parser) ====================
3245
3246fn (mut p Parser) expr(min_bp token.BindingPower) flat.NodeId {
3247 lhs := p.prefix_expr()
3248 return p.expr_with_lhs(lhs, min_bp)
3249}
3250
3251fn (mut p Parser) expr_with_lhs(first flat.NodeId, min_bp token.BindingPower) flat.NodeId {
3252 mut lhs := first
3253 for {
3254 // selector / method call
3255 if p.tok == .dot {
3256 lhs = p.selector_or_method(lhs)
3257 continue
3258 }
3259 // module-qualified struct init: module.Type{} or module.Type{field: val, ...}
3260 if p.tok == .lcbr {
3261 lhs_node := p.a.nodes[int(lhs)]
3262 if lhs_node.kind == .index {
3263 if full_name := p.generic_struct_init_type_name(lhs) {
3264 lhs = p.struct_init(full_name)
3265 continue
3266 }
3267 }
3268 if lhs_node.kind == .selector && lhs_node.value.len > 0 {
3269 base := p.a.child_node(&lhs_node, 0)
3270 is_c_struct := base.kind == .ident && base.value == 'C'
3271 && !is_all_upper_ident(lhs_node.value)
3272 && (p.peek() == .rcbr || p.peek() == .name
3273 || p.peek() == .ellipsis)
3274 is_v_struct := base.kind == .ident && base.value != 'C' && lhs_node.value[0] >= `A`
3275 && lhs_node.value[0] <= `Z`
3276 if is_c_struct || is_v_struct {
3277 full_name := '${base.value}.${lhs_node.value}'
3278 lhs = p.struct_init(full_name)
3279 continue
3280 }
3281 }
3282 }
3283 // function call
3284 if p.tok == .lpar {
3285 lhs_node := p.a.nodes[int(lhs)]
3286 if lhs_node.kind == .selector && lhs_node.children_count > 0 && lhs_node.value.len > 0
3287 && lhs_node.value[0] >= `A` && lhs_node.value[0] <= `Z` {
3288 base := p.a.child_node(&lhs_node, 0)
3289 if base.kind == .ident && base.value != 'C' {
3290 full_name := '${base.value}.${lhs_node.value}'
3291 p.next() // skip (
3292 inner := p.expr(.lowest)
3293 p.check(.rpar)
3294 cstart := p.add_child(inner)
3295 lhs = p.a.add_node(flat.Node{
3296 kind: .cast_expr
3297 value: full_name
3298 children_start: cstart
3299 children_count: 1
3300 })
3301 continue
3302 }
3303 }
3304 lhs = p.call_args(lhs)
3305 continue
3306 }
3307 // index / generic
3308 if p.tok == .lsbr {
3309 lhs = p.index_expr(lhs)
3310 continue
3311 }
3312 // postfix: ++ -- ? !
3313 if token_is_postfix(p.tok) {
3314 op_id := int(p.tok)
3315 p.next()
3316 pstart := p.add_child(lhs)
3317 lhs = p.a.add_node(flat.Node{
3318 kind: .postfix
3319 op: token_id_to_op(op_id)
3320 children_start: pstart
3321 children_count: 1
3322 })
3323 continue
3324 }
3325 // postfix `!` error propagation: expr!
3326 if p.tok == .not {
3327 p.next()
3328 ostart := p.add_children2(lhs, p.a.add(flat.NodeKind.empty))
3329 lhs = p.a.add_node(flat.Node{
3330 kind: .or_expr
3331 value: '!'
3332 children_start: ostart
3333 children_count: 2
3334 })
3335 continue
3336 }
3337 // postfix `?` optional propagation: expr?
3338 if p.tok == .question {
3339 p.next()
3340 ostart := p.add_children2(lhs, p.a.add(flat.NodeKind.empty))
3341 lhs = p.a.add_node(flat.Node{
3342 kind: .or_expr
3343 value: '?'
3344 children_start: ostart
3345 children_count: 2
3346 })
3347 continue
3348 }
3349 // `as` cast: expr as Type
3350 if p.tok == .key_as {
3351 p.next()
3352 type_name := p.parse_type_name()
3353 astart := p.add_child(lhs)
3354 lhs = p.a.add_node(flat.Node{
3355 kind: .as_expr
3356 value: type_name
3357 children_start: astart
3358 children_count: 1
3359 })
3360 continue
3361 }
3362 // `or` block: expr or { ... }
3363 if p.tok == .key_or {
3364 p.next()
3365 or_body := p.block_stmt()
3366 ostart := p.add_children2(lhs, or_body)
3367 lhs = p.a.add_node(flat.Node{
3368 kind: .or_expr
3369 children_start: ostart
3370 children_count: 2
3371 })
3372 continue
3373 }
3374 // range: expr .. expr
3375 if p.tok == .dotdot {
3376 if int(min_bp) > int(token.BindingPower.lowest) {
3377 break
3378 }
3379 p.next()
3380 rhs := p.expr(.lowest)
3381 rstart := p.add_children2(lhs, rhs)
3382 lhs = p.a.add_node(flat.Node{
3383 kind: .range
3384 children_start: rstart
3385 children_count: 2
3386 })
3387 continue
3388 }
3389 // `is` / `!is` / `not_is` type check
3390 if p.tok == .key_is || p.tok == .not_is {
3391 bp := token.BindingPower.compare
3392 if int(bp) < int(min_bp) {
3393 break
3394 }
3395 is_negated := p.tok == .not_is
3396 p.next()
3397 type_name := p.parse_type_name()
3398 istart := p.add_child(lhs)
3399 is_node := p.a.add_node(flat.Node{
3400 kind: .is_expr
3401 value: type_name
3402 children_start: istart
3403 children_count: 1
3404 })
3405 if is_negated {
3406 nstart := p.add_child(is_node)
3407 lhs = p.a.add_node(flat.Node{
3408 kind: .prefix
3409 op: .not
3410 children_start: nstart
3411 children_count: 1
3412 })
3413 } else {
3414 lhs = is_node
3415 }
3416 continue
3417 }
3418 if p.tok == .not && p.peek() == .key_is {
3419 bp := token.BindingPower.compare
3420 if int(bp) < int(min_bp) {
3421 break
3422 }
3423 p.next() // skip !
3424 p.next() // skip is
3425 type_name := p.parse_type_name()
3426 istart := p.add_child(lhs)
3427 is_node := p.a.add_node(flat.Node{
3428 kind: .is_expr
3429 value: type_name
3430 children_start: istart
3431 children_count: 1
3432 })
3433 nstart := p.add_child(is_node)
3434 lhs = p.a.add_node(flat.Node{
3435 kind: .prefix
3436 op: .not
3437 children_start: nstart
3438 children_count: 1
3439 })
3440 continue
3441 }
3442 // `in` / `!in` / `not_in`
3443 if p.tok == .key_in || p.tok == .not_in {
3444 is_negated := p.tok == .not_in
3445 bp := token.BindingPower.compare
3446 if int(bp) < int(min_bp) {
3447 break
3448 }
3449 p.next()
3450 mut rhs := p.expr(token.BindingPower.bit_or)
3451 if p.tok == .dotdot {
3452 p.next()
3453 range_rhs := p.expr(.lowest)
3454 rstart := p.add_children2(rhs, range_rhs)
3455 rhs = p.a.add_node(flat.Node{
3456 kind: .range
3457 children_start: rstart
3458 children_count: 2
3459 })
3460 }
3461 istart := p.add_children2(lhs, rhs)
3462 in_node := p.a.add_node(flat.Node{
3463 kind: .in_expr
3464 children_start: istart
3465 children_count: 2
3466 })
3467 if is_negated {
3468 nstart := p.add_child(in_node)
3469 lhs = p.a.add_node(flat.Node{
3470 kind: .prefix
3471 op: .not
3472 children_start: nstart
3473 children_count: 1
3474 })
3475 } else {
3476 lhs = in_node
3477 }
3478 continue
3479 }
3480 if p.tok == .not && p.peek() == .key_in {
3481 bp := token.BindingPower.compare
3482 if int(bp) < int(min_bp) {
3483 break
3484 }
3485 p.next() // skip !
3486 p.next() // skip in
3487 mut rhs := p.expr(token.BindingPower.bit_or)
3488 if p.tok == .dotdot {
3489 p.next()
3490 range_rhs := p.expr(.lowest)
3491 rstart := p.add_children2(rhs, range_rhs)
3492 rhs = p.a.add_node(flat.Node{
3493 kind: .range
3494 children_start: rstart
3495 children_count: 2
3496 })
3497 }
3498 istart := p.add_children2(lhs, rhs)
3499 in_node := p.a.add_node(flat.Node{
3500 kind: .in_expr
3501 children_start: istart
3502 children_count: 2
3503 })
3504 nstart := p.add_child(in_node)
3505 lhs = p.a.add_node(flat.Node{
3506 kind: .prefix
3507 op: .not
3508 children_start: nstart
3509 children_count: 1
3510 })
3511 continue
3512 }
3513 // skip auto-semicolons before infix operators (multi-line expressions)
3514 if p.tok == .semicolon {
3515 peek_tok := p.peek()
3516 if token_is_infix(peek_tok) && int(peek_tok) != 85 && int(peek_tok) != 0 {
3517 p.next()
3518 }
3519 }
3520 if token_is_assignment(p.tok) {
3521 break
3522 }
3523 // infix operators
3524 if !token_is_infix(p.tok) {
3525 break
3526 }
3527 op_id := int(p.tok)
3528 bp := token_id_left_binding_power(op_id)
3529 if int(bp) < int(min_bp) {
3530 break
3531 }
3532 p.next()
3533 rhs := p.expr(token_id_right_binding_power(op_id))
3534 istart := p.add_children2(lhs, rhs)
3535 lhs = p.a.add_node(flat.Node{
3536 kind: .infix
3537 op: token_id_to_op(op_id)
3538 children_start: istart
3539 children_count: 2
3540 })
3541 }
3542
3543 return lhs
3544}
3545
3546fn (mut p Parser) sql_expr() flat.NodeId {
3547 db_expr := if p.tok != .lcbr && p.tok != .eof {
3548 p.expr(.lowest)
3549 } else {
3550 flat.empty_node
3551 }
3552 tokens := p.sql_block_tokens()
3553 typ := sql_result_type(tokens)
3554 mut children_start := 0
3555 mut children_count := 0
3556 if int(db_expr) >= 0 {
3557 children_start = p.add_child(db_expr)
3558 children_count = 1
3559 }
3560 return p.a.add_node(flat.Node{
3561 kind: .sql_expr
3562 value: tokens.join(' ')
3563 typ: typ
3564 children_start: children_start
3565 children_count: flat.child_count(children_count)
3566 })
3567}
3568
3569fn (mut p Parser) starts_sql_expr() bool {
3570 if p.tok != .name {
3571 return false
3572 }
3573 saved_s := p.s
3574 saved_tok := p.tok
3575 saved_lit := p.lit
3576 saved_tok_pos := p.tok_pos
3577 saved_prev_tok := p.prev_tok
3578 saved_peek_tok := p.peek_tok
3579 saved_peek_lit := p.peek_lit
3580 saved_peek_pos := p.peek_pos
3581 saved_has_peek := p.has_peek
3582 p.next()
3583 for p.tok == .dot {
3584 p.next()
3585 if p.tok != .name && !p.tok.is_keyword() {
3586 break
3587 }
3588 p.next()
3589 }
3590 ok := p.tok == .lcbr
3591 p.s = saved_s
3592 p.tok = saved_tok
3593 p.lit = saved_lit
3594 p.tok_pos = saved_tok_pos
3595 p.prev_tok = saved_prev_tok
3596 p.peek_tok = saved_peek_tok
3597 p.peek_lit = saved_peek_lit
3598 p.peek_pos = saved_peek_pos
3599 p.has_peek = saved_has_peek
3600 return ok
3601}
3602
3603fn (mut p Parser) sql_block_tokens() []string {
3604 mut tokens := []string{}
3605 if p.tok != .lcbr {
3606 return tokens
3607 }
3608 mut depth := 1
3609 p.next()
3610 for depth > 0 && p.tok != .eof {
3611 if p.tok == .lcbr {
3612 depth++
3613 } else if p.tok == .rcbr {
3614 depth--
3615 if depth == 0 {
3616 p.next()
3617 break
3618 }
3619 }
3620 if depth > 0 {
3621 text := p.sql_token_text()
3622 if text.len > 0 {
3623 tokens << text
3624 }
3625 p.next()
3626 }
3627 }
3628 return tokens
3629}
3630
3631fn (p &Parser) sql_token_text() string {
3632 if p.lit.len > 0 {
3633 return p.lit
3634 }
3635 return match p.tok {
3636 .key_select { 'select' }
3637 .key_in { 'in' }
3638 .key_or { 'or' }
3639 .key_as { 'as' }
3640 .key_true { 'true' }
3641 .key_false { 'false' }
3642 else { '' }
3643 }
3644}
3645
3646fn sql_result_type(tokens []string) string {
3647 if tokens.len == 0 {
3648 return '!void'
3649 }
3650 if tokens[0] != 'select' {
3651 return '!void'
3652 }
3653 if tokens.len > 1 && tokens[1] == 'count' {
3654 return '!int'
3655 }
3656 for i, tok in tokens {
3657 if tok == 'from' && i + 1 < tokens.len {
3658 table := sql_type_name(tokens[i + 1])
3659 if table.len > 0 {
3660 return '![]${table}'
3661 }
3662 }
3663 }
3664 return '![]int'
3665}
3666
3667fn sql_type_name(raw string) string {
3668 mut out := strings.new_builder(raw.len)
3669 for ch in raw {
3670 if (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`)
3671 || (ch >= `0` && ch <= `9`) || ch == `_` || ch == `.` {
3672 out.write_u8(ch)
3673 } else {
3674 break
3675 }
3676 }
3677 return out.str()
3678}
3679
3680fn is_float_number_literal(val string) bool {
3681 if val.len > 2 && val[0] == `0` {
3682 second := val[1]
3683 if second == `x` || second == `X` || second == `b` || second == `B` || second == `o`
3684 || second == `O` {
3685 return false
3686 }
3687 }
3688 return val.contains('.') || val.contains('e') || val.contains('E')
3689}
3690
3691fn (mut p Parser) prefix_expr() flat.NodeId {
3692 tok_id := int(p.tok)
3693 if tok_id == 92 {
3694 val := p.lit
3695 p.next()
3696 kind_id := if is_float_number_literal(val) {
3697 2
3698 } else {
3699 1
3700 }
3701 return p.a.add_val_id(kind_id, val)
3702 }
3703 if tok_id == 107 {
3704 return p.string_literal()
3705 }
3706 if tok_id == 7 {
3707 val := p.lit
3708 p.next()
3709 return p.a.add_val_id(4, val)
3710 }
3711 if tok_id == 66 {
3712 p.next()
3713 return p.a.add_val_id(3, 'true')
3714 }
3715 if tok_id == 36 {
3716 p.next()
3717 return p.a.add_val_id(3, 'false')
3718 }
3719 if tok_id == 53 {
3720 p.next()
3721 return p.a.add_id(28)
3722 }
3723 if tok_id == 54 {
3724 p.next()
3725 return p.a.add_id(29)
3726 }
3727 if tok_id == 3 {
3728 p.next()
3729 inner := p.expr(.highest)
3730 return p.a.add_node(flat.Node{
3731 kind: .prefix
3732 op: .arrow
3733 children_start: p.add_child(inner)
3734 children_count: 1
3735 })
3736 }
3737 if tok_id == 6 || tok_id == 81 || tok_id == 85 || tok_id == 89 {
3738 p.next()
3739 operand := p.expr(.highest)
3740 pstart := p.add_child(operand)
3741 return p.a.add_node(flat.Node{
3742 kind: .prefix
3743 op: token_id_to_op(tok_id)
3744 children_start: pstart
3745 children_count: 1
3746 })
3747 }
3748 match p.tok {
3749 .number {
3750 val := p.lit
3751 p.next()
3752 kind_id := if is_float_number_literal(val) {
3753 2
3754 } else {
3755 1
3756 }
3757 return p.a.add_val_id(kind_id, val)
3758 }
3759 .string {
3760 return p.string_literal()
3761 }
3762 .char {
3763 val := p.lit
3764 p.next()
3765 return p.a.add_val_id(4, val)
3766 }
3767 .key_true {
3768 p.next()
3769 return p.a.add_val_id(3, 'true')
3770 }
3771 .key_false {
3772 p.next()
3773 return p.a.add_val_id(3, 'false')
3774 }
3775 .key_nil {
3776 p.next()
3777 return p.a.add_id(28)
3778 }
3779 .key_none {
3780 p.next()
3781 return p.a.add_id(29)
3782 }
3783 .arrow {
3784 p.next()
3785 inner := p.expr(.highest)
3786 return p.a.add_node(flat.Node{
3787 kind: .prefix
3788 op: .arrow
3789 children_start: p.add_child(inner)
3790 children_count: 1
3791 })
3792 }
3793 .key_mut, .key_shared {
3794 p.next()
3795 return p.prefix_expr()
3796 }
3797 .name, .key_module {
3798 name := p.lit
3799 p.next()
3800 if name == 'sql' && p.starts_sql_expr() {
3801 return p.sql_expr()
3802 }
3803 if name == '@FILE' {
3804 return p.a.add_val_id(5, p.cur_file)
3805 }
3806 if name == '@VMODROOT' {
3807 return p.a.add_val_id(5, vmod_root_for_file(p.cur_file))
3808 }
3809 if name == '@VEXEROOT' {
3810 return p.a.add_val_id(5, p.prefs.vroot)
3811 }
3812 if name == '@VEXE' {
3813 return p.a.add_val_id(5, p.prefs.vroot + '/v')
3814 }
3815 if name == '@LINE' {
3816 return p.a.add_val_id(1, '0')
3817 }
3818 if name == '@FN' || name == '@METHOD' {
3819 return p.a.add_val_id(5, p.cur_fn)
3820 }
3821 if name == '@LOCATION' {
3822 return p.a.add_val_id(5, '${p.cur_file}:0: ${p.cur_fn}')
3823 }
3824 if name == '@VCURRENTHASH' || name == '@VHASH' {
3825 return p.a.add_val_id(5, '')
3826 }
3827 if name == 'chan' && p.can_start_type_name() {
3828 elem_type := p.parse_type_name()
3829 chan_type := 'chan ${elem_type}'
3830 if p.tok == .lcbr {
3831 return p.struct_init(chan_type)
3832 }
3833 return p.a.add_val(.ident, chan_type)
3834 }
3835 // map init: map[K]V{} or map[K]V{k1: v1, ...}
3836 if name == 'map' && p.tok == .lsbr {
3837 p.next() // skip [
3838 key_type := p.parse_type_name()
3839 p.check(.rsbr)
3840 val_type := p.parse_type_name()
3841 map_type := 'map[${key_type}]${val_type}'
3842 if p.tok == .lcbr {
3843 p.next() // skip {
3844 mut ids := []flat.NodeId{}
3845 for p.tok != .rcbr && p.tok != .eof {
3846 if p.tok == .semicolon {
3847 p.next()
3848 continue
3849 }
3850 k := p.expr(.lowest)
3851 p.check(.colon)
3852 v := p.expr(.lowest)
3853 ids << k
3854 ids << v
3855 if p.tok == .comma || p.tok == .semicolon {
3856 p.next()
3857 }
3858 }
3859 p.check(.rcbr)
3860 if ids.len > 0 {
3861 istart := p.add_children(ids)
3862 return p.a.add_node(flat.Node{
3863 kind: .map_init
3864 value: map_type
3865 children_start: istart
3866 children_count: flat.child_count(ids.len)
3867 })
3868 }
3869 }
3870 return p.a.add_val(.map_init, map_type)
3871 }
3872 // struct init: Name{...}; vlib/builtin also uses concrete lowercase
3873 // runtime structs like array{} and string{}.
3874 if !p.in_for_container && p.tok == .lcbr && name.len > 0
3875 && ((name[0] >= `A` && name[0] <= `Z`)
3876 || name in ['array', 'string', 'map', 'mapnode', '_result', '_option']) {
3877 return p.struct_init(name)
3878 }
3879 // type cast: TypeName(expr) or builtin_type(expr)
3880 if p.tok == .lpar && name.len > 0 && ((name[0] >= `A` && name[0] <= `Z`)
3881 || is_builtin_type(name)) {
3882 p.next() // skip (
3883 inner := p.expr(.lowest)
3884 p.check(.rpar)
3885 cstart := p.add_child(inner)
3886 return p.a.add_node(flat.Node{
3887 kind: .cast_expr
3888 value: name
3889 children_start: cstart
3890 children_count: 1
3891 })
3892 }
3893 return p.a.add_val(.ident, name)
3894 }
3895 .lpar {
3896 p.next()
3897 inner := p.expr(.lowest)
3898 p.check(.rpar)
3899 pstart := p.add_child(inner)
3900 return p.a.add_node(flat.Node{
3901 kind: .paren
3902 children_start: pstart
3903 children_count: 1
3904 })
3905 }
3906 .lcbr {
3907 p.next()
3908 mut ids := []flat.NodeId{}
3909 for p.tok != .rcbr && p.tok != .eof {
3910 if p.tok == .semicolon || p.tok == .comma {
3911 p.next()
3912 continue
3913 }
3914 k := p.expr(.lowest)
3915 p.check(.colon)
3916 v := p.expr(.lowest)
3917 ids << k
3918 ids << v
3919 if p.tok == .comma || p.tok == .semicolon {
3920 p.next()
3921 }
3922 }
3923 p.check(.rcbr)
3924 start := p.add_children(ids)
3925 return p.a.add_node(flat.Node{
3926 kind: .map_init
3927 children_start: start
3928 children_count: flat.child_count(ids.len)
3929 })
3930 }
3931 .amp {
3932 p.next()
3933 if p.tok == .amp {
3934 p.next()
3935 if p.tok == .name && is_builtin_type(p.lit) {
3936 type_name := '&&${p.lit}'
3937 p.next()
3938 if p.tok == .lpar {
3939 p.next()
3940 inner := p.expr(.lowest)
3941 p.check(.rpar)
3942 cstart := p.add_child(inner)
3943 return p.a.add_node(flat.Node{
3944 kind: .cast_expr
3945 value: type_name
3946 children_start: cstart
3947 children_count: 1
3948 })
3949 }
3950 id := p.a.add_val(.ident, type_name[2..])
3951 first := p.a.add_node(flat.Node{
3952 kind: .prefix
3953 op: .amp
3954 children_start: p.add_child(id)
3955 children_count: 1
3956 })
3957 return p.a.add_node(flat.Node{
3958 kind: .prefix
3959 op: .amp
3960 children_start: p.add_child(first)
3961 children_count: 1
3962 })
3963 }
3964 } else if p.tok == .name && is_builtin_type(p.lit) {
3965 name := p.lit
3966 type_name := '&${name}'
3967 p.next()
3968 if p.tok == .lpar {
3969 p.next()
3970 inner := p.expr(.lowest)
3971 p.check(.rpar)
3972 cstart := p.add_child(inner)
3973 return p.a.add_node(flat.Node{
3974 kind: .cast_expr
3975 value: type_name
3976 children_start: cstart
3977 children_count: 1
3978 })
3979 }
3980 if p.tok == .lcbr {
3981 inner := p.struct_init(name)
3982 return p.a.add_node(flat.Node{
3983 kind: .prefix
3984 op: .amp
3985 children_start: p.add_child(inner)
3986 children_count: 1
3987 })
3988 }
3989 id := p.a.add_val(.ident, name)
3990 return p.a.add_node(flat.Node{
3991 kind: .prefix
3992 op: .amp
3993 children_start: p.add_child(id)
3994 children_count: 1
3995 })
3996 } else if p.tok == .lsbr && p.peek() == .rsbr {
3997 type_name := '&' + p.parse_type_name()
3998 if p.tok == .lpar {
3999 p.next()
4000 inner := p.expr(.lowest)
4001 p.check(.rpar)
4002 cstart := p.add_child(inner)
4003 return p.a.add_node(flat.Node{
4004 kind: .cast_expr
4005 value: type_name
4006 children_start: cstart
4007 children_count: 1
4008 })
4009 }
4010 }
4011 operand := p.expr(.highest)
4012 pstart := p.add_child(operand)
4013 return p.a.add_node(flat.Node{
4014 kind: .prefix
4015 op: .amp
4016 children_start: pstart
4017 children_count: 1
4018 })
4019 }
4020 .and {
4021 p.next()
4022 if p.tok == .name && is_builtin_type(p.lit) {
4023 type_name := '&&${p.lit}'
4024 p.next()
4025 if p.tok == .lpar {
4026 p.next()
4027 inner := p.expr(.lowest)
4028 p.check(.rpar)
4029 cstart := p.add_child(inner)
4030 return p.a.add_node(flat.Node{
4031 kind: .cast_expr
4032 value: type_name
4033 children_start: cstart
4034 children_count: 1
4035 })
4036 }
4037 }
4038 return p.a.add(.empty)
4039 }
4040 .minus, .not, .bit_not, .mul {
4041 op_id := int(p.tok)
4042 p.next()
4043 operand := p.expr(.highest)
4044 pstart := p.add_child(operand)
4045 return p.a.add_node(flat.Node{
4046 kind: .prefix
4047 op: token_id_to_op(op_id)
4048 children_start: pstart
4049 children_count: 1
4050 })
4051 }
4052 .question {
4053 p.next()
4054 inner_type := p.parse_type_name()
4055 type_name := if inner_type.len > 0 { '?${inner_type}' } else { '?' }
4056 if p.tok == .lpar {
4057 p.next()
4058 inner := p.expr(.lowest)
4059 p.check(.rpar)
4060 cstart := p.add_child(inner)
4061 return p.a.add_node(flat.Node{
4062 kind: .cast_expr
4063 value: type_name
4064 children_start: cstart
4065 children_count: 1
4066 })
4067 }
4068 return p.a.add(flat.NodeKind.empty)
4069 }
4070 .key_if {
4071 return p.if_stmt()
4072 }
4073 .key_match {
4074 return p.match_stmt()
4075 }
4076 .key_fn {
4077 return p.fn_literal()
4078 }
4079 .key_go, .key_spawn {
4080 p.next()
4081 inner := p.expr(.lowest)
4082 sstart := p.add_child(inner)
4083 return p.a.add_node(flat.Node{
4084 kind: .spawn_expr
4085 children_start: sstart
4086 children_count: 1
4087 })
4088 }
4089 .key_lock, .key_rlock {
4090 return p.lock_expr()
4091 }
4092 .key_select {
4093 if p.peek() == .lpar {
4094 p.next()
4095 return p.a.add_val(.ident, 'select')
4096 }
4097 return p.select_expr()
4098 }
4099 .key_sizeof {
4100 return p.sizeof_expr()
4101 }
4102 .key_typeof {
4103 return p.typeof_expr()
4104 }
4105 .key_offsetof {
4106 return p.offsetof_expr()
4107 }
4108 .key_dump {
4109 return p.dump_expr()
4110 }
4111 .key_likely, .key_unlikely {
4112 p.next()
4113 p.check(.lpar)
4114 inner := p.expr(.lowest)
4115 p.check(.rpar)
4116 return inner
4117 }
4118 .key_isreftype {
4119 p.next()
4120 p.check(.lpar)
4121 p.parse_type_name()
4122 p.check(.rpar)
4123 return p.a.add_val_id(3, 'false')
4124 }
4125 .dot {
4126 // enum value: .member
4127 p.next()
4128 member := p.expect_name_or_keyword()
4129 return p.a.add_val(.enum_val, member)
4130 }
4131 .lsbr {
4132 return p.array_literal()
4133 }
4134 .key_unsafe {
4135 p.next()
4136 return p.block_stmt()
4137 }
4138 .ellipsis {
4139 // spread: ...expr
4140 p.next()
4141 inner := p.expr(.lowest)
4142 pstart := p.add_child(inner)
4143 return p.a.add_node(flat.Node{
4144 kind: .prefix
4145 op: .none
4146 value: '...'
4147 children_start: pstart
4148 children_count: 1
4149 })
4150 }
4151 .dollar {
4152 return p.parse_comptime_expr()
4153 }
4154 else {
4155 p.next()
4156 return p.a.add(flat.NodeKind.empty)
4157 }
4158 }
4159}
4160
4161fn (mut p Parser) selector_or_method(lhs flat.NodeId) flat.NodeId {
4162 p.next() // skip '.'
4163 field_name := p.expect_name_or_keyword()
4164 sel_start := p.add_child(lhs)
4165 sel := p.a.add_node(flat.Node{
4166 kind: .selector
4167 value: field_name
4168 children_start: sel_start
4169 children_count: 1
4170 })
4171 if p.tok == .lpar {
4172 lhs_node := p.a.nodes[int(lhs)]
4173 if lhs_node.kind == .ident && lhs_node.value != 'C' && field_name.len > 0
4174 && field_name[0] >= `A` && field_name[0] <= `Z` {
4175 full_name := '${lhs_node.value}.${field_name}'
4176 p.next() // skip (
4177 inner := p.expr(.lowest)
4178 p.check(.rpar)
4179 cstart := p.add_child(inner)
4180 return p.a.add_node(flat.Node{
4181 kind: .cast_expr
4182 value: full_name
4183 children_start: cstart
4184 children_count: 1
4185 })
4186 }
4187 return p.call_args(sel)
4188 }
4189 return sel
4190}
4191
4192fn (mut p Parser) call_args(fn_expr flat.NodeId) flat.NodeId {
4193 p.check(.lpar)
4194 mut ids := []flat.NodeId{}
4195 ids << fn_expr
4196 for p.tok != .rpar && p.tok != .eof {
4197 prev_offset := p.s.offset
4198 prev_tok := p.tok
4199 if p.tok == .key_mut || p.tok == .key_shared {
4200 p.next()
4201 }
4202 // vararg spread: ...expr
4203 if p.tok == .ellipsis {
4204 p.next()
4205 inner := p.expr(.lowest)
4206 sstart := p.add_child(inner)
4207 ids << p.a.add_node(flat.Node{
4208 kind: .prefix
4209 op: .none
4210 value: '...'
4211 children_start: sstart
4212 children_count: 1
4213 })
4214 } else if p.tok == .logical_or {
4215 // lambda no args: || expr
4216 p.next()
4217 lambda_body := p.expr(.lowest)
4218 lstart := p.add_child(lambda_body)
4219 ids << p.a.add_node(flat.Node{
4220 kind: .lambda_expr
4221 children_start: lstart
4222 children_count: 1
4223 })
4224 } else if p.tok == .pipe {
4225 // lambda with args: |a, b| expr
4226 p.next()
4227 mut lambda_params := []flat.NodeId{}
4228 lambda_params << p.a.add_val(.ident, p.expect_name())
4229 for p.tok == .comma {
4230 p.next()
4231 lambda_params << p.a.add_val(.ident, p.expect_name())
4232 }
4233 p.check(.pipe)
4234 lambda_body := p.expr(.lowest)
4235 mut lids := lambda_params.clone()
4236 lids << lambda_body
4237 lstart := p.add_children(lids)
4238 ids << p.a.add_node(flat.Node{
4239 kind: .lambda_expr
4240 children_start: lstart
4241 children_count: flat.child_count(lids.len)
4242 })
4243 } else {
4244 arg := p.expr(.lowest)
4245 // struct config syntax: name: value
4246 if p.tok == .colon {
4247 p.next()
4248 val := p.expr(.lowest)
4249 arg_node := p.a.nodes[int(arg)]
4250 vstart := p.add_child(val)
4251 ids << p.a.add_node(flat.Node{
4252 kind: .field_init
4253 value: arg_node.value
4254 children_start: vstart
4255 children_count: 1
4256 })
4257 if p.tok == .semicolon {
4258 p.next()
4259 }
4260 } else {
4261 ids << arg
4262 }
4263 }
4264 if p.tok == .comma {
4265 p.next()
4266 }
4267 if p.s.offset == prev_offset && p.tok == prev_tok {
4268 p.next()
4269 }
4270 }
4271 p.check(.rpar)
4272 cstart := p.add_children(ids)
4273 return p.a.add_node(flat.Node{
4274 kind: .call
4275 children_start: cstart
4276 children_count: flat.child_count(ids.len)
4277 })
4278}
4279
4280fn (mut p Parser) index_expr(lhs flat.NodeId) flat.NodeId {
4281 p.check(.lsbr)
4282 // range index: arr[..b]
4283 if p.tok == .dotdot {
4284 p.next()
4285 mut end_id := flat.empty_node
4286 if p.tok != .rsbr {
4287 end_id = p.expr(.lowest)
4288 }
4289 p.check(.rsbr)
4290 start_id := p.a.add(flat.NodeKind.empty)
4291 mut ids := []flat.NodeId{}
4292 ids << lhs
4293 ids << start_id
4294 if int(end_id) >= 0 {
4295 ids << end_id
4296 }
4297 istart := p.add_children(ids)
4298 return p.a.add_node(flat.Node{
4299 kind: .index
4300 value: 'range'
4301 children_start: istart
4302 children_count: flat.child_count(ids.len)
4303 })
4304 }
4305 idx := p.expr(.logical_or)
4306 // fast path for the common simple index `arr[i]`: avoid the children array
4307 if p.tok == .rsbr {
4308 p.next()
4309 istart := p.add_children2(lhs, idx)
4310 return p.a.add_node(flat.Node{
4311 kind: .index
4312 children_start: istart
4313 children_count: 2
4314 })
4315 }
4316 mut ids := []flat.NodeId{}
4317 ids << lhs
4318 ids << idx
4319 for p.tok == .comma {
4320 p.next()
4321 ids << p.expr(.logical_or)
4322 }
4323 // range index: arr[a..b]
4324 if p.tok == .dotdot {
4325 p.next()
4326 mut end_id := flat.empty_node
4327 if p.tok != .rsbr {
4328 end_id = p.expr(.lowest)
4329 }
4330 p.check(.rsbr)
4331 mut range_ids := []flat.NodeId{}
4332 range_ids << lhs
4333 range_ids << idx
4334 if int(end_id) >= 0 {
4335 range_ids << end_id
4336 }
4337 istart := p.add_children(range_ids)
4338 return p.a.add_node(flat.Node{
4339 kind: .index
4340 value: 'range'
4341 children_start: istart
4342 children_count: flat.child_count(range_ids.len)
4343 })
4344 }
4345 p.check(.rsbr)
4346 istart := p.add_children(ids)
4347 return p.a.add_node(flat.Node{
4348 kind: .index
4349 children_start: istart
4350 children_count: flat.child_count(ids.len)
4351 })
4352}
4353
4354fn (p &Parser) generic_struct_init_type_name(id flat.NodeId) ?string {
4355 type_name := p.type_expr_name(id)
4356 if !generic_type_name_can_init(type_name) {
4357 return none
4358 }
4359 return type_name
4360}
4361
4362fn (p &Parser) type_expr_name(id flat.NodeId) string {
4363 if int(id) < 0 {
4364 return ''
4365 }
4366 node := p.a.nodes[int(id)]
4367 match node.kind {
4368 .ident {
4369 return node.value
4370 }
4371 .selector {
4372 if node.children_count == 0 {
4373 return node.value
4374 }
4375 base := p.type_expr_name(p.a.child(&node, 0))
4376 if base.len == 0 {
4377 return node.value
4378 }
4379 return '${base}.${node.value}'
4380 }
4381 .index {
4382 if node.children_count < 2 || node.value == 'range' {
4383 return ''
4384 }
4385 base := p.type_expr_name(p.a.child(&node, 0))
4386 if base.len == 0 {
4387 return ''
4388 }
4389 mut args := []string{}
4390 for i in 1 .. node.children_count {
4391 arg := p.type_expr_name(p.a.child(&node, i))
4392 if arg.len == 0 {
4393 return ''
4394 }
4395 args << arg
4396 }
4397 return '${base}[${args.join(', ')}]'
4398 }
4399 .array_init {
4400 if node.value.len == 0 {
4401 return ''
4402 }
4403 return '[]${node.value}'
4404 }
4405 .prefix {
4406 if node.children_count == 0 {
4407 return ''
4408 }
4409 child := p.type_expr_name(p.a.child(&node, 0))
4410 if child.len == 0 {
4411 return ''
4412 }
4413 if node.op == .amp {
4414 return '&${child}'
4415 }
4416 return child
4417 }
4418 else {
4419 return ''
4420 }
4421 }
4422}
4423
4424fn generic_type_name_can_init(type_name string) bool {
4425 if !type_name.contains('[') {
4426 return false
4427 }
4428 base := type_name.all_before('[')
4429 short := if base.contains('.') { base.all_after_last('.') } else { base }
4430 return short.len > 0 && short[0] >= `A` && short[0] <= `Z`
4431}
4432
4433fn (mut p Parser) struct_init(name string) flat.NodeId {
4434 p.check(.lcbr)
4435 mut ids := []flat.NodeId{}
4436 // assoc syntax: Type{...base, field: val}
4437 if p.tok == .ellipsis {
4438 p.next()
4439 base_expr := p.expr(.lowest)
4440 if p.tok == .comma || p.tok == .semicolon {
4441 p.next()
4442 }
4443 mut field_ids := []flat.NodeId{}
4444 field_ids << base_expr
4445 for p.tok != .rcbr && p.tok != .eof {
4446 if p.tok == .comma {
4447 p.next()
4448 }
4449 fname := p.expect_name_or_keyword()
4450 p.check(.colon)
4451 val := p.expr(.lowest)
4452 vstart := p.add_child(val)
4453 field_ids << p.a.add_node(flat.Node{
4454 kind: .field_init
4455 value: fname
4456 children_start: vstart
4457 children_count: 1
4458 })
4459 if p.tok == .semicolon {
4460 p.next()
4461 }
4462 }
4463 p.check(.rcbr)
4464 start := p.add_children(field_ids)
4465 return p.a.add_node(flat.Node{
4466 kind: .assoc
4467 value: name
4468 children_start: start
4469 children_count: flat.child_count(field_ids.len)
4470 })
4471 }
4472 for p.tok != .rcbr && p.tok != .eof {
4473 if p.tok == .semicolon {
4474 p.next()
4475 continue
4476 }
4477 // named field: name: expr
4478 if (p.tok == .name || p.tok.is_keyword()) && p.peek() == .colon {
4479 fname := p.expect_name_or_keyword()
4480 p.check(.colon)
4481 val := p.expr(.lowest)
4482 vstart := p.add_child(val)
4483 ids << p.a.add_node(flat.Node{
4484 kind: .field_init
4485 value: fname
4486 children_start: vstart
4487 children_count: 1
4488 })
4489 } else {
4490 // positional value (unnamed)
4491 val := p.expr(.lowest)
4492 vstart := p.add_child(val)
4493 ids << p.a.add_node(flat.Node{
4494 kind: .field_init
4495 children_start: vstart
4496 children_count: 1
4497 })
4498 }
4499 if p.tok == .comma {
4500 p.next()
4501 }
4502 if p.tok == .semicolon {
4503 p.next()
4504 }
4505 }
4506 p.check(.rcbr)
4507 start := p.add_children(ids)
4508 return p.a.add_node(flat.Node{
4509 kind: .struct_init
4510 value: name
4511 children_start: start
4512 children_count: flat.child_count(ids.len)
4513 })
4514}
4515
4516fn (mut p Parser) string_literal() flat.NodeId {
4517 mut q := u8(`'`)
4518 lit := p.lit
4519 if lit.len > 0 {
4520 if lit[0] == `r` && lit.len > 1 {
4521 q = p.lit[1]
4522 } else if lit[0] == `'` || lit[0] == `"` {
4523 q = lit[0]
4524 }
4525 }
4526 p.next()
4527 if int(p.tok) != 106 {
4528 val := strip_quotes(lit)
4529 return p.a.add_val_id(5, val)
4530 }
4531 // string interpolation
4532 val := strip_interp_start_quotes(lit)
4533 return p.string_interp(val, q)
4534}
4535
4536// string_interp supports string interp handling for Parser.
4537fn (mut p Parser) string_interp(first_part string, quote u8) flat.NodeId {
4538 mut ids := []flat.NodeId{}
4539 if first_part.len > 0 {
4540 ids << p.a.add_val_id(5, first_part)
4541 }
4542 for int(p.tok) == 106 {
4543 p.next() // skip $
4544 p.check(.lcbr) // skip {
4545 ids << p.expr(.lowest)
4546 // format spec: :fmt
4547 if p.tok == .colon {
4548 p.next()
4549 for p.tok != .rcbr && p.tok != .eof {
4550 p.next()
4551 }
4552 }
4553 p.check(.rcbr) // skip }
4554 if int(p.tok) == 107 {
4555 part := strip_interp_quotes(p.lit, quote)
4556 p.next()
4557 if part.len > 0 {
4558 ids << p.a.add_val_id(5, part)
4559 }
4560 // check for more interpolation after this string part
4561 }
4562 }
4563 start := p.add_children(ids)
4564 return p.a.add_node(flat.Node{
4565 kind: .string_interp
4566 children_start: start
4567 children_count: flat.child_count(ids.len)
4568 })
4569}
4570
4571// array_literal supports array literal handling for Parser.
4572fn (mut p Parser) array_literal() flat.NodeId {
4573 p.next() // skip '['
4574 // empty array or fixed array type: []Type{} or [N]Type{}
4575 if p.tok == .rsbr {
4576 p.next()
4577 elem_type := p.parse_type_name()
4578 if p.tok == .lpar {
4579 p.next()
4580 inner := p.expr(.lowest)
4581 p.check(.rpar)
4582 cstart := p.add_child(inner)
4583 return p.a.add_node(flat.Node{
4584 kind: .cast_expr
4585 value: '[]${elem_type}'
4586 children_start: cstart
4587 children_count: 1
4588 })
4589 }
4590 // array init: []Type{len: n, cap: c, init: v}
4591 if p.tok == .lcbr {
4592 p.next()
4593 mut ids := []flat.NodeId{}
4594 for p.tok != .rcbr && p.tok != .eof {
4595 if p.tok == .semicolon {
4596 p.next()
4597 continue
4598 }
4599 if p.tok == .name && p.peek() == .colon {
4600 fname := p.expect_name()
4601 p.check(.colon)
4602 val := p.expr(.lowest)
4603 vstart := p.add_child(val)
4604 ids << p.a.add_node(flat.Node{
4605 kind: .field_init
4606 value: fname
4607 children_start: vstart
4608 children_count: 1
4609 })
4610 } else {
4611 ids << p.expr(.lowest)
4612 }
4613 if p.tok == .comma {
4614 p.next()
4615 }
4616 }
4617 p.check(.rcbr)
4618 start := p.add_children(ids)
4619 return p.a.add_node(flat.Node{
4620 kind: .array_init
4621 value: elem_type
4622 typ: '[]${elem_type}'
4623 children_start: start
4624 children_count: flat.child_count(ids.len)
4625 })
4626 }
4627 return p.a.add_node(flat.Node{
4628 kind: .array_init
4629 value: elem_type
4630 typ: '[]${elem_type}'
4631 })
4632 }
4633 // array literal: [1, 2, 3]
4634 // or fixed array type: [3]int
4635 mut ids := []flat.NodeId{}
4636 size_start := p.tok_pos
4637 ids << p.expr(.lowest)
4638 // check if it's [N]Type (fixed array type)
4639 if p.tok == .rsbr {
4640 size_end := p.tok_pos
4641 p.next()
4642 if p.tok == .name || p.tok == .amp || p.tok == .lsbr || p.tok == .question {
4643 // fixed array type: [N]Type. Use the literal node value for a plain integer
4644 // size, but recover the full source text for a const expression (e.g.
4645 // `[segs + 1]f32`) — an infix node has no `.value`, which would otherwise
4646 // collapse the type to a dynamic `[]f32`.
4647 lit_size := p.a.nodes[int(ids[0])].value
4648 size_str := if lit_size.len > 0 {
4649 lit_size
4650 } else if size_start >= 0 && size_end > size_start && size_end <= p.s.src.len {
4651 p.s.src[size_start..size_end].trim_space()
4652 } else {
4653 lit_size
4654 }
4655 elem_type := p.parse_type_name()
4656 fixed_type := '[${size_str}]${elem_type}'
4657 // may have init
4658 if p.tok == .lcbr {
4659 p.next()
4660 mut init_ids := []flat.NodeId{}
4661 for p.tok != .rcbr && p.tok != .eof {
4662 if p.tok == .semicolon {
4663 p.next()
4664 continue
4665 }
4666 if p.tok == .name && p.peek() == .colon {
4667 fname := p.expect_name()
4668 p.check(.colon)
4669 val := p.expr(.lowest)
4670 vstart := p.add_child(val)
4671 init_ids << p.a.add_node(flat.Node{
4672 kind: .field_init
4673 value: fname
4674 children_start: vstart
4675 children_count: 1
4676 })
4677 } else {
4678 init_ids << p.expr(.lowest)
4679 }
4680 if p.tok == .comma {
4681 p.next()
4682 }
4683 }
4684 p.check(.rcbr)
4685 start := p.add_children(init_ids)
4686 return p.a.add_node(flat.Node{
4687 kind: .array_init
4688 value: fixed_type
4689 typ: fixed_type
4690 children_start: start
4691 children_count: flat.child_count(init_ids.len)
4692 })
4693 }
4694 return p.a.add_node(flat.Node{
4695 kind: .array_init
4696 value: fixed_type
4697 typ: fixed_type
4698 })
4699 }
4700 // single-element array: [expr]
4701 start := p.add_children(ids)
4702 return p.a.add_node(flat.Node{
4703 kind: .array_literal
4704 children_start: start
4705 children_count: flat.child_count(ids.len)
4706 })
4707 }
4708 // multi-element array: [a, b, c]
4709 for p.tok == .comma {
4710 p.next()
4711 if p.tok == .rsbr {
4712 break
4713 }
4714 ids << p.expr(.lowest)
4715 }
4716 p.check(.rsbr)
4717 // check for `!` (fixed array with values)
4718 if p.tok == .not {
4719 p.next()
4720 }
4721 start := p.add_children(ids)
4722 return p.a.add_node(flat.Node{
4723 kind: .array_literal
4724 children_start: start
4725 children_count: flat.child_count(ids.len)
4726 })
4727}
4728
4729// fn_literal supports fn literal handling for Parser.
4730fn (mut p Parser) fn_literal() flat.NodeId {
4731 p.next() // skip 'fn'
4732 // capture list: fn [a, b] (params) ret { }
4733 mut capture_ids := []flat.NodeId{}
4734 if p.tok == .lsbr {
4735 p.next()
4736 for p.tok != .rsbr && p.tok != .eof {
4737 if p.tok == .key_mut {
4738 p.next()
4739 }
4740 capture_ids << p.a.add_val(.ident, p.expect_name())
4741 if p.tok == .comma {
4742 p.next()
4743 }
4744 }
4745 p.check(.rsbr)
4746 }
4747 // params
4748 p.check(.lpar)
4749 mut param_ids := []flat.NodeId{}
4750 for p.tok != .rpar && p.tok != .eof {
4751 start_offset := p.s.offset
4752 param_ids << p.parse_param_group(false)
4753 if p.s.offset == start_offset && p.tok != .rpar && p.tok != .eof {
4754 p.next()
4755 }
4756 }
4757 p.check(.rpar)
4758 // return type
4759 mut ret_type := 'void'
4760 if p.tok != .lcbr && p.tok != .semicolon && p.tok != .eof {
4761 if p.tok == .name || p.tok == .amp || p.tok == .question || p.tok == .not || p.tok == .lsbr
4762 || p.tok == .lpar || p.tok == .key_fn {
4763 ret_type = p.parse_type_name()
4764 }
4765 }
4766 // body
4767 mut body_ids := []flat.NodeId{}
4768 if p.tok == .lcbr {
4769 p.check(.lcbr)
4770 for p.tok != .rcbr && p.tok != .eof {
4771 id := p.stmt()
4772 if int(id) >= 0 {
4773 body_ids << id
4774 }
4775 }
4776 p.check(.rcbr)
4777 }
4778 mut all_ids := []flat.NodeId{cap: capture_ids.len + param_ids.len + body_ids.len}
4779 for id in capture_ids {
4780 all_ids << id
4781 }
4782 for id in param_ids {
4783 all_ids << id
4784 }
4785 for id in body_ids {
4786 all_ids << id
4787 }
4788 start := p.add_children(all_ids)
4789 return p.a.add_node(flat.Node{
4790 kind: .fn_literal
4791 typ: ret_type
4792 children_start: start
4793 children_count: flat.child_count(all_ids.len)
4794 })
4795}
4796
4797// lock_expr supports lock expr handling for Parser.
4798fn (mut p Parser) lock_expr() flat.NodeId {
4799 is_rlock := p.tok == .key_rlock
4800 p.next() // skip 'lock' or 'rlock'
4801 mut obj_ids := []flat.NodeId{}
4802 // lock objects
4803 if p.tok != .lcbr {
4804 obj_ids << p.expr(.lowest)
4805 for p.tok == .comma {
4806 p.next()
4807 obj_ids << p.expr(.lowest)
4808 }
4809 }
4810 body := p.block_stmt()
4811 mut ids := obj_ids.clone()
4812 ids << body
4813 lstart := p.add_children(ids)
4814 return p.a.add_node(flat.Node{
4815 kind: .lock_expr
4816 value: if is_rlock { 'rlock' } else { 'lock' }
4817 children_start: lstart
4818 children_count: flat.child_count(ids.len)
4819 })
4820}
4821
4822// select_expr resolves select expr information for parser.
4823fn (mut p Parser) select_expr() flat.NodeId {
4824 p.next() // skip 'select'
4825 p.check(.lcbr)
4826 mut ids := []flat.NodeId{}
4827 for p.tok != .rcbr && p.tok != .eof {
4828 if p.tok == .semicolon {
4829 p.next()
4830 continue
4831 }
4832 ids << p.select_branch()
4833 }
4834 p.check(.rcbr)
4835 start := p.add_children(ids)
4836 return p.a.add_node(flat.Node{
4837 kind: .select_stmt
4838 children_start: start
4839 children_count: flat.child_count(ids.len)
4840 })
4841}
4842
4843// select_branch resolves select branch information for parser.
4844fn (mut p Parser) select_branch() flat.NodeId {
4845 mut is_else := false
4846 mut is_recv_decl := false
4847 mut cond_ids := []flat.NodeId{}
4848 if p.tok == .key_else {
4849 is_else = true
4850 p.next()
4851 } else {
4852 cond_ids << p.expr(.lowest)
4853 // could be assignment: ch <- val or val := <-ch
4854 if token_is_assignment(p.tok) || p.tok == .decl_assign {
4855 op := p.tok
4856 // Record `val := <-ch` so the type checker can bind `val` (the
4857 // channel's element type) in the branch body's scope.
4858 if op == .decl_assign {
4859 is_recv_decl = true
4860 }
4861 p.next()
4862 cond_ids << p.expr(.lowest)
4863 }
4864 }
4865 mut body_ids := []flat.NodeId{}
4866 if p.tok == .lcbr {
4867 body_ids = p.parse_block_body()
4868 }
4869 mut all_ids := cond_ids.clone()
4870 for id in body_ids {
4871 all_ids << id
4872 }
4873 start := p.add_children(all_ids)
4874 branch_value := if is_else {
4875 'else'
4876 } else if is_recv_decl {
4877 'recv'
4878 } else {
4879 ''
4880 }
4881 return p.a.add_node(flat.Node{
4882 kind: .select_branch
4883 value: branch_value
4884 children_start: start
4885 children_count: flat.child_count(all_ids.len)
4886 })
4887}
4888
4889// sizeof_expr supports sizeof expr handling for Parser.
4890fn (mut p Parser) sizeof_expr() flat.NodeId {
4891 p.next() // skip 'sizeof'
4892 p.check(.lpar)
4893 type_name := p.parse_type_name()
4894 p.check(.rpar)
4895 return p.a.add_val(.sizeof_expr, type_name)
4896}
4897
4898// typeof_expr supports typeof expr handling for Parser.
4899fn (mut p Parser) typeof_expr() flat.NodeId {
4900 p.next() // skip 'typeof'
4901 if p.tok == .lsbr {
4902 p.next()
4903 type_name := p.parse_type_name()
4904 p.check(.rsbr)
4905 if p.tok == .lpar {
4906 p.next()
4907 p.check(.rpar)
4908 }
4909 return p.a.add_node(flat.Node{
4910 kind: .typeof_expr
4911 value: type_name
4912 })
4913 }
4914 p.check(.lpar)
4915 inner := p.expr(.lowest)
4916 p.check(.rpar)
4917 tstart := p.add_child(inner)
4918 return p.a.add_node(flat.Node{
4919 kind: .typeof_expr
4920 children_start: tstart
4921 children_count: 1
4922 })
4923}
4924
4925// dump_expr updates dump expr state for Parser.
4926fn (mut p Parser) dump_expr() flat.NodeId {
4927 p.next() // skip 'dump'
4928 p.check(.lpar)
4929 inner := p.expr(.lowest)
4930 p.check(.rpar)
4931 dstart := p.add_child(inner)
4932 return p.a.add_node(flat.Node{
4933 kind: .dump_expr
4934 children_start: dstart
4935 children_count: 1
4936 })
4937}
4938
4939// offsetof_expr supports offsetof expr handling for Parser.
4940fn (mut p Parser) offsetof_expr() flat.NodeId {
4941 p.next() // skip '__offsetof'
4942 p.check(.lpar)
4943 type_name := p.parse_type_name()
4944 p.check(.comma)
4945 field_name := p.expect_name()
4946 p.check(.rpar)
4947 return p.a.add_node(flat.Node{
4948 kind: .offsetof_expr
4949 value: type_name
4950 typ: field_name
4951 })
4952}
4953
4954// ==================== types ====================
4955
4956// parse_type_name_progress reads parse type name progress input for parser.
4957fn (mut p Parser) parse_type_name_progress() string {
4958 start_offset := p.s.offset
4959 typ := p.parse_type_name()
4960 if p.s.offset == start_offset && p.tok != .eof {
4961 p.next()
4962 }
4963 return typ
4964}
4965
4966// peek_lbr_starts_array_type supports peek lbr starts array type handling for Parser.
4967fn (mut p Parser) peek_lbr_starts_array_type() bool {
4968 if p.peek() != .lsbr {
4969 return false
4970 }
4971 mut idx := p.s.offset
4972 for idx < p.s.src.len && (p.s.src[idx] == ` ` || p.s.src[idx] == `\t`
4973 || p.s.src[idx] == `\n` || p.s.src[idx] == `\r`) {
4974 idx++
4975 }
4976 mut depth := 1
4977 for idx < p.s.src.len {
4978 c := p.s.src[idx]
4979 if c == `[` {
4980 depth++
4981 } else if c == `]` {
4982 depth--
4983 if depth == 0 {
4984 idx++
4985 for idx < p.s.src.len && (p.s.src[idx] == ` ` || p.s.src[idx] == `\t`
4986 || p.s.src[idx] == `\n` || p.s.src[idx] == `\r`) {
4987 idx++
4988 }
4989 if idx >= p.s.src.len {
4990 return false
4991 }
4992 next := p.s.src[idx]
4993 return (next >= `a` && next <= `z`) || (next >= `A` && next <= `Z`)
4994 || next == `_` || next == `&` || next == `?` || next == `!`
4995 || next == `[` || next == `(`
4996 }
4997 }
4998 idx++
4999 }
5000 return false
5001}
5002
5003// can_start_type_name reports whether can start type name applies in parser.
5004fn (p &Parser) can_start_type_name() bool {
5005 return p.tok == .name || p.tok == .amp || p.tok == .question || p.tok == .not || p.tok == .lsbr
5006 || p.tok == .lpar || p.tok == .key_fn || p.tok == .ellipsis || p.tok == .key_mut
5007 || p.tok == .key_shared || p.tok == .key_atomic
5008}
5009
5010// fn_type_param_with_mut supports fn type param with mut handling for parser.
5011fn fn_type_param_with_mut(typ string, is_mut bool) string {
5012 if !is_mut || typ.len == 0 || typ.starts_with('&') {
5013 return typ
5014 }
5015 return '&' + typ
5016}
5017
5018// parse_fn_type_param reads parse fn type param input for parser.
5019fn (mut p Parser) parse_fn_type_param() string {
5020 mut is_mut := false
5021 if p.tok == .key_mut {
5022 is_mut = true
5023 p.next()
5024 }
5025 first := p.parse_type_name_progress()
5026 if first.len == 0 {
5027 return first
5028 }
5029 if p.tok != .comma && p.tok != .rpar && p.tok != .eof && p.can_start_type_name() {
5030 second := p.parse_type_name_progress()
5031 if second.len > 0 {
5032 return '${first} ${fn_type_param_with_mut(second, is_mut)}'
5033 }
5034 }
5035 return fn_type_param_with_mut(first, is_mut)
5036}
5037
5038fn (mut p Parser) parse_type_generic_suffix() string {
5039 if p.tok != .lsbr {
5040 return ''
5041 }
5042 pk := p.peek()
5043 if pk != .name && pk != .amp && pk != .lsbr && pk != .question && pk != .key_fn {
5044 return ''
5045 }
5046 p.next() // skip [
5047 mut params := []string{}
5048 params << p.parse_type_name()
5049 for p.tok == .comma {
5050 p.next()
5051 params << p.parse_type_name()
5052 }
5053 p.check(.rsbr)
5054 return '[' + params.join(', ') + ']'
5055}
5056
5057// parse_type_name reads parse type name input for parser.
5058fn (mut p Parser) parse_type_name() string {
5059 if p.tok == .name && p.lit == '&' {
5060 p.next()
5061 return '&' + p.parse_type_name()
5062 }
5063 if p.tok == .key_mut {
5064 p.next()
5065 return fn_type_param_with_mut(p.parse_type_name(), true)
5066 }
5067 // option ?T
5068 if p.tok == .question {
5069 p.next()
5070 inner := p.parse_type_name()
5071 if inner.len > 0 {
5072 return '?' + inner
5073 }
5074 return '?'
5075 }
5076 // result !T
5077 if p.tok == .not {
5078 p.next()
5079 inner := p.parse_type_name()
5080 if inner.len > 0 {
5081 return '!' + inner
5082 }
5083 return '!'
5084 }
5085 // pointer &T
5086 if p.tok == .amp {
5087 p.next()
5088 return '&' + p.parse_type_name()
5089 }
5090 if p.tok == .and {
5091 p.next()
5092 return '&&' + p.parse_type_name()
5093 }
5094 // variadic ...T
5095 if p.tok == .ellipsis {
5096 p.next()
5097 return '...' + p.parse_type_name()
5098 }
5099 // array []T or fixed [N]T
5100 if p.tok == .lsbr {
5101 p.next()
5102 if p.tok == .rsbr {
5103 p.next()
5104 return '[]' + p.parse_type_name()
5105 }
5106 // fixed array [N]T — the size may be a const expression (`[segs + 1]f32`),
5107 // not just a single token, so parse the whole expression and recover its
5108 // source text when it is not a plain literal (mirrors the array-literal path).
5109 size_start := p.tok_pos
5110 size_node := p.expr(.lowest)
5111 size_end := p.tok_pos
5112 p.check(.rsbr)
5113 lit_size := p.a.nodes[int(size_node)].value
5114 size_str := if lit_size.len > 0 {
5115 lit_size
5116 } else if size_start >= 0 && size_end > size_start && size_end <= p.s.src.len {
5117 p.s.src[size_start..size_end].trim_space()
5118 } else {
5119 lit_size
5120 }
5121 return '[${size_str}]' + p.parse_type_name()
5122 }
5123 // multi-return (T, U)
5124 if p.tok == .lpar {
5125 p.next()
5126 mut type_list := []string{}
5127 type_list << p.parse_type_name()
5128 for p.tok == .comma {
5129 p.next()
5130 type_list << p.parse_type_name()
5131 }
5132 p.check(.rpar)
5133 return '(' + type_list.join(', ') + ')'
5134 }
5135 // function type fn(T) U
5136 if p.tok == .key_fn {
5137 p.next()
5138 if p.tok != .lpar {
5139 return 'fn'
5140 }
5141 p.next()
5142 mut ptypes := []string{}
5143 for p.tok != .rpar && p.tok != .eof {
5144 typ := p.parse_fn_type_param()
5145 if typ.len > 0 {
5146 ptypes << typ
5147 }
5148 if p.tok == .comma {
5149 p.next()
5150 }
5151 }
5152 p.check(.rpar)
5153 mut ret := ''
5154 if p.can_start_type_name() {
5155 ret = p.parse_type_name()
5156 }
5157 if ret.len > 0 {
5158 return 'fn(${ptypes.join(', ')}) ${ret}'
5159 }
5160 return 'fn(${ptypes.join(', ')})'
5161 }
5162 // atomic / shared modifier
5163 if p.tok == .key_atomic || p.tok == .key_shared {
5164 modifier := p.lit
5165 p.next()
5166 return '${modifier} ' + p.parse_type_name()
5167 }
5168 // nil type
5169 if p.tok == .key_nil {
5170 p.next()
5171 return 'nil'
5172 }
5173 // none type
5174 if p.tok == .key_none {
5175 p.next()
5176 return 'none'
5177 }
5178 // struct type (inline/anonymous)
5179 if p.tok == .key_struct {
5180 p.next()
5181 if p.tok == .lcbr {
5182 p.skip_block()
5183 }
5184 return 'struct'
5185 }
5186 // name
5187 mut name := ''
5188 if p.tok == .name {
5189 name = p.lit
5190 p.next()
5191 // map[K]V
5192 if name == 'map' && p.tok == .lsbr {
5193 p.next()
5194 key := p.parse_type_name()
5195 p.check(.rsbr)
5196 val := p.parse_type_name()
5197 return 'map[${key}]${val}'
5198 }
5199 // chan T
5200 if name == 'chan' {
5201 if p.tok == .name || p.tok == .amp || p.tok == .lsbr || p.tok == .question {
5202 elem := p.parse_type_name()
5203 return 'chan ${elem}'
5204 }
5205 return 'chan'
5206 }
5207 // thread T
5208 if name == 'thread' {
5209 if p.tok == .name || p.tok == .amp || p.tok == .lsbr || p.tok == .question {
5210 elem := p.parse_type_name()
5211 return 'thread ${elem}'
5212 }
5213 return 'thread'
5214 }
5215 // qualified: mod.Type
5216 if p.tok == .dot {
5217 p.next()
5218 if p.tok == .name {
5219 name += '.' + p.lit
5220 p.next()
5221 }
5222 }
5223 // generic: Type[T, U]
5224 name += p.parse_type_generic_suffix()
5225 }
5226 return name
5227}
5228
5229// ==================== helpers ====================
5230
5231// strip_quotes supports strip quotes handling for parser.
5232fn strip_quotes(s string) string {
5233 mut raw := s
5234 is_raw := s.len >= 3 && s[0] == `r`
5235 if is_raw {
5236 raw = s[1..]
5237 }
5238 if raw.len >= 2 && ((raw[0] == `'` && raw[raw.len - 1] == `'`)
5239 || (raw[0] == `"` && raw[raw.len - 1] == `"`)) {
5240 raw = raw[1..raw.len - 1]
5241 } else if raw.len >= 1 && (raw[0] == `'` || raw[0] == `"`) {
5242 raw = raw[1..]
5243 }
5244 if is_raw {
5245 return raw
5246 }
5247 return unescape_string(raw)
5248}
5249
5250fn strip_interp_start_quotes(s string) string {
5251 mut raw := s
5252 is_raw := s.len >= 3 && s[0] == `r`
5253 if is_raw {
5254 raw = s[1..]
5255 }
5256 if raw.len >= 1 && (raw[0] == `'` || raw[0] == `"`) {
5257 raw = raw[1..]
5258 }
5259 if is_raw {
5260 return raw
5261 }
5262 return unescape_string(raw)
5263}
5264
5265fn strip_interp_quotes(s string, quote u8) string {
5266 mut raw := s
5267 if raw.len >= 1 && raw[raw.len - 1] == quote {
5268 raw = raw[..raw.len - 1]
5269 }
5270 return unescape_string(raw)
5271}
5272
5273fn unescape_string(s string) string {
5274 if !s.contains('\\') {
5275 return s
5276 }
5277 mut buf := unsafe { malloc(s.len + 1) }
5278 mut j := 0
5279 mut i := 0
5280 for i < s.len {
5281 if s[i] == `\\` && i + 1 < s.len {
5282 c := match s[i + 1] {
5283 `n` { u8(`\n`) }
5284 `t` { u8(`\t`) }
5285 `r` { u8(`\r`) }
5286 `\\` { u8(`\\`) }
5287 `'` { u8(`'`) }
5288 `"` { u8(`"`) }
5289 `$` { u8(`$`) }
5290 `0` { u8(0) }
5291 `a` { u8(7) }
5292 `b` { u8(8) }
5293 `f` { u8(12) }
5294 `v` { u8(11) }
5295 else { u8(0xff) }
5296 }
5297
5298 if c != 0xff {
5299 unsafe {
5300 buf[j] = c
5301 }
5302 j++
5303 } else {
5304 unsafe {
5305 buf[j] = s[i]
5306 buf[j + 1] = s[i + 1]
5307 }
5308 j += 2
5309 }
5310 i += 2
5311 } else {
5312 unsafe {
5313 buf[j] = s[i]
5314 }
5315 j++
5316 i++
5317 }
5318 }
5319 unsafe {
5320 buf[j] = 0
5321 return tos(buf, j)
5322 }
5323}
5324
5325fn is_builtin_type(name string) bool {
5326 return name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64',
5327 'byte', 'bool', 'string', 'rune', 'char', 'voidptr', 'charptr', 'byteptr', 'usize', 'isize',
5328 'array', 'map', 'mapnode', '_result', '_option', 'any']
5329}
5330
5331fn is_c_anon_simple_type_name(name string) bool {
5332 if name.len == 0 {
5333 return false
5334 }
5335 if name.ends_with('_t') {
5336 return true
5337 }
5338 return name[0] >= `A` && name[0] <= `Z`
5339}
5340
5341fn is_all_upper_ident(s string) bool {
5342 if s.len == 0 {
5343 return false
5344 }
5345 mut has_letter := false
5346 for ch in s {
5347 if ch >= `a` && ch <= `z` {
5348 return false
5349 }
5350 if ch >= `A` && ch <= `Z` {
5351 has_letter = true
5352 }
5353 }
5354 return has_letter
5355}
5356
5357fn token_to_op(tok token.Token) flat.Op {
5358 tv := int(tok)
5359 if tv == 95 {
5360 return flat.Op.plus
5361 }
5362 if tv == 81 {
5363 return flat.Op.minus
5364 }
5365 if tv == 85 {
5366 return flat.Op.mul
5367 }
5368 if tv == 13 {
5369 return flat.Op.div
5370 }
5371 if tv == 83 {
5372 return flat.Op.mod
5373 }
5374 if tv == 20 {
5375 return flat.Op.eq
5376 }
5377 if tv == 88 {
5378 return flat.Op.ne
5379 }
5380 if tv == 80 {
5381 return flat.Op.lt
5382 }
5383 if tv == 22 {
5384 return flat.Op.gt
5385 }
5386 if tv == 74 {
5387 return flat.Op.le
5388 }
5389 if tv == 21 {
5390 return flat.Op.ge
5391 }
5392 if tv == 0 {
5393 return flat.Op.amp
5394 }
5395 if tv == 94 {
5396 return flat.Op.pipe
5397 }
5398 if tv == 109 {
5399 return flat.Op.xor
5400 }
5401 if tv == 75 {
5402 return flat.Op.left_shift
5403 }
5404 if tv == 99 {
5405 return flat.Op.right_shift
5406 }
5407 if tv == 101 {
5408 return flat.Op.right_shift_unsigned
5409 }
5410 if tv == 1 {
5411 return flat.Op.logical_and
5412 }
5413 if tv == 77 {
5414 return flat.Op.logical_or
5415 }
5416 if tv == 89 {
5417 return flat.Op.not
5418 }
5419 if tv == 6 {
5420 return flat.Op.bit_not
5421 }
5422 if tv == 4 {
5423 return flat.Op.assign
5424 }
5425 if tv == 96 {
5426 return flat.Op.plus_assign
5427 }
5428 if tv == 82 {
5429 return flat.Op.minus_assign
5430 }
5431 if tv == 86 {
5432 return flat.Op.mul_assign
5433 }
5434 if tv == 14 {
5435 return flat.Op.div_assign
5436 }
5437 if tv == 84 {
5438 return flat.Op.mod_assign
5439 }
5440 if tv == 2 {
5441 return flat.Op.amp_assign
5442 }
5443 if tv == 93 {
5444 return flat.Op.pipe_assign
5445 }
5446 if tv == 110 {
5447 return flat.Op.xor_assign
5448 }
5449 if tv == 76 {
5450 return flat.Op.left_shift_assign
5451 }
5452 if tv == 100 {
5453 return flat.Op.right_shift_assign
5454 }
5455 if tv == 102 {
5456 return flat.Op.right_shift_unsigned_assign
5457 }
5458 if tv == 24 {
5459 return flat.Op.inc
5460 }
5461 if tv == 11 {
5462 return flat.Op.dec
5463 }
5464 if tv == 3 {
5465 return flat.Op.arrow
5466 }
5467 if tv == 12 {
5468 return flat.Op.assign
5469 }
5470 return flat.Op.none
5471}
5472
5473fn overload_token_name(tok token.Token) string {
5474 tv := int(tok)
5475 if tv == 95 {
5476 return '+'
5477 }
5478 if tv == 81 {
5479 return '-'
5480 }
5481 if tv == 85 {
5482 return '*'
5483 }
5484 if tv == 13 {
5485 return '/'
5486 }
5487 if tv == 83 {
5488 return '%'
5489 }
5490 if tv == 20 {
5491 return '=='
5492 }
5493 if tv == 88 {
5494 return '!='
5495 }
5496 if tv == 80 {
5497 return '<'
5498 }
5499 if tv == 22 {
5500 return '>'
5501 }
5502 if tv == 74 {
5503 return '<='
5504 }
5505 if tv == 21 {
5506 return '>='
5507 }
5508 if tv == 94 {
5509 return '|'
5510 }
5511 if tv == 109 {
5512 return '^'
5513 }
5514 return ''
5515}
5516