v2 / vlib / v / parser / if_match.v
980 lines · 961 sloc · 24.65 KB · b22305ec7fe40790310def9e36f3b50aa3f1b7e4
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module parser
5
6import v.ast
7import v.token
8import v.pkgconfig
9import v.pref
10
11fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
12 was_inside_if_expr := p.inside_if_expr
13 was_inside_ct_if_expr := p.inside_ct_if_expr
14 defer {
15 p.inside_if_expr = was_inside_if_expr
16 p.inside_ct_if_expr = was_inside_ct_if_expr
17 }
18 p.inside_if_expr = true
19 is_expr_ := p.prev_tok.kind == .key_return || is_expr
20 mut pos := p.tok.pos()
21 if is_comptime {
22 p.inside_ct_if_expr = true
23 p.next() // `$`
24 pos = p.prev_tok.pos().extend(p.tok.pos())
25 }
26 mut branches := []ast.IfBranch{}
27 mut has_else := false
28 mut comments := []ast.Comment{}
29 mut prev_guard := false
30 mut comptime_skip_curr_stmts := false
31 mut comptime_has_true_branch := false
32 for p.tok.kind in [.key_if, .key_else] {
33 p.inside_if = true
34 if is_comptime {
35 p.inside_comptime_if = true
36 }
37 start_pos := if is_comptime { p.prev_tok.pos().extend(p.tok.pos()) } else { p.tok.pos() }
38 if p.tok.kind == .key_else {
39 comments << p.eat_comments()
40 p.check(.key_else)
41 comments << p.eat_comments()
42 if p.tok.kind == .key_match {
43 p.error('cannot use `match` with `if` statements')
44 return ast.IfExpr{}
45 }
46 if p.tok.kind == .lcbr {
47 // else {
48 has_else = true
49 p.inside_if = false
50 p.inside_comptime_if = false
51 end_pos := p.prev_tok.pos()
52 body_pos := p.tok.pos()
53 p.open_scope()
54 // only declare `err` if previous branch was an `if` guard
55 if prev_guard {
56 p.scope.register(ast.Var{
57 name: 'err'
58 typ: ast.error_type
59 pos: p.tok.pos()
60 is_used: false
61 is_stack_obj: true
62 is_special: true
63 })
64 }
65 if is_comptime && comptime_has_true_branch && !p.pref.is_fmt
66 && !p.pref.output_cross_c {
67 p.skip_scope()
68 branches << ast.IfBranch{
69 pos: start_pos.extend(end_pos)
70 body_pos: body_pos.extend(p.tok.pos())
71 comments: comments
72 scope: p.scope
73 }
74 } else {
75 branches << ast.IfBranch{
76 stmts: p.parse_block_no_scope(false)
77 pos: start_pos.extend(end_pos)
78 body_pos: body_pos.extend(p.tok.pos())
79 comments: comments
80 scope: p.scope
81 }
82 }
83 p.close_scope()
84 comments = []
85 break
86 }
87 if is_comptime {
88 p.check(.dollar)
89 }
90 }
91 if_pos := p.tok.pos()
92 // `if` or `else if`
93 p.check(.key_if)
94 if p.tok.kind == .key_match {
95 p.error('cannot use `match` with `if` statements')
96 return ast.IfExpr{}
97 }
98 comments << p.eat_comments()
99 mut cond := ast.empty_expr
100 mut is_guard := false
101
102 // if guard `if x,y := opt() {`
103 if !is_comptime && p.peek_token_after_var_list().kind == .decl_assign {
104 p.open_scope()
105 is_guard = true
106 mut vars := []ast.IfGuardVar{}
107 mut var_names := []string{}
108 for {
109 mut var := ast.IfGuardVar{}
110 mut is_mut := false
111 if p.tok.kind == .key_mut {
112 is_mut = true
113 p.next()
114 }
115 var.is_mut = p.scope_var_is_mut(is_mut)
116 var.pos = p.tok.pos()
117 var.name = p.check_name()
118 var_names << var.name
119
120 if p.scope.known_var(var.name) {
121 p.error_with_pos('redefinition of `${var.name}`', var.pos)
122 }
123 vars << var
124 if p.tok.kind != .comma {
125 break
126 }
127 p.next()
128 }
129 comments << p.eat_comments()
130 p.check(.decl_assign)
131 comments << p.eat_comments()
132 old_assign_rhs := p.inside_assign_rhs
133 p.inside_assign_rhs = true
134 expr := p.expr(0)
135 p.inside_assign_rhs = old_assign_rhs
136 if expr !in [ast.CallExpr, ast.IndexExpr, ast.PrefixExpr, ast.SelectorExpr, ast.Ident] {
137 p.error_with_pos('if guard condition expression is illegal, it should return an Option',
138 expr.pos())
139 }
140 p.check_undefined_variables(var_names, expr) or {
141 p.error_with_pos(err.msg(), pos)
142 break
143 }
144 cond = ast.IfGuardExpr{
145 vars: vars
146 expr: expr
147 }
148 for var in vars {
149 p.scope.register(ast.Var{
150 name: var.name
151 is_mut: var.is_mut
152 expr: cond
153 pos: var.pos
154 })
155 }
156 prev_guard = true
157 } else {
158 prev_guard = false
159 p.comptime_if_cond = true
160 p.inside_if_cond = true
161 cond = p.expr(0)
162 if is_comptime && p.is_in_top_level_comptime(p.inside_assign_rhs) {
163 comptime_skip_curr_stmts = !p.comptime_if_cond(mut cond)
164 if !comptime_skip_curr_stmts {
165 comptime_has_true_branch = true
166 }
167 }
168 if mut cond is ast.InfixExpr && !is_comptime {
169 if cond.op in [.key_is, .not_is] {
170 if mut cond.left is ast.Ident {
171 if cond.left.name.len == 1 && cond.left.name.is_capital()
172 && cond.right is ast.TypeNode {
173 p.error_with_pos('use `\$if` instead of `if`', if_pos)
174 return ast.IfExpr{}
175 }
176 }
177 }
178 }
179 p.inside_if_cond = false
180 if p.if_cond_comments.len > 0 {
181 comments << p.if_cond_comments
182 p.if_cond_comments = []
183 }
184 p.comptime_if_cond = false
185 }
186 comments << p.eat_comments()
187 end_pos := p.prev_tok.pos()
188 body_pos := p.tok.pos()
189 p.inside_if = false
190 p.inside_comptime_if = false
191 if p.opened_scopes > p.max_opened_scopes {
192 p.should_abort = true
193 p.error('too many nested conditionals, scopes: ${p.opened_scopes}')
194 return ast.IfExpr{}
195 }
196 p.open_scope()
197 if is_comptime && comptime_skip_curr_stmts
198 && p.is_in_top_level_comptime(p.inside_assign_rhs) && !p.pref.is_fmt
199 && !p.pref.output_cross_c {
200 p.skip_scope()
201 branches << ast.IfBranch{
202 cond: cond
203 pos: start_pos.extend(end_pos)
204 body_pos: body_pos.extend(p.prev_tok.pos())
205 comments: comments
206 scope: p.scope
207 }
208 } else {
209 stmts := p.parse_block_no_scope(false)
210 branches << ast.IfBranch{
211 cond: cond
212 stmts: stmts
213 pos: start_pos.extend(end_pos)
214 body_pos: body_pos.extend(p.prev_tok.pos())
215 comments: comments
216 scope: p.scope
217 }
218 }
219 p.close_scope()
220 if is_guard {
221 p.close_scope()
222 }
223 comments = p.eat_comments()
224 if is_comptime {
225 if p.tok.kind == .key_else {
226 p.error('use `\$else` instead of `else` in compile-time `if` branches')
227 return ast.IfExpr{}
228 }
229 if p.tok.kind != .rcbr && p.peek_tok.kind == .key_else {
230 p.check(.dollar)
231 }
232 }
233 if p.tok.kind != .key_else {
234 break
235 }
236 }
237 pos.update_last_line(p.prev_tok.line_nr)
238 if comments.len > 0 {
239 pos.last_line = comments.last().pos.last_line
240 }
241 return ast.IfExpr{
242 is_comptime: is_comptime
243 branches: branches
244 post_comments: comments
245 pos: pos
246 has_else: has_else
247 is_expr: is_expr_
248 }
249}
250
251fn (mut p Parser) is_only_array_type() bool {
252 if p.tok.kind == .lsbr {
253 for i in 1 .. 20 {
254 if p.peek_token(i).kind == .rsbr {
255 next_kind := p.peek_token(i + 1).kind
256 if next_kind == .name {
257 return true
258 } else if next_kind == .question && p.peek_token(i + 2).kind == .name {
259 return true
260 } else if next_kind == .lsbr {
261 continue
262 } else {
263 return false
264 }
265 }
266 }
267 }
268 return false
269}
270
271fn (mut p Parser) is_match_sumtype_type() bool {
272 is_option := p.tok.kind == .question
273 name_tok := if is_option { p.peek_tok } else { p.tok }
274 next_tok_kind := if is_option { p.peek_token(2).kind } else { p.peek_tok.kind }
275 next_next_idx := if is_option { 3 } else { 2 }
276 next_next_tok := p.peek_token(next_next_idx)
277 return name_tok.kind == .name && !(name_tok.lit == 'C' && next_tok_kind == .dot)
278 && (((ast.builtin_type_names_matcher.matches(name_tok.lit) || name_tok.lit[0].is_capital())
279 && next_tok_kind != .lpar && !(next_tok_kind == .dot && next_next_tok.kind == .name
280 && p.peek_token(next_next_idx + 1).kind == .lpar)) || (next_tok_kind == .dot
281 && next_next_tok.lit.len > 0 && next_next_tok.lit[0].is_capital()))
282}
283
284fn (mut p Parser) resolve_at_expr(expr ast.AtExpr) !string {
285 match expr.kind {
286 .mod_name {
287 return p.mod
288 }
289 .os {
290 return pref.get_host_os().lower()
291 }
292 .ccompiler {
293 return p.pref.ccompiler_type.str()
294 }
295 .backend {
296 return p.pref.backend.str()
297 }
298 .platform {
299 return p.pref.arch.str()
300 }
301 else {
302 return error('top level comptime only support `@MOD` `@OS` `@CCOMPILER` `@BACKEND` or `@PLATFORM`')
303 }
304 }
305
306 return ''
307}
308
309enum MatchCondOrBlockMode {
310 unsupported
311 no_err_var
312 with_err_var
313 already_set
314}
315
316fn (p &Parser) match_cond_or_block_mode(cond ast.Expr) MatchCondOrBlockMode {
317 return match cond {
318 ast.CallExpr {
319 if cond.or_block.kind == ast.OrKind.absent {
320 MatchCondOrBlockMode.with_err_var
321 } else {
322 MatchCondOrBlockMode.already_set
323 }
324 }
325 ast.Ident {
326 if cond.or_expr.kind == ast.OrKind.absent {
327 MatchCondOrBlockMode.no_err_var
328 } else {
329 MatchCondOrBlockMode.already_set
330 }
331 }
332 ast.IndexExpr {
333 if cond.or_expr.kind == ast.OrKind.absent {
334 MatchCondOrBlockMode.no_err_var
335 } else {
336 MatchCondOrBlockMode.already_set
337 }
338 }
339 ast.ParExpr {
340 p.match_cond_or_block_mode(cond.expr)
341 }
342 ast.PrefixExpr {
343 if cond.op == .arrow {
344 if cond.or_block.kind == ast.OrKind.absent {
345 MatchCondOrBlockMode.with_err_var
346 } else {
347 MatchCondOrBlockMode.already_set
348 }
349 } else {
350 MatchCondOrBlockMode.unsupported
351 }
352 }
353 ast.SelectorExpr {
354 if cond.or_block.kind == ast.OrKind.absent {
355 MatchCondOrBlockMode.with_err_var
356 } else {
357 MatchCondOrBlockMode.already_set
358 }
359 }
360 else {
361 MatchCondOrBlockMode.unsupported
362 }
363 }
364}
365
366fn (mut p Parser) set_match_cond_or_block(mut cond ast.Expr, or_expr ast.OrExpr) {
367 match mut cond {
368 ast.CallExpr {
369 cond.or_block = or_expr
370 }
371 ast.Ident {
372 cond.or_expr = or_expr
373 }
374 ast.IndexExpr {
375 cond.or_expr = or_expr
376 }
377 ast.ParExpr {
378 p.set_match_cond_or_block(mut cond.expr, or_expr)
379 }
380 ast.PrefixExpr {
381 cond.or_block = or_expr
382 }
383 ast.SelectorExpr {
384 cond.or_block = or_expr
385 }
386 else {}
387 }
388}
389
390fn (mut p Parser) match_expr(is_comptime bool, is_expr bool) ast.MatchExpr {
391 mut match_first_pos := p.tok.pos()
392 old_inside_ct_match := p.inside_ct_match
393 is_expr_ := p.prev_tok.kind == .key_return || is_expr
394 if is_comptime {
395 p.next() // `$`
396 match_first_pos = p.prev_tok.pos().extend(p.tok.pos())
397 p.inside_ct_match = true
398 }
399 old_inside_match := p.inside_match
400 p.inside_match = true
401 p.check(.key_match)
402 mut is_sum_type := false
403 mut cond := p.expr(0)
404 mut cond_str := ''
405 if is_comptime && cond is ast.AtExpr && p.is_in_top_level_comptime(p.inside_assign_rhs) {
406 cond_str = p.resolve_at_expr(cond as ast.AtExpr) or {
407 p.error(err.msg())
408 return ast.MatchExpr{}
409 }
410 }
411 p.inside_match = old_inside_match
412 p.inside_ct_match = old_inside_ct_match
413 no_lcbr := p.tok.kind != .lcbr
414 if !no_lcbr {
415 p.check(.lcbr)
416 }
417 comments := p.eat_comments() // comments before the first branch
418 mut branches := []ast.MatchBranch{}
419 mut comptime_skip_curr_stmts := false
420 mut comptime_has_true_branch := false
421 for p.tok.kind != .eof {
422 branch_first_pos := p.tok.pos()
423 mut exprs := []ast.Expr{}
424 mut ecmnts := [][]ast.Comment{}
425 // final else
426 mut is_else := false
427 if is_comptime {
428 if p.tok.kind == .key_else {
429 p.error('use `\$else` instead of `else` in compile-time `match` branches')
430 return ast.MatchExpr{}
431 }
432 if p.tok.kind != .rcbr && p.peek_tok.kind == .key_else {
433 p.check(.dollar)
434 }
435 }
436 if p.tok.kind == .key_else {
437 is_else = true
438 p.next()
439 } else if p.is_match_sumtype_type() || p.is_only_array_type()
440 || p.tok.kind == .key_fn
441 || (p.tok.kind == .lsbr && p.peek_token(2).kind == .amp) {
442 mut types := []ast.Type{}
443 for {
444 // Sum type match
445 parsed_type := p.parse_type()
446 types << parsed_type
447 exprs << ast.TypeNode{
448 typ: parsed_type
449 pos: p.prev_tok.pos()
450 }
451 if p.tok.kind != .comma {
452 ecmnts << p.eat_comments()
453 break
454 }
455 p.check(.comma)
456 if p.pref.is_fmt {
457 if p.tok.kind == .lcbr {
458 break
459 }
460 for p.tok.kind == .comma {
461 p.next()
462 }
463 ecmnts << p.eat_comments()
464 }
465 }
466 is_sum_type = true
467 } else {
468 if p.tok.kind == .rcbr {
469 p.next()
470 return ast.MatchExpr{
471 pos: match_first_pos
472 }
473 }
474 // Expression match
475 for {
476 if is_comptime {
477 p.inside_ct_match_case = true
478 }
479 p.inside_match_case = true
480 mut range_pos := p.tok.pos()
481 mut case_str := ''
482 mut expr := p.expr(0)
483 p.inside_match_case = false
484 p.inside_ct_match_case = false
485 match mut expr {
486 ast.StringLiteral {
487 case_str = expr.val
488 }
489 ast.IntegerLiteral {
490 case_str = expr.val.str()
491 }
492 ast.BoolLiteral {
493 case_str = expr.val.str()
494 }
495 else {}
496 }
497
498 comptime_skip_curr_stmts = cond_str != case_str
499 if !comptime_skip_curr_stmts {
500 comptime_has_true_branch = true
501 }
502 if p.tok.kind == .dotdot {
503 p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)',
504 p.tok.pos())
505 return ast.MatchExpr{}
506 } else if p.tok.kind == .ellipsis {
507 p.next()
508 if is_comptime {
509 p.inside_ct_match_case = true
510 }
511 p.inside_match_case = true
512 expr2 := p.expr(0)
513 p.inside_match_case = false
514 p.inside_ct_match_case = false
515 exprs << ast.RangeExpr{
516 low: expr
517 high: expr2
518 has_low: true
519 has_high: true
520 pos: range_pos.extend(p.prev_tok.pos())
521 }
522 } else {
523 exprs << expr
524 }
525 if p.tok.kind != .comma {
526 ecmnts << p.eat_comments()
527 break
528 }
529
530 p.check(.comma)
531 if p.pref.is_fmt {
532 if p.tok.kind == .lcbr {
533 break
534 }
535 for p.tok.kind == .comma {
536 p.next()
537 }
538 ecmnts << p.eat_comments()
539 }
540 }
541 }
542 branch_last_pos := p.prev_tok.pos()
543 // p.warn('match block')
544 if is_comptime {
545 p.inside_ct_match_body = true
546 }
547 p.inside_match_body = true
548 p.open_scope()
549 mut stmts := []ast.Stmt{}
550 if is_comptime && ((!is_else && comptime_skip_curr_stmts)
551 || (is_else && comptime_has_true_branch))
552 && p.is_in_top_level_comptime(p.inside_assign_rhs) && !p.pref.is_fmt
553 && !p.pref.output_cross_c {
554 p.skip_scope()
555 } else {
556 stmts = p.parse_block_no_scope(false)
557 }
558 branch_scope := p.scope
559 p.close_scope()
560 p.inside_match_body = false
561 p.inside_ct_match_body = false
562 pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr)
563 branch_pos := branch_first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
564 post_comments := p.eat_comments()
565 branches << ast.MatchBranch{
566 exprs: exprs
567 ecmnts: ecmnts
568 stmts: stmts
569 pos: pos
570 branch_pos: branch_pos
571 is_else: is_else
572 post_comments: post_comments
573 scope: branch_scope
574 }
575 if p.tok.kind == .rcbr || (is_else && no_lcbr) {
576 break
577 }
578 }
579 mut match_last_pos := p.tok.pos()
580 if p.tok.kind == .rcbr {
581 p.check(.rcbr)
582 match_last_pos = p.prev_tok.pos()
583 }
584 if p.tok.kind == .key_orelse {
585 cond_or_mode := p.match_cond_or_block_mode(cond)
586 if cond_or_mode == .already_set {
587 p.error_with_pos('match condition already has an `or {}` block', p.tok.pos())
588 return ast.MatchExpr{}
589 }
590 if cond_or_mode == .unsupported {
591 p.error_with_pos('trailing `or {}` is only supported for match conditions that can use `or {}` directly',
592 p.tok.pos())
593 return ast.MatchExpr{}
594 }
595 err_var_mode := if cond_or_mode == .with_err_var {
596 OrBlockErrVarMode.with_err_var
597 } else {
598 OrBlockErrVarMode.no_err_var
599 }
600 or_stmts, or_pos, or_scope := p.or_block(err_var_mode)
601 p.set_match_cond_or_block(mut cond, ast.OrExpr{
602 kind: .block
603 stmts: or_stmts
604 pos: or_pos
605 scope: or_scope
606 })
607 match_last_pos = p.prev_tok.pos()
608 }
609 // return ast.StructInit{}
610 mut pos := token.Pos{
611 line_nr: match_first_pos.line_nr
612 pos: match_first_pos.pos
613 len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len
614 col: match_first_pos.col
615 }
616 pos.update_last_line(match_last_pos.line_nr)
617 return ast.MatchExpr{
618 is_comptime: is_comptime
619 is_expr: is_expr_
620 branches: branches
621 cond: cond
622 is_sum_type: is_sum_type
623 pos: pos
624 comments: comments
625 }
626}
627
628fn (mut p Parser) select_expr() ast.SelectExpr {
629 match_first_pos := p.tok.pos()
630 p.check(.key_select)
631 no_lcbr := p.tok.kind != .lcbr
632 if !no_lcbr {
633 p.check(.lcbr)
634 }
635 mut branches := []ast.SelectBranch{}
636 mut has_else := false
637 mut has_timeout := false
638 for {
639 branch_first_pos := p.tok.pos()
640 comment := p.check_comment() // comment before {}
641 p.open_scope()
642 // final else
643 mut is_else := false
644 mut is_timeout := false
645 mut stmt := ast.empty_stmt
646 if p.tok.kind == .key_else {
647 if has_timeout {
648 p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys',
649 p.tok.pos())
650 return ast.SelectExpr{}
651 }
652 if has_else {
653 p.error_with_pos('at most one `else` branch allowed in `select` block', p.tok.pos())
654 return ast.SelectExpr{}
655 }
656 is_else = true
657 has_else = true
658 p.next()
659 } else {
660 mut is_gt := false
661 if p.tok.kind == .gt {
662 is_gt = true
663 p.note_with_pos('`>` is deprecated and will soon be forbidden - just state the timeout in nanoseconds',
664 p.tok.pos())
665 p.next()
666 }
667 p.inside_match = true
668 p.inside_select = true
669 exprs := p.expr_list(true)
670 if exprs.len != 1 {
671 p.error('only one expression allowed as `select` key')
672 return ast.SelectExpr{}
673 }
674 if p.tok.kind in [.assign, .decl_assign] {
675 stmt = p.partial_assign_stmt(exprs)
676 } else {
677 stmt = ast.ExprStmt{
678 expr: exprs[0]
679 pos: exprs[0].pos()
680 comments: [comment]
681 is_expr: true
682 }
683 }
684 p.inside_match = false
685 p.inside_select = false
686 match mut stmt {
687 ast.ExprStmt {
688 mut check_timeout := false
689 if !stmt.is_expr {
690 p.error_with_pos('select: invalid expression', stmt.pos)
691 return ast.SelectExpr{}
692 } else {
693 match mut stmt.expr {
694 ast.InfixExpr {
695 if stmt.expr.op != .arrow {
696 check_timeout = true
697 } else if is_gt {
698 p.error_with_pos('send expression cannot be used as timeout',
699 stmt.pos)
700 }
701 }
702 else {
703 check_timeout = true
704 }
705 }
706 }
707 if check_timeout {
708 if has_else {
709 p.error_with_pos('`else` and timeout value are mutually exclusive `select` keys',
710 stmt.pos)
711 return ast.SelectExpr{}
712 }
713 if has_timeout {
714 p.error_with_pos('at most one timeout branch allowed in `select` block',
715 stmt.pos)
716 return ast.SelectExpr{}
717 }
718 is_timeout = true
719 has_timeout = true
720 }
721 }
722 ast.AssignStmt {
723 expr := stmt.right[0]
724 match expr {
725 ast.PrefixExpr {
726 if expr.op != .arrow {
727 p.error_with_pos('select key: `<-` operator expected', expr.pos)
728 return ast.SelectExpr{}
729 }
730 }
731 else {
732 p.error_with_pos('select key: receive expression expected',
733 stmt.right[0].pos())
734 return ast.SelectExpr{}
735 }
736 }
737 }
738 else {
739 p.error_with_pos('select: transmission statement, timeout (in ns) or `else` expected',
740 stmt.pos)
741 return ast.SelectExpr{}
742 }
743 }
744 }
745 branch_last_pos := p.tok.pos()
746 p.inside_match_body = true
747 p.inside_for = false
748 stmts := p.parse_block_no_scope(false)
749 branch_scope := p.scope
750 p.close_scope()
751 p.inside_match_body = false
752 mut pos := token.Pos{
753 line_nr: branch_first_pos.line_nr
754 pos: branch_first_pos.pos
755 len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len
756 col: branch_first_pos.col
757 }
758 post_comments := p.eat_comments()
759 pos.update_last_line(p.prev_tok.line_nr)
760 if post_comments.len > 0 {
761 pos.last_line = post_comments.last().pos.last_line
762 }
763 branches << ast.SelectBranch{
764 stmt: stmt
765 stmts: stmts
766 pos: pos
767 scope: branch_scope
768 comment: comment
769 is_else: is_else
770 is_timeout: is_timeout
771 post_comments: post_comments
772 }
773 if p.tok.kind == .rcbr || ((is_else || is_timeout) && no_lcbr) {
774 break
775 }
776 }
777 match_last_pos := p.tok.pos()
778 pos := token.Pos{
779 line_nr: match_first_pos.line_nr
780 pos: match_first_pos.pos
781 len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len
782 col: match_first_pos.col
783 }
784 if p.tok.kind == .rcbr {
785 p.check(.rcbr)
786 }
787 p.register_auto_import('sync')
788 return ast.SelectExpr{
789 branches: branches
790 pos: pos.extend_with_last_line(p.prev_tok.pos(), p.prev_tok.line_nr)
791 has_exception: has_else || has_timeout
792 }
793}
794
795fn (mut p Parser) comptime_if_cond(mut cond ast.Expr) bool {
796 mut is_true := false
797 match mut cond {
798 ast.BoolLiteral {
799 return cond.val
800 }
801 ast.ParExpr {
802 return p.comptime_if_cond(mut cond.expr)
803 }
804 ast.PrefixExpr {
805 if cond.op != .not {
806 p.error('invalid \$if prefix operator, only allow `!`.')
807 return false
808 }
809 return !p.comptime_if_cond(mut cond.right)
810 }
811 ast.PostfixExpr {
812 if cond.op != .question {
813 p.error('invalid \$if postfix operator, only allow `?`.')
814 return false
815 }
816 if cond.expr !is ast.Ident {
817 p.error('invalid \$if postfix condition, only allow `Indent`.')
818 return false
819 }
820 cname := (cond.expr as ast.Ident).name
821 return cname in p.pref.compile_defines
822 }
823 ast.InfixExpr {
824 match cond.op {
825 .and, .logical_or {
826 l := p.comptime_if_cond(mut cond.left)
827 r := p.comptime_if_cond(mut cond.right)
828 // if at least one of the cond has `keep_stmts`, we should keep stmts
829 return if cond.op == .and { l && r } else { l || r }
830 }
831 .eq, .ne, .gt, .lt, .ge, .le {
832 match mut cond.left {
833 ast.AtExpr {
834 // @OS == 'linux'
835 left_str := p.resolve_at_expr(cond.left) or {
836 p.error(err.msg())
837 return false
838 }
839 if cond.right !is ast.StringLiteral {
840 p.error('`${cond.left} can only compare with string type')
841 return false
842 }
843 right_str := (cond.right as ast.StringLiteral).val
844 if cond.op == .eq {
845 is_true = left_str == right_str
846 } else if cond.op == .ne {
847 is_true = left_str != right_str
848 } else {
849 p.error('string type only support `==` and `!=` operator')
850 return false
851 }
852 return is_true
853 }
854 ast.Ident {
855 // $if version == 2
856 match mut cond.right {
857 ast.StringLiteral {
858 match cond.op {
859 .eq {
860 is_true = cond.left.str() == cond.right.str()
861 }
862 .ne {
863 is_true = cond.left.str() != cond.right.str()
864 }
865 else {
866 p.error('string type only support `==` and `!=` operator')
867 return false
868 }
869 }
870 }
871 ast.IntegerLiteral {
872 match cond.op {
873 .eq {
874 is_true = cond.left.str().i64() == cond.right.val.i64()
875 }
876 .ne {
877 is_true = cond.left.str().i64() != cond.right.val.i64()
878 }
879 .gt {
880 is_true = cond.left.str().i64() > cond.right.val.i64()
881 }
882 .lt {
883 is_true = cond.left.str().i64() < cond.right.val.i64()
884 }
885 .ge {
886 is_true = cond.left.str().i64() >= cond.right.val.i64()
887 }
888 .le {
889 is_true = cond.left.str().i64() <= cond.right.val.i64()
890 }
891 else {
892 p.error('int type only support `==` `!=` `>` `<` `>=` and `<=` operator')
893 return false
894 }
895 }
896 }
897 ast.BoolLiteral {
898 match cond.op {
899 .eq {
900 is_true = cond.left.str().bool() == cond.right.val
901 }
902 .ne {
903 is_true = cond.left.str().bool() != cond.right.val
904 }
905 else {
906 p.error('bool type only support `==` and `!=` operator')
907 return false
908 }
909 }
910 }
911 else {
912 p.error('compare only support string int and bool type')
913 return false
914 }
915 }
916
917 return is_true
918 }
919 else {
920 p.error('invalid \$if condition')
921 return false
922 }
923 }
924
925 p.error('invalid \$if condition')
926 return false
927 }
928 else {
929 p.error('invalid \$if operator: ${cond.op}')
930 return false
931 }
932 }
933 }
934 ast.Ident {
935 cname := cond.name
936 if cname in ast.valid_comptime_not_user_defined {
937 if cname == 'threads' {
938 is_true = p.table.gostmts > 0
939 } else {
940 is_true = ast.eval_comptime_not_user_defined_ident(cname, p.pref) or {
941 p.error(err.msg())
942 return false
943 }
944 }
945 } else {
946 p.error('invalid \$if condition: unknown indent `${cname}`')
947 return false
948 }
949 return is_true
950 }
951 ast.ComptimeCall {
952 if cond.kind == .pkgconfig {
953 if mut m := pkgconfig.main([cond.args_var]) {
954 if _ := m.run() {
955 is_true = true
956 } else {
957 // pkgconfig not found, do not issue error, just set false
958 is_true = false
959 }
960 } else {
961 p.error(err.msg())
962 is_true = false
963 }
964 return is_true
965 }
966 if cond.kind == .d {
967 is_true = cond.compile_value.bool()
968 return is_true
969 }
970 p.error('invalid \$if condition: unknown ComptimeCall')
971 return false
972 }
973 else {
974 p.error('invalid \$if condition ${cond}')
975 return false
976 }
977 }
978
979 return is_true
980}
981