v / vlib / v2 / parser / parser.v
4038 lines · 3908 sloc · 101.69 KB · e7738c112c787d477501fa4a87edd0e1d72159bd
Raw
1// Copyright (c) 2020-2024 Joe Conigliaro. 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 os
7import time
8import v2.ast
9import v2.errors
10import v2.pref
11import v2.scanner
12import v2.token
13
14pub struct Parser {
15 pref &pref.Preferences
16mut:
17 file &token.File = &token.File{}
18 scanner &scanner.Scanner
19 // track state
20 exp_lcbr bool // expecting `{` parsing `x` in `for|if|match x {` etc
21 allow_init_in_exp_lcbr bool
22 exp_pt bool // expecting (p)ossible (t)ype from `p.expr()`
23 in_top_level bool // inside top-level context (file scope / top-level comptime block)
24 stop_line_leading_dot bool
25 selector_names map[int]string
26 // token info : start
27 line int
28 lit string
29 pos token.Pos
30 tok token.Token = .unknown
31 tok_next_ token.Token = .unknown // DO NOT access directly, use `p.peek()`
32 // token info : end
33}
34
35pub fn Parser.new(prefs &pref.Preferences) &Parser {
36 return &Parser{
37 pref: unsafe { prefs }
38 scanner: scanner.new_scanner(prefs, .normal)
39 // scanner: scanner.new_scanner(prefs, .skip_interpolation)
40 }
41}
42
43fn expr_infix_payload(expr ast.Expr) ast.InfixExpr {
44 slot0 := unsafe { (&u64(&expr))[0] }
45 slot1 := unsafe { (&u64(&expr))[1] }
46 data := sumtype_payload_slot(slot0, slot1)
47 if data == 0 {
48 return ast.InfixExpr{}
49 }
50 return unsafe { *(&ast.InfixExpr(voidptr(data))) }
51}
52
53fn expr_infix_payload_checked(expr ast.Expr) ?ast.InfixExpr {
54 slot0 := unsafe { (&u64(&expr))[0] }
55 slot1 := unsafe { (&u64(&expr))[1] }
56 tag := sumtype_tag_slot(slot0, slot1)
57 if tag == u64(17) {
58 data := sumtype_payload_slot(slot0, slot1)
59 if data == 0 {
60 return none
61 }
62 return unsafe { *(&ast.InfixExpr(voidptr(data))) }
63 }
64
65 match expr {
66 ast.InfixExpr {
67 return expr
68 }
69 else {}
70 }
71
72 return none
73}
74
75fn expr_ident_payload_checked(expr ast.Expr) ?ast.Ident {
76 slot0 := unsafe { (&u64(&expr))[0] }
77 slot1 := unsafe { (&u64(&expr))[1] }
78 tag := sumtype_tag_slot(slot0, slot1)
79 if tag == u64(13) {
80 data := sumtype_payload_slot(slot0, slot1)
81 if data == 0 {
82 return none
83 }
84 return unsafe { *(&ast.Ident(voidptr(data))) }
85 }
86
87 match expr {
88 ast.Ident {
89 return expr
90 }
91 else {}
92 }
93
94 return none
95}
96
97fn ast_expr_infix_tag() u64 {
98 expr := ast.Expr(ast.InfixExpr{})
99 slot0 := unsafe { (&u64(&expr))[0] }
100 slot1 := unsafe { (&u64(&expr))[1] }
101 return sumtype_tag_slot(slot0, slot1)
102}
103
104fn ast_expr_ident_tag() u64 {
105 expr := ast.Expr(ast.Ident{})
106 slot0 := unsafe { (&u64(&expr))[0] }
107 slot1 := unsafe { (&u64(&expr))[1] }
108 return sumtype_tag_slot(slot0, slot1)
109}
110
111fn sumtype_tag_slot(slot0 u64, slot1 u64) u64 {
112 if sumtype_slot_is_payload(slot0) {
113 return slot1
114 }
115 return slot0
116}
117
118fn sumtype_payload_slot(slot0 u64, slot1 u64) u64 {
119 if sumtype_slot_is_payload(slot0) {
120 return slot0
121 }
122 if sumtype_slot_is_payload(slot1) {
123 return slot1
124 }
125 return 0
126}
127
128fn sumtype_slot_is_payload(slot u64) bool {
129 return slot >= 4096 && slot < 281474976710656
130}
131
132fn (mut p Parser) init(filename string, src string, mut file_set token.FileSet) {
133 // reset since parser instance may be reused
134 p.exp_lcbr = false
135 p.allow_init_in_exp_lcbr = false
136 p.exp_pt = false
137 p.in_top_level = false
138 p.stop_line_leading_dot = false
139 p.line = 0
140 p.lit = ''
141 p.pos = token.Pos{}
142 p.tok = .unknown
143 p.tok_next_ = .unknown
144 p.selector_names = map[int]string{}
145 // init
146 // TODO: consider another way to pass in file set?
147 p.file = file_set.add_file(filename, -1, src.len)
148 p.scanner.init(p.file, src)
149}
150
151pub fn (mut p Parser) parse_files(files []string, mut file_set token.FileSet) []ast.File {
152 mut ast_files := []ast.File{}
153 for file in files {
154 if file == '' {
155 continue
156 }
157 ast_files << p.parse_file(file, mut file_set)
158 }
159 return ast_files
160}
161
162// parse_files_to_flat parses each input file, immediately appends it to a
163// FlatBuilder, and drops the legacy File. This is the Phase 2 entry point:
164// it produces the same FlatAst that `ast.flatten_files(p.parse_files(...))`
165// would produce, but never holds more than one file's legacy AST resident.
166// The result is signature-equivalent to the two-step path (regression-tested).
167pub fn (mut p Parser) parse_files_to_flat(files []string, mut file_set token.FileSet) ast.FlatAst {
168 mut total_bytes := i64(0)
169 for file in files {
170 if file == '' {
171 continue
172 }
173 total_bytes += os.file_size(file)
174 }
175 nodes_cap, edges_cap, strings_cap := ast.arena_caps_for_bytes(total_bytes)
176 mut builder := ast.new_flat_builder_with_capacity(nodes_cap, edges_cap, strings_cap)
177 p.parse_files_into_flat(files, mut file_set, mut builder)
178 return builder.flat
179}
180
181// parse_files_into_flat streams files into a caller-owned FlatBuilder.
182// Used by the builder to accumulate a single FlatAst across multiple
183// parse batches (core, user, imports) without throwing away the builder
184// between calls.
185pub fn (mut p Parser) parse_files_into_flat(files []string, mut file_set token.FileSet, mut builder ast.FlatBuilder) {
186 for file in files {
187 if file == '' {
188 continue
189 }
190 legacy := p.parse_file(file, mut file_set)
191 builder.append_file(legacy)
192 }
193}
194
195pub fn (mut p Parser) parse_file(filename string, mut file_set token.FileSet) ast.File {
196 if filename == '' {
197 panic('parser.parse_file empty filename')
198 }
199 mut src := ''
200 mut sw := time.StopWatch{}
201 if !p.pref.verbose {
202 unsafe {
203 goto start_no_time
204 }
205 }
206 sw = time.new_stopwatch()
207 start_no_time:
208 src = os.read_file(filename) or { p.error('error reading `' + filename + '`') }
209 p.init(filename, src, mut file_set)
210 // start
211 p.next()
212 mut top_stmts := []ast.Stmt{}
213 // mut decls := []ast.Decl{}
214 mut imports := []ast.ImportStmt{}
215 mut mod := 'main'
216 // file level attributes
217 // or we are missing a stmt which supports attributes in this match
218 mut attributes := []ast.Attribute{}
219 if p.tok in [.attribute, .lsbr] {
220 attribute_stmt := p.attribute_stmt()
221 top_stmts << attribute_stmt
222 match attribute_stmt {
223 []ast.Attribute {
224 attrs := attribute_stmt as []ast.Attribute
225 for attribute in attrs {
226 attributes << attribute
227 if attribute.value is ast.Ident {
228 if attribute.value.name.len > 0 && attribute.value.name.len < 256
229 && attribute.value.name !in ['has_globals', 'generated', 'manualfree', 'translated'] {
230 p.warn('invalid file level attribute `${attribute.name}` (or should `${p.tok}` support attributes)')
231 }
232 }
233 }
234 }
235 else {}
236 }
237 }
238 // TODO: script mode support?
239 // I really hope it gets dropped.
240 if p.tok == .key_module {
241 p.next()
242 module_stmt := ast.ModuleStmt{
243 name: p.expect_name()
244 }
245 p.expect(.semicolon)
246 top_stmts << module_stmt
247 mod = module_stmt.name
248 } else {
249 // TODO: set is_test somewhre, probably work it out in builder
250 // and pass it to parser, or in prefs. (check current v)
251 // if !p.file.name.contains('_test.') {
252 // p.error('expectng module')
253 // }
254 // NOTE: allowing no moule for now
255 // need to verify rules
256 }
257 for p.tok == .key_import {
258 import_stmt := p.import_stmt()
259 p.expect(.semicolon)
260 imports << import_stmt
261 top_stmts << import_stmt
262 }
263 p.in_top_level = true
264 // Script-mode: when in `main` module, top-level statements that are not
265 // declarations are collected into a synthesized `fn main()`, allowing
266 // programs without an explicit `fn main` (matching v1 behavior).
267 mut script_stmts := []ast.Stmt{}
268 mut main_already_defined := false
269 // Once the first script statement is seen, the rest of the file is
270 // parsed in function-body context (in_top_level=false). This matches
271 // v1's "all definitions must occur before code in script mode" — a
272 // `const`/`fn`/etc. that appears after a script stmt then surfaces as
273 // a parse error rather than being silently kept as a file-scope decl.
274 mut script_started := false
275 for p.tok != .eof {
276 // `import` is only allowed in the initial import section at the
277 // top of the file. A late `import` would otherwise (in script mode)
278 // be routed through `p.stmt()` into the synthesized `fn main()` —
279 // reject it with a clear error matching v1's behavior.
280 if p.tok == .key_import {
281 p.error('`import x` can only be declared at the beginning of the file')
282 }
283 if mod == 'main' && (script_started || !p.is_top_stmt_start()) {
284 // Script-mode statements parse as if inside a function body
285 // (no top-level decl dispatch), since they will be wrapped
286 // into the synthesized `fn main()`.
287 p.in_top_level = false
288 stmt := p.stmt()
289 p.in_top_level = true
290 script_stmts << stmt
291 script_started = true
292 continue
293 }
294 top_stmt := p.top_stmt()
295 // if top_stmt is ast.Decl {
296 // decls << top_stmt
297 // }
298 if top_stmt is ast.FnDecl {
299 if !top_stmt.is_method && top_stmt.name == 'main' {
300 main_already_defined = true
301 }
302 }
303 // Script-mode: a top-level `$if cond { ... }` whose body contains
304 // runtime statements (e.g. `println(...)`) needs to be wrapped into
305 // the synthesized `fn main()` so it actually executes. Comptime $if
306 // blocks containing only declarations (e.g. `$if !macos { fn C.x() }`)
307 // stay at file scope. A `fn main` nested inside any branch counts
308 // as user-defined main and suppresses script-main synthesis.
309 if mod == 'main' && top_stmt is ast.ExprStmt {
310 inner := unwrap_comptime_expr(top_stmt.expr)
311 if inner is ast.IfExpr {
312 if comptime_if_expr_contains_fn_main(inner) {
313 main_already_defined = true
314 }
315 if !comptime_if_expr_contains_top_stmt(inner) {
316 script_stmts << top_stmt
317 script_started = true
318 continue
319 }
320 }
321 }
322 top_stmts << top_stmt
323 }
324 p.in_top_level = false
325 if script_stmts.len > 0 {
326 if main_already_defined {
327 p.error('function `main` is already defined, put your script statements inside it')
328 }
329 top_stmts << ast.FnDecl{
330 name: 'main'
331 typ: fn_type_with_return_type([]ast.Expr{}, []ast.Parameter{}, ast.empty_expr)
332 stmts: script_stmts
333 }
334 }
335 if p.pref.verbose {
336 parse_time := sw.elapsed()
337 println('scan & parse ${filename} (${p.file.line_count()} LOC): ${parse_time.milliseconds()}ms (${parse_time.microseconds()}µs)')
338 }
339 return ast.File{
340 attributes: attributes
341 mod: mod
342 name: filename
343 imports: imports
344 selector_names: p.selector_names.clone()
345 // decls: decls
346 stmts: top_stmts
347 }
348}
349
350// unwrap_comptime_expr returns the inner expression of a `$expr` wrapper,
351// or the expression itself when it is not a ComptimeExpr.
352fn unwrap_comptime_expr(expr ast.Expr) ast.Expr {
353 if expr is ast.ComptimeExpr {
354 return expr.expr
355 }
356 return expr
357}
358
359// comptime_if_expr_contains_top_stmt reports whether every branch of a
360// comptime `$if` chain contains only top-level statements (declarations,
361// directives, or nested top-level `$if`s). Returns false if any branch
362// contains a runtime CallExpr / CallOrCastExpr / AssignStmt — in script
363// mode such blocks are wrapped into the synthesized `fn main()`.
364// Ported from v1 `vlib/v/parser/parser.v::comptime_if_expr_contains_top_stmt`.
365fn comptime_if_expr_contains_top_stmt(if_expr ast.IfExpr) bool {
366 for stmt in if_expr.stmts {
367 if stmt is ast.ExprStmt {
368 inner := unwrap_comptime_expr(stmt.expr)
369 if inner is ast.IfExpr {
370 if !comptime_if_expr_contains_top_stmt(inner) {
371 return false
372 }
373 } else if inner is ast.CallExpr || inner is ast.CallOrCastExpr {
374 return false
375 }
376 } else if stmt is ast.AssignStmt {
377 return false
378 }
379 // `ast.Directive` (e.g. `#flag`, `#include`) is only valid at file
380 // scope — it does not by itself prove the branch is declaration-only,
381 // so we keep scanning the remaining stmts (and the `$else` chain
382 // below) instead of short-circuiting. A runtime stmt found later
383 // still disqualifies the branch and forces script-main wrapping.
384 }
385 if if_expr.else_expr is ast.IfExpr {
386 else_ie := if_expr.else_expr as ast.IfExpr
387 if !comptime_if_expr_contains_top_stmt(else_ie) {
388 return false
389 }
390 }
391 return true
392}
393
394// comptime_if_expr_contains_fn_main reports whether any branch of a
395// comptime `$if` chain declares `fn main`. Used to suppress synthesis
396// of a script `main` when a user-defined `main` is hidden behind a
397// platform-conditional block (e.g. `$if windows { fn main() {} }`).
398fn comptime_if_expr_contains_fn_main(if_expr ast.IfExpr) bool {
399 for stmt in if_expr.stmts {
400 if stmt is ast.FnDecl {
401 if !stmt.is_method && stmt.name == 'main' {
402 return true
403 }
404 } else if stmt is ast.ExprStmt {
405 inner := unwrap_comptime_expr(stmt.expr)
406 if inner is ast.IfExpr {
407 if comptime_if_expr_contains_fn_main(inner) {
408 return true
409 }
410 }
411 }
412 }
413 if if_expr.else_expr is ast.IfExpr {
414 else_ie := if_expr.else_expr as ast.IfExpr
415 if comptime_if_expr_contains_fn_main(else_ie) {
416 return true
417 }
418 }
419 return false
420}
421
422// is_top_stmt_start reports whether the current token can start a top-level
423// declaration. Used to detect script-mode statements (e.g. bare `println(...)`
424// in `main` module) which are wrapped into a synthesized `fn main()`.
425fn (p &Parser) is_top_stmt_start() bool {
426 return match p.tok {
427 .dollar, .hash, .key_asm, .key_const, .key_enum, .key_fn, .key_global, .key_interface,
428 .key_pub, .key_struct, .key_union, .key_type, .attribute, .lsbr {
429 true
430 }
431 else {
432 false
433 }
434 }
435}
436
437fn (mut p Parser) top_stmt() ast.Stmt {
438 match p.tok {
439 .dollar {
440 return p.comptime_stmt()
441 }
442 .hash {
443 return p.directive()
444 }
445 .key_asm {
446 return p.asm_stmt()
447 }
448 .key_const {
449 return p.const_decl(false)
450 }
451 .key_enum {
452 return p.enum_decl(false, [])
453 }
454 .key_fn {
455 return p.fn_decl(false, [])
456 }
457 .key_global {
458 return p.global_decl(false, [])
459 }
460 // NOTE: handling moved to parse_file
461 // .key_import {
462 // return p.import_stmt()
463 // }
464 .key_interface {
465 return p.interface_decl(false, [])
466 }
467 // NOTE: handling moved to parse_file
468 // .key_module {
469 // p.next()
470 // name := p.expect_name()
471 // p.expect(.semicolon)
472 // return ast.ModuleStmt{
473 // name: name
474 // }
475 // }
476 .key_pub {
477 p.next()
478 match p.tok {
479 .key_const { return p.const_decl(true) }
480 .key_enum { return p.enum_decl(true, []) }
481 .key_fn { return p.fn_decl(true, []) }
482 .key_global { return p.global_decl(true, []) }
483 .key_interface { return p.interface_decl(true, []) }
484 .key_struct, .key_union { return p.struct_decl(true, []) }
485 .key_type { return p.type_decl(true) }
486 else { p.error('not implemented: pub ${p.tok}') }
487 }
488 }
489 .key_struct, .key_union {
490 return p.struct_decl(false, [])
491 }
492 .key_type {
493 return p.type_decl(false)
494 }
495 .attribute, .lsbr {
496 return p.attribute_stmt()
497 }
498 else {
499 p.error('unknown top stmt: ${p.tok} - ${p.file.name}:${p.line}')
500 }
501 }
502}
503
504fn (mut p Parser) stmt() ast.Stmt {
505 // p.log('STMT: ${p.tok} - ${p.file.name}:${p.line}')
506 // Top-level declarations that can appear inside comptime $if blocks at
507 // file scope. Only parsed when in_top_level is set (file-level context),
508 // not inside regular function bodies where they would be invalid.
509 if p.in_top_level {
510 match p.tok {
511 .attribute, .lsbr {
512 return p.attribute_stmt()
513 }
514 .key_const {
515 return p.const_decl(false)
516 }
517 .key_enum {
518 return p.enum_decl(false, [])
519 }
520 .key_fn {
521 // `fn name(...)` or `fn C.name(...)` is a declaration;
522 // `fn (recv Type) method(...)` is a method declaration.
523 next := p.peek()
524 if next == .name || next == .lpar {
525 return p.fn_decl(false, [])
526 }
527 }
528 .key_global {
529 return p.global_decl(false, [])
530 }
531 .key_interface {
532 return p.interface_decl(false, [])
533 }
534 .key_pub {
535 p.next()
536 match p.tok {
537 .key_const { return p.const_decl(true) }
538 .key_enum { return p.enum_decl(true, []) }
539 .key_fn { return p.fn_decl(true, []) }
540 .key_global { return p.global_decl(true, []) }
541 .key_interface { return p.interface_decl(true, []) }
542 .key_struct, .key_union { return p.struct_decl(true, []) }
543 .key_type { return p.type_decl(true) }
544 else { p.error('not implemented: pub ${p.tok}') }
545 }
546 }
547 .key_struct, .key_union {
548 return p.struct_decl(false, [])
549 }
550 .key_type {
551 return p.type_decl(false)
552 }
553 else {}
554 }
555 }
556 match p.tok {
557 .dollar {
558 return p.comptime_stmt()
559 }
560 .hash {
561 return p.directive()
562 }
563 .key_asm {
564 return p.asm_stmt()
565 }
566 .key_assert {
567 p.next()
568 expr := p.expr(.lowest)
569 stmt := ast.AssertStmt{
570 expr: expr
571 extra: if p.tok == .comma {
572 p.next()
573 p.expr(.lowest)
574 } else {
575 ast.empty_expr
576 }
577 }
578 p.expect_semi()
579 return stmt
580 }
581 .key_break, .key_continue, .key_goto {
582 op := p.tok()
583 if p.tok == .name {
584 label := p.lit()
585 p.expect_semi()
586 return ast.FlowControlStmt{
587 op: op
588 label: label
589 }
590 } else {
591 p.expect_semi()
592 return ast.FlowControlStmt{
593 op: op
594 }
595 }
596 }
597 .key_defer {
598 p.next()
599 mut defer_mode := ast.DeferMode.scoped
600 if p.tok == .lpar {
601 p.next()
602 if p.tok == .key_fn {
603 defer_mode = .function
604 p.next()
605 } else {
606 mode := p.expect_name()
607 p.error('unknown `defer` mode: `${mode}`')
608 }
609 p.expect(.rpar)
610 }
611 stmts := p.block()
612 p.expect(.semicolon)
613 return ast.DeferStmt{
614 mode: defer_mode
615 stmts: stmts
616 }
617 }
618 .key_for {
619 return p.for_stmt()
620 }
621 .key_import {
622 import_stmt := p.import_stmt()
623 p.expect(.semicolon)
624 return import_stmt
625 }
626 .key_return {
627 return p.return_stmt()
628 }
629 .lcbr {
630 // anonymous / scoped block `{ a := 1 }`
631 stmts := p.block()
632 p.expect_semi()
633 return ast.BlockStmt{
634 stmts: stmts
635 }
636 }
637 .semicolon {
638 // empty statement (e.g., auto-inserted semicolon after asm block)
639 p.next()
640 return ast.empty_stmt
641 }
642 else {
643 expr := p.expr(.lowest)
644 // label `start:`
645 if p.tok == .colon {
646 mut name := ''
647 if expr is ast.Ident {
648 name = expr.name
649 } else {
650 p.error('expecting identifier')
651 }
652 p.next()
653 return ast.LabelStmt{
654 name: name
655 stmt: if p.tok == .key_for { ast.Stmt(p.for_stmt()) } else { ast.empty_stmt }
656 }
657 }
658 return p.complete_simple_stmt(expr, false)
659 }
660 }
661
662 p.error('unknown stmt: ${p.tok}')
663}
664
665fn (mut p Parser) attribute_stmt() ast.Stmt {
666 // NOTE: could also return AttributeStmt{attributes: attributes, stmt: stmt}
667 attributes := p.attributes()
668 mut is_pub := false
669 if p.tok == .key_pub {
670 p.next()
671 is_pub = true
672 }
673 match p.tok {
674 .key_enum {
675 return p.enum_decl(is_pub, attributes)
676 }
677 .key_fn {
678 return p.fn_decl(is_pub, attributes)
679 }
680 .key_global {
681 return p.global_decl(is_pub, attributes)
682 }
683 .key_interface {
684 return p.interface_decl(is_pub, attributes)
685 }
686 .key_struct, .key_union {
687 return p.struct_decl(is_pub, attributes)
688 }
689 else {
690 return attributes
691 }
692 }
693}
694
695@[inline]
696fn (mut p Parser) simple_stmt() ast.Stmt {
697 expr := p.expr(.lowest)
698 return p.complete_simple_stmt(expr, false)
699}
700
701fn (mut p Parser) return_stmt() ast.ReturnStmt {
702 return_pos := p.pos
703 p.next()
704 if p.return_has_indented_expr_continuation(return_pos) {
705 p.next()
706 }
707 if p.tok in [.semicolon, .rcbr] {
708 if p.tok == .semicolon {
709 p.next()
710 }
711 return ast.ReturnStmt{}
712 }
713 exp_pt := p.exp_pt
714 p.exp_pt = false
715 return_exprs := p.expr_list()
716 p.exp_pt = exp_pt
717 rs := ast.ReturnStmt{
718 exprs: return_exprs
719 }
720 p.expect_semi()
721 return rs
722}
723
724fn token_can_start_expr(tok token.Token) bool {
725 return
726 tok in [.name, .number, .string, .char, .key_true, .key_false, .key_nil, .key_none, .key_if, .key_match, .key_lock, .key_rlock, .key_select, .key_sizeof, .key_typeof, .key_offsetof, .key_unsafe, .lpar, .lsbr, .lcbr, .dollar]
727 || tok.is_prefix()
728}
729
730fn (mut p Parser) return_has_indented_expr_continuation(return_pos token.Pos) bool {
731 if p.tok != .semicolon {
732 return false
733 }
734 next_tok := p.peek()
735 if !token_can_start_expr(next_tok) {
736 return false
737 }
738 return_position := p.file.position(return_pos)
739 next_line, next_column := p.file.find_line_and_column(p.scanner.pos)
740 return next_line > return_position.line && next_column > return_position.column
741}
742
743fn (mut p Parser) complete_simple_stmt(expr ast.Expr, expecting_semi bool) ast.Stmt {
744 // stand alone expression in a statement list
745 // eg: `if x == 1 {`, `x++`, `mut x := 1`, `a,`b := 1,2`
746 // multi assign from match/if `a, b := if x == 1 { 1,2 } else { 3,4 }
747 if p.tok == .comma {
748 tuple_pos := p.pos
749 p.next()
750 // a little extra code, but also a little more efficient
751 mut exprs := [expr]
752 exprs << p.expr(.lowest)
753 for p.tok == .comma {
754 p.next()
755 exprs << p.expr(.lowest)
756 }
757 // TODO: can we get rid of this dupe code?
758 if p.tok.is_assignment() {
759 assign_stmt := p.assign_stmt(exprs)
760 // TODO: best way? decide if we will force them
761 // NOTE: allow multiple exprs on single line without `;` should be removed
762 if p.tok == .semicolon && !expecting_semi {
763 p.next()
764 }
765 return assign_stmt
766 }
767 // multi return values (last statement, no return keyword)
768 return ast.ExprStmt{
769 expr: ast.Expr(ast.Tuple{
770 exprs: exprs
771 pos: tuple_pos
772 })
773 }
774 } else if p.tok.is_assignment() {
775 assign_stmt := p.assign_stmt([expr])
776 // TODO: best way? decide if we will force them
777 // NOTE: allow multiple exprs on single line without `;` should be removed
778 if p.tok == .semicolon && !expecting_semi {
779 p.next()
780 }
781 return assign_stmt
782 }
783 // TODO: best way? decide if we will force them
784 // NOTE: allow multiple exprs on single line without `;` should be removed
785 if p.tok == .semicolon && !expecting_semi {
786 p.next()
787 }
788 // TODO: add check for all ExprStmt eg.
789 // if expr is ast.ArrayInitExpr {
790 // p.error('UNUSED')
791 // }
792 return ast.ExprStmt{
793 expr: expr
794 }
795}
796
797fn (mut p Parser) expr(min_bp token.BindingPower) ast.Expr {
798 // p.log('EXPR: ${p.tok} - ${p.line}')
799 if p.tok == .xor {
800 lifetime_pos := p.pos
801 p.next()
802 return p.finish_expr(ast.Expr(ast.LifetimeExpr{
803 name: p.expect_name()
804 pos: lifetime_pos
805 }), min_bp)
806 }
807 if p.tok == .mul {
808 prefix_pos := p.pos
809 p.next()
810 mut prefix_rhs := ast.empty_expr
811 if p.tok == .name {
812 prefix_rhs = p.ident_or_named_type()
813 if p.tok == .lcbr && p.can_parse_init_expr(prefix_rhs) {
814 prefix_rhs = p.assoc_or_init_expr(prefix_rhs)
815 }
816 prefix_rhs = p.finish_expr(prefix_rhs, .highest)
817 } else {
818 prefix_rhs = p.expr(.highest)
819 }
820 return p.finish_expr(ast.Expr(ast.PrefixExpr{
821 pos: prefix_pos
822 op: .mul
823 expr: prefix_rhs
824 }), min_bp)
825 }
826 mut lhs := ast.empty_expr
827 match p.tok {
828 .key_false, .key_true, .number {
829 lhs = ast.Expr(ast.BasicLiteral{
830 kind: p.tok
831 value: p.lit()
832 pos: p.pos
833 })
834 }
835 .char {
836 lhs = ast.Expr(ast.BasicLiteral{
837 kind: .char
838 value: p.lit()
839 pos: p.pos
840 })
841 }
842 .string {
843 lhs = p.string_literal(.v)
844 }
845 .key_fn {
846 fn_pos := p.pos
847 p.next()
848 // TODO: closure variable capture syntax is the same as generic param syntax. IMO This should change.
849 // If we have both a capture list and generic params, we can always assume that the capture list comes
850 // first. However if we only have one or the other we will need to work out what we have. the only way
851 // to currently do this is to check for an ident where the name's first char is a capital, this is not
852 // great at all, and would be the only place in the parser where a capital letter is relied upon, or even
853 // the name at all is relied upon. imo this is context the parser should not need, and we should either
854 // change the variable capture syntax, or move the position of the capture list, for example:
855 // change syntax: `fn <var_a, var_b> [T] () { ... }`, move position: `fn [T] () { ... } [var_a, var_b]`
856 // personally I think `fn <var_a, var_b> [T] () { ... }` is a great option.
857 mut captured_vars := []ast.Expr{}
858 mut generic_params := []ast.Expr{}
859 if p.tok == .lsbr {
860 p.next()
861 captured_vars = p.expr_list()
862 p.expect(.rsbr)
863 }
864 // we have generic params after capture list
865 if p.tok == .lsbr {
866 generic_params = p.generic_list()
867 }
868 // if we had one or the other determine what it is
869 else if captured_vars.len > 0 {
870 expr_0 := captured_vars[0]
871 if expr_0 is ast.Ident {
872 if u8(expr_0.name[0]).is_capital() {
873 generic_params = captured_vars.clone()
874 captured_vars = []
875 }
876 }
877 }
878 mut typ := p.fn_type()
879 // if we had generic params update the fn type / sig (params are stored here)
880 if generic_params.len > 0 {
881 typ = ast.FnType{
882 ...typ
883 generic_params: generic_params
884 }
885 }
886 if p.exp_pt && (p.tok != .lcbr || p.exp_lcbr) {
887 return ast.Type(typ)
888 }
889 lhs = ast.Expr(ast.FnLiteral{
890 typ: typ
891 stmts: p.block()
892 captured_vars: captured_vars
893 pos: fn_pos
894 })
895 }
896 .key_if {
897 lhs = ast.Expr(p.if_expr(false))
898 }
899 // NOTE: I would much rather dump, likely, and unlikely were
900 // some type of comptime fn/macro's which come as part of the
901 // v stdlib, as apposed to being language keywords.
902 // TODO: should these be replaced with something
903 // like `CallExpr{lhs: KeywordOperator}` ?
904 .key_isreftype, .key_sizeof, .key_typeof {
905 kw_pos := p.pos
906 op := p.tok()
907 // p.expect(.lpar)
908 if p.tok == .lpar {
909 p.next()
910 lhs = ast.Expr(ast.KeywordOperator{
911 op: op
912 exprs: []ast.Expr{len: 1, init: p.expr_or_type(.lowest)}
913 pos: kw_pos
914 })
915 p.expect(.rpar)
916 } else {
917 // TODO: is this the best way to handle this? (prob not :D)
918 // this allows `typeof[type]()` to work
919 lhs = ast.Expr(ident_with_name(kw_pos, op.str()))
920 }
921 }
922 .key_dump, .key_likely, .key_unlikely {
923 kw_pos := p.pos
924 op := p.tok()
925 p.expect(.lpar)
926 lhs = ast.Expr(ast.KeywordOperator{
927 op: op
928 exprs: []ast.Expr{len: 1, init: p.expr(.lowest)}
929 pos: kw_pos
930 })
931 p.expect(.rpar)
932 }
933 .key_offsetof {
934 kw_pos := p.pos
935 op := p.tok()
936 p.expect(.lpar)
937 expr := p.expr(.lowest)
938 p.expect(.comma)
939 lhs = ast.Expr(ast.KeywordOperator{
940 op: op
941 exprs: [expr, p.expr(.lowest)]
942 pos: kw_pos
943 })
944 p.expect(.rpar)
945 }
946 .key_go, .key_spawn {
947 kw_pos := p.pos
948 op := p.tok()
949 lhs = ast.Expr(ast.KeywordOperator{
950 op: op
951 exprs: []ast.Expr{len: 1, init: p.expr(.lowest)}
952 pos: kw_pos
953 })
954 }
955 .key_nil {
956 p.next()
957 return ast.Type(ast.NilType{})
958 }
959 .key_none {
960 p.next()
961 return ast.Type(ast.NoneType{})
962 }
963 .key_lock, .key_rlock {
964 lock_pos := p.pos
965 mut kind := p.tok()
966 // `lock { stmts... }`
967 if p.tok == .lcbr {
968 return ast.LockExpr{
969 stmts: p.block()
970 pos: lock_pos
971 }
972 }
973 mut lock_exprs := []ast.Expr{}
974 mut rlock_exprs := []ast.Expr{}
975 exp_lcbr := p.exp_lcbr
976 p.exp_lcbr = true
977 // `r?lock exprs { stmts... }`
978 // NOTE/TODO: `lock a; rlock c; lock b; rlock d {` will become
979 // `lock a, b; rlock c, d` unlike in the main parser, where it remains
980 // the same. this may need to be changed to match the current behaviour.
981 for p.tok != .lcbr {
982 if kind == .key_lock {
983 lock_exprs << p.expr_list()
984 } else if kind == .key_rlock {
985 rlock_exprs << p.expr_list()
986 }
987 if p.tok != .semicolon {
988 break
989 }
990 p.next()
991 kind = p.tok()
992 }
993 p.exp_lcbr = exp_lcbr
994 return ast.LockExpr{
995 lock_exprs: lock_exprs
996 rlock_exprs: rlock_exprs
997 stmts: p.block()
998 pos: lock_pos
999 }
1000 }
1001 .key_struct {
1002 p.next()
1003 typ := ast.Keyword{
1004 tok: .key_struct
1005 }
1006 if p.exp_pt && p.tok != .lcbr {
1007 return typ
1008 }
1009 lhs = p.assoc_or_init_expr(typ)
1010 }
1011 .key_select {
1012 if p.peek() == .lpar {
1013 // `select(...)` - treat as function call, not select statement
1014 lhs = ast.Expr(ident_with_name(p.pos, 'select'))
1015 p.next()
1016 } else {
1017 p.next()
1018 p.expect(.lcbr)
1019 se := p.select_expr()
1020 p.expect(.rcbr)
1021 return se
1022 }
1023 }
1024 .dollar {
1025 p.next()
1026 return p.comptime_expr()
1027 }
1028 // enum value `.green`
1029 // TODO: use ast.EnumValue{} or stick with SelectorExpr?
1030 // .dot {}
1031 .lpar {
1032 paren_pos := p.pos
1033 p.next()
1034 exp_lcbr := p.exp_lcbr
1035 p.exp_lcbr = false
1036 mut inner_expr := ast.empty_expr
1037 if p.tok.is_prefix() {
1038 prefix_pos := p.pos
1039 prefix_op := p.tok()
1040 prefix_rhs := p.expr(.highest)
1041 inner_prefix := ast.Expr(ast.PrefixExpr{
1042 pos: prefix_pos
1043 op: prefix_op
1044 expr: prefix_rhs
1045 })
1046 inner_expr = p.finish_expr(inner_prefix, .lowest)
1047 } else {
1048 inner_expr = p.expr(.lowest)
1049 }
1050 // p.log('ast.ParenExpr:')
1051 lhs = ast.Expr(ast.ParenExpr{
1052 expr: inner_expr
1053 pos: paren_pos
1054 })
1055 p.exp_lcbr = exp_lcbr
1056 p.expect(.rpar)
1057 }
1058 .lcbr {
1059 // if p.exp_lcbr {
1060 // p.error('unexpected `{`')
1061 // }
1062 // shorthand map / struct init
1063 // NOTE: config syntax handled in `p.fn_arguments()`
1064 // which afaik is the only place it's supported
1065 // lhs = p.struct_init()
1066 p.next()
1067 if p.tok == .ellipsis {
1068 p.error('this assoc syntax is no longer supported `{...`. You must explicitly specify a type `MyType{...`')
1069 }
1070 // empty map init `{}`
1071 if p.tok == .rcbr {
1072 p.next()
1073 return ast.MapInitExpr{
1074 pos: p.pos
1075 }
1076 }
1077 // map init
1078 pos := p.pos
1079 mut keys := []ast.Expr{}
1080 mut vals := []ast.Expr{}
1081 for p.tok != .rcbr {
1082 key := if p.tok == .dot {
1083 dot_pos := p.pos
1084 p.next()
1085 rhs_pos := p.pos
1086 rhs_name := p.expect_name_or_keyword()
1087 selector_expr_with_rhs_name(ast.empty_expr, rhs_pos, rhs_name, dot_pos)
1088 } else {
1089 p.expr(.lowest)
1090 }
1091 if key_infix := expr_infix_payload_checked(key) {
1092 if key_infix.op == .pipe {
1093 p.error('this assoc syntax is no longer supported `{MyType|`. Use `MyType{...` instead')
1094 }
1095 }
1096 keys << key
1097 p.expect(.colon)
1098 old_stop_line_leading_dot := p.stop_line_leading_dot
1099 p.stop_line_leading_dot = true
1100 val := p.expr(.lowest)
1101 p.stop_line_leading_dot = old_stop_line_leading_dot
1102 vals << val
1103 // if p.tok == .comma {
1104 if p.tok in [.comma, .semicolon] {
1105 p.next()
1106 }
1107 }
1108 p.next()
1109 lhs = ast.Expr(ast.MapInitExpr{
1110 keys: keys
1111 vals: vals
1112 pos: pos
1113 })
1114 }
1115 .lsbr {
1116 // ArrayInitExpr: `[1,2,3,4]` | `[]type{}` | `[]type{len: 4}` | `[2]type{init: 0}` etc...
1117 // ArrayInitExpr->IndexExpr: `[1,2,3,4][0]` handled here for reasons listed in comment below
1118 // ArrayType in CastExpr: `[]type` in `[]type(x)` set lhs to type, cast handled later
1119 pos := p.pos
1120 p.next()
1121 // Spread syntax `[...base, e1, e2]` — parsed before regular elements.
1122 mut update_expr := ast.empty_expr
1123 if p.tok == .ellipsis {
1124 p.next()
1125 update_expr = p.expr(.lowest)
1126 if p.tok == .comma || p.tok == .semicolon {
1127 p.next()
1128 }
1129 }
1130 // exprs in first `[]` eg. (`1,2,3,4` in `[1,2,3,4]) | (`2` in `[2]int{}`)
1131 mut exprs := []ast.Expr{}
1132 for p.tok != .rsbr {
1133 exprs << p.expr(.lowest)
1134 if p.tok == .comma {
1135 p.next()
1136 }
1137 // `[
1138 // 1,
1139 // 2
1140 // ]`
1141 // In V, trailing commas are optional in array literals.
1142 // Semicolons (auto-inserted newlines) act as separators.
1143 else if p.tok == .semicolon {
1144 p.next()
1145 }
1146 }
1147 p.next()
1148 // (`[2]type{}` | `[2][2]type{}` | `[2][]type{}`) | `[1,2,3,4][0]` | `[2]type`
1149 // NOTE: it's tricky to differentiate between a fixed array of fixed array(s)
1150 // and an index directly after initialization. for example, the following:
1151 // a) fixed array of fixed array(s): `[2][2]type{}` | `[2][2][2]type{}`
1152 // b) index directly after init: `[1][0]` | `[x][2][2]` <- vs (a) above
1153 // only in this case collect exprs in following `[x][x]` then decide what to do
1154 if exprs.len > 0 && p.tok == .lsbr {
1155 // collect exprs in all the following `[x][x]`
1156 mut exprs_arr := [exprs]
1157 // NOTE: checking line here for this case:
1158 // `pub const const_a = ['a', 'b', 'c', 'd']`
1159 // '[attribute_a; attribute_b]''
1160 for p.tok == .lsbr {
1161 p.next()
1162 mut exprs2 := []ast.Expr{}
1163 for p.tok != .rsbr {
1164 index_expr := p.expr(.lowest)
1165 exprs2 << p.range_expr(index_expr)
1166 if p.tok == .comma {
1167 p.next()
1168 }
1169 }
1170 p.next()
1171 exprs_arr << exprs2
1172 }
1173 // (`[2]type{}` | `[2][]type{}` | `[2]&type{init: Foo{}}`) | `[2]type`
1174 if p.tok in [.amp, .key_struct, .name] {
1175 elem_type := p.expect_type()
1176 // Build nested type from innermost to outermost dimension.
1177 // For `[3][3]int`, exprs_arr = [[3], [3]], and we iterate
1178 // from the last (innermost) to first (outermost), nesting
1179 // each dimension around the previous one.
1180 mut inner_type := ast.Expr(elem_type)
1181 for i := exprs_arr.len - 1; i >= 0; i-- {
1182 exprs2 := exprs_arr[i]
1183 if exprs2.len == 0 {
1184 inner_type = ast.Expr(ast.Type(ast.ArrayType{
1185 elem_type: inner_type
1186 }))
1187 } else if exprs2.len == 1 {
1188 inner_type = ast.Expr(ast.Type(ast.ArrayFixedType{
1189 elem_type: inner_type
1190 len: exprs2[0]
1191 }))
1192 } else {
1193 // TODO: use same error message as typ() `expect(.rsbr)`
1194 p.error('expecting single expr for fixed array length')
1195 }
1196 }
1197 lhs = inner_type
1198 // `[2]type{}`
1199 if p.tok == .lcbr && !p.exp_lcbr {
1200 p.next()
1201 mut init := ast.empty_expr
1202 if p.tok != .rcbr {
1203 key := p.expect_name()
1204 p.expect(.colon)
1205 match key {
1206 'init' { init = p.expr(.lowest) }
1207 else { p.error('expecting `init`, got `${key}`') }
1208 }
1209 }
1210 p.next()
1211 lhs = array_init_expr_with_parts(lhs, []ast.Expr{}, init, ast.empty_expr,
1212 ast.empty_expr, pos)
1213 }
1214 // `[2]type`
1215 // casts are completed in expr loop
1216 else if p.tok != .lpar {
1217 if !p.exp_pt {
1218 p.error('unexpected type')
1219 }
1220 // no need to chain here
1221 return lhs
1222 }
1223 }
1224 // `[1][0]` | `[1,2,3,4][0]` | `[[1,2,3,4]][0][1]` <-- index directly after init
1225 else {
1226 lhs = array_init_expr_with_parts(ast.empty_expr, exprs, ast.empty_expr,
1227 ast.empty_expr, ast.empty_expr, pos)
1228 if update_expr !is ast.EmptyExpr {
1229 mut arr_init := lhs as ast.ArrayInitExpr
1230 arr_init.update_expr = update_expr
1231 lhs = ast.Expr(arr_init)
1232 }
1233 for i := 1; i < exprs_arr.len; i++ {
1234 exprs2 := exprs_arr[i]
1235 if exprs2.len != 1 {
1236 // TODO: use same error message as IndexExpr in expr loop `expect(.rsbr)`
1237 p.error('invalid index expr')
1238 }
1239 lhs = index_expr_with_parts(lhs, exprs2[0], false, pos)
1240 }
1241 }
1242 }
1243 // (`[n]type{}` | `[n]type`) - single-dimensional fixed array with one size expr
1244 else if exprs.len == 1 && p.tok in [.amp, .key_struct, .name] {
1245 lhs = ast.Expr(ast.Type(ast.ArrayFixedType{
1246 elem_type: p.expect_type()
1247 len: exprs[0]
1248 }))
1249 // `[n]type{}`
1250 if p.tok == .lcbr && p.can_parse_init_expr(lhs) {
1251 p.next()
1252 mut init := ast.empty_expr
1253 if p.tok != .rcbr {
1254 key := p.expect_name()
1255 p.expect(.colon)
1256 match key {
1257 'init' { init = p.expr(.lowest) }
1258 else { p.error('expecting `init`, got `${key}`') }
1259 }
1260 }
1261 p.next()
1262 lhs = array_init_expr_with_parts(lhs, []ast.Expr{}, init, ast.empty_expr,
1263 ast.empty_expr, pos)
1264 }
1265 // `[n]type`
1266 // casts are completed in expr loop
1267 else if p.tok != .lpar {
1268 if !p.exp_pt {
1269 p.error('unexpected type')
1270 }
1271 // no need to chain here
1272 return lhs
1273 }
1274 }
1275 // (`[]type{}` | `[][]type{}` | `[]&type{len: 2}`) | `[]type`
1276 else if p.tok in [.amp, .key_struct, .lsbr, .name] {
1277 lhs = ast.Expr(ast.Type(ast.ArrayType{
1278 elem_type: p.expect_type()
1279 }))
1280 // `[]type{}`
1281 if p.tok == .lcbr && !p.exp_lcbr {
1282 p.next()
1283 mut cap, mut init, mut len := ast.empty_expr, ast.empty_expr, ast.empty_expr
1284 for p.tok != .rcbr {
1285 key := p.expect_name()
1286 p.expect(.colon)
1287 match key {
1288 'cap' { cap = p.expr(.lowest) }
1289 'init' { init = p.expr(.lowest) }
1290 'len' { len = p.expr(.lowest) }
1291 else { p.error('expecting one of `cap, init, len`, got `${key}`') }
1292 }
1293
1294 if p.tok == .comma {
1295 p.next()
1296 }
1297 }
1298 p.next()
1299 lhs = array_init_expr_with_parts(lhs, []ast.Expr{}, init, cap, len, pos)
1300 }
1301 // `[]type`
1302 // casts are completed in expr loop
1303 else if p.tok != .lpar {
1304 if !p.exp_pt {
1305 p.error('unexpected type')
1306 }
1307 // no need to chain here
1308 return lhs
1309 }
1310 }
1311 // `[1,2,3,4]!`
1312 else if p.tok == .not {
1313 if exprs.len == 0 {
1314 p.error('expecting at least one initialization expr: `[expr, expr2]!`')
1315 }
1316 p.next()
1317 lhs = array_init_expr_with_parts(ast.empty_expr, exprs, ast.empty_expr,
1318 ast.empty_expr, ast.Expr(ast.PostfixExpr{
1319 op: .not
1320 expr: ast.empty_expr
1321 pos: pos
1322 }), pos)
1323 // `[]` | `[1,2,3,4]`
1324 } else {
1325 lhs = array_init_expr_with_parts(ast.empty_expr, exprs, ast.empty_expr,
1326 ast.empty_expr, ast.empty_expr, pos)
1327 if update_expr !is ast.EmptyExpr {
1328 mut arr_init := lhs as ast.ArrayInitExpr
1329 arr_init.update_expr = update_expr
1330 lhs = ast.Expr(arr_init)
1331 }
1332 }
1333 }
1334 .key_match {
1335 match_pos := p.pos
1336 p.next()
1337 mut exp_lcbr := p.exp_lcbr
1338 p.exp_lcbr = true
1339 expr := p.expr(.lowest)
1340 p.exp_lcbr = exp_lcbr
1341 p.expect(.lcbr)
1342 mut branches := []ast.MatchBranch{}
1343 for p.tok != .rcbr {
1344 exp_lcbr = p.exp_lcbr
1345 branch_pos := p.pos
1346 p.exp_lcbr = true
1347 first_cond_expr := p.expr_or_type(.lowest)
1348 mut cond := [p.range_expr(first_cond_expr)]
1349 for p.tok == .comma {
1350 p.next()
1351 next_cond_expr := p.expr_or_type(.lowest)
1352 cond << p.range_expr(next_cond_expr)
1353 }
1354 p.exp_lcbr = exp_lcbr
1355 branches << ast.MatchBranch{
1356 cond: cond
1357 stmts: p.block()
1358 pos: branch_pos
1359 }
1360 p.expect_semi()
1361 if p.tok == .key_else {
1362 p.next()
1363 branches << ast.MatchBranch{
1364 stmts: p.block()
1365 pos: branch_pos
1366 }
1367 p.expect_semi()
1368 }
1369 }
1370 // rcbr
1371 p.next()
1372 lhs = ast.Expr(ast.MatchExpr{
1373 expr: expr
1374 branches: branches
1375 pos: match_pos
1376 })
1377 }
1378 .key_atomic, .key_mut, .key_shared, .key_static, .key_volatile {
1379 mod_pos := p.pos
1380 mod_kind := p.tok()
1381 mod_expr := p.expr(.highest)
1382 lhs = modifier_expr_with_expr(mod_kind, mod_expr, mod_pos)
1383 }
1384 .key_unsafe {
1385 // p.log('ast.UnsafeExpr')
1386 unsafe_pos := p.pos
1387 p.next()
1388 // exp_lcbr := p.exp_lcbr
1389 // p.exp_lcbr = false
1390 lhs = ast.Expr(ast.UnsafeExpr{
1391 stmts: p.block()
1392 pos: unsafe_pos
1393 })
1394 // p.exp_lcbr = exp_lcbr
1395 }
1396 .name {
1397 sql_pos := p.pos
1398 lit := p.lit
1399 lhs = p.ident_or_named_type()
1400 // `sql x {}` otherwise ident named `sql`
1401 if lit == 'sql' && p.tok == .name {
1402 exp_lcbr := p.exp_lcbr
1403 p.exp_lcbr = true
1404 expr := p.expr(.lowest)
1405 p.exp_lcbr = exp_lcbr
1406 table_name, is_count, is_create := p.skip_sql_block_metadata()
1407 lhs = ast.Expr(ast.SqlExpr{
1408 expr: expr
1409 table_name: table_name
1410 is_count: is_count
1411 is_create: is_create
1412 pos: sql_pos
1413 })
1414 }
1415 // raw/c/js string: `r'hello'`
1416 else if p.tok == .string {
1417 lhs = p.string_literal(ast.StringLiteralKind.from_string_tinyv(lit))
1418 }
1419 // `ident{}`
1420 else if p.tok == .lcbr && p.can_parse_init_expr(lhs) {
1421 // TODO: move inits to expr loop? currently just handled where needed
1422 // since this is not very many places. consider if it should be moved
1423 // TODO: consider the following (tricky to parse)
1424 // `if err == IError(Eof{}) {`
1425 // `if Foo{} == Foo{} {`
1426 lhs = p.assoc_or_init_expr(lhs)
1427 }
1428 }
1429 // native optionals `x := ?mod_a.StructA{}`
1430 // could also simply be handled by `Token.is_prefix()` below
1431 .question {
1432 lhs = p.expect_type()
1433 // only handle where actually needed instead of expr loop
1434 // I may change my mind, however for now this seems best
1435 if p.tok == .lcbr && p.can_parse_init_expr(lhs) {
1436 lhs = p.assoc_or_init_expr(lhs)
1437 } else if !p.exp_pt && p.tok != .lpar {
1438 p.error('unexpected type')
1439 }
1440 }
1441 // selector handled in expr chaining loop below
1442 // range handled in `p.range_expr()`
1443 .dot, .dotdot, .ellipsis {}
1444 else {
1445 if p.tok.is_prefix() {
1446 prefix_pos := p.pos
1447 prefix_op := p.tok()
1448 prefix_expr := p.prefix_rhs_expr(prefix_op)
1449 lhs = ast.Expr(ast.PrefixExpr{
1450 pos: prefix_pos
1451 op: prefix_op
1452 expr: prefix_expr
1453 })
1454 } else {
1455 p.error('expr: unexpected token `${p.tok}`')
1456 }
1457 }
1458 }
1459
1460 return p.finish_expr(lhs, min_bp)
1461}
1462
1463fn (mut p Parser) prefix_rhs_expr(prefix_op token.Token) ast.Expr {
1464 if prefix_op == .not && p.tok == .name {
1465 mut rhs := p.ident_or_named_type()
1466 if p.tok == .lcbr && p.can_parse_init_expr(rhs) {
1467 rhs = p.assoc_or_init_expr(rhs)
1468 }
1469 return p.finish_expr(rhs, .highest)
1470 }
1471 return p.expr(.highest)
1472}
1473
1474fn expr_looks_like_type_init(expr ast.Expr) bool {
1475 match expr {
1476 ast.GenericArgs {
1477 return expr_looks_like_type_init(expr.lhs)
1478 }
1479 ast.Ident {
1480 return expr.name.len > 0 && u8(expr.name[0]).is_capital()
1481 }
1482 ast.SelectorExpr {
1483 return expr.rhs.name.len > 0 && u8(expr.rhs.name[0]).is_capital()
1484 }
1485 ast.Type {
1486 return true
1487 }
1488 else {
1489 return false
1490 }
1491 }
1492}
1493
1494fn (mut p Parser) can_parse_init_expr(expr ast.Expr) bool {
1495 return !p.exp_lcbr
1496 || (p.allow_init_in_exp_lcbr && expr_looks_like_type_init(expr)
1497 && p.peek_allows_init_in_exp_lcbr())
1498}
1499
1500fn (mut p Parser) peek_allows_init_in_exp_lcbr() bool {
1501 next_tok := p.peek()
1502 if next_tok == .rcbr || next_tok == .ellipsis {
1503 return true
1504 }
1505 if next_tok != .name && !next_tok.is_keyword() {
1506 return false
1507 }
1508 mut offset := p.scanner.offset
1509 for offset < p.scanner.src.len {
1510 ch := p.scanner.src[offset]
1511 if ch in [` `, `\t`, `\r`, `\n`] {
1512 offset++
1513 continue
1514 }
1515 return ch == `:` && (offset + 1 >= p.scanner.src.len || p.scanner.src[offset + 1] != `=`)
1516 }
1517 return false
1518}
1519
1520fn (mut p Parser) finish_expr(input_lhs ast.Expr, min_bp token.BindingPower) ast.Expr {
1521 mut lhs := input_lhs
1522 mut lhs_name := ''
1523 if lhs is ast.Ident {
1524 lhs_name = lhs.name
1525 } else if lhs is ast.SelectorExpr {
1526 lhs_name = p.selector_names[lhs.pos.id] or { '' }
1527 }
1528 for {
1529 if p.tok == .semicolon && p.peek() == .dot {
1530 p.next()
1531 continue
1532 }
1533 if p.tok == .dot && p.stop_line_leading_dot && p.token_starts_after_line_break() {
1534 break
1535 }
1536 if p.tok == .key_as {
1537 p.next()
1538 lhs = ast.Expr(ast.AsCastExpr{
1539 expr: lhs
1540 typ: p.expect_type()
1541 })
1542 lhs_name = ''
1543 } else if p.tok == .lpar {
1544 pos := p.pos
1545 exp_lcbr := p.exp_lcbr
1546 p.exp_lcbr = false
1547 args := p.fn_arguments()
1548 p.exp_lcbr = exp_lcbr
1549 if p.tok in [.not, .question] {
1550 lhs = call_expr_with_lhs(lhs, args, pos)
1551 } else if args.len == 1 {
1552 if lhs is ast.Type {
1553 lhs_type := lhs as ast.Type
1554 lhs = cast_expr_with_type(ast.Expr(lhs_type), args[0], pos)
1555 } else {
1556 lhs = call_or_cast_expr_with_lhs(lhs, args[0], pos)
1557 }
1558 } else {
1559 lhs = call_expr_with_lhs(lhs, args, pos)
1560 }
1561 lhs_name = ''
1562 } else if p.tok in [.hash, .lsbr] {
1563 idx_pos := p.pos
1564 if p.tok == .hash {
1565 p.next()
1566 p.expect(.lsbr)
1567 gated_expr := p.expr(.lowest)
1568 lhs = index_expr_with_parts(lhs, p.range_expr(gated_expr), true, idx_pos)
1569 p.expect(.rsbr)
1570 } else {
1571 p.next()
1572 first_index_expr := p.expr_or_type(.lowest)
1573 expr := p.range_expr(first_index_expr)
1574 mut exprs := [expr]
1575 for p.tok == .comma {
1576 p.next()
1577 exprs << p.expr_or_type(.lowest)
1578 }
1579 p.expect(.rsbr)
1580 if p.tok == .lcbr && !p.exp_lcbr {
1581 lhs = p.assoc_or_init_expr(ast.GenericArgs{
1582 lhs: lhs
1583 args: exprs
1584 pos: idx_pos
1585 })
1586 } else if p.tok == .lpar {
1587 if exprs.len > 1 || expr is ast.GenericArgs || expr is ast.LifetimeExpr {
1588 lhs = ast.Expr(ast.GenericArgs{
1589 lhs: lhs
1590 args: exprs
1591 pos: idx_pos
1592 })
1593 } else if expr is ast.Ident || expr is ast.SelectorExpr {
1594 lhs = ast.Expr(ast.GenericArgOrIndexExpr{
1595 lhs: lhs
1596 expr: expr
1597 pos: idx_pos
1598 })
1599 } else if expr is ast.Type {
1600 lhs = ast.Expr(ast.GenericArgs{
1601 lhs: lhs
1602 args: exprs
1603 pos: idx_pos
1604 })
1605 } else {
1606 lhs = index_expr_with_parts(lhs, expr, false, idx_pos)
1607 }
1608 } else {
1609 if exprs.len > 1 || (p.exp_pt && (expr is ast.GenericArgs
1610 || expr is ast.Ident || expr is ast.SelectorExpr
1611 || expr is ast.LifetimeExpr)) {
1612 lhs = ast.Expr(ast.GenericArgs{
1613 lhs: lhs
1614 args: exprs
1615 pos: idx_pos
1616 })
1617 } else {
1618 lhs = index_expr_with_parts(lhs, expr, false, idx_pos)
1619 }
1620 }
1621 }
1622 lhs_name = ''
1623 } else if p.tok == .dot {
1624 dot_pos := p.pos
1625 p.next()
1626 if p.tok == .dollar {
1627 p.next()
1628 inner := if p.tok == .lpar {
1629 p.next()
1630 expr := p.expr(.lowest)
1631 p.expect(.rpar)
1632 expr
1633 } else {
1634 p.expr(.lowest)
1635 }
1636 rhs_pos := p.pos
1637 rhs_name := '__comptime_selector__'
1638 full_name := if lhs_name == '' { rhs_name } else { lhs_name + '.' + rhs_name }
1639 if dot_pos.is_valid() {
1640 p.selector_names[dot_pos.id] = full_name
1641 }
1642 lhs = selector_expr_with_rhs_name(lhs, rhs_pos, rhs_name, dot_pos)
1643 // `app.$method(args)` - inner parses as CallExpr or CallOrCastExpr;
1644 // lift its args onto our SelectorExpr so the call form is preserved.
1645 if inner is ast.CallExpr {
1646 lhs = ast.Expr(ast.CallExpr{
1647 lhs: lhs
1648 args: inner.args
1649 pos: dot_pos
1650 })
1651 } else if inner is ast.CallOrCastExpr {
1652 lhs = ast.Expr(ast.CallExpr{
1653 lhs: lhs
1654 args: [inner.expr]
1655 pos: dot_pos
1656 })
1657 }
1658 lhs_name = full_name
1659 } else {
1660 rhs_pos := p.pos
1661 rhs_name := p.expect_name_or_keyword()
1662 full_name := if lhs_name == '' { rhs_name } else { lhs_name + '.' + rhs_name }
1663 if dot_pos.is_valid() {
1664 p.selector_names[dot_pos.id] = full_name
1665 }
1666 lhs = selector_expr_with_rhs_name(lhs, rhs_pos, rhs_name, dot_pos)
1667 lhs_name = full_name
1668 }
1669 } else if p.tok in [.not, .question] {
1670 postfix_pos := p.pos
1671 postfix_op := p.tok()
1672 lhs = ast.Expr(ast.PostfixExpr{
1673 op: postfix_op
1674 expr: lhs
1675 pos: postfix_pos
1676 })
1677 lhs_name = ''
1678 } else if p.tok == .key_or {
1679 pos := p.pos
1680 p.next()
1681 lhs = ast.Expr(ast.OrExpr{
1682 expr: lhs
1683 stmts: p.block()
1684 pos: pos
1685 })
1686 lhs_name = ''
1687 } else if min_bp == .lowest && p.tok in [.dotdot, .ellipsis] {
1688 range_pos := p.pos
1689 range_op := p.tok()
1690 range_end := if p.tok == .rsbr { ast.empty_expr } else { p.expr(.lowest) }
1691 return ast.RangeExpr{
1692 op: range_op
1693 start: lhs
1694 end: range_end
1695 pos: range_pos
1696 }
1697 } else {
1698 break
1699 }
1700 }
1701 for int(min_bp) <= int(p.tok.left_binding_power())
1702 || (p.tok == .semicolon && p.peek().is_infix() && !p.peek().is_prefix()
1703 && int(min_bp) <= int(p.peek().left_binding_power())) {
1704 if p.tok == .semicolon && p.peek().is_infix() && !p.peek().is_prefix() {
1705 p.next()
1706 }
1707 if p.tok.is_infix() {
1708 pos := p.pos
1709 op := p.tok()
1710 rhs := if op in [.key_in, .not_in] {
1711 range_rhs := p.expr(op.right_binding_power())
1712 p.range_expr(range_rhs)
1713 } else if op in [.key_is, .not_is] {
1714 p.expect_type()
1715 } else {
1716 p.expr(op.right_binding_power())
1717 }
1718 lhs = infix_expr_with_parts(op, lhs, rhs, pos)
1719 } else if p.tok.is_postfix() {
1720 postfix_pos := p.pos
1721 postfix_op := p.tok()
1722 lhs = ast.Expr(ast.PostfixExpr{
1723 op: postfix_op
1724 expr: lhs
1725 pos: postfix_pos
1726 })
1727 } else {
1728 break
1729 }
1730 }
1731 return lhs
1732}
1733
1734fn call_expr_with_lhs(lhs ast.Expr, args []ast.Expr, pos token.Pos) ast.Expr {
1735 mut call_expr := ast.CallExpr{
1736 lhs: ast.empty_expr
1737 args: args
1738 pos: pos
1739 }
1740 call_expr.lhs = lhs
1741 return ast.Expr(call_expr)
1742}
1743
1744fn call_or_cast_expr_with_lhs(lhs ast.Expr, expr ast.Expr, pos token.Pos) ast.Expr {
1745 mut call_or_cast_expr := ast.CallOrCastExpr{
1746 lhs: ast.empty_expr
1747 expr: ast.empty_expr
1748 pos: pos
1749 }
1750 call_or_cast_expr.lhs = lhs
1751 call_or_cast_expr.expr = expr
1752 return ast.Expr(call_or_cast_expr)
1753}
1754
1755fn cast_expr_with_type(typ ast.Expr, expr ast.Expr, pos token.Pos) ast.Expr {
1756 mut cast_expr := ast.CastExpr{
1757 typ: ast.empty_expr
1758 expr: ast.empty_expr
1759 pos: pos
1760 }
1761 cast_expr.typ = typ
1762 cast_expr.expr = expr
1763 return ast.Expr(cast_expr)
1764}
1765
1766fn infix_expr_with_parts(op token.Token, lhs ast.Expr, rhs ast.Expr, pos token.Pos) ast.Expr {
1767 mut infix_expr := ast.InfixExpr{
1768 op: op
1769 lhs: ast.empty_expr
1770 rhs: ast.empty_expr
1771 pos: pos
1772 }
1773 infix_expr.lhs = lhs
1774 infix_expr.rhs = rhs
1775 return ast.Expr(infix_expr)
1776}
1777
1778fn index_expr_with_parts(lhs ast.Expr, expr ast.Expr, is_gated bool, pos token.Pos) ast.Expr {
1779 mut index_expr := ast.IndexExpr{
1780 lhs: ast.empty_expr
1781 expr: ast.empty_expr
1782 is_gated: is_gated
1783 pos: pos
1784 }
1785 index_expr.lhs = lhs
1786 index_expr.expr = expr
1787 return ast.Expr(index_expr)
1788}
1789
1790fn selector_expr_with_lhs(lhs ast.Expr, rhs ast.Ident, pos token.Pos) ast.Expr {
1791 mut selector_expr := ast.SelectorExpr{
1792 lhs: ast.empty_expr
1793 rhs: ast.Ident{}
1794 pos: pos
1795 }
1796 selector_expr.lhs = lhs
1797 selector_expr.rhs.name = rhs.name
1798 selector_expr.rhs.pos = rhs.pos
1799 return ast.Expr(selector_expr)
1800}
1801
1802fn selector_expr_with_rhs_name(lhs ast.Expr, rhs_pos token.Pos, rhs_name string, pos token.Pos) ast.Expr {
1803 mut selector_expr := ast.SelectorExpr{
1804 lhs: ast.empty_expr
1805 rhs: ast.Ident{}
1806 pos: pos
1807 }
1808 selector_expr.lhs = lhs
1809 selector_expr.rhs.pos = rhs_pos
1810 selector_expr.rhs.name = rhs_name
1811 return ast.Expr(selector_expr)
1812}
1813
1814fn modifier_expr_with_expr(kind token.Token, expr ast.Expr, pos token.Pos) ast.Expr {
1815 mut modifier_expr := ast.ModifierExpr{
1816 kind: kind
1817 expr: ast.empty_expr
1818 pos: pos
1819 }
1820 modifier_expr.expr = expr
1821 return ast.Expr(modifier_expr)
1822}
1823
1824fn parameter_with_type(name string, typ ast.Expr, is_mut bool, pos token.Pos) ast.Parameter {
1825 mut param := ast.Parameter{
1826 name: name
1827 typ: ast.empty_expr
1828 is_mut: is_mut
1829 pos: pos
1830 }
1831 param.typ = typ
1832 return param
1833}
1834
1835fn for_in_stmt_with_parts(key ast.Expr, value ast.Expr, expr ast.Expr) ast.ForInStmt {
1836 mut stmt := ast.ForInStmt{
1837 key: ast.empty_expr
1838 value: ast.empty_expr
1839 expr: ast.empty_expr
1840 }
1841 stmt.key = key
1842 stmt.value = value
1843 stmt.expr = expr
1844 return stmt
1845}
1846
1847fn for_stmt_with_parts(init ast.Stmt, cond ast.Expr, post ast.Stmt, stmts []ast.Stmt) ast.ForStmt {
1848 mut stmt := ast.ForStmt{
1849 init: ast.empty_stmt
1850 cond: ast.empty_expr
1851 post: ast.empty_stmt
1852 stmts: stmts
1853 }
1854 stmt.init = init
1855 stmt.cond = cond
1856 stmt.post = post
1857 return stmt
1858}
1859
1860fn if_expr_with_parts(cond ast.Expr, else_expr ast.Expr, stmts []ast.Stmt, pos token.Pos) ast.IfExpr {
1861 mut expr := ast.IfExpr{
1862 cond: ast.empty_expr
1863 else_expr: ast.empty_expr
1864 stmts: stmts
1865 pos: pos
1866 }
1867 expr.cond = cond
1868 expr.else_expr = else_expr
1869 return expr
1870}
1871
1872fn field_init_with_value(name string, value ast.Expr) ast.FieldInit {
1873 mut field := ast.FieldInit{
1874 name: name
1875 value: ast.empty_expr
1876 }
1877 field.value = value
1878 return field
1879}
1880
1881fn init_expr_with_type(typ ast.Expr, fields []ast.FieldInit, pos token.Pos) ast.InitExpr {
1882 mut init_expr := ast.InitExpr{
1883 typ: ast.empty_expr
1884 fields: fields
1885 pos: pos
1886 }
1887 init_expr.typ = typ
1888 return init_expr
1889}
1890
1891fn array_init_expr_with_parts(typ ast.Expr, exprs []ast.Expr, init ast.Expr, cap ast.Expr, len ast.Expr, pos token.Pos) ast.Expr {
1892 mut array_init_expr := ast.ArrayInitExpr{
1893 typ: ast.empty_expr
1894 exprs: exprs
1895 init: ast.empty_expr
1896 cap: ast.empty_expr
1897 len: ast.empty_expr
1898 pos: pos
1899 }
1900 array_init_expr.typ = typ
1901 array_init_expr.init = init
1902 array_init_expr.cap = cap
1903 array_init_expr.len = len
1904 return ast.Expr(array_init_expr)
1905}
1906
1907fn (p &Parser) token_starts_after_line_break() bool {
1908 mut idx := p.pos.offset - p.file.base - 1
1909 for idx >= 0 {
1910 ch := p.scanner.src[idx]
1911 if ch == `\n` || ch == `\r` {
1912 return true
1913 }
1914 if !ch.is_space() {
1915 return false
1916 }
1917 idx--
1918 }
1919 return false
1920}
1921
1922// parse and return `ast.RangeExpr` if found, otherwise return `lhs_expr`
1923@[inline]
1924fn (mut p Parser) range_expr(lhs_expr ast.Expr) ast.Expr {
1925 if p.tok in [.dotdot, .ellipsis] {
1926 range_pos := p.pos
1927 return ast.RangeExpr{
1928 op: p.tok()
1929 start: lhs_expr
1930 end: if p.tok == .rsbr { ast.empty_expr } else { p.expr(.lowest) }
1931 pos: range_pos
1932 }
1933 }
1934 return lhs_expr
1935}
1936
1937// parse type or expr, eg. `typeof(expr|type)` | `array_or_generic_call[expr|type]()`
1938@[inline]
1939fn (mut p Parser) expr_or_type(min_bp token.BindingPower) ast.Expr {
1940 // TODO: is there a better way to do this? see uses of `p.exp_pt`
1941 exp_pt := p.exp_pt
1942 p.exp_pt = true
1943 expr := p.expr(min_bp)
1944 p.exp_pt = exp_pt
1945 return expr
1946}
1947
1948// use peek() over always keeping next_tok one token ahead.
1949// I have done it this way to keep scanner & parser in sync.
1950// this simplifies getting any extra information from scanner
1951// as I can retrieve it directly, no need to store somewhere.
1952// this also help enforce the hard 1 token look ahead limit.
1953@[inline]
1954fn (mut p Parser) peek() token.Token {
1955 if p.tok_next_ == .unknown {
1956 p.tok_next_ = p.scanner.scan()
1957 // Keep parser/scanner file state synchronized for backends without pointer aliasing.
1958 p.file = p.scanner.current_file()
1959 }
1960 return p.tok_next_
1961}
1962
1963@[inline]
1964fn (mut p Parser) next() {
1965 if p.tok_next_ != .unknown {
1966 p.tok = p.tok_next_
1967 p.tok_next_ = .unknown
1968 } else {
1969 p.tok = p.scanner.scan()
1970 }
1971 // Keep parser/scanner file state synchronized for backends without pointer aliasing.
1972 p.file = p.scanner.current_file()
1973 p.line = p.file.line_count()
1974 p.lit = p.scanner.lit
1975 p.pos = p.file.pos(p.scanner.pos)
1976}
1977
1978// expect `tok` & go to next token
1979@[inline]
1980fn (mut p Parser) expect(tok token.Token) {
1981 if p.tok != tok {
1982 p.error_expected(tok, p.tok)
1983 }
1984 p.next()
1985}
1986
1987@[inline]
1988pub fn (mut p Parser) expect_semi() {
1989 match p.tok {
1990 // semicolon is optional before a closing ')' or '}'
1991 .rpar, .rcbr {}
1992 .semicolon {
1993 p.next()
1994 }
1995 else {
1996 p.error_expected(.semicolon, p.tok)
1997 }
1998 }
1999}
2000
2001// expect `.name` & return `p.lit` & go to next token
2002@[inline]
2003fn (mut p Parser) expect_name() string {
2004 if p.tok != .name {
2005 p.error_expected(.name, p.tok)
2006 }
2007 name := p.lit
2008 p.next()
2009 return name
2010}
2011
2012// expect `.name` or keyword & return `p.lit` & go to next token
2013// used for C/JS function names where keywords are allowed (e.g. `C.select`)
2014@[inline]
2015fn (mut p Parser) expect_name_or_keyword() string {
2016 if p.tok != .name && !p.tok.is_keyword() {
2017 p.error_expected(.name, p.tok)
2018 }
2019 name := p.lit
2020 p.next()
2021 return name
2022}
2023
2024// return `p.lit` & go to next token
2025@[inline]
2026fn (mut p Parser) lit() string {
2027 // TODO: check if there is a better way to handle this?
2028 // we should never use lit() in cases where p.lit is empty anyway
2029 // lit := if p.lit.len == 0 { p.tok.str() } else { p.lit }
2030 lit := p.lit
2031 p.next()
2032 return lit
2033}
2034
2035// return `p.tok` & go to next token
2036@[inline]
2037fn (mut p Parser) tok() token.Token {
2038 tok := p.tok
2039 p.next()
2040 return tok
2041}
2042
2043@[inline]
2044fn (mut p Parser) block() []ast.Stmt {
2045 mut stmts := []ast.Stmt{}
2046 p.expect(.lcbr)
2047 for p.tok != .rcbr {
2048 stmts << p.stmt()
2049 }
2050 // rcbr
2051 p.next()
2052 // TODO: correct way to error on `if x == Type{} {`
2053 // is this the correct place for this, will it work in every case?
2054 // if p.tok == .lcbr {
2055 // // TODO: better error message
2056 // p.error('init must be in parens when `{` is expected, eg. `if x == (Type{}) {`')
2057 // }
2058 return stmts
2059}
2060
2061fn (mut p Parser) skip_balanced_brace_block() {
2062 p.expect(.lcbr)
2063 mut depth := 1
2064 for depth > 0 {
2065 match p.tok {
2066 .eof {
2067 p.error('unexpected eof while parsing block')
2068 }
2069 .lcbr {
2070 depth++
2071 p.next()
2072 }
2073 .rcbr {
2074 depth--
2075 p.next()
2076 }
2077 // Consume the full string/interpolation so braces inside `${...}` do
2078 // not interfere with raw block skipping.
2079 .string {
2080 _ = p.string_literal(.v)
2081 }
2082 else {
2083 p.next()
2084 }
2085 }
2086 }
2087}
2088
2089fn (mut p Parser) skip_sql_block_metadata() (string, bool, bool) {
2090 p.expect(.lcbr)
2091 mut depth := 1
2092 mut saw_select := false
2093 mut saw_from := false
2094 mut saw_create := false
2095 mut saw_create_table := false
2096 mut is_count := false
2097 mut is_create := false
2098 mut table_name := ''
2099 for depth > 0 {
2100 match p.tok {
2101 .eof {
2102 p.error('unexpected eof while parsing block')
2103 }
2104 .lcbr {
2105 depth++
2106 p.next()
2107 }
2108 .rcbr {
2109 depth--
2110 p.next()
2111 }
2112 .string {
2113 _ = p.string_literal(.v)
2114 }
2115 .key_select {
2116 if depth == 1 {
2117 saw_select = true
2118 }
2119 p.next()
2120 }
2121 .name {
2122 if depth == 1 && table_name == '' {
2123 if !saw_select && p.lit == 'select' {
2124 saw_select = true
2125 } else if p.lit == 'create' {
2126 saw_create = true
2127 } else if saw_create && p.lit == 'table' {
2128 saw_create_table = true
2129 } else if saw_select && !saw_from && p.lit == 'count' {
2130 is_count = true
2131 } else if saw_select && p.lit == 'from' {
2132 saw_from = true
2133 } else if saw_create_table {
2134 is_create = true
2135 table_name = p.lit
2136 } else if saw_from {
2137 table_name = p.lit
2138 }
2139 }
2140 p.next()
2141 }
2142 else {
2143 p.next()
2144 }
2145 }
2146 }
2147 return table_name, is_count, is_create
2148}
2149
2150@[inline]
2151fn (mut p Parser) expr_list() []ast.Expr {
2152 mut exprs := []ast.Expr{}
2153 for {
2154 expr := p.expr(.lowest)
2155 exprs << expr
2156 // TODO: was this just for previous generics impl or was there another need?
2157 // expr := p.expr(.lowest)
2158 // // TODO: is this the best place/way to handle this?
2159 // if expr is ast.EmptyExpr {
2160 // p.error('expecting expr, got `${p.tok}`')
2161 // }
2162 // exprs << expr
2163 if p.tok != .comma {
2164 break
2165 }
2166 p.next()
2167 }
2168 return exprs
2169}
2170
2171// @[attribute] | [attribute]
2172fn (mut p Parser) attributes() []ast.Attribute {
2173 p.next()
2174 mut attributes := []ast.Attribute{}
2175 for {
2176 // TODO: perhaps attrs with `.name` token before `:` we can set name
2177 // as apposed to value of `Ident{name}`
2178 mut name := ''
2179 mut value := ast.empty_expr
2180 mut comptime_cond := ast.empty_expr
2181 attr_pos := p.pos
2182 // since unsafe is a keyword
2183 if p.tok == .key_unsafe {
2184 p.next()
2185 // name = 'unsafe'
2186 value = ast.Expr(ident_with_name(p.pos, 'unsafe'))
2187 }
2188 // TODO: properly
2189 // consider using normal if expr
2190 else if p.tok == .key_if {
2191 p.next()
2192 comptime_cond = p.expr(.lowest)
2193 // if p.tok == .question {
2194 // p.next()
2195 // comptime_cond = ast.PostfixExpr{
2196 // op: .question
2197 // expr: comptime_cond
2198 // }
2199 // }
2200 } else {
2201 // name = p.expect_name()
2202 value = p.expr(.lowest)
2203 if p.tok == .colon {
2204 if mut value is ast.Ident {
2205 name = value.name
2206 } else {
2207 p.error('expecting identifier')
2208 }
2209 p.next() // ;
2210 // NOTE: use tok instead of defining AttributeKind
2211 // kind := p.tok
2212 // TODO: do we need the match below or should we use:
2213 // if p.tok in [.semicolon, .rsbr] { p.error('...') }
2214 value = p.expr(.lowest)
2215 // value = match p.tok {
2216 // .name, .number, .string { p.lit() }
2217 // else { p.error('unexpected ${p.tok}, an argument is expected after `:`') }
2218 // }
2219 }
2220 }
2221 attributes << ast.Attribute{
2222 name: name
2223 value: value
2224 comptime_cond: comptime_cond
2225 pos: attr_pos
2226 }
2227 if p.tok == .semicolon {
2228 p.next()
2229 continue
2230 }
2231 p.expect(.rsbr)
2232 p.expect_semi()
2233 // @[attribute_a]
2234 // [attribute_a]
2235 if p.tok in [.attribute, .lsbr] {
2236 p.next()
2237 continue
2238 }
2239 break
2240 }
2241 // p.log('ast.Attribute: ${name}')
2242 return attributes
2243}
2244
2245// TODO:
2246fn (mut p Parser) asm_stmt() ast.AsmStmt {
2247 p.next()
2248 _ = if p.tok == .key_volatile {
2249 p.next()
2250 true
2251 } else {
2252 false
2253 }
2254 arch := p.expect_name()
2255 p.expect(.lcbr)
2256 for p.tok != .rcbr {
2257 p.next()
2258 }
2259 p.expect(.rcbr)
2260 return ast.AsmStmt{
2261 arch: arch
2262 }
2263}
2264
2265@[inline]
2266fn (mut p Parser) assign_stmt(lhs []ast.Expr) ast.AssignStmt {
2267 return ast.AssignStmt{
2268 pos: p.pos
2269 op: p.tok()
2270 lhs: lhs
2271 rhs: p.expr_list()
2272 }
2273}
2274
2275@[inline]
2276fn (mut p Parser) comptime_expr() ast.Expr {
2277 pos := p.pos
2278 match p.tok {
2279 .key_if {
2280 return ast.ComptimeExpr{
2281 expr: ast.Expr(p.if_expr(true))
2282 pos: pos
2283 }
2284 }
2285 else {
2286 return ast.ComptimeExpr{
2287 expr: p.expr(.lowest)
2288 pos: p.pos
2289 }
2290 }
2291 }
2292}
2293
2294@[inline]
2295fn (mut p Parser) comptime_stmt() ast.Stmt {
2296 p.next()
2297 match p.tok {
2298 .key_for {
2299 return ast.ComptimeStmt{
2300 stmt: ast.Stmt(p.for_stmt())
2301 }
2302 }
2303 .key_if {
2304 expr := p.comptime_expr()
2305 // semicolon may already be consumed by if_expr when checking for $else
2306 if p.tok == .semicolon {
2307 p.next()
2308 }
2309 return ast.ExprStmt{
2310 expr: expr
2311 }
2312 }
2313 .key_match {
2314 // $match T.unaliased_typ { ... $else { ... } }
2315 return ast.ComptimeStmt{
2316 stmt: ast.Stmt(ast.ExprStmt{
2317 expr: p.comptime_match_expr()
2318 })
2319 }
2320 }
2321 else {
2322 expr := p.comptime_expr()
2323 p.expect_semi()
2324 return ast.ExprStmt{
2325 expr: expr
2326 }
2327 }
2328 }
2329}
2330
2331fn (mut p Parser) comptime_match_expr() ast.Expr {
2332 match_pos := p.pos
2333 p.next() // skip 'match'
2334 mut exp_lcbr := p.exp_lcbr
2335 p.exp_lcbr = true
2336 expr := p.expr_or_type(.lowest)
2337 p.exp_lcbr = exp_lcbr
2338 p.expect(.lcbr)
2339 mut branches := []ast.MatchBranch{}
2340 for p.tok != .rcbr {
2341 // $else branch
2342 if p.tok == .dollar && p.peek_dollar_keyword() == 'else' {
2343 branch_pos := p.pos
2344 p.next() // skip $
2345 p.next() // skip else
2346 branches << ast.MatchBranch{
2347 stmts: p.block()
2348 pos: branch_pos
2349 }
2350 p.expect_semi()
2351 continue
2352 }
2353 exp_lcbr = p.exp_lcbr
2354 branch_pos := p.pos
2355 p.exp_lcbr = true
2356 first_cond_expr := p.expr_or_type(.lowest)
2357 mut cond := [p.range_expr(first_cond_expr)]
2358 for p.tok == .comma {
2359 p.next()
2360 next_cond_expr := p.expr_or_type(.lowest)
2361 cond << p.range_expr(next_cond_expr)
2362 }
2363 p.exp_lcbr = exp_lcbr
2364 branches << ast.MatchBranch{
2365 cond: cond
2366 stmts: p.block()
2367 pos: branch_pos
2368 }
2369 p.expect_semi()
2370 }
2371 // rcbr
2372 p.next()
2373 return ast.Expr(ast.ComptimeExpr{
2374 expr: ast.Expr(ast.MatchExpr{
2375 expr: expr
2376 branches: branches
2377 pos: match_pos
2378 })
2379 pos: match_pos
2380 })
2381}
2382
2383fn is_for_in_binding_expr(expr ast.Expr) bool {
2384 if _ := expr_ident_payload_checked(expr) {
2385 return true
2386 }
2387 return match expr {
2388 ast.Ident {
2389 true
2390 }
2391 ast.ModifierExpr {
2392 expr.kind == .key_mut && is_for_in_binding_expr(expr.expr)
2393 }
2394 else {
2395 false
2396 }
2397 }
2398}
2399
2400fn (mut p Parser) for_stmt() ast.ForStmt {
2401 p.next()
2402 exp_lcbr := p.exp_lcbr
2403 p.exp_lcbr = true
2404 mut init, mut cond, mut post := ast.empty_stmt, ast.empty_expr, ast.empty_stmt
2405 // `for x < y {` | `for x:=1; x<=10; x++ {`
2406 if p.tok != .lcbr {
2407 mut expr := if p.tok != .semicolon { p.expr(.lowest) } else { ast.empty_expr }
2408 // `x, y` in (`for mut x, y in z {` | `for x, y := 1, 2; ; {`)
2409 expr2 := if p.tok == .comma {
2410 p.next()
2411 p.expr(.highest)
2412 } else {
2413 ast.empty_expr
2414 }
2415 // `for x in {`
2416 if p.tok == .key_in {
2417 // p.expect(.key_in)
2418 p.next()
2419 init = ast.Stmt(for_in_stmt_with_parts(expr, expr2, p.expr(.lowest)))
2420 } else if p.tok == .lcbr {
2421 // `for x in y {`
2422 // TODO: maybe handle this differently
2423 mut is_for_in := false
2424 if infix := expr_infix_payload_checked(expr) {
2425 if infix.op == .key_in && is_for_in_binding_expr(infix.lhs) {
2426 init = ast.Stmt(for_in_stmt_with_parts(ast.empty_expr, infix.lhs, infix.rhs))
2427 is_for_in = true
2428 }
2429 }
2430 // `for x < y {`
2431 if !is_for_in {
2432 cond = expr
2433 }
2434 }
2435 // `for x:=1; x<=10; x++ {`
2436 else {
2437 if p.tok != .semicolon {
2438 // init = p.complete_simple_stmt(expr, true)
2439 if expr2 is ast.EmptyExpr {
2440 init = p.complete_simple_stmt(expr, true)
2441 } else {
2442 mut exprs := [expr, expr2]
2443 for p.tok == .comma {
2444 p.next()
2445 exprs << p.expr(.lowest)
2446 }
2447 if !p.tok.is_assignment() {
2448 p.error('expecting assignment `for a, b, c := 1, 2, 3; ... {`')
2449 }
2450 init = ast.Stmt(p.assign_stmt(exprs))
2451 }
2452 }
2453 p.expect(.semicolon)
2454 if p.tok != .semicolon {
2455 cond = p.expr(.lowest)
2456 }
2457 p.expect(.semicolon)
2458 if p.tok != .lcbr {
2459 post_expr := p.expr(.lowest)
2460 post = p.complete_simple_stmt(post_expr, false)
2461 }
2462 }
2463 }
2464 p.exp_lcbr = exp_lcbr
2465 stmts := p.block()
2466 p.expect_semi()
2467 return for_stmt_with_parts(init, cond, post, stmts)
2468}
2469
2470fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
2471 // p.log('ast.IfExpr')
2472 pos := p.pos
2473 p.next()
2474 // else if
2475 // NOTE: it's a bit weird to parse because of the way comptime has
2476 // `$` on every branch. Removing this would simplify things
2477 if p.tok == .key_if || (p.tok == .dollar && p.peek() == .key_if) {
2478 if is_comptime {
2479 p.expect(.dollar)
2480 }
2481 // p.expect(.key_if)
2482 p.next()
2483 }
2484 exp_lcbr := p.exp_lcbr
2485 allow_init_in_exp_lcbr := p.allow_init_in_exp_lcbr
2486 p.exp_lcbr = true
2487 p.allow_init_in_exp_lcbr = true
2488 // mut cond := p.expr(.lowest)
2489 // NOTE: the line above works, but avoid calling p.expr()
2490 mut cond := if p.tok == .lcbr {
2491 ast.empty_expr
2492 } else {
2493 // eg. `$if T in [?int, ?int] {`
2494 if is_comptime { p.expr_or_type(.lowest) } else { p.expr(.lowest) }
2495 }
2496 p.allow_init_in_exp_lcbr = allow_init_in_exp_lcbr
2497 mut else_expr := ast.empty_expr
2498 // if p.tok == .question {
2499 // // TODO: handle individual cases like this or globally
2500 // // use postfix for this and add to token.is_postfix()?
2501 // cond = ast.PostfixExpr{
2502 // expr: cond
2503 // op: p.tok
2504 // }
2505 // p.next()
2506 // }
2507 // if guard
2508 guard_pos := cond.pos()
2509 if p.tok == .comma {
2510 s := p.complete_simple_stmt(cond, false)
2511 if s is ast.AssignStmt {
2512 cond = ast.Expr(ast.IfGuardExpr{
2513 stmt: s
2514 pos: guard_pos
2515 })
2516 } else {
2517 p.error('expecting assignment `if a, b := c {`')
2518 }
2519 } else if p.tok in [.assign, .decl_assign] {
2520 cond = ast.Expr(ast.IfGuardExpr{
2521 stmt: p.assign_stmt([cond])
2522 pos: guard_pos
2523 })
2524 }
2525 p.exp_lcbr = exp_lcbr
2526 // Allow `{` on next line after if condition (skip semicolon)
2527 if p.tok == .semicolon && p.peek() == .lcbr {
2528 p.next()
2529 }
2530 stmts := p.block()
2531 // this is because semis get inserted after branches (same in Go)
2532 // only consume semicolon if there's a non-comptime else following
2533 // (for comptime $else, there should be no semicolon between } and $)
2534 if p.tok == .semicolon && (p.peek() == .key_else || (is_comptime && p.peek() == .dollar
2535 && p.peek_dollar_keyword() == 'else')) {
2536 p.next()
2537 }
2538 // else
2539 if p.tok == .key_else || (p.tok == .dollar && is_comptime && p.peek_dollar_keyword() == 'else') {
2540 // we are using expect instead of next to ensure we error when `is_comptime`
2541 // and not all branches have `$`, or `!is_comptime` and any branches have `$`.
2542 // the same applies for the `else if` condition directly below.
2543 if is_comptime {
2544 p.expect(.dollar)
2545 }
2546 // p.expect(.key_else)
2547 // p.next()
2548 else_expr = ast.Expr(p.if_expr(is_comptime))
2549 }
2550 return if_expr_with_parts(cond, else_expr, stmts, pos)
2551}
2552
2553fn (p &Parser) peek_dollar_keyword() string {
2554 if p.scanner.offset >= p.scanner.src.len {
2555 return ''
2556 }
2557 mut idx := p.scanner.offset
2558 for idx < p.scanner.src.len && p.scanner.src[idx].is_space() {
2559 idx++
2560 }
2561 start := idx
2562 for idx < p.scanner.src.len && p.scanner.src[idx].is_letter() {
2563 idx++
2564 }
2565 if start == idx {
2566 return ''
2567 }
2568 return p.scanner.src[start..idx]
2569}
2570
2571fn (mut p Parser) import_stmt() ast.ImportStmt {
2572 p.next()
2573 // NOTE: we can also use SelectorExpr if we like
2574 // mod := p.expr(.lowest)
2575 mut name := p.expect_name()
2576 mut alias := name
2577 for p.tok == .dot {
2578 p.next()
2579 alias = p.expect_name()
2580 name += '.' + alias
2581 }
2582 is_aliased := p.tok == .key_as
2583 if is_aliased {
2584 p.next()
2585 alias = p.expect_name()
2586 }
2587 mut symbols := []ast.Expr{}
2588 // `import mod { sym1, sym2 }`
2589 if p.tok == .lcbr {
2590 p.next()
2591 for p.tok == .name {
2592 // symbols << p.expr_or_type(.lowest)
2593 symbols << p.ident_or_type()
2594 if p.tok == .comma {
2595 p.next()
2596 } else {
2597 break
2598 }
2599 }
2600 p.expect(.rcbr)
2601 }
2602 // p.log('ast.ImportStmt: ${name} as ${alias}')
2603 return ast.ImportStmt{
2604 name: name
2605 alias: alias
2606 is_aliased: is_aliased
2607 symbols: symbols
2608 }
2609}
2610
2611fn (mut p Parser) directive() ast.Directive {
2612 line := p.line
2613 // value := p.lit() // if we scan whole line see scanner
2614 p.next()
2615 name := p.expect_name()
2616 mut value := ''
2617 if p.tok != .eof && p.line == line {
2618 start := p.scanner.pos
2619 mut end := start
2620 for end < p.scanner.src.len && p.scanner.src[end] !in [`\n`, `\r`] {
2621 end++
2622 }
2623 value = p.scanner.src[start..end].trim_space()
2624 for p.tok != .eof && p.line == line {
2625 p.next()
2626 }
2627 }
2628 mut ct_cond := ''
2629 if name in ['include', 'preinclude', 'postinclude', 'insert'] {
2630 leading := directive_leading_name(value)
2631 if leading != '' {
2632 rest := value[leading.len..].trim_space()
2633 if rest.starts_with('<') || rest.starts_with('"') {
2634 ct_cond = leading
2635 value = rest
2636 }
2637 }
2638 }
2639 return ast.Directive{
2640 name: name
2641 value: value
2642 ct_cond: ct_cond
2643 }
2644}
2645
2646fn directive_leading_name(value string) string {
2647 if value == '' || !(value[0].is_letter() || value[0] == `_`) {
2648 return ''
2649 }
2650 mut end := 1
2651 for end < value.len && (value[end].is_alnum() || value[end] == `_`) {
2652 end++
2653 }
2654 return value[..end]
2655}
2656
2657fn (mut p Parser) const_decl(is_public bool) ast.ConstDecl {
2658 p.next()
2659 is_grouped := p.tok == .lpar
2660 is_v_header := p.file.name.ends_with('.vh')
2661 if is_grouped {
2662 p.next()
2663 }
2664 mut fields := []ast.FieldInit{}
2665 for {
2666 mut name := p.expect_name()
2667 mut value := ast.empty_expr
2668 // C.NAME type - extern C constant declaration (no value)
2669 if name == 'C' && p.tok == .dot {
2670 p.next() // skip .
2671 c_name := p.expect_name_or_keyword()
2672 name = 'C.${c_name}'
2673 // Type follows directly (e.g., `pub const C.AF_INET u8`)
2674 value = p.expect_type()
2675 } else if p.tok == .assign {
2676 p.next()
2677 value = p.expr(.lowest)
2678 } else if is_v_header {
2679 value = p.expect_type()
2680 } else {
2681 p.error('expecting `=` in const declaration')
2682 }
2683 fields << ast.FieldInit{
2684 name: name
2685 value: value
2686 }
2687 p.expect(.semicolon)
2688 if !is_grouped {
2689 break
2690 } else if p.tok == .rpar {
2691 p.next()
2692 p.expect(.semicolon)
2693 break
2694 }
2695 }
2696 return ast.ConstDecl{
2697 is_public: is_public
2698 fields: fields
2699 }
2700}
2701
2702fn (mut p Parser) fn_decl(is_public bool, attributes []ast.Attribute) ast.FnDecl {
2703 pos := p.pos
2704 p.next()
2705 // method
2706 mut is_method := false
2707 mut receiver := ast.Parameter{}
2708 if p.tok == .lpar {
2709 is_method = true
2710 p.next()
2711 // TODO: use parse_ident & type
2712 // receiver := p.ident() ?
2713 is_mut := p.tok == .key_mut
2714 is_shared := p.tok == .key_shared
2715 if is_mut || is_shared {
2716 p.next()
2717 }
2718 // // TODO: clean up, will this be done here or in checker
2719 // receiver_name := p.expect_name()
2720 // mut receiver_type := p.expect_type()
2721 // if is_mut {
2722 // if mut receiver_type is ast.PrefixExpr {
2723 // if receiver_type.op == .amp {
2724 // p.error('use `mut Type` not `mut &Type`. TODO: proper error message')
2725 // }
2726 // }
2727 // receiver_type = ast.PrefixExpr{op: .amp, expr: receiver_type}
2728 // }
2729 receiver_pos := p.pos
2730 receiver_name := p.expect_name()
2731 receiver = parameter_with_type(receiver_name, p.expect_type(), is_mut, receiver_pos)
2732 p.expect(.rpar)
2733 // operator overload
2734 if p.tok.is_overloadable() {
2735 op_name := p.tok.str() // e.g., '+', '-', etc.
2736 p.next()
2737 p.expect(.lpar)
2738 is_mut2 := p.tok == .key_mut
2739 if is_mut2 {
2740 p.next()
2741 }
2742 param_name := p.expect_name()
2743 param_typ := p.expect_type()
2744 param := parameter_with_type(param_name, param_typ, is_mut2, token.Pos{})
2745 p.expect(.rpar)
2746 mut return_type := ast.empty_expr
2747 if p.tok != .lcbr {
2748 return_type = p.expect_type()
2749 }
2750 prev_top_level := p.in_top_level
2751 p.in_top_level = false
2752 stmts := if p.tok == .lcbr {
2753 p.block()
2754 } else {
2755 []ast.Stmt{}
2756 }
2757 p.in_top_level = prev_top_level
2758 p.expect(.semicolon)
2759 return ast.FnDecl{
2760 attributes: attributes
2761 is_public: is_public
2762 is_method: true
2763 receiver: receiver
2764 name: op_name
2765 typ: fn_type_with_return_type([]ast.Expr{}, [param], return_type)
2766 stmts: stmts
2767 pos: pos
2768 }
2769 }
2770 }
2771 language := p.decl_language()
2772 // Allow keywords as function/method names (e.g. `lock`, `select`)
2773 name_ident := p.ident_or_keyword()
2774 mut name := name_ident.name
2775 mut is_static := false
2776 if p.tok == .dot {
2777 p.next()
2778 // static method `Type.name`
2779 if language == .v {
2780 name = p.expect_name()
2781 is_method = true
2782 is_static = true
2783 receiver = parameter_with_type('', name_ident, false, token.Pos{})
2784 }
2785 // eg. `Promise.resolve` in `JS.Promise.resolve`
2786 // use expect_name_or_keyword() to allow keywords as names (e.g. `C.select`)
2787 else {
2788 name += '.' + p.expect_name_or_keyword()
2789 for p.tok == .dot {
2790 p.next()
2791 name += '.' + p.expect_name_or_keyword()
2792 }
2793 }
2794 }
2795 typ := p.fn_type()
2796 // p.log('ast.FnDecl: ${name} ${p.lit} - ${p.tok} (${p.lit}) - ${p.tok_next_}')
2797 // also check line for better error detection
2798 prev_top_level := p.in_top_level
2799 p.in_top_level = false
2800 stmts := if p.tok == .lcbr {
2801 p.block()
2802 } else {
2803 []ast.Stmt{}
2804 }
2805 p.in_top_level = prev_top_level
2806 p.expect(.semicolon)
2807 return ast.FnDecl{
2808 attributes: attributes
2809 is_public: is_public
2810 is_method: is_method
2811 is_static: is_static
2812 receiver: receiver
2813 name: name
2814 language: language
2815 typ: typ
2816 stmts: stmts
2817 pos: pos
2818 }
2819}
2820
2821fn (mut p Parser) fn_parameters() []ast.Parameter {
2822 p.expect(.lpar)
2823 mut params := []ast.Parameter{}
2824 for p.tok != .rpar {
2825 // TODO: parse all modifiers (shared)
2826 pos := p.pos
2827 is_mut := p.tok == .key_mut
2828 if is_mut {
2829 p.next()
2830 }
2831 // NOTE: case documented in `p.try_type()` todo
2832 mut typ := p.expect_type()
2833 mut name := ''
2834 if p.tok !in [.comma, .rpar] {
2835 if typ is ast.Ident {
2836 name = (typ as ast.Ident).name
2837 typ = p.expect_type()
2838 }
2839 }
2840 params << parameter_with_type(name, typ, is_mut, pos)
2841 if p.tok == .comma {
2842 p.next()
2843 }
2844 }
2845 p.next()
2846 return params
2847}
2848
2849fn (mut p Parser) fn_arguments() []ast.Expr {
2850 p.expect(.lpar)
2851 // args := if p.tok == .rpar { []ast.Expr{} } else { p.expr_list() }
2852 // NOTE: not using p.expr_list() as I need to support some special
2853 // things like varg, lambda expression, and struct config syntax
2854 // TODO: config syntax is getting deprecated, will become maps
2855 // eventually use named default params instead (once implemented)
2856 mut args := []ast.Expr{}
2857 for p.tok != .rpar {
2858 // NOTE: since these are only supported in fn arguments
2859 // we will only handle them here, rather than in `p.expr`
2860 expr := match p.tok {
2861 // `...varg`
2862 .ellipsis {
2863 prefix_pos := p.pos
2864 prefix_op := p.tok()
2865 prefix_expr := p.expr(.lowest)
2866 ast.Expr(ast.PrefixExpr{
2867 pos: prefix_pos
2868 op: prefix_op
2869 expr: prefix_expr
2870 })
2871 }
2872 // lambda expression - no args
2873 .logical_or {
2874 lambda_pos := p.pos
2875 p.next()
2876 ast.Expr(ast.LambdaExpr{
2877 expr: p.expr(.lowest)
2878 pos: lambda_pos
2879 })
2880 }
2881 // lambda expression - with args
2882 .pipe {
2883 lambda_pos := p.pos
2884 p.next()
2885 mut le_args := [p.ident()]
2886 for p.tok == .comma {
2887 p.next()
2888 le_args << p.ident()
2889 }
2890 p.expect(.pipe)
2891 ast.Expr(ast.LambdaExpr{
2892 args: le_args
2893 expr: p.expr(.lowest)
2894 pos: lambda_pos
2895 })
2896 }
2897 else {
2898 if p.tok == .lsbr && p.peek() == .rsbr {
2899 // Type literals are accepted as metadata arguments in calls
2900 // like json.decode([]User, data). Keep this narrow so
2901 // ordinary calls such as foo(arr[i]) still parse `arr[i]`
2902 // as an index expression, not as generic arguments.
2903 p.expr_or_type(.lowest)
2904 } else {
2905 p.expr(.lowest)
2906 }
2907 }
2908 }
2909
2910 // short struct config syntax
2911 // TODO: if also supported anywhere else it can be moved to `p.expr()`
2912 if p.tok == .colon {
2913 p.next()
2914 // println('looks like config syntax')
2915 if expr !is ast.Ident {
2916 p.error('expecting ident for struct config syntax?')
2917 }
2918 args << ast.FieldInit{
2919 name: (expr as ast.Ident).name
2920 value: p.expr(.lowest)
2921 }
2922 if p.tok == .semicolon {
2923 p.next()
2924 }
2925 } else {
2926 args << expr
2927 }
2928 // args << expr
2929 if p.tok == .comma {
2930 p.next()
2931 }
2932 }
2933 p.next()
2934 return args
2935}
2936
2937fn (mut p Parser) enum_decl(is_public bool, attributes []ast.Attribute) ast.EnumDecl {
2938 p.next()
2939 name := p.expect_name()
2940 as_type := if p.tok == .key_as {
2941 p.next()
2942 p.expect_type()
2943 } else {
2944 ast.empty_expr
2945 }
2946 // p.log('ast.EnumDecl: ${name}')
2947 p.expect(.lcbr)
2948 mut fields := []ast.FieldDecl{}
2949 for p.tok != .rcbr {
2950 // Allow keywords as enum field names (e.g., `select`)
2951 field_name := p.expect_name_or_keyword()
2952 mut value := ast.empty_expr
2953 if p.tok == .assign {
2954 p.next()
2955 value = p.expr(.lowest)
2956 }
2957 field_attributes := if p.tok in [.attribute, .lsbr] {
2958 p.attributes()
2959 } else {
2960 []ast.Attribute{}
2961 }
2962 // p.expect_semi()
2963 // p.expect(.semicolon)
2964 if p.tok == .semicolon {
2965 p.next()
2966 }
2967 fields << ast.FieldDecl{
2968 name: field_name
2969 value: value
2970 attributes: field_attributes
2971 }
2972 }
2973 p.next()
2974 // p.expect_semi()
2975 p.expect(.semicolon)
2976 return ast.EnumDecl{
2977 attributes: attributes
2978 is_public: is_public
2979 name: name
2980 as_type: as_type
2981 fields: fields
2982 }
2983}
2984
2985fn (mut p Parser) global_decl(is_public bool, attributes []ast.Attribute) ast.GlobalDecl {
2986 p.next()
2987 // NOTE: this got changed at some stage (or perhaps was never forced)
2988 // if p.tok != .lpar {
2989 // p.error('globals must be grouped, e.g. `__global ( a = int(1) )`')
2990 // }
2991 // p.next()
2992 is_grouped := p.tok == .lpar
2993 if is_grouped {
2994 p.next()
2995 }
2996 mut fields := []ast.FieldDecl{}
2997 for {
2998 mut field_is_public := is_public
2999 if p.tok == .key_pub {
3000 p.next()
3001 field_is_public = true
3002 }
3003 field_is_mut := p.tok == .key_mut
3004 if field_is_mut {
3005 p.next()
3006 }
3007 mut name := p.expect_name()
3008 // Handle qualified names like C.errno, C.stdin, etc.
3009 for p.tok == .dot {
3010 p.next()
3011 name += '.' + p.expect_name_or_keyword()
3012 }
3013 if p.tok == .assign {
3014 p.next()
3015 prev_top_level := p.in_top_level
3016 p.in_top_level = false
3017 value := p.expr(.lowest)
3018 p.in_top_level = prev_top_level
3019 fields << ast.FieldDecl{
3020 name: name
3021 value: value
3022 is_public: field_is_public
3023 is_mut: field_is_mut
3024 }
3025 } else {
3026 fields << ast.FieldDecl{
3027 name: name
3028 typ: p.expect_type()
3029 is_public: field_is_public
3030 is_mut: field_is_mut
3031 }
3032 }
3033 p.expect(.semicolon)
3034 if !is_grouped {
3035 break
3036 } else if p.tok == .rpar {
3037 p.next()
3038 p.expect(.semicolon)
3039 break
3040 }
3041 }
3042 return ast.GlobalDecl{
3043 attributes: attributes
3044 fields: fields
3045 is_public: is_public
3046 }
3047}
3048
3049fn (mut p Parser) interface_decl(is_public bool, attributes []ast.Attribute) ast.InterfaceDecl {
3050 p.next()
3051 mut name := p.expect_name()
3052 for p.tok == .dot {
3053 p.next()
3054 name += p.expect_name()
3055 }
3056 generic_params := if p.tok == .lsbr { p.generic_list() } else { []ast.Expr{} }
3057 p.expect(.lcbr)
3058 mut fields := []ast.FieldDecl{}
3059 mut embedded := []ast.Expr{}
3060 mut block_is_mut := false
3061 for p.tok != .rcbr {
3062 if p.tok == .key_mut {
3063 p.next()
3064 p.expect(.colon)
3065 block_is_mut = true
3066 }
3067 if p.tok.is_keyword() && p.peek() == .lpar {
3068 field_name := p.expect_name_or_keyword()
3069 fields << ast.FieldDecl{
3070 name: field_name
3071 typ: ast.Expr(ast.Type(p.fn_type()))
3072 is_mut: block_is_mut
3073 is_interface_method: true
3074 }
3075 p.expect(.semicolon)
3076 continue
3077 }
3078 mut field_name := ''
3079 mut field_type := p.expect_type()
3080 // `field type`
3081 if p.tok != .semicolon {
3082 mut method_generic_params := []ast.Expr{}
3083 if mut field_type is ast.Ident {
3084 field_name = field_type.name
3085 } else if field_type is ast.Type && field_type is ast.GenericType {
3086 generic_type := field_type as ast.GenericType
3087 if generic_type.name is ast.Ident {
3088 field_name = generic_type.name.name
3089 method_generic_params = generic_type.params.clone()
3090 } else {
3091 p.error('expecting field name')
3092 }
3093 } else {
3094 p.error('expecting field name')
3095 }
3096 mut is_interface_method := false
3097 field_typ := if p.tok == .lpar {
3098 is_interface_method = true
3099 mut typ := p.fn_type()
3100 if method_generic_params.len > 0 {
3101 typ = ast.FnType{
3102 ...typ
3103 generic_params: method_generic_params
3104 }
3105 }
3106 ast.Expr(ast.Type(typ))
3107 } else {
3108 p.expect_type()
3109 }
3110 fields << ast.FieldDecl{
3111 name: field_name
3112 typ: field_typ
3113 is_mut: block_is_mut
3114 is_interface_method: is_interface_method
3115 }
3116 }
3117 // embedded interface
3118 else {
3119 embedded << field_type
3120 }
3121 // p.expect_semi()
3122 p.expect(.semicolon)
3123 }
3124 // rcbr
3125 p.next()
3126 p.expect(.semicolon)
3127 return ast.InterfaceDecl{
3128 is_public: is_public
3129 attributes: attributes
3130 name: name
3131 generic_params: generic_params
3132 embedded: embedded
3133 fields: fields
3134 }
3135}
3136
3137fn (mut p Parser) struct_decl(is_public bool, attributes []ast.Attribute) ast.StructDecl {
3138 is_union := p.tok == .key_union
3139 pos := p.pos
3140 p.next()
3141 language := p.decl_language()
3142 name := p.expect_name()
3143 // p.log('ast.StructDecl: ${name}')
3144 mut generic_params := []ast.Expr{}
3145 mut impl_types := []ast.Expr{}
3146 if p.tok == .lsbr {
3147 generic_params = p.generic_list()
3148 }
3149 if p.tok == .name && p.lit == 'implements' {
3150 p.next()
3151 impl_types << p.expect_type()
3152 for p.tok == .comma {
3153 p.next()
3154 impl_types << p.expect_type()
3155 }
3156 }
3157 // probably C struct decl with no body or {}
3158 if p.tok != .lcbr {
3159 if language == .v {
3160 p.error('v struct decl must have a body')
3161 }
3162 return ast.StructDecl{
3163 is_public: is_public
3164 is_union: is_union
3165 implements: impl_types
3166 language: language
3167 name: name
3168 generic_params: generic_params
3169 pos: pos
3170 }
3171 }
3172 embedded, fields := p.struct_decl_fields(language, is_union, true)
3173 return ast.StructDecl{
3174 attributes: attributes
3175 is_public: is_public
3176 is_union: is_union
3177 implements: impl_types
3178 embedded: embedded
3179 language: language
3180 name: name
3181 generic_params: generic_params
3182 fields: fields
3183 pos: pos
3184 }
3185}
3186
3187// returns (embedded_types, fields)
3188fn (mut p Parser) struct_decl_fields(language ast.Language, is_union bool, expect_semi bool) ([]ast.Expr, []ast.FieldDecl) {
3189 p.expect(.lcbr)
3190 mut embedded := []ast.Expr{}
3191 mut fields := []ast.FieldDecl{}
3192 _ = p.parse_struct_field_list(language, is_union, StructFieldAccessState{}, mut embedded, mut
3193 fields)
3194 p.next() // rcbr
3195 if expect_semi {
3196 p.expect(.semicolon)
3197 }
3198 return embedded, fields
3199}
3200
3201struct StructFieldAccessState {
3202 is_public bool
3203 is_mut bool
3204 is_module_mut bool
3205}
3206
3207// parse_struct_field_list parses fields until it hits `}`. Handles `$if` blocks
3208// and `@[if cond ?]` field attributes by evaluating the condition at parse time
3209// and omitting non-selected fields from the AST.
3210fn (mut p Parser) parse_struct_field_list(language ast.Language, is_union bool, start_access StructFieldAccessState, mut embedded []ast.Expr, mut fields []ast.FieldDecl) StructFieldAccessState {
3211 mut access := start_access
3212 for p.tok != .rcbr {
3213 // `$if cond { ... } $else { ... }` block grouping a set of fields.
3214 if p.tok == .dollar && p.peek() == .key_if {
3215 access = p.parse_comptime_struct_field_branch(language, is_union, access, false, mut
3216 embedded, mut fields)
3217 continue
3218 }
3219 leading_attributes := if p.tok in [.attribute, .lsbr] {
3220 p.attributes()
3221 } else {
3222 []ast.Attribute{}
3223 }
3224 if leading_attributes.len > 0 {
3225 p.error_with_pos('attributes on struct fields must be placed after the field declaration',
3226 leading_attributes[0].pos)
3227 }
3228 is_pub := p.tok == .key_pub
3229 if is_pub {
3230 p.next()
3231 }
3232 is_mut := p.tok == .key_mut
3233 if is_mut {
3234 p.next()
3235 }
3236 mut module_mut_access := false
3237 if is_pub && !is_mut && p.tok == .name && p.lit == 'module_mut' {
3238 module_mut_access = true
3239 p.next()
3240 if p.tok == .key_mut {
3241 p.error('`pub module_mut mut:` is invalid; use `pub module_mut:`')
3242 }
3243 }
3244 if is_mut && p.tok == .name && p.lit == 'module_mut' {
3245 if is_pub {
3246 p.error('`pub mut module_mut:` is invalid; use `pub module_mut:`')
3247 }
3248 p.error('`mut module_mut:` is invalid; use `pub module_mut:`')
3249 }
3250 if is_pub || is_mut {
3251 if module_mut_access {
3252 if language != .v || is_union {
3253 p.error('`pub module_mut:` is only supported on V struct fields')
3254 }
3255 }
3256 p.expect(.colon)
3257 access = StructFieldAccessState{
3258 is_public: is_pub
3259 is_mut: is_mut || module_mut_access
3260 is_module_mut: module_mut_access
3261 }
3262 continue
3263 }
3264 if p.tok == .name && p.lit == 'module_mut' && p.peek() == .colon {
3265 p.error('`module_mut:` must be written as `pub module_mut:`')
3266 }
3267 // C interop structs can use keywords as field names (e.g. `type int`).
3268 if p.tok.is_keyword() {
3269 field_name := p.expect_name_or_keyword()
3270 field_type := p.expect_type()
3271 field_value := if p.tok == .assign {
3272 p.next()
3273 p.expr(.lowest)
3274 } else {
3275 ast.empty_expr
3276 }
3277 field_attributes := if p.tok in [.attribute, .lsbr] {
3278 p.attributes()
3279 } else {
3280 []ast.Attribute{}
3281 }
3282 if p.tok == .semicolon {
3283 p.next()
3284 }
3285 field_elided := attributes_elide_field(field_attributes, mut p)
3286 if !field_elided {
3287 fields << ast.FieldDecl{
3288 name: field_name
3289 typ: field_type
3290 value: field_value
3291 attributes: field_attributes
3292 is_public: access.is_public
3293 is_mut: access.is_mut
3294 is_module_mut: access.is_module_mut
3295 }
3296 }
3297 continue
3298 }
3299 // NOTE: case documented in `p.try_type()` todo
3300 embed_or_name := p.expect_type()
3301 // embedded struct
3302 if p.tok == .semicolon {
3303 if language != .v {
3304 p.error('${language} structs do not support embedding')
3305 }
3306 if access.is_module_mut {
3307 p.error_with_pos('`pub module_mut:` cannot be applied to embedded struct fields',
3308 embed_or_name.pos())
3309 }
3310 p.next()
3311 embedded << embed_or_name
3312 continue
3313 }
3314 // field
3315 mut field_name := ''
3316 if embed_or_name is ast.Ident {
3317 field_name = embed_or_name.name
3318 } else {
3319 p.error('invalid field name')
3320 }
3321 field_type := p.expect_type()
3322 // field - default value
3323 field_value := if p.tok == .assign {
3324 p.next()
3325 p.expr(.lowest)
3326 } else {
3327 ast.empty_expr
3328 }
3329 field_attributes := if p.tok in [.attribute, .lsbr] {
3330 p.attributes()
3331 } else {
3332 []ast.Attribute{}
3333 }
3334 if p.tok == .semicolon {
3335 p.next()
3336 }
3337 field_elided := attributes_elide_field(field_attributes, mut p)
3338 if !field_elided {
3339 fields << ast.FieldDecl{
3340 name: field_name
3341 typ: field_type
3342 value: field_value
3343 attributes: field_attributes
3344 is_public: access.is_public
3345 is_mut: access.is_mut
3346 is_module_mut: access.is_module_mut
3347 }
3348 }
3349 }
3350 return access
3351}
3352
3353// attributes_elide_field returns true if any `@[if cond ?]` attribute evaluates
3354// to false, meaning the field should be omitted from the struct. Conditions
3355// that aren't shaped like flag expressions (e.g. type checks) are rejected
3356// rather than silently dropped — see `can_eval_comptime_cond`.
3357fn attributes_elide_field(attributes []ast.Attribute, mut p Parser) bool {
3358 for attr in attributes {
3359 if attr.comptime_cond !is ast.EmptyExpr {
3360 if !p.can_eval_comptime_cond(attr.comptime_cond) {
3361 p.error_with_pos('@[if ...] struct-field condition must be a compile-time flag expression (no type checks)',
3362 attr.comptime_cond.pos())
3363 }
3364 if !p.eval_comptime_cond(attr.comptime_cond) {
3365 return true
3366 }
3367 }
3368 }
3369 return false
3370}
3371
3372// parse_comptime_struct_field_branch parses a `$if cond { ... } $else ...`
3373// block in a struct field position. Only the matched branch's fields are added
3374// to `embedded`/`fields`; other branches are parsed and discarded so token
3375// positions stay correct. `force_skip` propagates "already matched" through
3376// `$else $if` chains so at most one branch contributes fields.
3377fn (mut p Parser) parse_comptime_struct_field_branch(language ast.Language, is_union bool, access StructFieldAccessState, force_skip bool, mut embedded []ast.Expr, mut fields []ast.FieldDecl) StructFieldAccessState {
3378 p.next() // $
3379 p.next() // if
3380 // `p.expr` would otherwise greedily parse `linux { ... }` as a struct init.
3381 exp_lcbr := p.exp_lcbr
3382 allow_init_in_exp_lcbr := p.allow_init_in_exp_lcbr
3383 p.exp_lcbr = true
3384 p.allow_init_in_exp_lcbr = true
3385 cond := p.expr(.lowest)
3386 p.exp_lcbr = exp_lcbr
3387 p.allow_init_in_exp_lcbr = allow_init_in_exp_lcbr
3388 if !p.can_eval_comptime_cond(cond) {
3389 p.error_with_pos('\$if struct-field condition must be a compile-time flag expression (no type checks)',
3390 cond.pos())
3391 }
3392 cond_matches := !force_skip && p.eval_comptime_cond(cond)
3393 // Allow `{` on next line after `cond ?` — scanner inserts `;` after `?`.
3394 if p.tok == .semicolon && p.peek() == .lcbr {
3395 p.next()
3396 }
3397 p.expect(.lcbr)
3398 mut selected_access := access
3399 if cond_matches {
3400 selected_access = p.parse_struct_field_list(language, is_union, access, mut embedded, mut
3401 fields)
3402 } else {
3403 mut tmp_emb := []ast.Expr{}
3404 mut tmp_flds := []ast.FieldDecl{}
3405 _ = p.parse_struct_field_list(language, is_union, access, mut tmp_emb, mut tmp_flds)
3406 }
3407 p.next() // rcbr
3408 // `};\n$else` — auto-inserted `;` sits between `}` and `$`. Consume it so
3409 // `peek_dollar_keyword` (which reads the source bytes at the scanner offset
3410 // for the *current* token) sees the `else` letters past `$`.
3411 if p.tok == .semicolon && p.peek() == .dollar {
3412 p.next()
3413 }
3414 if !(p.tok == .dollar && p.peek_dollar_keyword() == 'else') {
3415 if p.tok == .semicolon {
3416 p.next()
3417 }
3418 return selected_access
3419 }
3420 p.next() // $
3421 p.next() // else
3422 // `else` may be followed by auto-inserted `;` if `$if` / `{` is on the next line.
3423 if p.tok == .semicolon && p.peek() == .dollar {
3424 p.next()
3425 }
3426 if p.tok == .dollar && p.peek() == .key_if {
3427 // `$else $if` — propagate match status so at most one branch fires.
3428 new_force_skip := force_skip || cond_matches
3429 else_if_access := p.parse_comptime_struct_field_branch(language, is_union, access,
3430 new_force_skip, mut embedded, mut fields)
3431 return if cond_matches { selected_access } else { else_if_access }
3432 }
3433 if p.tok == .semicolon && p.peek() == .lcbr {
3434 p.next()
3435 }
3436 p.expect(.lcbr)
3437 else_matches := !force_skip && !cond_matches
3438 if else_matches {
3439 selected_access = p.parse_struct_field_list(language, is_union, access, mut embedded, mut
3440 fields)
3441 } else {
3442 mut tmp_emb := []ast.Expr{}
3443 mut tmp_flds := []ast.FieldDecl{}
3444 _ = p.parse_struct_field_list(language, is_union, access, mut tmp_emb, mut tmp_flds)
3445 }
3446 p.next() // rcbr
3447 if p.tok == .semicolon {
3448 p.next()
3449 }
3450 return selected_access
3451}
3452
3453fn (mut p Parser) select_expr() ast.SelectExpr {
3454 if p.tok == .key_else {
3455 pos := p.pos
3456 p.next()
3457 mut stmts := []ast.Stmt{}
3458 if p.tok == .lcbr {
3459 stmts = p.block()
3460 p.expect_semi()
3461 }
3462 return ast.SelectExpr{
3463 pos: pos
3464 stmt: ast.empty_stmt
3465 stmts: stmts
3466 }
3467 }
3468 exp_lcbr := p.exp_lcbr
3469 p.exp_lcbr = true
3470 stmt_expr := p.expr(.lowest)
3471 stmt := p.complete_simple_stmt(stmt_expr, false)
3472 p.exp_lcbr = exp_lcbr
3473 mut stmts := []ast.Stmt{}
3474 if p.tok == .lcbr {
3475 stmts = p.block()
3476 p.expect_semi()
3477 }
3478 select_expr := ast.SelectExpr{
3479 pos: p.pos
3480 stmt: stmt
3481 stmts: stmts
3482 next: if p.tok != .rcbr { ast.Expr(p.select_expr()) } else { ast.empty_expr }
3483 }
3484 return select_expr
3485}
3486
3487fn (mut p Parser) assoc_or_init_expr(typ ast.Expr) ast.Expr {
3488 init_pos := p.pos
3489 p.next() // .lcbr
3490 // assoc
3491 if p.tok == .ellipsis {
3492 p.next()
3493 lx := p.expr(.lowest)
3494 if p.tok == .comma || p.tok == .semicolon {
3495 p.next()
3496 } else if p.tok != .rcbr {
3497 p.expect(.semicolon)
3498 }
3499 mut fields := []ast.FieldInit{}
3500 for p.tok != .rcbr {
3501 if p.tok == .comma {
3502 p.next()
3503 }
3504 field_name := p.expect_name_or_keyword()
3505 p.expect(.colon)
3506 fields << ast.FieldInit{
3507 name: field_name
3508 value: p.expr(.lowest)
3509 }
3510 p.expect_semi()
3511 }
3512 p.next()
3513 return ast.AssocExpr{
3514 typ: typ
3515 expr: lx
3516 fields: fields
3517 pos: init_pos
3518 }
3519 }
3520 // struct init
3521 mut fields := []ast.FieldInit{}
3522 mut prev_has_name := false
3523 for p.tok != .rcbr {
3524 // could be name or init without field name
3525 mut field_name := ''
3526 mut value := ast.empty_expr
3527 if (p.tok == .name || p.tok.is_keyword()) && p.peek() == .colon {
3528 field_name = p.expect_name_or_keyword()
3529 p.expect(.colon)
3530 value = p.expr(.lowest)
3531 } else {
3532 value = p.expr(.lowest)
3533 }
3534 // name / value
3535 if p.tok == .colon {
3536 match mut value {
3537 // ast.BasicLiteral { field_name = value.value }
3538 // ast.StringLiteral { field_name = value.value }
3539 ast.Ident { field_name = value.name }
3540 else { p.error('expected field name, got ${value.name()}') }
3541 }
3542
3543 p.next()
3544 value = p.expr(.lowest)
3545 }
3546 has_name := field_name.len > 0
3547 if fields.len > 0 && has_name != prev_has_name {
3548 p.error('cant mix & match name & no name')
3549 }
3550 prev_has_name = has_name
3551 if p.tok == .comma {
3552 p.next()
3553 }
3554 fields << field_init_with_value(field_name, value)
3555 if p.tok == .semicolon {
3556 p.next()
3557 }
3558 // p.expect(.semicolon)
3559 }
3560 p.next()
3561 return init_expr_with_type(typ, fields, init_pos)
3562}
3563
3564fn (mut p Parser) string_literal(kind ast.StringLiteralKind) ast.Expr {
3565 pos := p.pos
3566 value0 := p.lit()
3567 if p.tok != .str_dollar {
3568 return ast.StringLiteral{
3569 kind: kind
3570 value: value0
3571 pos: pos
3572 }
3573 }
3574 mut values := []string{}
3575 mut inters := []ast.StringInter{}
3576 values << value0
3577 p.next()
3578 p.expect(.lcbr)
3579 inters << p.string_inter()
3580 p.expect(.rcbr)
3581 for p.tok == .string {
3582 value := p.lit()
3583 values << value
3584 if p.tok == .str_dollar {
3585 p.next()
3586 p.expect(.lcbr)
3587 inters << p.string_inter()
3588 p.expect(.rcbr)
3589 }
3590 }
3591 return ast.StringInterLiteral{
3592 kind: kind
3593 values: values
3594 inters: inters
3595 pos: pos
3596 }
3597}
3598
3599// TODO: finish
3600fn (mut p Parser) string_inter() ast.StringInter {
3601 expr := p.expr(.lowest)
3602 mut format := ast.StringInterFormat.unformatted
3603 mut format_expr := ast.empty_expr
3604 // TODO: proper
3605 if p.tok == .colon {
3606 p.next()
3607 // temp
3608 if p.tok in [.number, .minus, .plus] {
3609 format_expr = p.expr(.lowest)
3610 }
3611 // TODO
3612 // if p.tok == .minus {
3613 // p.next()
3614 // }
3615 // else if p.tok == .plus {
3616 // p.next()
3617 // }
3618 // if p.tok == .number {
3619 // _ = p.lit()
3620 // }
3621 if p.tok == .name {
3622 lit := p.lit
3623 format = ast.StringInterFormat.from_u8(lit[0])
3624 p.next()
3625 }
3626 }
3627 return ast.StringInter{
3628 format: format
3629 format_expr: format_expr
3630 expr: expr
3631 }
3632}
3633
3634@[inline]
3635fn (mut p Parser) generic_list() []ast.Expr {
3636 p.next()
3637 mut generic_list := [p.expect_type()]
3638 for p.tok == .comma {
3639 p.next()
3640 generic_list << p.expect_type()
3641 }
3642 p.expect(.rsbr)
3643 return generic_list
3644}
3645
3646@[direct_array_access]
3647fn (mut p Parser) decl_language() ast.Language {
3648 mut language := ast.Language.v
3649 lit := p.lit
3650 if lit.len == 1 && lit[0] == `C` {
3651 language = .c
3652 } else if lit.len == 2 && lit[0] == `J` && lit[1] == `S` {
3653 language = .js
3654 } else {
3655 return language
3656 }
3657 p.next()
3658 p.expect(.dot)
3659 return language
3660}
3661
3662fn (mut p Parser) type_decl(is_public bool) ast.TypeDecl {
3663 p.next()
3664 language := p.decl_language()
3665 name := p.expect_name()
3666 generic_params := if p.tok == .lsbr { p.generic_list() } else { []ast.Expr{} }
3667
3668 // p.log('ast.TypeDecl: ${name}')
3669 p.expect(.assign)
3670 typ := p.expect_type()
3671
3672 // alias `type MyType = int`
3673 // check for multi-line sum type: semicolon followed by pipe
3674 if p.tok != .pipe && !(p.tok == .semicolon && p.peek() == .pipe) {
3675 p.expect(.semicolon)
3676 return ast.TypeDecl{
3677 is_public: is_public
3678 language: language
3679 name: name
3680 generic_params: generic_params
3681 base_type: typ
3682 }
3683 }
3684 // sum type `type MyType = int | string`
3685 // skip semicolon if present (multi-line case)
3686 if p.tok == .semicolon {
3687 p.next()
3688 }
3689 p.next() // skip pipe
3690 mut variants := [typ, p.expect_type()]
3691 for p.tok == .pipe || (p.tok == .semicolon && p.peek() == .pipe) {
3692 if p.tok == .semicolon {
3693 p.next()
3694 }
3695 p.next() // skip pipe
3696 variants << p.expect_type()
3697 }
3698 p.expect(.semicolon)
3699 // TODO: consider separate node for alias / sum type ?
3700 return ast.TypeDecl{
3701 is_public: is_public
3702 language: language
3703 name: name
3704 generic_params: generic_params
3705 variants: variants
3706 }
3707}
3708
3709@[inline]
3710fn ident_with_name(pos token.Pos, name string) ast.Ident {
3711 mut ident := ast.Ident{}
3712 ident.pos = pos
3713 ident.name = name
3714 return ident
3715}
3716
3717@[inline]
3718fn (mut p Parser) ident() ast.Ident {
3719 pos := p.pos
3720 name := p.expect_name()
3721 mut ident := ast.Ident{}
3722 ident.pos = pos
3723 ident.name = name
3724 return ident
3725}
3726
3727// like ident() but allows keywords as names (for C/JS function names like `C.select`)
3728@[inline]
3729fn (mut p Parser) ident_or_keyword() ast.Ident {
3730 pos := p.pos
3731 name := p.expect_name_or_keyword()
3732 mut ident := ast.Ident{}
3733 ident.pos = pos
3734 ident.name = name
3735 return ident
3736}
3737
3738@[inline]
3739fn (mut p Parser) ident_or_selector_expr() ast.Expr {
3740 base := ast.Expr(p.ident())
3741 mut full_name := base.name()
3742 if p.tok != .dot {
3743 return base
3744 }
3745 dot_pos := p.pos
3746 p.next()
3747 mut rhs_pos := p.pos
3748 mut rhs_name := ''
3749 mut comptime_inner := ast.Expr(ast.empty_expr)
3750 mut is_comptime_selector := false
3751 if p.tok == .dollar {
3752 p.next()
3753 if p.tok == .lpar {
3754 p.next()
3755 comptime_inner = p.expr(.lowest)
3756 p.expect(.rpar)
3757 } else {
3758 comptime_inner = p.expr(.lowest)
3759 }
3760 rhs_pos = p.pos
3761 rhs_name = '__comptime_selector__'
3762 is_comptime_selector = true
3763 } else {
3764 rhs_name = p.expect_name_or_keyword()
3765 }
3766 full_name += '.' + rhs_name
3767 if dot_pos.is_valid() {
3768 p.selector_names[dot_pos.id] = full_name
3769 }
3770 mut lhs := selector_expr_with_rhs_name(base, rhs_pos, rhs_name, dot_pos)
3771 // `app.$method(args)` - inner parses as CallExpr or CallOrCastExpr;
3772 // lift its args onto our SelectorExpr so the call form is preserved.
3773 if is_comptime_selector {
3774 if comptime_inner is ast.CallExpr {
3775 lhs = ast.Expr(ast.CallExpr{
3776 lhs: lhs
3777 args: comptime_inner.args
3778 pos: dot_pos
3779 })
3780 } else if comptime_inner is ast.CallOrCastExpr {
3781 lhs = ast.Expr(ast.CallExpr{
3782 lhs: lhs
3783 args: [comptime_inner.expr]
3784 pos: dot_pos
3785 })
3786 }
3787 }
3788 for p.tok == .dot {
3789 next_dot_pos := p.pos
3790 p.next()
3791 rhs_pos = p.pos
3792 mut inner_comptime := ast.Expr(ast.empty_expr)
3793 mut inner_is_comptime := false
3794 if p.tok == .dollar {
3795 p.next()
3796 if p.tok == .lpar {
3797 p.next()
3798 inner_comptime = p.expr(.lowest)
3799 p.expect(.rpar)
3800 } else {
3801 inner_comptime = p.expr(.lowest)
3802 }
3803 rhs_pos = p.pos
3804 rhs_name = '__comptime_selector__'
3805 inner_is_comptime = true
3806 } else {
3807 rhs_name = p.expect_name_or_keyword()
3808 }
3809 full_name += '.' + rhs_name
3810 if next_dot_pos.is_valid() {
3811 p.selector_names[next_dot_pos.id] = full_name
3812 }
3813 lhs = selector_expr_with_rhs_name(lhs, rhs_pos, rhs_name, next_dot_pos)
3814 if inner_is_comptime {
3815 if inner_comptime is ast.CallExpr {
3816 lhs = ast.Expr(ast.CallExpr{
3817 lhs: lhs
3818 args: inner_comptime.args
3819 pos: next_dot_pos
3820 })
3821 } else if inner_comptime is ast.CallOrCastExpr {
3822 lhs = ast.Expr(ast.CallExpr{
3823 lhs: lhs
3824 args: [inner_comptime.expr]
3825 pos: next_dot_pos
3826 })
3827 }
3828 }
3829 }
3830 return lhs
3831}
3832
3833fn (mut p Parser) selector_rhs_ident() ast.Ident {
3834 if p.tok == .dollar {
3835 p.next()
3836 p.expr(.lowest)
3837 return ident_with_name(p.pos, 'TODO: comptime selector')
3838 }
3839 // Allow keywords as names for:
3840 // - C/JS calls (e.g. `C.select`)
3841 // - method calls (e.g. `mutex.lock()`)
3842 // - enum values (e.g. `.select`)
3843 return p.ident_or_keyword()
3844}
3845
3846fn (mut p Parser) log(msg string) {
3847 if p.pref.verbose {
3848 println(msg)
3849 }
3850}
3851
3852// TODO/NOTE: this can be completely replaced with token.File.position()
3853// I was only using this since it skips the binary search and is slightly
3854// faster, howevrer if only used in error conditions this is irrelevant.
3855fn (mut p Parser) current_position() token.Position {
3856 offset := p.pos.offset - p.file.base
3857 return token.Position{
3858 filename: p.file.name
3859 line: p.line
3860 offset: offset
3861 column: offset - p.file.line_start(p.line) + 1
3862 }
3863}
3864
3865fn (mut p Parser) error_expected(exp token.Token, got token.Token) {
3866 p.error('unexpected token. expecting `${exp}`, got `${got}`')
3867}
3868
3869// so we can customize the error message used by warn & error
3870fn (mut p Parser) error_message(msg string, kind errors.Kind, pos token.Position) {
3871 errors.error(msg, errors.details(p.file, pos, 2), kind, pos)
3872}
3873
3874fn (mut p Parser) warn(msg string) {
3875 p.error_message(msg, .warning, p.current_position())
3876}
3877
3878@[noreturn]
3879fn (mut p Parser) error(msg string) {
3880 p.error_with_position(msg, p.current_position())
3881 // p.error_with_position(msg, p.file.position(p.pos))
3882}
3883
3884@[noreturn]
3885fn (mut p Parser) error_with_pos(msg string, pos token.Pos) {
3886 p.error_with_position(msg, p.file.position(pos))
3887}
3888
3889@[noreturn]
3890fn (mut p Parser) error_with_position(msg string, pos token.Position) {
3891 p.error_message(msg, .error, pos)
3892 exit(1)
3893}
3894
3895// can_eval_comptime_cond reports whether `cond` is shaped like a flag
3896// expression decidable at parse time (idents, `!`, `&&`, `||`, postfix `?`,
3897// parens). Anything else — notably type checks like `T is string` — is left
3898// for a later stage. Struct field parsing rejects non-evaluable conditions
3899// with a parse error rather than silently choosing a branch, because the
3900// field set affects layout / type checking.
3901fn (p &Parser) can_eval_comptime_cond(cond ast.Expr) bool {
3902 match cond {
3903 ast.Ident {
3904 return true
3905 }
3906 ast.ComptimeExpr {
3907 return p.can_eval_comptime_cond(cond.expr)
3908 }
3909 ast.CallExpr {
3910 return parser_pkgconfig_call_name(cond) != none
3911 }
3912 ast.CallOrCastExpr {
3913 return parser_pkgconfig_call_name(cond) != none
3914 }
3915 ast.PrefixExpr {
3916 if cond.op != .not {
3917 return false
3918 }
3919 return p.can_eval_comptime_cond(cond.expr)
3920 }
3921 ast.InfixExpr {
3922 if cond.op != .and && cond.op != .logical_or {
3923 return false
3924 }
3925 return p.can_eval_comptime_cond(cond.lhs) && p.can_eval_comptime_cond(cond.rhs)
3926 }
3927 ast.PostfixExpr {
3928 if cond.op != .question {
3929 return false
3930 }
3931 // `feature?` is a flag wrapped in `?`; the inner expression follows
3932 // the same shape rules (e.g. `(!feature)?` is allowed).
3933 return p.can_eval_comptime_cond(cond.expr)
3934 }
3935 ast.ParenExpr {
3936 return p.can_eval_comptime_cond(cond.expr)
3937 }
3938 else {
3939 return false
3940 }
3941 }
3942}
3943
3944// eval_comptime_cond evaluates a compile-time condition expression at parse
3945// time. Callers must have checked `can_eval_comptime_cond` first; this
3946// function assumes the condition is in the supported shape.
3947fn (p &Parser) eval_comptime_cond(cond ast.Expr) bool {
3948 match cond {
3949 ast.Ident {
3950 return p.eval_comptime_flag(cond.name)
3951 }
3952 ast.ComptimeExpr {
3953 return p.eval_comptime_cond(cond.expr)
3954 }
3955 ast.CallExpr {
3956 if pkg_name := parser_pkgconfig_call_name(cond) {
3957 return p.eval_pkgconfig_cond(pkg_name)
3958 }
3959 }
3960 ast.CallOrCastExpr {
3961 if pkg_name := parser_pkgconfig_call_name(cond) {
3962 return p.eval_pkgconfig_cond(pkg_name)
3963 }
3964 }
3965 ast.PrefixExpr {
3966 if cond.op == .not {
3967 return !p.eval_comptime_cond(cond.expr)
3968 }
3969 }
3970 ast.InfixExpr {
3971 if cond.op == .and {
3972 return p.eval_comptime_cond(cond.lhs) && p.eval_comptime_cond(cond.rhs)
3973 }
3974 if cond.op == .logical_or {
3975 return p.eval_comptime_cond(cond.lhs) || p.eval_comptime_cond(cond.rhs)
3976 }
3977 }
3978 ast.PostfixExpr {
3979 if cond.op == .question {
3980 if cond.expr is ast.Ident {
3981 return pref.comptime_optional_flag_value(p.pref, cond.expr.name)
3982 }
3983 }
3984 }
3985 ast.ParenExpr {
3986 return p.eval_comptime_cond(cond.expr)
3987 }
3988 else {}
3989 }
3990
3991 return false
3992}
3993
3994fn (p &Parser) eval_pkgconfig_cond(pkg_name string) bool {
3995 if p.pref.is_cross_target() {
3996 return false
3997 }
3998 return pref.comptime_pkgconfig_value(pkg_name)
3999}
4000
4001// eval_comptime_flag delegates to the shared `pref.comptime_flag_value` so
4002// the parser and the transformer recognize exactly the same flag names.
4003fn (p &Parser) eval_comptime_flag(name string) bool {
4004 return pref.comptime_flag_value(p.pref, name)
4005}
4006
4007fn parser_pkgconfig_call_name(expr ast.Expr) ?string {
4008 match expr {
4009 ast.CallExpr {
4010 if expr.lhs is ast.Ident && expr.lhs.name == 'pkgconfig' && expr.args.len == 1 {
4011 return parser_string_literal_value(expr.args[0])
4012 }
4013 }
4014 ast.CallOrCastExpr {
4015 if expr.lhs is ast.Ident && expr.lhs.name == 'pkgconfig' {
4016 return parser_string_literal_value(expr.expr)
4017 }
4018 }
4019 else {}
4020 }
4021
4022 return none
4023}
4024
4025fn parser_string_literal_value(expr ast.Expr) ?string {
4026 if expr is ast.StringLiteral {
4027 return parser_unquote_string_literal_value(expr.value)
4028 }
4029 return none
4030}
4031
4032fn parser_unquote_string_literal_value(value string) string {
4033 if value.len >= 2 && ((value[0] == `"` && value[value.len - 1] == `"`)
4034 || (value[0] == `'` && value[value.len - 1] == `'`)) {
4035 return value[1..value.len - 1]
4036 }
4037 return value
4038}
4039