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