v / vlib / v2 / eval / eval.v
6244 lines · 6006 sloc · 149.78 KB
Raw
1module eval
2
3import os
4import os.cmdline as host_cmdline
5import strconv
6import time
7import v2.ast
8import v2.parser
9import v2.pref
10import v2.token
11
12type Value = ArrayValue
13 | FlagsValue
14 | MapValue
15 | ModuleValue
16 | RangeValue
17 | StructValue
18 | TupleValue
19 | TypeValue
20 | VoidValue
21 | bool
22 | f64
23 | i64
24 | string
25
26fn value_f64_payload(value Value) f64 {
27 data := unsafe { (&u64(&value))[1] }
28 if data == 0 {
29 return 0.0
30 }
31 return unsafe { *(&f64(voidptr(data))) }
32}
33
34struct ArrayValue {
35mut:
36 elem_type_name string
37 values []Value
38}
39
40struct FlagsValue {}
41
42struct MapEntry {
43 key Value
44 value Value
45}
46
47struct MapValue {
48 default_value Value
49mut:
50 entries []MapEntry
51}
52
53struct ModuleValue {
54 name string
55}
56
57struct RangeValue {
58 start i64
59 end i64
60 inclusive bool
61}
62
63struct StructValue {
64 type_name string
65mut:
66 fields map[string]Value
67}
68
69struct TupleValue {
70 values []Value
71}
72
73struct TypeValue {
74 name string
75}
76
77struct VoidValue {}
78
79struct FunctionDef {
80 decl ast.FnDecl
81 file_name string
82}
83
84struct ConstEntry {
85 expr ast.Expr
86 file_name string
87mut:
88 cached bool
89 evaluating bool
90 value Value
91}
92
93struct SumTypeInfo {
94 module_name string
95 name string
96mut:
97 variant_tags map[string]int
98 tag_field_aliases map[int][]string
99}
100
101struct InferredSumTypeVariant {
102 tag int
103 variant_name string
104}
105
106struct WrappedSumTypeVariant {
107 tag int
108 payload Value
109}
110
111struct ScopeFrame {
112mut:
113 vars map[string]Value
114 defers [][]ast.Stmt
115}
116
117struct CallFrame {
118 module_name string
119 file_name string
120 fn_name string
121}
122
123struct MaybeValue {
124 found bool
125 value Value
126}
127
128struct MaybeCallResult {
129 found bool
130 result CallResult
131}
132
133struct MaybeFunctionTarget {
134 found bool
135 module_name string
136 fn_name string
137}
138
139struct CallResult {
140 values []Value
141mut:
142 mut_args map[int]Value
143}
144
145enum FlowKind {
146 normal
147 break_
148 continue_
149 goto_
150 return_
151}
152
153struct FlowSignal {
154 kind FlowKind
155 label string
156 values []Value
157}
158
159fn void_value() Value {
160 return VoidValue{}
161}
162
163fn wrap_result_ok(value Value) Value {
164 return StructValue{
165 type_name: 'Result'
166 fields: {
167 'is_error': Value(false)
168 'err': Value('')
169 'data': value
170 }
171 }
172}
173
174fn wrap_result_err(message string) Value {
175 return StructValue{
176 type_name: 'Result'
177 fields: {
178 'is_error': Value(true)
179 'err': Value(message)
180 'data': void_value()
181 }
182 }
183}
184
185fn wrap_option_ok(value Value) Value {
186 return StructValue{
187 type_name: 'Option'
188 fields: {
189 'state': Value(i64(0))
190 'err': Value('')
191 'data': value
192 }
193 }
194}
195
196fn wrap_option_none() Value {
197 return StructValue{
198 type_name: 'Option'
199 fields: {
200 'state': Value(i64(1))
201 'err': Value('')
202 'data': void_value()
203 }
204 }
205}
206
207fn os_result_value(result os.Result) Value {
208 return StructValue{
209 type_name: 'os.Result'
210 fields: {
211 'exit_code': Value(i64(result.exit_code))
212 'output': Value(result.output)
213 }
214 }
215}
216
217fn is_option_value(value Value) bool {
218 return value is StructValue && 'state' in value.fields && 'err' in value.fields
219 && 'data' in value.fields
220}
221
222fn is_result_value(value Value) bool {
223 return value is StructValue && 'is_error' in value.fields && 'err' in value.fields
224 && 'data' in value.fields
225}
226
227// Eval interprets the v2 AST directly for a limited subset of V.
228pub struct Eval {
229pub mut:
230 capture_output bool
231 prefs pref.Preferences
232mut:
233 stdout_data string
234 stderr_data string
235 functions map[string]map[string]FunctionDef
236 consts map[string]map[string]ConstEntry
237 sum_types map[string]SumTypeInfo
238 struct_field_types map[string]map[string]map[string]ast.Expr
239 struct_embeds map[string]map[string][]string
240 type_kinds map[string]map[string]string
241 type_names map[string]map[string]bool
242 file_import_alias map[string]map[string]string
243 modules map[string]bool
244 scopes []ScopeFrame
245 call_stack []CallFrame
246 next_token_pos_id i64
247}
248
249// new returns a new evaluator configured for direct execution.
250pub fn new(prefs_ &pref.Preferences) Eval {
251 return Eval{
252 capture_output: false
253 prefs: *prefs_
254 }
255}
256
257// create returns a capturing evaluator convenient for tests.
258pub fn create() Eval {
259 return Eval{
260 capture_output: true
261 prefs: pref.new_preferences()
262 }
263}
264
265// stdout returns the captured stdout stream.
266pub fn (e &Eval) stdout() string {
267 return e.stdout_data
268}
269
270// stderr returns the captured stderr stream.
271pub fn (e &Eval) stderr() string {
272 return e.stderr_data
273}
274
275// run_text parses and executes a single V source string.
276pub fn (mut e Eval) run_text(code string) ![]Value {
277 tmp_file := os.join_path(os.temp_dir(), 'v2_eval_${os.getpid()}_${time.now().unix_micro()}.v')
278 os.write_file(tmp_file, code)!
279 defer {
280 os.rm(tmp_file) or {}
281 }
282 mut file_set := token.FileSet.new()
283 mut par := parser.Parser.new(&e.prefs)
284 files := par.parse_files([tmp_file], mut file_set)
285 return e.run_files(files)
286}
287
288// run_files executes parsed AST files and invokes `main.main`.
289pub fn (mut e Eval) run_files(files []ast.File) ![]Value {
290 e.reset()
291 e.register_files(files)!
292 return e.call_function('main', 'main', []Value{})!.values
293}
294
295fn (mut e Eval) reset() {
296 e.stdout_data = ''
297 e.stderr_data = ''
298 e.functions = map[string]map[string]FunctionDef{}
299 e.consts = map[string]map[string]ConstEntry{}
300 e.sum_types = map[string]SumTypeInfo{}
301 e.struct_field_types = map[string]map[string]map[string]ast.Expr{}
302 e.struct_embeds = map[string]map[string][]string{}
303 e.type_kinds = map[string]map[string]string{}
304 e.type_names = map[string]map[string]bool{}
305 e.file_import_alias = map[string]map[string]string{}
306 e.modules = map[string]bool{}
307 e.scopes = []ScopeFrame{}
308 e.call_stack = []CallFrame{}
309 e.next_token_pos_id = 0
310}
311
312fn (mut e Eval) register_files(files []ast.File) ! {
313 for file in files {
314 e.modules[file.mod] = true
315 mut import_aliases := map[string]string{}
316 for imp in file.imports {
317 import_aliases[imp.alias] = imp.name.all_after_last('.')
318 }
319 e.file_import_alias[file.name] = import_aliases.clone()
320 if file.mod !in e.functions {
321 e.functions[file.mod] = map[string]FunctionDef{}
322 }
323 if file.mod !in e.consts {
324 e.consts[file.mod] = map[string]ConstEntry{}
325 }
326 if file.mod !in e.type_names {
327 e.type_names[file.mod] = map[string]bool{}
328 }
329 if file.mod !in e.struct_field_types {
330 e.struct_field_types[file.mod] = map[string]map[string]ast.Expr{}
331 }
332 if file.mod !in e.struct_embeds {
333 e.struct_embeds[file.mod] = map[string][]string{}
334 }
335 if file.mod !in e.type_kinds {
336 e.type_kinds[file.mod] = map[string]string{}
337 }
338 for stmt in file.stmts {
339 match stmt {
340 ast.ConstDecl {
341 for field in stmt.fields {
342 key := if field.name.contains('.') {
343 field.name.all_after_last('.')
344 } else {
345 field.name
346 }
347 e.consts[file.mod][key] = ConstEntry{
348 expr: field.value
349 file_name: file.name
350 value: void_value()
351 }
352 }
353 }
354 ast.EnumDecl {
355 e.type_names[file.mod][stmt.name] = true
356 e.type_kinds[file.mod][stmt.name] = 'Enum'
357 is_flag := stmt.attributes.has('flag')
358 mut next_value := if is_flag { i64(1) } else { i64(0) }
359 for field in stmt.fields {
360 key := field.name
361 value := if field.value is ast.EmptyExpr {
362 Value(next_value)
363 } else if field.value is ast.BasicLiteral && field.value.kind == .number {
364 Value(strconv.parse_int(field.value.value, 0, 64)!)
365 } else {
366 Value(next_value)
367 }
368 int_value := e.value_as_int(value)!
369 e.consts[file.mod][key] = ConstEntry{
370 expr: ast.empty_expr
371 file_name: file.name
372 cached: true
373 value: int_value
374 }
375 next_value = if is_flag {
376 if int_value <= 0 { i64(1) } else { int_value * 2 }
377 } else {
378 int_value + 1
379 }
380 }
381 }
382 ast.InterfaceDecl {
383 e.type_names[file.mod][stmt.name] = true
384 e.type_kinds[file.mod][stmt.name] = 'Interface'
385 }
386 ast.FnDecl {
387 e.functions[file.mod][stmt.name] = FunctionDef{
388 decl: stmt
389 file_name: file.name
390 }
391 if stmt.is_method {
392 receiver_type_name := stmt.receiver.typ.name()
393 if receiver_type_name != '' && receiver_type_name != 'EmptyExpr' {
394 e.functions[file.mod]['${receiver_type_name}__${stmt.name}'] = FunctionDef{
395 decl: stmt
396 file_name: file.name
397 }
398 short_receiver_name := receiver_type_name.all_after_last('.')
399 if short_receiver_name != receiver_type_name {
400 e.functions[file.mod]['${short_receiver_name}__${stmt.name}'] = FunctionDef{
401 decl: stmt
402 file_name: file.name
403 }
404 }
405 normalized_receiver_name :=
406 receiver_type_name.trim_left('&').trim_right('*')
407 if normalized_receiver_name != ''
408 && normalized_receiver_name != receiver_type_name {
409 e.functions[file.mod]['${normalized_receiver_name}__${stmt.name}'] = FunctionDef{
410 decl: stmt
411 file_name: file.name
412 }
413 short_normalized_name :=
414 normalized_receiver_name.all_after_last('.')
415 if short_normalized_name != normalized_receiver_name {
416 e.functions[file.mod]['${short_normalized_name}__${stmt.name}'] = FunctionDef{
417 decl: stmt
418 file_name: file.name
419 }
420 }
421 }
422 }
423 }
424 }
425 ast.StructDecl {
426 e.type_names[file.mod][stmt.name] = true
427 e.type_kinds[file.mod][stmt.name] = 'Struct'
428 mut field_types := map[string]ast.Expr{}
429 for field in stmt.fields {
430 field_types[field.name] = field.typ
431 }
432 e.struct_field_types[file.mod][stmt.name] = field_types.clone()
433 e.struct_embeds[file.mod][stmt.name] = stmt.embedded.map(e.type_expr_name(it))
434 }
435 ast.TypeDecl {
436 e.type_names[file.mod][stmt.name] = true
437 if stmt.variants.len > 0 {
438 e.type_kinds[file.mod][stmt.name] = 'SumType'
439 e.register_sum_type(file.mod, stmt)
440 } else {
441 e.type_kinds[file.mod][stmt.name] = 'Alias'
442 }
443 }
444 else {}
445 }
446 }
447 }
448 if 'main' !in e.functions || 'main' !in e.functions['main'] {
449 return error('v2.eval: missing main.main entry point')
450 }
451}
452
453fn add_type_name_alias(mut aliases []string, alias string) {
454 if alias != '' && alias !in aliases {
455 aliases << alias
456 }
457}
458
459fn (e &Eval) type_name_aliases(module_name string, type_name string) []string {
460 mut aliases := []string{}
461 if type_name == '' {
462 return aliases
463 }
464 add_type_name_alias(mut aliases, type_name)
465 if type_name.starts_with('[]') {
466 for inner_alias in e.type_name_aliases(module_name, type_name[2..]) {
467 add_type_name_alias(mut aliases, '[]${inner_alias}')
468 }
469 return aliases
470 }
471 if type_name.starts_with('map[') {
472 inner := type_name[4..]
473 if bracket_idx := inner.index(']') {
474 key_name := inner[..bracket_idx]
475 value_name := inner[bracket_idx + 1..]
476 key_aliases := e.type_name_aliases(module_name, key_name)
477 value_aliases := e.type_name_aliases(module_name, value_name)
478 for key_alias in key_aliases {
479 for value_alias in value_aliases {
480 add_type_name_alias(mut aliases, 'map[${key_alias}]${value_alias}')
481 }
482 }
483 }
484 return aliases
485 }
486 if type_name.contains('__') {
487 add_type_name_alias(mut aliases, type_name.replace('__', '.'))
488 add_type_name_alias(mut aliases, type_name.all_after_last('__'))
489 return aliases
490 }
491 if type_name.contains('.') {
492 add_type_name_alias(mut aliases, type_name.replace('.', '__'))
493 add_type_name_alias(mut aliases, type_name.all_after_last('.'))
494 return aliases
495 }
496 if module_name != '' {
497 add_type_name_alias(mut aliases, '${module_name}.${type_name}')
498 add_type_name_alias(mut aliases, '${e.mangled_module_name(module_name)}__${type_name}')
499 }
500 return aliases
501}
502
503fn (e &Eval) sumtype_variant_field_name(variant_name string) string {
504 if variant_name.starts_with('[]') {
505 return 'Array_${variant_name[2..].replace('.', '__')}'
506 }
507 if variant_name.starts_with('map[') {
508 inner := variant_name[4..]
509 if bracket_idx := inner.index(']') {
510 key := inner[..bracket_idx].replace('.', '__')
511 val := inner[bracket_idx + 1..].replace('.', '__')
512 return 'Map_${key}_${val}'
513 }
514 }
515 if variant_name.contains('__') {
516 return variant_name.all_after_last('__')
517 }
518 if variant_name.contains('.') {
519 return variant_name.all_after_last('.')
520 }
521 return variant_name
522}
523
524fn (mut e Eval) register_sum_type(module_name string, decl ast.TypeDecl) {
525 mut info := SumTypeInfo{
526 module_name: module_name
527 name: decl.name
528 variant_tags: map[string]int{}
529 tag_field_aliases: map[int][]string{}
530 }
531 for tag, variant in decl.variants {
532 variant_name := e.type_expr_name(variant)
533 for alias in e.type_name_aliases(module_name, variant_name) {
534 info.variant_tags[alias] = tag
535 field_alias := '_' + e.sumtype_variant_field_name(alias)
536 mut field_aliases := []string{}
537 if tag in info.tag_field_aliases {
538 field_aliases = info.tag_field_aliases[tag]
539 }
540 if field_alias !in field_aliases {
541 field_aliases << field_alias
542 }
543 info.tag_field_aliases[tag] = field_aliases
544 }
545 }
546 for alias in e.type_name_aliases(module_name, decl.name) {
547 e.sum_types[alias] = info
548 }
549}
550
551fn is_composite_sumtype_lookup_name(name string) bool {
552 return name.starts_with('[]') || name.starts_with('map[')
553 || (name.starts_with('[') && name.contains(']')) || name.starts_with('?')
554 || name.starts_with('!') || name.starts_with('chan ')
555 || name.starts_with('thread ')
556}
557
558fn (e &Eval) sum_type_info(type_name string) ?SumTypeInfo {
559 trimmed := type_name.trim_left('&').trim_right('*')
560 if trimmed in e.sum_types {
561 return e.sum_types[trimmed]
562 }
563 if is_composite_sumtype_lookup_name(trimmed) {
564 return none
565 }
566 if trimmed.contains('.') {
567 short_name := trimmed.all_after_last('.')
568 if short_name in e.sum_types {
569 return e.sum_types[short_name]
570 }
571 }
572 if trimmed.contains('__') {
573 short_name := trimmed.all_after_last('__')
574 if short_name in e.sum_types {
575 return e.sum_types[short_name]
576 }
577 dotted := trimmed.replace('__', '.')
578 if dotted in e.sum_types {
579 return e.sum_types[dotted]
580 }
581 }
582 return none
583}
584
585fn (e &Eval) is_sum_type_name(type_name string) bool {
586 _ = e.sum_type_info(type_name) or { return false }
587 return true
588}
589
590fn (e &Eval) lookup_sumtype_variant_tag(info SumTypeInfo, variant_name string) ?int {
591 for alias in e.type_name_aliases(info.module_name, variant_name) {
592 if alias in info.variant_tags {
593 return info.variant_tags[alias]
594 }
595 }
596 return none
597}
598
599fn builtin_type_kind_alias(name string) ?string {
600 trimmed := name.all_after_last('.')
601 return match trimmed {
602 'bool', 'f32', 'f64', 'float_literal', 'i8', 'i16', 'i32', 'int', 'i64', 'int_literal',
603 'u8', 'byte', 'u16', 'u32', 'u64' {
604 'Primitive'
605 }
606 'byteptr', 'charptr', 'voidptr' {
607 'Alias'
608 }
609 'char' {
610 'Char'
611 }
612 'isize' {
613 'ISize'
614 }
615 'nil' {
616 'Nil'
617 }
618 'none' {
619 'None'
620 }
621 'rune' {
622 'Rune'
623 }
624 'string' {
625 'String'
626 }
627 'usize' {
628 'USize'
629 }
630 'void' {
631 'Void'
632 }
633 else {
634 none
635 }
636 }
637}
638
639fn (e &Eval) registered_type_kind(type_name string) ?string {
640 for alias in struct_field_lookup_candidates(type_name) {
641 mut module_name := ''
642 mut short_type_name := alias
643 if alias.contains('.') {
644 module_name = alias.all_before_last('.')
645 short_type_name = alias.all_after_last('.')
646 }
647 if module_name != '' {
648 if module_kinds := e.type_kinds[module_name] {
649 if short_type_name in module_kinds {
650 return module_kinds[short_type_name] or { return none }
651 }
652 }
653 continue
654 }
655 current_module := e.current_module_name()
656 if module_kinds := e.type_kinds[current_module] {
657 if short_type_name in module_kinds {
658 return module_kinds[short_type_name] or { return none }
659 }
660 }
661 for module_kinds in e.type_kinds.values() {
662 if short_type_name in module_kinds {
663 return module_kinds[short_type_name] or { return none }
664 }
665 }
666 }
667 return none
668}
669
670fn (e &Eval) type_value_variant_aliases(info SumTypeInfo, value TypeValue) []string {
671 mut aliases := e.type_name_aliases(info.module_name, value.name)
672 if type_kind := e.registered_type_kind(value.name) {
673 add_type_name_alias(mut aliases, type_kind)
674 }
675 if builtin_kind := builtin_type_kind_alias(value.name) {
676 add_type_name_alias(mut aliases, builtin_kind)
677 }
678 add_type_name_alias(mut aliases, 'Type')
679 return aliases
680}
681
682fn (e &Eval) canonical_sumtype_variant_name(info SumTypeInfo, alias string) string {
683 if alias.contains('.') {
684 return alias
685 }
686 if alias.contains('__') {
687 dotted := alias.replace('__', '.')
688 if dotted.contains('.') {
689 return dotted
690 }
691 }
692 short_name := alias.all_after_last('__')
693 if info.module_name != '' && short_name != '' {
694 return '${info.module_name}.${short_name}'
695 }
696 if short_name != '' {
697 return short_name
698 }
699 return alias
700}
701
702fn (e &Eval) lookup_struct_field_names(type_name string) ?[]string {
703 for candidate in struct_field_lookup_candidates(type_name) {
704 mut module_name := ''
705 mut short_type_name := candidate
706 if candidate.contains('.') {
707 module_name = candidate.all_before_last('.')
708 short_type_name = candidate.all_after_last('.')
709 }
710 if module_name != '' {
711 if module_fields := e.struct_field_types[module_name] {
712 if type_fields := module_fields[short_type_name] {
713 return type_fields.keys()
714 }
715 }
716 continue
717 }
718 current_module := e.current_module_name()
719 if module_fields := e.struct_field_types[current_module] {
720 if type_fields := module_fields[short_type_name] {
721 return type_fields.keys()
722 }
723 }
724 for _, module_fields in e.struct_field_types {
725 if type_fields := module_fields[short_type_name] {
726 return type_fields.keys()
727 }
728 }
729 }
730 return none
731}
732
733fn (e &Eval) zero_value_for_type_name(type_name string) Value {
734 if type_name == '' {
735 return void_value()
736 }
737 if type_name.starts_with('[]') {
738 return ArrayValue{}
739 }
740 if type_name.starts_with('map[') {
741 inner := type_name[4..]
742 if bracket_idx := inner.index(']') {
743 return MapValue{
744 default_value: e.zero_value_for_type_name(inner[bracket_idx + 1..])
745 }
746 }
747 return MapValue{}
748 }
749 if type_name.starts_with('?') || type_name.starts_with('!') {
750 return void_value()
751 }
752 match type_name {
753 'bool' {
754 return false
755 }
756 'byte', 'char', 'i8', 'i16', 'i32', 'int', 'i64', 'isize', 'rune', 'u8', 'u16', 'u32',
757 'u64', 'usize' {
758 return i64(0)
759 }
760 'f32', 'f64' {
761 return f64(0.0)
762 }
763 'string' {
764 return ''
765 }
766 else {
767 return e.zero_struct_value(type_name)
768 }
769 }
770}
771
772fn (e &Eval) zero_value_for_sumtype_data_field(data_type_name string, field_name string) ?Value {
773 if !data_type_name.ends_with('._data') || !field_name.starts_with('_') {
774 return none
775 }
776 parent_sumtype_name := data_type_name.all_before_last('._data')
777 info := e.sum_type_info(parent_sumtype_name) or { return none }
778 mut variant_alias := ''
779 for alias in info.variant_tags.keys() {
780 if '_' + e.sumtype_variant_field_name(alias) == field_name {
781 variant_alias = e.canonical_sumtype_variant_name(info, alias)
782 break
783 }
784 }
785 if variant_alias == '' {
786 return none
787 }
788 return e.zero_value_for_type_name(variant_alias)
789}
790
791fn (e &Eval) sumtype_variant_field_value(sum_value StructValue, field_name string) ?Value {
792 if !field_name.starts_with('_') {
793 return none
794 }
795 info := e.sum_type_info(sum_value.type_name) or { return none }
796 tag_value := sum_value.fields['_tag'] or { return none }
797 tag := int(e.value_as_int(tag_value) or { return none })
798 mut variant_alias := ''
799 mut variant_tag := -1
800 for alias, alias_tag in info.variant_tags {
801 if '_' + e.sumtype_variant_field_name(alias) == field_name {
802 variant_alias = e.canonical_sumtype_variant_name(info, alias)
803 variant_tag = alias_tag
804 break
805 }
806 }
807 if variant_alias == '' {
808 return none
809 }
810 if tag == variant_tag {
811 if payload := e.unwrap_sumtype_value(sum_value, variant_alias) {
812 if !is_semantically_empty_value(payload) {
813 return payload
814 }
815 }
816 }
817 return e.zero_value_for_type_name(variant_alias)
818}
819
820fn (e &Eval) infer_bare_sumtype_variant(info SumTypeInfo, value StructValue) ?InferredSumTypeVariant {
821 if '_tag' in value.fields || '_data' in value.fields {
822 return none
823 }
824 if value.fields.len == 0 {
825 return none
826 }
827 mut matches := map[int]string{}
828 for alias, tag in info.variant_tags {
829 field_names := e.lookup_struct_field_names(alias) or { continue }
830 if field_names.len != value.fields.len {
831 continue
832 }
833 mut all_fields_match := true
834 for field_name in value.fields.keys() {
835 if field_name !in field_names {
836 all_fields_match = false
837 break
838 }
839 }
840 if !all_fields_match {
841 continue
842 }
843 if tag !in matches {
844 matches[tag] = e.canonical_sumtype_variant_name(info, alias)
845 }
846 }
847 if matches.len != 1 {
848 return none
849 }
850 for tag, variant_name in matches {
851 return InferredSumTypeVariant{
852 tag: tag
853 variant_name: variant_name
854 }
855 }
856 return none
857}
858
859fn (e &Eval) infer_nested_sumtype_variant(info SumTypeInfo, value Value) ?WrappedSumTypeVariant {
860 for alias, tag in info.variant_tags {
861 nested_info := e.sum_type_info(alias) or { continue }
862 if nested_info.name == info.name && nested_info.module_name == info.module_name {
863 continue
864 }
865 nested_payload := e.cast_value(value, alias) or { continue }
866 match nested_payload {
867 StructValue {
868 if e.type_name_matches(nested_payload.type_name, alias)
869 && '_tag' in nested_payload.fields && '_data' in nested_payload.fields {
870 return WrappedSumTypeVariant{
871 tag: tag
872 payload: nested_payload
873 }
874 }
875 }
876 else {}
877 }
878 }
879 return none
880}
881
882fn (e &Eval) sumtype_tag_from_value(info SumTypeInfo, value Value) ?int {
883 match value {
884 StructValue {
885 if e.type_name_matches(value.type_name, '${info.module_name}.${info.name}') {
886 if tag_value := value.fields['_tag'] {
887 return int(e.value_as_int(tag_value) or { return none })
888 }
889 }
890 for alias in e.type_name_aliases(info.module_name, value.type_name) {
891 if alias in info.variant_tags {
892 return info.variant_tags[alias]
893 }
894 }
895 if inferred := e.infer_bare_sumtype_variant(info, value) {
896 return inferred.tag
897 }
898 }
899 string {
900 return e.lookup_sumtype_variant_tag(info, 'string')
901 }
902 bool {
903 return e.lookup_sumtype_variant_tag(info, 'bool')
904 }
905 i64 {
906 for alias in ['int', 'i64', 'i32', 'i16', 'i8', 'u64', 'u32', 'u16', 'u8', 'byte',
907 'char', 'rune'] {
908 if tag := e.lookup_sumtype_variant_tag(info, alias) {
909 return tag
910 }
911 }
912 }
913 f64 {
914 for alias in ['f64', 'f32'] {
915 if tag := e.lookup_sumtype_variant_tag(info, alias) {
916 return tag
917 }
918 }
919 }
920 ArrayValue {
921 mut tags := []int{}
922 for alias, tag in info.variant_tags {
923 if alias.starts_with('[]') && tag !in tags {
924 tags << tag
925 }
926 }
927 if tags.len == 1 {
928 return tags[0]
929 }
930 }
931 MapValue {
932 mut tags := []int{}
933 for alias, tag in info.variant_tags {
934 if alias.starts_with('map[') && tag !in tags {
935 tags << tag
936 }
937 }
938 if tags.len == 1 {
939 return tags[0]
940 }
941 }
942 TypeValue {
943 for alias in e.type_value_variant_aliases(info, value) {
944 if alias in info.variant_tags {
945 return info.variant_tags[alias]
946 }
947 }
948 }
949 else {}
950 }
951
952 if nested := e.infer_nested_sumtype_variant(info, value) {
953 return nested.tag
954 }
955 return none
956}
957
958fn (e &Eval) lookup_const_expr(module_name string, name string) ?ast.Expr {
959 if module_name !in e.consts {
960 return none
961 }
962 entry := e.consts[module_name][name] or { return none }
963 if entry.expr is ast.EmptyExpr {
964 return none
965 }
966 return entry.expr
967}
968
969fn (e &Eval) sumtype_variant_name_from_expr(expr ast.Expr) ?string {
970 return e.sumtype_variant_name_from_expr_with_depth(expr, 0)
971}
972
973fn (e &Eval) sumtype_variant_name_from_expr_with_depth(expr ast.Expr, depth int) ?string {
974 if depth > 8 {
975 return none
976 }
977 return match expr {
978 ast.ArrayInitExpr {
979 if expr.typ is ast.EmptyExpr {
980 none
981 } else {
982 e.type_expr_name(expr.typ)
983 }
984 }
985 ast.BasicLiteral {
986 match expr.kind {
987 .key_false, .key_true {
988 'bool'
989 }
990 .char {
991 'char'
992 }
993 .number {
994 if expr.value.contains('.') || expr.value.contains('e')
995 || expr.value.contains('E') {
996 'f64'
997 } else {
998 'int'
999 }
1000 }
1001 else {
1002 none
1003 }
1004 }
1005 }
1006 ast.CallOrCastExpr {
1007 if e.is_type_expr(expr.lhs) {
1008 e.type_expr_name(expr.lhs)
1009 } else {
1010 none
1011 }
1012 }
1013 ast.CastExpr {
1014 e.type_expr_name(expr.typ)
1015 }
1016 ast.InitExpr {
1017 e.type_expr_name(expr.typ)
1018 }
1019 ast.Ident {
1020 cur_module := e.current_module_name()
1021 if const_expr := e.lookup_const_expr(cur_module, expr.name) {
1022 e.sumtype_variant_name_from_expr_with_depth(const_expr, depth + 1)
1023 } else {
1024 if const_expr2 := e.lookup_const_expr('builtin', expr.name) {
1025 e.sumtype_variant_name_from_expr_with_depth(const_expr2, depth + 1)
1026 } else {
1027 none
1028 }
1029 }
1030 }
1031 ast.MapInitExpr {
1032 if expr.typ is ast.EmptyExpr {
1033 none
1034 } else {
1035 e.type_expr_name(expr.typ)
1036 }
1037 }
1038 ast.SelectorExpr {
1039 if expr.lhs is ast.Ident {
1040 module_name := e.resolve_module_name(expr.lhs.name)
1041 if module_name != '' {
1042 if const_expr := e.lookup_const_expr(module_name, expr.rhs.name) {
1043 e.sumtype_variant_name_from_expr_with_depth(const_expr, depth + 1)
1044 } else {
1045 none
1046 }
1047 } else {
1048 none
1049 }
1050 } else {
1051 none
1052 }
1053 }
1054 ast.StringInterLiteral, ast.StringLiteral {
1055 'string'
1056 }
1057 else {
1058 none
1059 }
1060 }
1061}
1062
1063fn (e &Eval) build_sumtype_wrapper(type_name string, info SumTypeInfo, tag int, payload Value) StructValue {
1064 mut data_fields := map[string]Value{}
1065 mut field_aliases := []string{}
1066 if tag in info.tag_field_aliases {
1067 field_aliases = info.tag_field_aliases[tag]
1068 }
1069 for field_alias in field_aliases {
1070 data_fields[field_alias] = payload
1071 }
1072 return StructValue{
1073 type_name: type_name
1074 fields: {
1075 '_tag': Value(i64(tag))
1076 '_data': Value(StructValue{
1077 type_name: '${type_name}._data'
1078 fields: data_fields
1079 })
1080 }
1081 }
1082}
1083
1084fn (e &Eval) wrap_sumtype_value(sum_type_name string, value Value, expr ast.Expr) !Value {
1085 if value is StructValue && value.type_name == sum_type_name && '_tag' in value.fields
1086 && '_data' in value.fields {
1087 return value
1088 }
1089 info := e.sum_type_info(sum_type_name) or {
1090 return error('v2.eval: unknown sum type `${sum_type_name}`')
1091 }
1092 if value is StructValue {
1093 if inferred := e.infer_bare_sumtype_variant(info, value) {
1094 payload := StructValue{
1095 type_name: inferred.variant_name
1096 fields: value.fields.clone()
1097 }
1098 return e.build_sumtype_wrapper(sum_type_name, info, inferred.tag, payload)
1099 }
1100 }
1101 if nested := e.infer_nested_sumtype_variant(info, value) {
1102 return e.build_sumtype_wrapper(sum_type_name, info, nested.tag, nested.payload)
1103 }
1104 if variant_name := e.sumtype_variant_name_from_expr(expr) {
1105 if tag := e.lookup_sumtype_variant_tag(info, variant_name) {
1106 return e.build_sumtype_wrapper(sum_type_name, info, tag, value)
1107 }
1108 }
1109 if tag := e.sumtype_tag_from_value(info, value) {
1110 return e.build_sumtype_wrapper(sum_type_name, info, tag, value)
1111 }
1112 return error('v2.eval: can not wrap `${e.runtime_type_name(value)}` into `${sum_type_name}` in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
1113}
1114
1115fn (e &Eval) unwrap_sumtype_value(sum_value StructValue, target_type string) ?Value {
1116 info := e.sum_type_info(sum_value.type_name) or { return none }
1117 tag_value := sum_value.fields['_tag'] or { return none }
1118 tag := int(e.value_as_int(tag_value) or { return none })
1119 target_tag := e.lookup_sumtype_variant_tag(info, target_type) or { return none }
1120 if tag != target_tag {
1121 return none
1122 }
1123 data_value := sum_value.fields['_data'] or { return none }
1124 if data_value !is StructValue {
1125 return none
1126 }
1127 data_struct := data_value as StructValue
1128 mut field_aliases := []string{}
1129 if tag in info.tag_field_aliases {
1130 field_aliases = info.tag_field_aliases[tag]
1131 }
1132 for field_alias in field_aliases {
1133 for key, payload in data_struct.fields {
1134 if key == field_alias {
1135 return payload
1136 }
1137 }
1138 }
1139 for _, payload in data_struct.fields {
1140 return payload
1141 }
1142 return none
1143}
1144
1145fn (e &Eval) current_module_name() string {
1146 if e.call_stack.len == 0 {
1147 return 'main'
1148 }
1149 return e.call_stack[e.call_stack.len - 1].module_name
1150}
1151
1152fn (e &Eval) current_file_name() string {
1153 if e.call_stack.len == 0 {
1154 return ''
1155 }
1156 return e.call_stack[e.call_stack.len - 1].file_name
1157}
1158
1159fn (e &Eval) current_function_label() string {
1160 if e.call_stack.len == 0 {
1161 return 'main.main'
1162 }
1163 frame := e.call_stack[e.call_stack.len - 1]
1164 return '${frame.module_name}.${frame.fn_name}'
1165}
1166
1167fn (e &Eval) call_stack_trace() string {
1168 if e.call_stack.len == 0 {
1169 return 'main.main'
1170 }
1171 return e.call_stack.map('${it.module_name}.${it.fn_name}').join(' <- ')
1172}
1173
1174fn (e &Eval) unknown_variable_error(name string) IError {
1175 return error('v2.eval: unknown variable `${name}` in `${e.current_function_label()}`')
1176}
1177
1178fn call_result_value(result CallResult) Value {
1179 return if result.values.len == 0 {
1180 void_value()
1181 } else if result.values.len == 1 {
1182 result.values[0]
1183 } else {
1184 TupleValue{
1185 values: result.values
1186 }
1187 }
1188}
1189
1190fn (mut e Eval) writeback_mut_args(arg_exprs []ast.Expr, result CallResult) ! {
1191 for idx, value in result.mut_args {
1192 if idx < 0 || idx >= arg_exprs.len {
1193 continue
1194 }
1195 if can_update_target(arg_exprs[idx]) {
1196 e.update_target(arg_exprs[idx], value)!
1197 }
1198 }
1199}
1200
1201fn (mut e Eval) call_function(module_name string, fn_name string, args []Value) !CallResult {
1202 if builtin_call_result := e.maybe_call_builtin_call_result(module_name, fn_name, args) {
1203 return builtin_call_result.result
1204 }
1205 builtin_result := e.maybe_call_builtin_function(module_name, fn_name, args)
1206 if builtin_result.found {
1207 return CallResult{
1208 values: [builtin_result.value]
1209 mut_args: map[int]Value{}
1210 }
1211 }
1212 if target := e.resolve_mangled_function_target(fn_name) {
1213 return e.call_function(target.module_name, target.fn_name, args)
1214 }
1215 if module_name !in e.functions {
1216 return error('v2.eval: unknown module `${module_name}`')
1217 }
1218 def := e.functions[module_name][fn_name] or {
1219 return error('v2.eval: unknown function `${module_name}.${fn_name}`')
1220 }
1221 e.call_stack << CallFrame{
1222 module_name: module_name
1223 file_name: def.file_name
1224 fn_name: fn_name
1225 }
1226 defer {
1227 e.call_stack.pop()
1228 }
1229 scope_start := e.scopes.len
1230 e.open_scope()
1231 defer {
1232 for e.scopes.len > scope_start {
1233 e.close_scope() or {}
1234 }
1235 }
1236 has_receiver_arg := def.decl.is_method && !def.decl.is_static
1237 expected_args := def.decl.typ.params.len + if has_receiver_arg { 1 } else { 0 }
1238 if expected_args != args.len {
1239 return error('v2.eval: `${module_name}.${fn_name}` expected ${expected_args} arguments, got ${args.len}')
1240 }
1241 arg_offset := if has_receiver_arg { 1 } else { 0 }
1242 if has_receiver_arg && def.decl.receiver.name != '' && def.decl.receiver.name != '_' {
1243 receiver_value := if def.decl.receiver.typ is ast.EmptyExpr {
1244 args[0]
1245 } else {
1246 e.adapt_value_to_type(args[0], def.decl.receiver.typ)
1247 }
1248 e.declare_var(def.decl.receiver.name, receiver_value)
1249 }
1250 for i, param in def.decl.typ.params {
1251 param_value := if param.typ is ast.EmptyExpr {
1252 args[i + arg_offset]
1253 } else {
1254 e.adapt_value_to_type(args[i + arg_offset], param.typ)
1255 }
1256 e.declare_var(param.name, param_value)
1257 }
1258 signal := e.exec_stmts(def.decl.stmts)!
1259 mut returned_values := []Value{}
1260 if signal.kind == .return_ {
1261 returned_values = signal.values.clone()
1262 } else if signal.kind == .goto_ {
1263 return error('v2.eval: unknown goto label `${signal.label}`')
1264 } else if signal.kind != .normal {
1265 return error('v2.eval: unexpected `${signal.kind}` escaped `${module_name}.${fn_name}`')
1266 }
1267 if def.decl.typ.return_type is ast.Type {
1268 match def.decl.typ.return_type {
1269 ast.OptionType {
1270 if returned_values.len == 1 && is_option_value(returned_values[0]) {
1271 return CallResult{
1272 values: [returned_values[0]]
1273 mut_args: map[int]Value{}
1274 }
1275 }
1276 if returned_values.len == 0
1277 || (returned_values.len == 1 && returned_values[0] is VoidValue) {
1278 return CallResult{
1279 values: [wrap_option_none()]
1280 mut_args: map[int]Value{}
1281 }
1282 }
1283 payload := if returned_values.len == 1 {
1284 e.adapt_value_to_type(returned_values[0], def.decl.typ.return_type.base_type)
1285 } else {
1286 Value(TupleValue{
1287 values: returned_values
1288 })
1289 }
1290 return CallResult{
1291 values: [wrap_option_ok(payload)]
1292 mut_args: map[int]Value{}
1293 }
1294 }
1295 ast.ResultType {
1296 if returned_values.len == 1 && is_result_value(returned_values[0]) {
1297 return CallResult{
1298 values: [returned_values[0]]
1299 mut_args: map[int]Value{}
1300 }
1301 }
1302 payload := if returned_values.len == 0 {
1303 void_value()
1304 } else if returned_values.len == 1 {
1305 e.adapt_value_to_type(returned_values[0], def.decl.typ.return_type.base_type)
1306 } else {
1307 Value(TupleValue{
1308 values: returned_values
1309 })
1310 }
1311 return CallResult{
1312 values: [wrap_result_ok(payload)]
1313 mut_args: map[int]Value{}
1314 }
1315 }
1316 else {}
1317 }
1318 }
1319 if returned_values.len == 1 && def.decl.typ.return_type !is ast.EmptyExpr {
1320 returned_values[0] = e.adapt_value_to_type(returned_values[0], def.decl.typ.return_type)
1321 }
1322 mut result := CallResult{
1323 values: returned_values
1324 mut_args: map[int]Value{}
1325 }
1326 if has_receiver_arg && def.decl.receiver.is_mut && def.decl.receiver.name != ''
1327 && def.decl.receiver.name != '_' {
1328 receiver_value := e.lookup_var(def.decl.receiver.name)
1329 if receiver_value.found {
1330 result.mut_args[0] = receiver_value.value
1331 }
1332 }
1333 for i, param in def.decl.typ.params {
1334 if !param.is_mut || param.name == '' || param.name == '_' {
1335 continue
1336 }
1337 param_value := e.lookup_var(param.name)
1338 if param_value.found {
1339 result.mut_args[i + arg_offset] = param_value.value
1340 }
1341 }
1342 return result
1343}
1344
1345fn (mut e Eval) maybe_call_builtin_call_result(module_name string, fn_name string, args []Value) ?MaybeCallResult {
1346 if module_name !in ['', 'builtin'] && !fn_name.starts_with('__Map_') {
1347 return none
1348 }
1349 if map_result := e.maybe_call_map_builtin_result(fn_name, args) {
1350 return map_result
1351 }
1352 return none
1353}
1354
1355fn builtin_map_helper_matches(fn_name string, suffix string) bool {
1356 return fn_name == 'map__${suffix}' || fn_name.ends_with('map__${suffix}')
1357 || (fn_name.starts_with('__Map_') && fn_name.ends_with('_${suffix}'))
1358}
1359
1360fn mut_call_result(value Value, index int, updated Value) CallResult {
1361 mut mut_args := map[int]Value{}
1362 mut_args[index] = updated
1363 return CallResult{
1364 values: [value]
1365 mut_args: mut_args
1366 }
1367}
1368
1369fn (mut e Eval) maybe_call_map_builtin_result(fn_name string, args []Value) ?MaybeCallResult {
1370 if args.len == 0 || args[0] !is MapValue {
1371 return none
1372 }
1373 receiver := args[0] as MapValue
1374 if builtin_map_helper_matches(fn_name, 'set') && args.len >= 3 {
1375 updated := e.map_set_value(receiver, args[1], args[2])
1376 return MaybeCallResult{
1377 found: true
1378 result: mut_call_result(void_value(), 0, updated)
1379 }
1380 }
1381 if builtin_map_helper_matches(fn_name, 'get') && args.len >= 3 {
1382 value, found := e.map_lookup(receiver, args[1])
1383 return MaybeCallResult{
1384 found: true
1385 result: CallResult{
1386 values: [if found { value } else { args[2] }]
1387 mut_args: map[int]Value{}
1388 }
1389 }
1390 }
1391 if builtin_map_helper_matches(fn_name, 'get_and_set') && args.len >= 3 {
1392 value, found := e.map_lookup(receiver, args[1])
1393 if found {
1394 return MaybeCallResult{
1395 found: true
1396 result: CallResult{
1397 values: [value]
1398 mut_args: map[int]Value{}
1399 }
1400 }
1401 }
1402 updated := e.map_set_value(receiver, args[1], args[2])
1403 return MaybeCallResult{
1404 found: true
1405 result: mut_call_result(args[2], 0, updated)
1406 }
1407 }
1408 if builtin_map_helper_matches(fn_name, 'exists') && args.len >= 2 {
1409 return MaybeCallResult{
1410 found: true
1411 result: CallResult{
1412 values: [e.map_contains_key(receiver, args[1])]
1413 mut_args: map[int]Value{}
1414 }
1415 }
1416 }
1417 if builtin_map_helper_matches(fn_name, 'delete') && args.len >= 2 {
1418 updated := e.map_delete_value(receiver, args[1])
1419 return MaybeCallResult{
1420 found: true
1421 result: mut_call_result(void_value(), 0, updated)
1422 }
1423 }
1424 if builtin_map_helper_matches(fn_name, 'keys') {
1425 return MaybeCallResult{
1426 found: true
1427 result: CallResult{
1428 values: [e.map_keys(receiver)]
1429 mut_args: map[int]Value{}
1430 }
1431 }
1432 }
1433 if builtin_map_helper_matches(fn_name, 'values') {
1434 return MaybeCallResult{
1435 found: true
1436 result: CallResult{
1437 values: [e.map_values(receiver)]
1438 mut_args: map[int]Value{}
1439 }
1440 }
1441 }
1442 if builtin_map_helper_matches(fn_name, 'clone') {
1443 return MaybeCallResult{
1444 found: true
1445 result: CallResult{
1446 values: [e.map_clone(receiver)]
1447 mut_args: map[int]Value{}
1448 }
1449 }
1450 }
1451 if builtin_map_helper_matches(fn_name, 'clear') {
1452 return MaybeCallResult{
1453 found: true
1454 result: mut_call_result(void_value(), 0, e.map_clear(receiver))
1455 }
1456 }
1457 if builtin_map_helper_matches(fn_name, 'move') {
1458 return MaybeCallResult{
1459 found: true
1460 result: mut_call_result(receiver, 0, e.map_clear(receiver))
1461 }
1462 }
1463 return none
1464}
1465
1466fn safe_arg(args []Value, idx int) Value {
1467 if idx < args.len {
1468 return args[idx]
1469 }
1470 return void_value()
1471}
1472
1473fn builtin_method_name(fn_name string) string {
1474 if !fn_name.contains('__') {
1475 return fn_name
1476 }
1477 return fn_name.all_after_last('__')
1478}
1479
1480fn builtin_receiver_name(fn_name string) string {
1481 if !fn_name.contains('__') {
1482 return ''
1483 }
1484 mut prefix := fn_name.all_before_last('__')
1485 if prefix.contains('__') {
1486 prefix = prefix.all_after_last('__')
1487 }
1488 return prefix
1489}
1490
1491fn (e &Eval) expect_token_arg(args []Value, index int) !token.Token {
1492 raw := e.value_as_int(safe_arg(args, index))!
1493 // Enum values are stored as ints in eval, so host-side token helpers need a narrow cast here.
1494 return unsafe { token.Token(int(raw)) }
1495}
1496
1497fn (e &Eval) expect_byte_arg(args []Value, index int) !u8 {
1498 raw := e.value_as_int(safe_arg(args, index))!
1499 return u8(raw)
1500}
1501
1502fn (e &Eval) infer_array_elem_type(values []Value) string {
1503 if values.len == 0 {
1504 return ''
1505 }
1506 return e.runtime_type_name(values[0])
1507}
1508
1509fn (e &Eval) array_elem_type_name(expr ast.Expr) string {
1510 return match expr {
1511 ast.Type {
1512 match expr {
1513 ast.ArrayType, ast.ArrayFixedType {
1514 e.type_expr_name(expr.elem_type)
1515 }
1516 else {
1517 ''
1518 }
1519 }
1520 }
1521 else {
1522 type_name := e.type_expr_name(expr)
1523 if type_name.starts_with('[]') {
1524 type_name[2..]
1525 } else if type_name.starts_with('[') && type_name.contains(']') {
1526 type_name.all_after(']')
1527 } else {
1528 ''
1529 }
1530 }
1531 }
1532}
1533
1534fn (e &Eval) annotate_value_for_type(value Value, typ ast.Expr) Value {
1535 match value {
1536 ArrayValue {
1537 elem_type_name := if value.elem_type_name != '' {
1538 value.elem_type_name
1539 } else {
1540 e.array_elem_type_name(typ)
1541 }
1542 return ArrayValue{
1543 elem_type_name: elem_type_name
1544 values: value.values
1545 }
1546 }
1547 else {
1548 return value
1549 }
1550 }
1551}
1552
1553fn (e &Eval) is_existing_sumtype_wrapper(value Value, target_name string) bool {
1554 return match value {
1555 StructValue {
1556 e.type_name_matches(value.type_name, target_name)
1557 }
1558 else {
1559 false
1560 }
1561 }
1562}
1563
1564fn (e &Eval) adapt_value_to_type_name(value Value, type_name string) Value {
1565 if type_name == '' {
1566 return value
1567 }
1568 if type_name.starts_with('?') {
1569 if is_option_value(value) {
1570 return value
1571 }
1572 if value is VoidValue {
1573 return wrap_option_none()
1574 }
1575 return wrap_option_ok(e.adapt_value_to_type_name(value, type_name[1..]))
1576 }
1577 if type_name.starts_with('!') {
1578 if is_result_value(value) {
1579 return value
1580 }
1581 return wrap_result_ok(e.adapt_value_to_type_name(value, type_name[1..]))
1582 }
1583 if type_name.starts_with('[]') && value is ArrayValue {
1584 array_value := value as ArrayValue
1585 return ArrayValue{
1586 elem_type_name: e.qualify_type_name(e.current_module_name(), type_name[2..])
1587 values: array_value.values
1588 }
1589 }
1590 if e.is_sum_type_name(type_name) {
1591 if e.is_existing_sumtype_wrapper(value, type_name) {
1592 return value
1593 }
1594 return e.cast_value(value, type_name) or { value }
1595 }
1596 match type_name {
1597 'bool' {
1598 return e.value_as_bool(value) or { return value }
1599 }
1600 'byte', 'char', 'i8', 'i16', 'i32', 'int', 'i64', 'isize', 'rune', 'u8', 'u16', 'u32',
1601 'u64', 'usize' {
1602 return e.value_as_int(value) or { return value }
1603 }
1604 'f32', 'f64' {
1605 return e.value_as_f64(value) or { return value }
1606 }
1607 'string' {
1608 return e.value_string(value)
1609 }
1610 else {
1611 if value is StructValue {
1612 return e.cast_value(value, type_name) or { Value(value) }
1613 }
1614 return value
1615 }
1616 }
1617}
1618
1619fn (e &Eval) adapt_value_to_type(value Value, typ ast.Expr) Value {
1620 adapted := e.annotate_value_for_type(value, typ)
1621 if typ is ast.Type {
1622 match typ {
1623 ast.OptionType {
1624 if is_option_value(adapted) {
1625 return adapted
1626 }
1627 if adapted is VoidValue {
1628 return wrap_option_none()
1629 }
1630 return wrap_option_ok(e.adapt_value_to_type(adapted, typ.base_type))
1631 }
1632 ast.ResultType {
1633 if is_result_value(adapted) {
1634 return adapted
1635 }
1636 return wrap_result_ok(e.adapt_value_to_type(adapted, typ.base_type))
1637 }
1638 else {}
1639 }
1640 }
1641 target_name := e.type_expr_name(typ)
1642 if target_name != '' && e.is_sum_type_name(target_name) {
1643 if e.is_existing_sumtype_wrapper(adapted, target_name) {
1644 return adapted
1645 }
1646 return e.cast_value(adapted, target_name) or { adapted }
1647 }
1648 return adapted
1649}
1650
1651fn (e &Eval) should_append_many(container ArrayValue, value ArrayValue) bool {
1652 if container.elem_type_name != '' && value.elem_type_name != '' {
1653 return container.elem_type_name == value.elem_type_name
1654 }
1655 if container.elem_type_name != '' && value.values.len > 0 {
1656 return container.elem_type_name == e.runtime_type_name(value.values[0])
1657 }
1658 if container.values.len > 0 && value.values.len > 0 {
1659 return e.runtime_type_name(container.values[0]) == e.runtime_type_name(value.values[0])
1660 }
1661 return false
1662}
1663
1664fn (e &Eval) inferred_sumtype_tag(value Value) ?int {
1665 mut matches := map[string]int{}
1666 for _, info in e.sum_types {
1667 if tag := e.sumtype_tag_from_value(info, value) {
1668 key := '${info.module_name}.${info.name}:${tag}'
1669 matches[key] = tag
1670 }
1671 }
1672 if matches.len != 1 {
1673 return none
1674 }
1675 for _, tag in matches {
1676 return tag
1677 }
1678 return none
1679}
1680
1681fn (mut e Eval) contextual_sumtype_tag(target_expr ast.Expr, value Value) ?int {
1682 match target_expr {
1683 ast.SelectorExpr {
1684 base := e.eval_expr(target_expr.lhs) or { return none }
1685 if base is StructValue {
1686 if field_type := e.lookup_struct_field_type(base.type_name, target_expr.rhs.name) {
1687 target_name := e.type_expr_name(field_type)
1688 if info := e.sum_type_info(target_name) {
1689 return e.sumtype_tag_from_value(info, value)
1690 }
1691 }
1692 }
1693 }
1694 ast.IndexExpr {
1695 container := e.eval_expr(target_expr.lhs) or { return none }
1696 match container {
1697 ArrayValue {
1698 if info := e.sum_type_info(container.elem_type_name) {
1699 return e.sumtype_tag_from_value(info, value)
1700 }
1701 }
1702 MapValue {
1703 if container.default_value is StructValue {
1704 default_value := container.default_value as StructValue
1705 if info := e.sum_type_info(default_value.type_name) {
1706 return e.sumtype_tag_from_value(info, value)
1707 }
1708 }
1709 }
1710 else {}
1711 }
1712 }
1713 else {}
1714 }
1715
1716 return none
1717}
1718
1719fn eval_token_method_value(tok token.Token, method_name string) ?Value {
1720 return match method_name {
1721 'str' { Value(tok.str()) }
1722 'left_binding_power' { Value(i64(int(tok.left_binding_power()))) }
1723 'right_binding_power' { Value(i64(int(tok.right_binding_power()))) }
1724 'is_keyword' { Value(tok.is_keyword()) }
1725 'is_prefix' { Value(tok.is_prefix()) }
1726 'is_infix' { Value(tok.is_infix()) }
1727 'is_postfix' { Value(tok.is_postfix()) }
1728 'is_assignment' { Value(tok.is_assignment()) }
1729 'is_overloadable' { Value(tok.is_overloadable()) }
1730 'is_comparison' { Value(tok.is_comparison()) }
1731 else { none }
1732 }
1733}
1734
1735fn eval_byte_method_value(c u8, method_name string) ?Value {
1736 return match method_name {
1737 'is_space' {
1738 Value(c == 32 || (c > 8 && c < 14) || c == 0x85 || c == 0xa0)
1739 }
1740 'is_digit' {
1741 Value(c >= `0` && c <= `9`)
1742 }
1743 'is_hex_digit' {
1744 Value((c >= `0` && c <= `9`) || (c >= `a` && c <= `f`) || (c >= `A` && c <= `F`))
1745 }
1746 'is_oct_digit' {
1747 Value(c >= `0` && c <= `7`)
1748 }
1749 'is_bin_digit' {
1750 Value(c == `0` || c == `1`)
1751 }
1752 'is_letter' {
1753 Value((c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`))
1754 }
1755 'is_alnum' {
1756 Value((c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`))
1757 }
1758 'is_capital' {
1759 Value(c >= `A` && c <= `Z`)
1760 }
1761 else {
1762 none
1763 }
1764 }
1765}
1766
1767fn (e &Eval) maybe_call_token_builtin(fn_name string, args []Value) MaybeValue {
1768 method_name := builtin_method_name(fn_name)
1769 receiver_name := builtin_receiver_name(fn_name)
1770 if receiver_name == 'Token' {
1771 tok := e.expect_token_arg(args, 0) or { return MaybeValue{} }
1772 if value := eval_token_method_value(tok, method_name) {
1773 return MaybeValue{
1774 found: true
1775 value: value
1776 }
1777 }
1778 return MaybeValue{}
1779 }
1780 if method_name == 'from_string_tinyv' && receiver_name in ['', 'Token'] {
1781 name := e.expect_string_arg(args, args.len - 1) or { return MaybeValue{} }
1782 return MaybeValue{
1783 found: true
1784 value: i64(int(token.Token.from_string_tinyv(name)))
1785 }
1786 }
1787 return MaybeValue{}
1788}
1789
1790fn (e &Eval) maybe_call_ast_builtin(fn_name string, args []Value) MaybeValue {
1791 method_name := builtin_method_name(fn_name)
1792 receiver_name := builtin_receiver_name(fn_name)
1793 if receiver_name in ['', 'StringLiteralKind'] {
1794 match method_name {
1795 'str' {
1796 raw := e.value_as_int(safe_arg(args, 0)) or { return MaybeValue{} }
1797 // StringLiteralKind is stored as an int in eval and only used with known enum values here.
1798 kind := unsafe { ast.StringLiteralKind(int(raw)) }
1799 return MaybeValue{
1800 found: true
1801 value: kind.str()
1802 }
1803 }
1804 'from_string_tinyv' {
1805 name := e.expect_string_arg(args, args.len - 1) or { return MaybeValue{} }
1806 return MaybeValue{
1807 found: true
1808 value: i64(int(ast.StringLiteralKind.from_string_tinyv(name)))
1809 }
1810 }
1811 else {}
1812 }
1813 }
1814 return MaybeValue{}
1815}
1816
1817fn (e &Eval) maybe_call_byte_builtin(module_name string, fn_name string, args []Value) MaybeValue {
1818 if module_name !in ['', 'builtin'] {
1819 return MaybeValue{}
1820 }
1821 receiver_name := builtin_receiver_name(fn_name)
1822 if receiver_name !in ['u8', 'byte'] {
1823 return MaybeValue{}
1824 }
1825 c := e.expect_byte_arg(args, 0) or { return MaybeValue{} }
1826 if value := eval_byte_method_value(c, builtin_method_name(fn_name)) {
1827 return MaybeValue{
1828 found: true
1829 value: value
1830 }
1831 }
1832 return MaybeValue{}
1833}
1834
1835fn is_semantically_empty_value(value Value) bool {
1836 return match value {
1837 VoidValue {
1838 true
1839 }
1840 StructValue {
1841 for field_value in value.fields.values() {
1842 if !is_semantically_empty_value(field_value) {
1843 return false
1844 }
1845 }
1846 true
1847 }
1848 else {
1849 false
1850 }
1851 }
1852}
1853
1854fn (e &Eval) type_sum_data_is_nil(value Value) bool {
1855 match value {
1856 StructValue {
1857 if '_data' !in value.fields {
1858 return false
1859 }
1860 data_value := value.fields['_data'] or { return false }
1861 return is_semantically_empty_value(data_value)
1862 }
1863 TypeValue {
1864 return false
1865 }
1866 else {
1867 return is_semantically_empty_value(value)
1868 }
1869 }
1870}
1871
1872fn has_flag_bit(bits i64, flag i64) bool {
1873 return (bits & flag) != 0
1874}
1875
1876fn (e &Eval) primitive_type_name(value StructValue) string {
1877 props := e.value_as_int(value.fields['props'] or { Value(i64(0)) }) or { 0 }
1878 size := e.value_as_int(value.fields['size'] or { Value(i64(0)) }) or { 0 }
1879 if has_flag_bit(props, 1) {
1880 return 'bool'
1881 }
1882 if has_flag_bit(props, 16) {
1883 if has_flag_bit(props, 4) {
1884 return 'int_literal'
1885 }
1886 if has_flag_bit(props, 2) {
1887 return 'float_literal'
1888 }
1889 }
1890 if has_flag_bit(props, 2) {
1891 return if size == 32 { 'f32' } else { 'f64' }
1892 }
1893 if has_flag_bit(props, 4) {
1894 if has_flag_bit(props, 8) {
1895 return match size {
1896 8 { 'u8' }
1897 16 { 'u16' }
1898 32 { 'u32' }
1899 64 { 'u64' }
1900 else { 'u64' }
1901 }
1902 }
1903 return match size {
1904 8 { 'i8' }
1905 16 { 'i16' }
1906 32 { 'i32' }
1907 64 { 'i64' }
1908 else { 'int' }
1909 }
1910 }
1911 return 'int'
1912}
1913
1914fn (e &Eval) option_payload_type_name(value Value) string {
1915 if !is_option_value(value) || value !is StructValue {
1916 return ''
1917 }
1918 option_value := value as StructValue
1919 state := e.value_as_int(option_value.fields['state'] or { Value(i64(1)) }) or { 1 }
1920 if state != 0 {
1921 return ''
1922 }
1923 return e.types_value_name(option_value.fields['data'] or { return '' })
1924}
1925
1926fn (e &Eval) tuple_type_name(value StructValue) string {
1927 if types_value := value.fields['types'] {
1928 if types_value is ArrayValue {
1929 return '(' + types_value.values.map(e.types_value_name(it)).join(',') + ')'
1930 }
1931 }
1932 return '()'
1933}
1934
1935fn (e &Eval) sumtype_payload_name(value StructValue) string {
1936 data_value := value.fields['_data'] or { return 'void' }
1937 if data_value is StructValue {
1938 data_struct := data_value as StructValue
1939 if tag_value := value.fields['_tag'] {
1940 tag := int(e.value_as_int(tag_value) or { -1 })
1941 if info := e.sum_type_info(value.type_name) {
1942 if field_aliases := info.tag_field_aliases[tag] {
1943 for field_alias in field_aliases {
1944 if payload := data_struct.fields[field_alias] {
1945 return e.types_value_name(payload)
1946 }
1947 }
1948 }
1949 }
1950 }
1951 for payload in data_struct.fields.values() {
1952 if !is_semantically_empty_value(payload) {
1953 return e.types_value_name(payload)
1954 }
1955 }
1956 for payload in data_struct.fields.values() {
1957 return e.types_value_name(payload)
1958 }
1959 }
1960 return 'void'
1961}
1962
1963fn (e &Eval) types_value_name(value Value) string {
1964 return match value {
1965 StructValue {
1966 mut short_name := value.type_name.all_after_last('.')
1967 if short_name.contains('__') {
1968 short_name = short_name.all_after_last('__')
1969 }
1970 match short_name {
1971 'Type' {
1972 e.sumtype_payload_name(value)
1973 }
1974 'Primitive' {
1975 e.primitive_type_name(value)
1976 }
1977 'Alias' {
1978 name_value := value.fields['name'] or { Value('') }
1979 name := e.value_string(name_value)
1980 if name != '' {
1981 name
1982 } else if base_type := value.fields['base_type'] {
1983 e.types_value_name(base_type)
1984 } else {
1985 'alias'
1986 }
1987 }
1988 'Array' {
1989 elem_type := value.fields['elem_type'] or { return '[]void' }
1990 '[]' + e.types_value_name(elem_type)
1991 }
1992 'ArrayFixed' {
1993 len_value := value.fields['len'] or { return '[0]void' }
1994 elem_type := value.fields['elem_type'] or { return '[0]void' }
1995 '[${e.value_as_int(len_value) or { 0 }}]${e.types_value_name(elem_type)}'
1996 }
1997 'Channel' {
1998 elem_type_name := e.option_payload_type_name(value.fields['elem_type'] or {
1999 return 'chan'
2000 })
2001 if elem_type_name == '' {
2002 'chan'
2003 } else {
2004 'chan ${elem_type_name}'
2005 }
2006 }
2007 'Char' {
2008 'char'
2009 }
2010 'Enum', 'Interface', 'Struct', 'SumType' {
2011 e.value_string(value.fields['name'] or { Value(short_name.to_lower()) })
2012 }
2013 'FnType' {
2014 'fn'
2015 }
2016 'ISize' {
2017 'isize'
2018 }
2019 'Map' {
2020 key_type := value.fields['key_type'] or { return 'map[void]void' }
2021 value_type := value.fields['value_type'] or { return 'map[void]void' }
2022 'map[${e.types_value_name(key_type)}]${e.types_value_name(value_type)}'
2023 }
2024 'None' {
2025 'none'
2026 }
2027 'OptionType' {
2028 base_type := value.fields['base_type'] or { return '?void' }
2029 '?' + e.types_value_name(base_type)
2030 }
2031 'Pointer' {
2032 base_type := value.fields['base_type'] or { return '&void' }
2033 '&' + e.types_value_name(base_type)
2034 }
2035 'ResultType' {
2036 base_type := value.fields['base_type'] or { return '!void' }
2037 '!' + e.types_value_name(base_type)
2038 }
2039 'Rune' {
2040 'rune'
2041 }
2042 'String' {
2043 'string'
2044 }
2045 'Thread' {
2046 elem_type_name := e.option_payload_type_name(value.fields['elem_type'] or {
2047 return 'thread'
2048 })
2049 if elem_type_name == '' {
2050 'thread'
2051 } else {
2052 'thread ${elem_type_name}'
2053 }
2054 }
2055 'Tuple' {
2056 e.tuple_type_name(value)
2057 }
2058 'USize' {
2059 'usize'
2060 }
2061 'Void' {
2062 'void'
2063 }
2064 'Nil' {
2065 'nil'
2066 }
2067 else {
2068 e.value_string(value.fields['name'] or { Value(short_name) })
2069 }
2070 }
2071 }
2072 TypeValue {
2073 mut short_name := value.name.all_after_last('.')
2074 if short_name.contains('__') {
2075 short_name = short_name.all_after_last('__')
2076 }
2077 short_name
2078 }
2079 else {
2080 e.runtime_type_name(value)
2081 }
2082 }
2083}
2084
2085fn (mut e Eval) maybe_call_builtin_function(module_name string, fn_name string, args []Value) MaybeValue {
2086 if fn_name == 'free' || fn_name.ends_with('__free') {
2087 return MaybeValue{
2088 found: true
2089 value: void_value()
2090 }
2091 }
2092 if fn_name == 'type_data_ptr_is_nil' || fn_name.ends_with('__type_data_ptr_is_nil')
2093 || fn_name == 'type_has_null_data' || fn_name.ends_with('__type_has_null_data') {
2094 return MaybeValue{
2095 found: true
2096 value: e.type_sum_data_is_nil(safe_arg(args, 0))
2097 }
2098 }
2099 if module_name == 'types'
2100 && (fn_name == 'type_name' || fn_name == 'Type__name' || fn_name.ends_with('Type__name')) {
2101 return MaybeValue{
2102 found: true
2103 value: e.types_value_name(safe_arg(args, 0))
2104 }
2105 }
2106 if fn_name == 'map__get_check' || fn_name.ends_with('map__get_check')
2107 || (fn_name.starts_with('__Map_') && fn_name.ends_with('_get_check')) {
2108 if args.len >= 2 && args[0] is MapValue {
2109 map_value := args[0] as MapValue
2110 value, found := e.map_lookup(map_value, args[1])
2111 return MaybeValue{
2112 found: true
2113 value: if found { value } else { void_value() }
2114 }
2115 }
2116 }
2117 if fn_name in ['sync__RwMutex_lock', 'sync__RwMutex_unlock', 'sync__RwMutex_rlock', 'sync__RwMutex_runlock']
2118 || fn_name.ends_with('RwMutex_lock') || fn_name.ends_with('RwMutex_unlock')
2119 || fn_name.ends_with('RwMutex_rlock') || fn_name.ends_with('RwMutex_runlock') {
2120 return MaybeValue{
2121 found: true
2122 value: void_value()
2123 }
2124 }
2125 if module_name == 'token' {
2126 token_result := e.maybe_call_token_builtin(fn_name, args)
2127 if token_result.found {
2128 return token_result
2129 }
2130 }
2131 if module_name == 'ast' {
2132 ast_result := e.maybe_call_ast_builtin(fn_name, args)
2133 if ast_result.found {
2134 return ast_result
2135 }
2136 }
2137 if fn_name.ends_with('__str') && args.len == 1 {
2138 return MaybeValue{
2139 found: true
2140 value: e.value_string(args[0])
2141 }
2142 }
2143 if fn_name.ends_with('__eq') && args.len == 2 {
2144 return MaybeValue{
2145 found: true
2146 value: e.value_eq(args[0], args[1])
2147 }
2148 }
2149 if fn_name.starts_with('string__') && args.len >= 1 {
2150 receiver := e.value_string(args[0])
2151 method_name := fn_name.all_after('string__')
2152 if result := e.call_string_method(receiver, method_name, args[1..]) {
2153 return MaybeValue{
2154 found: true
2155 value: result
2156 }
2157 }
2158 }
2159 if array_helper := e.maybe_call_generated_array_helper(fn_name, args) {
2160 return array_helper
2161 }
2162 byte_result := e.maybe_call_byte_builtin(module_name, fn_name, args)
2163 if byte_result.found {
2164 return byte_result
2165 }
2166 if fn_name in ['builtin__new_array_from_c_array_noscan', 'builtin__new_array_from_c_array', 'new_array_from_c_array_noscan', 'new_array_from_c_array']
2167 && args.len >= 4 {
2168 mut values := []Value{}
2169 if args[3] is ArrayValue {
2170 array_arg := args[3] as ArrayValue
2171 values = array_arg.values.clone()
2172 } else if args[3] is string {
2173 string_arg := args[3] as string
2174 for b in string_arg.bytes() {
2175 values << i64(b)
2176 }
2177 }
2178 target_len := int(e.value_as_int(args[0]) or { values.len })
2179 if target_len >= 0 && target_len < values.len {
2180 values = values[..target_len].clone()
2181 }
2182 return MaybeValue{
2183 found: true
2184 value: ArrayValue{
2185 values: values
2186 }
2187 }
2188 }
2189 if fn_name == '__new_array_with_default_noscan' && args.len >= 4 {
2190 target_len := int(e.value_as_int(args[0]) or { 0 })
2191 init_value := args[3]
2192 mut values := []Value{cap: target_len}
2193 for _ in 0 .. target_len {
2194 values << init_value
2195 }
2196 return MaybeValue{
2197 found: true
2198 value: ArrayValue{
2199 values: values
2200 }
2201 }
2202 }
2203 if module_name in ['', 'builtin'] {
2204 match fn_name {
2205 'arguments' {
2206 return MaybeValue{
2207 found: true
2208 value: ArrayValue{
2209 values: e.program_args().map(Value(it))
2210 }
2211 }
2212 }
2213 'array__slice' {
2214 if args.len < 3 || args[0] !is ArrayValue {
2215 return MaybeValue{}
2216 }
2217 arr := args[0] as ArrayValue
2218 start := int(e.value_as_int(args[1]) or { return MaybeValue{} })
2219 end := int(e.value_as_int(args[2]) or { return MaybeValue{} })
2220 return MaybeValue{
2221 found: true
2222 value: ArrayValue{
2223 values: arr.values[start..end]
2224 }
2225 }
2226 }
2227 'print' {
2228 e.write_stdout(e.value_string(safe_arg(args, 0)))
2229 return MaybeValue{
2230 found: true
2231 value: void_value()
2232 }
2233 }
2234 'println' {
2235 e.write_stdout(e.value_string(safe_arg(args, 0)) + '\n')
2236 return MaybeValue{
2237 found: true
2238 value: void_value()
2239 }
2240 }
2241 'eprint' {
2242 e.write_stderr(e.value_string(safe_arg(args, 0)))
2243 return MaybeValue{
2244 found: true
2245 value: void_value()
2246 }
2247 }
2248 'eprintln' {
2249 e.write_stderr(e.value_string(safe_arg(args, 0)) + '\n')
2250 return MaybeValue{
2251 found: true
2252 value: void_value()
2253 }
2254 }
2255 'panic' {
2256 panic(e.value_string(safe_arg(args, 0)))
2257 }
2258 else {}
2259 }
2260 }
2261 if module_name == 'token' && fn_name.ends_with('File__pos') && args.len >= 2 {
2262 offset := e.value_as_int(args[1]) or { return MaybeValue{} }
2263 mut base := i64(0)
2264 mut size := i64(0)
2265 if args[0] is StructValue {
2266 file := args[0] as StructValue
2267 if base_value := file.fields['base'] {
2268 base = e.value_as_int(base_value) or { 0 }
2269 }
2270 if size_value := file.fields['size'] {
2271 size = e.value_as_int(size_value) or { 0 }
2272 }
2273 }
2274 if offset > size {
2275 return MaybeValue{}
2276 }
2277 e.next_token_pos_id++
2278 return MaybeValue{
2279 found: true
2280 value: StructValue{
2281 type_name: 'token__Pos'
2282 fields: {
2283 'offset': Value(base + offset)
2284 'id': Value(e.next_token_pos_id)
2285 }
2286 }
2287 }
2288 }
2289 if module_name == 'os' {
2290 match fn_name {
2291 'execute' {
2292 cmd := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2293 return MaybeValue{
2294 found: true
2295 value: os_result_value(os.execute(cmd))
2296 }
2297 }
2298 'execute_opt' {
2299 cmd := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2300 res := os.execute(cmd)
2301 if res.exit_code != 0 {
2302 return MaybeValue{
2303 found: true
2304 value: wrap_result_err(res.output)
2305 }
2306 }
2307 return MaybeValue{
2308 found: true
2309 value: wrap_result_ok(os_result_value(res))
2310 }
2311 }
2312 'arguments' {
2313 return MaybeValue{
2314 found: true
2315 value: ArrayValue{
2316 values: e.program_args().map(Value(it))
2317 }
2318 }
2319 }
2320 'dir' {
2321 return MaybeValue{
2322 found: true
2323 value: os.dir(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2324 }
2325 }
2326 'file_name' {
2327 return MaybeValue{
2328 found: true
2329 value: os.file_name(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2330 }
2331 }
2332 'join_path' {
2333 mut parts := e.array_args_to_strings(args)
2334 if parts.len == 0 {
2335 return MaybeValue{
2336 found: true
2337 value: ''
2338 }
2339 }
2340 mut joined := parts[0]
2341 for part in parts[1..] {
2342 joined = os.join_path(joined, part)
2343 }
2344 return MaybeValue{
2345 found: true
2346 value: joined
2347 }
2348 }
2349 'exists' {
2350 return MaybeValue{
2351 found: true
2352 value: os.exists(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2353 }
2354 }
2355 'is_dir' {
2356 return MaybeValue{
2357 found: true
2358 value: os.is_dir(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2359 }
2360 }
2361 'getwd' {
2362 return MaybeValue{
2363 found: true
2364 value: os.getwd()
2365 }
2366 }
2367 'temp_dir' {
2368 return MaybeValue{
2369 found: true
2370 value: os.temp_dir()
2371 }
2372 }
2373 'getenv' {
2374 return MaybeValue{
2375 found: true
2376 value: os.getenv(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2377 }
2378 }
2379 'getenv_opt' {
2380 key := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2381 if value := os.getenv_opt(key) {
2382 return MaybeValue{
2383 found: true
2384 value: wrap_option_ok(value)
2385 }
2386 }
2387 return MaybeValue{
2388 found: true
2389 value: wrap_option_none()
2390 }
2391 }
2392 'is_abs_path' {
2393 return MaybeValue{
2394 found: true
2395 value: os.is_abs_path(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2396 }
2397 }
2398 'norm_path' {
2399 return MaybeValue{
2400 found: true
2401 value: os.norm_path(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2402 }
2403 }
2404 'real_path' {
2405 return MaybeValue{
2406 found: true
2407 value: os.real_path(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2408 }
2409 }
2410 'quoted_path' {
2411 return MaybeValue{
2412 found: true
2413 value: os.quoted_path(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2414 }
2415 }
2416 'base' {
2417 return MaybeValue{
2418 found: true
2419 value: os.base(e.expect_string_arg(args, 0) or { return MaybeValue{} })
2420 }
2421 }
2422 'user_os' {
2423 return MaybeValue{
2424 found: true
2425 value: os.user_os()
2426 }
2427 }
2428 'ls' {
2429 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2430 if path == '' {
2431 return MaybeValue{
2432 found: true
2433 value: wrap_result_err('os.ls empty path in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
2434 }
2435 }
2436 items := os.ls(path) or {
2437 return MaybeValue{
2438 found: true
2439 value: wrap_result_err(err.msg())
2440 }
2441 }
2442 return MaybeValue{
2443 found: true
2444 value: wrap_result_ok(ArrayValue{
2445 values: items.map(Value(it))
2446 })
2447 }
2448 }
2449 'read_file' {
2450 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2451 if path == '' {
2452 return MaybeValue{
2453 found: true
2454 value: wrap_result_err('os.read_file empty path in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
2455 }
2456 }
2457 content := os.read_file(path) or {
2458 return MaybeValue{
2459 found: true
2460 value: wrap_result_err(err.msg())
2461 }
2462 }
2463 return MaybeValue{
2464 found: true
2465 value: wrap_result_ok(content)
2466 }
2467 }
2468 'read_lines' {
2469 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2470 lines := os.read_lines(path) or {
2471 return MaybeValue{
2472 found: true
2473 value: wrap_result_err(err.msg())
2474 }
2475 }
2476 return MaybeValue{
2477 found: true
2478 value: wrap_result_ok(ArrayValue{
2479 elem_type_name: 'string'
2480 values: lines.map(Value(it))
2481 })
2482 }
2483 }
2484 'read_bytes' {
2485 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2486 bytes := os.read_bytes(path) or {
2487 return MaybeValue{
2488 found: true
2489 value: wrap_result_err(err.msg())
2490 }
2491 }
2492 return MaybeValue{
2493 found: true
2494 value: wrap_result_ok(ArrayValue{
2495 elem_type_name: 'u8'
2496 values: bytes.map(Value(i64(it)))
2497 })
2498 }
2499 }
2500 'write_file' {
2501 os.write_file(e.expect_string_arg(args, 0) or { return MaybeValue{} }, e.expect_string_arg(args,
2502 1) or { return MaybeValue{} }) or {
2503 return MaybeValue{
2504 found: true
2505 value: wrap_result_err(err.msg())
2506 }
2507 }
2508 return MaybeValue{
2509 found: true
2510 value: wrap_result_ok(void_value())
2511 }
2512 }
2513 'write_file_array' {
2514 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2515 bytes := e.value_to_byte_array(safe_arg(args, 1)) or { return MaybeValue{} }
2516 os.write_file_array(path, bytes) or {
2517 return MaybeValue{
2518 found: true
2519 value: wrap_result_err(err.msg())
2520 }
2521 }
2522 return MaybeValue{
2523 found: true
2524 value: wrap_result_ok(void_value())
2525 }
2526 }
2527 'mkdir_all' {
2528 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2529 os.mkdir_all(path) or {
2530 return MaybeValue{
2531 found: true
2532 value: wrap_result_err(err.msg())
2533 }
2534 }
2535 return MaybeValue{
2536 found: true
2537 value: wrap_result_ok(void_value())
2538 }
2539 }
2540 'rmdir_all' {
2541 path := e.expect_string_arg(args, 0) or { return MaybeValue{} }
2542 os.rmdir_all(path) or {
2543 return MaybeValue{
2544 found: true
2545 value: wrap_result_err(err.msg())
2546 }
2547 }
2548 return MaybeValue{
2549 found: true
2550 value: wrap_result_ok(void_value())
2551 }
2552 }
2553 'rm' {
2554 os.rm(e.expect_string_arg(args, 0) or { return MaybeValue{} }) or {
2555 return MaybeValue{
2556 found: true
2557 value: wrap_result_err(err.msg())
2558 }
2559 }
2560 return MaybeValue{
2561 found: true
2562 value: wrap_result_ok(void_value())
2563 }
2564 }
2565 'cp' {
2566 os.cp(e.expect_string_arg(args, 0) or { return MaybeValue{} }, e.expect_string_arg(args,
2567 1) or { return MaybeValue{} }) or {
2568 return MaybeValue{
2569 found: true
2570 value: wrap_result_err(err.msg())
2571 }
2572 }
2573 return MaybeValue{
2574 found: true
2575 value: wrap_result_ok(void_value())
2576 }
2577 }
2578 else {}
2579 }
2580 }
2581 if module_name == 'time' {
2582 match fn_name {
2583 'sys_mono_now', 'sys_mono_now_darwin', 'vpc_now', 'vpc_now_darwin' {
2584 return MaybeValue{
2585 found: true
2586 value: i64(time.now().unix_micro() * 1000)
2587 }
2588 }
2589 else {}
2590 }
2591 }
2592 if module_name == 'token' {
2593 match fn_name {
2594 'File__line_start', 'line_start' {
2595 if args.len < 2 || args[0] !is StructValue {
2596 return MaybeValue{}
2597 }
2598 file_value := args[0] as StructValue
2599 line := int(e.value_as_int(args[1]) or { return MaybeValue{} })
2600 if line <= 0 {
2601 return MaybeValue{
2602 found: true
2603 value: i64(0)
2604 }
2605 }
2606 line_offsets := file_value.fields['line_offsets'] or { return MaybeValue{} }
2607 if line_offsets !is ArrayValue {
2608 return MaybeValue{}
2609 }
2610 offsets := line_offsets as ArrayValue
2611 idx := line - 1
2612 if idx < 0 || idx >= offsets.values.len {
2613 return MaybeValue{
2614 found: true
2615 value: i64(0)
2616 }
2617 }
2618 return MaybeValue{
2619 found: true
2620 value: offsets.values[idx]
2621 }
2622 }
2623 else {}
2624 }
2625 }
2626 if module_name == 'cmdline' {
2627 match fn_name {
2628 'option' {
2629 if args.len < 1 {
2630 return MaybeValue{}
2631 }
2632 str_arr := e.value_to_string_array(args[0]) or { return MaybeValue{} }
2633 opt_arg1 := e.expect_string_arg(args, 1) or { return MaybeValue{} }
2634 opt_arg2 := e.expect_string_arg(args, 2) or { return MaybeValue{} }
2635 return MaybeValue{
2636 found: true
2637 value: host_cmdline.option(str_arr, opt_arg1, opt_arg2)
2638 }
2639 }
2640 'options' {
2641 if args.len < 1 {
2642 return MaybeValue{}
2643 }
2644 str_arr := e.value_to_string_array(args[0]) or { return MaybeValue{} }
2645 opt_arg1 := e.expect_string_arg(args, 1) or { return MaybeValue{} }
2646 return MaybeValue{
2647 found: true
2648 value: ArrayValue{
2649 values: host_cmdline.options(str_arr, opt_arg1).map(Value(it))
2650 }
2651 }
2652 }
2653 'only_options' {
2654 if args.len < 1 {
2655 return MaybeValue{}
2656 }
2657 str_arr := e.value_to_string_array(args[0]) or { return MaybeValue{} }
2658 return MaybeValue{
2659 found: true
2660 value: ArrayValue{
2661 values: host_cmdline.only_options(str_arr).map(Value(it))
2662 }
2663 }
2664 }
2665 else {}
2666 }
2667 }
2668 return MaybeValue{}
2669}
2670
2671fn (e &Eval) clone_array_item_to_depth(value Value, depth int) Value {
2672 if depth < 0 {
2673 return value
2674 }
2675 return match value {
2676 ArrayValue {
2677 e.clone_array_to_depth(value, depth)
2678 }
2679 string {
2680 value.clone()
2681 }
2682 else {
2683 value
2684 }
2685 }
2686}
2687
2688fn (e &Eval) clone_array_to_depth(array_value ArrayValue, depth int) ArrayValue {
2689 if depth <= 0 {
2690 return ArrayValue{
2691 elem_type_name: array_value.elem_type_name
2692 values: array_value.values.clone()
2693 }
2694 }
2695 mut values := []Value{cap: array_value.values.len}
2696 for item in array_value.values {
2697 values << e.clone_array_item_to_depth(item, depth - 1)
2698 }
2699 return ArrayValue{
2700 elem_type_name: array_value.elem_type_name
2701 values: values
2702 }
2703}
2704
2705fn (mut e Eval) maybe_call_generated_array_helper(fn_name string, args []Value) ?MaybeValue {
2706 if args.len > 0 && args[0] is ArrayValue {
2707 array_value := args[0] as ArrayValue
2708 if fn_name == 'array__first' || fn_name.ends_with('array__first') {
2709 return MaybeValue{
2710 found: true
2711 value: e.array_first_value(array_value) or { return none }
2712 }
2713 }
2714 if fn_name == 'array__last' || fn_name.ends_with('array__last') {
2715 return MaybeValue{
2716 found: true
2717 value: e.array_last_value(array_value) or { return none }
2718 }
2719 }
2720 if fn_name == 'array__clone' || fn_name.ends_with('array__clone') {
2721 return MaybeValue{
2722 found: true
2723 value: e.clone_array_to_depth(array_value, 0)
2724 }
2725 }
2726 if (fn_name == 'array__clone_to_depth' || fn_name.ends_with('array__clone_to_depth'))
2727 && args.len >= 2 {
2728 depth := int(e.value_as_int(args[1]) or { return none })
2729 return MaybeValue{
2730 found: true
2731 value: e.clone_array_to_depth(array_value, depth)
2732 }
2733 }
2734 }
2735 if !fn_name.starts_with('Array_') || args.len == 0 || args[0] !is ArrayValue {
2736 return none
2737 }
2738 array_value := args[0] as ArrayValue
2739 if fn_name.ends_with('_first') {
2740 return MaybeValue{
2741 found: true
2742 value: e.array_first_value(array_value) or { return none }
2743 }
2744 }
2745 if fn_name.ends_with('_last') {
2746 return MaybeValue{
2747 found: true
2748 value: e.array_last_value(array_value) or { return none }
2749 }
2750 }
2751 if fn_name.ends_with('_contains') && args.len >= 2 {
2752 return MaybeValue{
2753 found: true
2754 value: e.array_contains(array_value, args[1])
2755 }
2756 }
2757 if fn_name.ends_with('_has') && args.len >= 2 {
2758 return MaybeValue{
2759 found: true
2760 value: e.array_has(array_value, args[1])
2761 }
2762 }
2763 if fn_name.ends_with('_index') && !fn_name.ends_with('_last_index') && args.len >= 2 {
2764 return MaybeValue{
2765 found: true
2766 value: e.array_index(array_value, args[1], false)
2767 }
2768 }
2769 if fn_name.ends_with('_last_index') && args.len >= 2 {
2770 return MaybeValue{
2771 found: true
2772 value: e.array_index(array_value, args[1], true)
2773 }
2774 }
2775 if fn_name.ends_with('_join') && args.len >= 2 {
2776 return MaybeValue{
2777 found: true
2778 value: array_value.values.map(e.value_string(it)).join(e.expect_string_arg(args, 1) or {
2779 return none
2780 })
2781 }
2782 }
2783 if fn_name.ends_with('_str') {
2784 return MaybeValue{
2785 found: true
2786 value: e.value_string(array_value)
2787 }
2788 }
2789 return none
2790}
2791
2792fn (mut e Eval) open_scope() {
2793 e.scopes << ScopeFrame{
2794 vars: map[string]Value{}
2795 }
2796}
2797
2798fn (mut e Eval) close_scope() ! {
2799 if e.scopes.len == 0 {
2800 return
2801 }
2802 scope_idx := e.scopes.len - 1
2803 defers := e.scopes[scope_idx].defers.clone()
2804 for i := defers.len - 1; i >= 0; i-- {
2805 signal := e.exec_stmts(defers[i])!
2806 if signal.kind != .normal {
2807 return error('v2.eval: control flow inside defer is not supported')
2808 }
2809 }
2810 e.scopes.pop()
2811}
2812
2813fn (mut e Eval) declare_var(name string, value Value) {
2814 if name == '' || name == '_' {
2815 return
2816 }
2817 if e.scopes.len == 0 {
2818 e.open_scope()
2819 }
2820 e.scopes[e.scopes.len - 1].vars[name] = value
2821}
2822
2823fn (mut e Eval) set_var(name string, value Value) ! {
2824 for i := e.scopes.len - 1; i >= 0; i-- {
2825 if name in e.scopes[i].vars {
2826 e.scopes[i].vars[name] = value
2827 return
2828 }
2829 }
2830 return e.unknown_variable_error(name)
2831}
2832
2833fn (e &Eval) lookup_var(name string) MaybeValue {
2834 for i := e.scopes.len - 1; i >= 0; i-- {
2835 if value := e.scopes[i].vars[name] {
2836 return MaybeValue{
2837 found: true
2838 value: value
2839 }
2840 }
2841 }
2842 return MaybeValue{}
2843}
2844
2845fn (mut e Eval) exec_stmts(stmts []ast.Stmt) !FlowSignal {
2846 mut label_positions := map[string]int{}
2847 for i, stmt in stmts {
2848 if stmt is ast.LabelStmt {
2849 label_positions[stmt.name] = i
2850 }
2851 }
2852 mut i := 0
2853 for i < stmts.len {
2854 stmt := stmts[i]
2855 signal := e.exec_stmt(stmt)!
2856 if signal.kind == .goto_ {
2857 if signal.label in label_positions {
2858 i = label_positions[signal.label]
2859 continue
2860 }
2861 return signal
2862 }
2863 if signal.kind != .normal {
2864 return signal
2865 }
2866 i++
2867 }
2868 return FlowSignal{
2869 kind: .normal
2870 }
2871}
2872
2873fn (mut e Eval) exec_block(stmts []ast.Stmt) !FlowSignal {
2874 e.open_scope()
2875 signal := e.exec_stmts(stmts)!
2876 e.close_scope()!
2877 return signal
2878}
2879
2880fn (mut e Eval) exec_stmt(stmt ast.Stmt) !FlowSignal {
2881 match stmt {
2882 ast.AssertStmt {
2883 passed := e.value_as_bool(e.eval_expr(stmt.expr)!)!
2884 if !passed {
2885 message := if stmt.extra is ast.EmptyExpr {
2886 'assertion failed'
2887 } else {
2888 e.value_string(e.eval_expr(stmt.extra)!)
2889 }
2890 return error('v2.eval: ${message}')
2891 }
2892 }
2893 ast.AssignStmt {
2894 return e.exec_assign_stmt(stmt)
2895 }
2896 ast.BlockStmt {
2897 return e.exec_block(stmt.stmts)
2898 }
2899 ast.ComptimeStmt {
2900 return e.exec_stmt(stmt.stmt)
2901 }
2902 ast.DeferStmt {
2903 if e.scopes.len == 0 {
2904 return error('v2.eval: defer used outside of a scope')
2905 }
2906 e.scopes[e.scopes.len - 1].defers << stmt.stmts
2907 }
2908 ast.ExprStmt {
2909 return e.exec_expr_stmt(stmt.expr)
2910 }
2911 ast.FlowControlStmt {
2912 return match stmt.op {
2913 .key_break {
2914 FlowSignal{
2915 kind: .break_
2916 }
2917 }
2918 .key_continue {
2919 FlowSignal{
2920 kind: .continue_
2921 }
2922 }
2923 .key_goto {
2924 FlowSignal{
2925 kind: .goto_
2926 label: stmt.label
2927 }
2928 }
2929 else {
2930 return error('v2.eval: unsupported flow control `${stmt.op}`')
2931 }
2932 }
2933 }
2934 ast.ForStmt {
2935 return e.exec_for_stmt(stmt)
2936 }
2937 ast.ReturnStmt {
2938 mut values := []Value{cap: stmt.exprs.len}
2939 for expr in stmt.exprs {
2940 values << e.eval_expr(expr)!
2941 }
2942 return FlowSignal{
2943 kind: .return_
2944 values: values
2945 }
2946 }
2947 ast.ConstDecl, ast.Directive, ast.EnumDecl, ast.FnDecl, ast.GlobalDecl, ast.ImportStmt,
2948 ast.InterfaceDecl, ast.ModuleStmt, ast.StructDecl, ast.TypeDecl, []ast.Attribute {
2949 return FlowSignal{
2950 kind: .normal
2951 }
2952 }
2953 ast.LabelStmt {
2954 if stmt.stmt is ast.EmptyStmt {
2955 return FlowSignal{
2956 kind: .normal
2957 }
2958 }
2959 return e.exec_stmt(stmt.stmt)
2960 }
2961 else {
2962 return error('v2.eval: unsupported statement `${stmt.type_name()}`')
2963 }
2964 }
2965
2966 return FlowSignal{
2967 kind: .normal
2968 }
2969}
2970
2971fn (mut e Eval) exec_expr_stmt(expr ast.Expr) !FlowSignal {
2972 match expr {
2973 ast.ComptimeExpr {
2974 if expr.expr is ast.IfExpr {
2975 return e.exec_if_stmt(expr.expr)
2976 }
2977 _ = e.eval_expr(expr.expr)!
2978 }
2979 ast.IfExpr {
2980 return e.exec_if_stmt(expr)
2981 }
2982 ast.InfixExpr {
2983 if expr.op == .left_shift {
2984 e.exec_array_append(expr)!
2985 } else {
2986 _ = e.eval_expr(expr)!
2987 }
2988 }
2989 ast.PostfixExpr {
2990 _ = e.eval_postfix_expr(expr)!
2991 }
2992 ast.UnsafeExpr {
2993 return e.exec_block(expr.stmts)
2994 }
2995 else {
2996 _ = e.eval_expr(expr)!
2997 }
2998 }
2999
3000 return FlowSignal{
3001 kind: .normal
3002 }
3003}
3004
3005fn (mut e Eval) exec_assign_stmt(stmt ast.AssignStmt) !FlowSignal {
3006 mut values := []Value{}
3007 for rhs in stmt.rhs {
3008 values << e.eval_expr(rhs)!
3009 }
3010 if stmt.lhs.len > 1 && values.len == 1 && values[0] is TupleValue {
3011 tuple_value := values[0] as TupleValue
3012 values = tuple_value.values.clone()
3013 }
3014 if stmt.lhs.len != values.len {
3015 return error('v2.eval: assignment arity mismatch')
3016 }
3017 for i, lhs in stmt.lhs {
3018 match stmt.op {
3019 .decl_assign {
3020 e.assign_target(lhs, values[i], true)!
3021 }
3022 .assign {
3023 e.assign_target(lhs, values[i], false)!
3024 }
3025 .plus_assign, .minus_assign, .mul_assign, .div_assign, .mod_assign, .and_assign,
3026 .or_assign, .xor_assign, .left_shift_assign, .right_shift_assign,
3027 .right_shift_unsigned_assign {
3028 current := e.eval_expr(lhs)!
3029 op := assign_to_infix_token(stmt.op)
3030 updated := e.apply_infix(op, current, values[i])!
3031 e.assign_target(lhs, updated, false)!
3032 }
3033 else {
3034 return error('v2.eval: unsupported assignment operator `${stmt.op}`')
3035 }
3036 }
3037 }
3038 return FlowSignal{
3039 kind: .normal
3040 }
3041}
3042
3043fn assign_to_infix_token(op token.Token) token.Token {
3044 return match op {
3045 .plus_assign { .plus }
3046 .minus_assign { .minus }
3047 .mul_assign { .mul }
3048 .div_assign { .div }
3049 .mod_assign { .mod }
3050 .and_assign { .amp }
3051 .or_assign { .pipe }
3052 .xor_assign { .xor }
3053 .left_shift_assign { .left_shift }
3054 .right_shift_assign { .right_shift }
3055 .right_shift_unsigned_assign { .right_shift_unsigned }
3056 else { .unknown }
3057 }
3058}
3059
3060fn (mut e Eval) assign_target(lhs ast.Expr, value Value, declare bool) ! {
3061 if name := assignable_ident_name(lhs) {
3062 if name == '_' {
3063 return
3064 }
3065 if declare {
3066 e.declare_var(name, value)
3067 } else {
3068 e.update_target(lhs, value)!
3069 }
3070 return
3071 }
3072 if declare {
3073 return error('v2.eval: unsupported declaration target `${lhs.type_name()}`')
3074 }
3075 return e.update_target(lhs, value)
3076}
3077
3078fn can_update_target(expr ast.Expr) bool {
3079 if assignable_ident_name(expr) != none || expr is ast.IndexExpr || expr is ast.SelectorExpr {
3080 return true
3081 }
3082 match expr {
3083 ast.CastExpr, ast.ModifierExpr, ast.ParenExpr {
3084 return can_update_target(expr.expr)
3085 }
3086 else {}
3087 }
3088
3089 if expr is ast.PrefixExpr && expr.op in [.mul, .amp] {
3090 return can_update_target(expr.expr)
3091 }
3092 return false
3093}
3094
3095fn (mut e Eval) update_target(expr ast.Expr, value Value) ! {
3096 if name := assignable_ident_name(expr) {
3097 var_result := e.lookup_var(name)
3098 if var_result.found {
3099 e.set_var(name, value)!
3100 return
3101 }
3102 if e.set_runtime_const(e.current_module_name(), name, value) {
3103 return
3104 }
3105 return e.unknown_variable_error(name)
3106 }
3107 if expr is ast.IndexExpr {
3108 return e.update_index_target(expr, value)
3109 }
3110 if expr is ast.SelectorExpr {
3111 return e.update_selector_target(expr, value)
3112 }
3113 match expr {
3114 ast.CastExpr, ast.ModifierExpr, ast.ParenExpr {
3115 return e.update_target(expr.expr, value)
3116 }
3117 else {}
3118 }
3119
3120 if expr is ast.PrefixExpr && expr.op in [.mul, .amp] {
3121 return e.update_target(expr.expr, value)
3122 }
3123 return error('v2.eval: unsupported assignment target `${expr.type_name()}`')
3124}
3125
3126fn assignable_ident_name(expr ast.Expr) ?string {
3127 match expr {
3128 ast.Ident {
3129 return expr.name
3130 }
3131 ast.ModifierExpr {
3132 if expr.expr is ast.Ident {
3133 return expr.expr.name
3134 }
3135 }
3136 else {}
3137 }
3138
3139 return none
3140}
3141
3142fn (mut e Eval) update_index_target(expr ast.IndexExpr, value Value) ! {
3143 container := e.eval_expr(expr.lhs)!
3144 index_value := e.eval_expr(expr.expr)!
3145 match container {
3146 ArrayValue {
3147 index := int(e.value_as_int(index_value)!)
3148 if index < 0 || index >= container.values.len {
3149 return error('v2.eval: array index out of bounds')
3150 }
3151 mut updated := ArrayValue{
3152 ...container
3153 values: container.values.clone()
3154 }
3155 assigned_value := e.adapt_value_to_type_name(value, updated.elem_type_name)
3156 updated.values[index] = assigned_value
3157 e.update_target(expr.lhs, updated)!
3158 return
3159 }
3160 MapValue {
3161 expected_type_name := if container.default_value is VoidValue {
3162 ''
3163 } else {
3164 e.runtime_type_name(container.default_value)
3165 }
3166 assigned_value := e.adapt_value_to_type_name(value, expected_type_name)
3167 e.update_target(expr.lhs, e.map_set_value(container, index_value, assigned_value))!
3168 return
3169 }
3170 else {
3171 return error('v2.eval: indexed assignment expects an array or map')
3172 }
3173 }
3174}
3175
3176fn (mut e Eval) update_selector_target(expr ast.SelectorExpr, value Value) ! {
3177 container := e.eval_expr(expr.lhs)!
3178 match container {
3179 ArrayValue {
3180 if expr.rhs.name == 'flags' {
3181 return
3182 }
3183 }
3184 StructValue {
3185 mut updated := container
3186 mut assigned_value := value
3187 if field_type := e.lookup_struct_field_type(container.type_name, expr.rhs.name) {
3188 assigned_value = e.adapt_value_to_type(value, field_type)
3189 }
3190 updated.fields[expr.rhs.name] = assigned_value
3191 e.update_target(expr.lhs, updated)!
3192 return
3193 }
3194 else {
3195 return error('v2.eval: selector assignment expects a struct, got `${e.runtime_type_name(container)}` in `${e.current_function_label()}`')
3196 }
3197 }
3198}
3199
3200fn (mut e Eval) exec_array_append(expr ast.InfixExpr) ! {
3201 container := e.eval_expr(expr.lhs)!
3202 value := e.eval_expr(expr.rhs)!
3203 match container {
3204 ArrayValue {
3205 mut updated := container
3206 if value is ArrayValue && e.should_append_many(container, value) {
3207 if updated.elem_type_name == '' {
3208 updated.values << value.values
3209 } else {
3210 for item in value.values {
3211 updated.values << e.adapt_value_to_type_name(item, updated.elem_type_name)
3212 }
3213 }
3214 } else {
3215 updated.values << e.adapt_value_to_type_name(value, updated.elem_type_name)
3216 }
3217 if updated.elem_type_name == '' {
3218 updated.elem_type_name = e.infer_array_elem_type(updated.values)
3219 }
3220 e.update_target(expr.lhs, updated)!
3221 return
3222 }
3223 else {
3224 return error('v2.eval: `<<` is only supported for arrays')
3225 }
3226 }
3227}
3228
3229fn (mut e Eval) exec_for_stmt(stmt ast.ForStmt) !FlowSignal {
3230 e.open_scope()
3231 defer {
3232 e.close_scope() or {}
3233 }
3234 if stmt.init is ast.ForInStmt {
3235 return e.exec_for_in(stmt.init, stmt.stmts)
3236 }
3237 if stmt.init !is ast.EmptyStmt {
3238 init_signal := e.exec_stmt(stmt.init)!
3239 if init_signal.kind != .normal {
3240 return init_signal
3241 }
3242 }
3243 for {
3244 if stmt.cond !is ast.EmptyExpr {
3245 if !e.value_as_bool(e.eval_expr(stmt.cond)!)! {
3246 break
3247 }
3248 }
3249 body_signal := e.exec_block(stmt.stmts)!
3250 if body_signal.kind == .return_ {
3251 return body_signal
3252 }
3253 if body_signal.kind == .break_ {
3254 break
3255 }
3256 if stmt.post !is ast.EmptyStmt {
3257 post_signal := e.exec_stmt(stmt.post)!
3258 if post_signal.kind == .return_ {
3259 return post_signal
3260 }
3261 if post_signal.kind == .break_ {
3262 break
3263 }
3264 }
3265 }
3266 return FlowSignal{
3267 kind: .normal
3268 }
3269}
3270
3271fn (mut e Eval) exec_for_in(for_in ast.ForInStmt, body []ast.Stmt) !FlowSignal {
3272 mut key_name := assignable_ident_name(for_in.key) or { '' }
3273 mut value_name := assignable_ident_name(for_in.value) or { '' }
3274 if value_name == '' {
3275 value_name = key_name
3276 key_name = ''
3277 }
3278 iterable := e.eval_expr(for_in.expr)!
3279 match iterable {
3280 RangeValue {
3281 mut i := iterable.start
3282 limit := iterable.end
3283 for {
3284 if iterable.inclusive {
3285 if i > limit {
3286 break
3287 }
3288 } else if i >= limit {
3289 break
3290 }
3291 e.open_scope()
3292 if value_name != '' && value_name != '_' {
3293 e.declare_var(value_name, i)
3294 }
3295 body_signal := e.exec_stmts(body)!
3296 e.close_scope()!
3297 if body_signal.kind == .return_ {
3298 return body_signal
3299 }
3300 if body_signal.kind == .break_ {
3301 break
3302 }
3303 i++
3304 }
3305 }
3306 ArrayValue {
3307 for idx, item in iterable.values {
3308 e.open_scope()
3309 if key_name != '' && key_name != '_' {
3310 e.declare_var(key_name, i64(idx))
3311 }
3312 if value_name != '' && value_name != '_' {
3313 e.declare_var(value_name, item)
3314 }
3315 body_signal := e.exec_stmts(body)!
3316 e.close_scope()!
3317 if body_signal.kind == .return_ {
3318 return body_signal
3319 }
3320 if body_signal.kind == .break_ {
3321 break
3322 }
3323 }
3324 }
3325 MapValue {
3326 for entry in iterable.entries {
3327 e.open_scope()
3328 if key_name != '' && key_name != '_' {
3329 e.declare_var(key_name, entry.key)
3330 }
3331 if value_name != '' && value_name != '_' {
3332 e.declare_var(value_name, entry.value)
3333 }
3334 body_signal := e.exec_stmts(body)!
3335 e.close_scope()!
3336 if body_signal.kind == .return_ {
3337 return body_signal
3338 }
3339 if body_signal.kind == .break_ {
3340 break
3341 }
3342 }
3343 }
3344 string {
3345 for idx, ch in iterable.runes() {
3346 e.open_scope()
3347 if key_name != '' && key_name != '_' {
3348 e.declare_var(key_name, i64(idx))
3349 }
3350 if value_name != '' && value_name != '_' {
3351 e.declare_var(value_name, ch.str())
3352 }
3353 body_signal := e.exec_stmts(body)!
3354 e.close_scope()!
3355 if body_signal.kind == .return_ {
3356 return body_signal
3357 }
3358 if body_signal.kind == .break_ {
3359 break
3360 }
3361 }
3362 }
3363 else {
3364 return error('v2.eval: unsupported for-in iterable `${e.runtime_type_name(iterable)}`')
3365 }
3366 }
3367
3368 return FlowSignal{
3369 kind: .normal
3370 }
3371}
3372
3373fn (mut e Eval) exec_if_stmt(expr ast.IfExpr) !FlowSignal {
3374 should_run := if expr.cond is ast.EmptyExpr {
3375 true
3376 } else {
3377 e.value_as_bool(e.eval_expr(expr.cond)!)!
3378 }
3379 if should_run {
3380 return e.exec_block(expr.stmts)
3381 }
3382 if expr.else_expr is ast.IfExpr {
3383 return e.exec_if_stmt(expr.else_expr)
3384 }
3385 if expr.else_expr is ast.EmptyExpr {
3386 return FlowSignal{
3387 kind: .normal
3388 }
3389 }
3390 _ = e.eval_expr(expr.else_expr)!
3391 return FlowSignal{
3392 kind: .normal
3393 }
3394}
3395
3396fn (mut e Eval) eval_expr(expr ast.Expr) !Value {
3397 match expr {
3398 ast.ArrayInitExpr {
3399 mut elem_type_name := if expr.typ is ast.EmptyExpr {
3400 ''
3401 } else {
3402 e.array_elem_type_name(expr.typ)
3403 }
3404 if expr.len !is ast.EmptyExpr && expr.exprs.len == 0 {
3405 size := int(e.value_as_int(e.eval_expr(expr.len)!)!)
3406 init_value := if expr.init is ast.EmptyExpr {
3407 void_value()
3408 } else {
3409 e.eval_expr(expr.init)!
3410 }
3411 mut values := []Value{len: size, init: init_value}
3412 if elem_type_name == '' {
3413 elem_type_name = e.infer_array_elem_type(values)
3414 }
3415 return ArrayValue{
3416 elem_type_name: elem_type_name
3417 values: values
3418 }
3419 }
3420 mut values := []Value{cap: expr.exprs.len}
3421 // Spread syntax `[...base, e1, e2]` — prepend the base array's
3422 // values before any explicit elements. Deep-clone each item so
3423 // the new array does not alias the base's nested storage (mirrors
3424 // the runtime `array__clone_to_depth` semantics used by lowered
3425 // codegen paths).
3426 if expr.update_expr !is ast.EmptyExpr {
3427 base_value := e.eval_expr(expr.update_expr)!
3428 if base_value is ArrayValue {
3429 cloned_base := e.clone_array_to_depth(base_value, 100)
3430 for v in cloned_base.values {
3431 values << v
3432 }
3433 if elem_type_name == '' {
3434 elem_type_name = base_value.elem_type_name
3435 }
3436 }
3437 }
3438 for item in expr.exprs {
3439 values << e.eval_expr(item)!
3440 }
3441 if elem_type_name == '' {
3442 elem_type_name = e.infer_array_elem_type(values)
3443 }
3444 return ArrayValue{
3445 elem_type_name: elem_type_name
3446 values: values
3447 }
3448 }
3449 ast.InitExpr {
3450 mut fields := map[string]Value{}
3451 mut data_fields := map[string]Value{}
3452 type_name := expr.typ.name()
3453 for field in expr.fields {
3454 mut value := e.eval_expr(field.value)!
3455 if field.name != '' && !field.name.starts_with('_data.') {
3456 if field_type := e.lookup_struct_field_type(type_name, field.name) {
3457 value = e.adapt_value_to_type(value, field_type)
3458 }
3459 }
3460 if field.name.starts_with('_data.') {
3461 data_fields[field.name.all_after('_data.')] = value
3462 continue
3463 }
3464 fields[field.name] = value
3465 }
3466 if data_fields.len > 0 {
3467 mut data_struct := StructValue{
3468 type_name: type_name + '._data'
3469 fields: map[string]Value{}
3470 }
3471 if existing_data := fields['_data'] {
3472 if existing_data is StructValue {
3473 data_struct = existing_data
3474 }
3475 }
3476 mut merged_data_fields := data_struct.fields.clone()
3477 for name, value in data_fields {
3478 merged_data_fields[name] = value
3479 }
3480 fields['_data'] = StructValue{
3481 type_name: data_struct.type_name
3482 fields: merged_data_fields
3483 }
3484 }
3485 return StructValue{
3486 type_name: type_name
3487 fields: fields
3488 }
3489 }
3490 ast.AsCastExpr {
3491 return e.eval_as_cast_expr(expr)
3492 }
3493 ast.BasicLiteral {
3494 return e.eval_basic_literal(expr)
3495 }
3496 ast.CallExpr {
3497 return e.eval_call_expr(expr)
3498 }
3499 ast.CallOrCastExpr {
3500 if e.is_type_expr(expr.lhs) {
3501 value := e.eval_expr(expr.expr)!
3502 target_name := expr.lhs.name()
3503 if e.is_sum_type_name(target_name) {
3504 return e.wrap_sumtype_value(target_name, value, expr.expr) or {
3505 e.cast_value(value, target_name)!
3506 }
3507 }
3508 return e.cast_value(value, target_name)
3509 }
3510 return e.eval_call_expr(ast.CallExpr{
3511 lhs: expr.lhs
3512 args: [expr.expr]
3513 pos: expr.pos
3514 })
3515 }
3516 ast.CastExpr {
3517 value := e.eval_expr(expr.expr)!
3518 target_name := expr.typ.name()
3519 if e.is_sum_type_name(target_name) {
3520 return e.wrap_sumtype_value(target_name, value, expr.expr) or {
3521 e.cast_value(value, target_name)!
3522 }
3523 }
3524 return e.cast_value(value, target_name)
3525 }
3526 ast.ComptimeExpr {
3527 return e.eval_expr(expr.expr)
3528 }
3529 ast.GenericArgs {
3530 if e.is_type_expr(expr) {
3531 return TypeValue{
3532 name: generic_args_name(expr)
3533 }
3534 }
3535 return error('v2.eval: unsupported generic expression `${generic_args_name(expr)}`')
3536 }
3537 ast.Ident {
3538 return e.eval_ident(expr.name)
3539 }
3540 ast.IfExpr {
3541 return e.eval_if_value(expr)
3542 }
3543 ast.IndexExpr {
3544 return e.eval_index_expr(expr)
3545 }
3546 ast.InfixExpr {
3547 return e.eval_infix_expr(expr)
3548 }
3549 ast.KeywordOperator {
3550 return e.eval_keyword_operator(expr)
3551 }
3552 ast.MapInitExpr {
3553 mut entries := []MapEntry{cap: expr.keys.len}
3554 for i, key_expr in expr.keys {
3555 key := e.eval_expr(key_expr)!
3556 val := if i < expr.vals.len {
3557 e.eval_expr(expr.vals[i])!
3558 } else {
3559 void_value()
3560 }
3561 entries << MapEntry{
3562 key: key
3563 value: val
3564 }
3565 }
3566 default_value := if expr.typ is ast.Type && expr.typ is ast.MapType {
3567 e.zero_value_for_type_expr((expr.typ as ast.MapType).value_type)
3568 } else if entries.len > 0 {
3569 e.zero_value_like(entries[0].value)
3570 } else {
3571 void_value()
3572 }
3573 return MapValue{
3574 default_value: default_value
3575 entries: entries
3576 }
3577 }
3578 ast.ModifierExpr {
3579 return e.eval_expr(expr.expr)
3580 }
3581 ast.ParenExpr {
3582 return e.eval_expr(expr.expr)
3583 }
3584 ast.PostfixExpr {
3585 return e.eval_postfix_expr(expr)
3586 }
3587 ast.PrefixExpr {
3588 return e.eval_prefix_expr(expr)
3589 }
3590 ast.RangeExpr {
3591 return RangeValue{
3592 start: e.value_as_int(e.eval_expr(expr.start)!)!
3593 end: e.value_as_int(e.eval_expr(expr.end)!)!
3594 inclusive: expr.op == .ellipsis
3595 }
3596 }
3597 ast.SelectorExpr {
3598 return e.eval_selector_expr(expr)
3599 }
3600 ast.StringInterLiteral {
3601 return e.eval_string_inter(expr)
3602 }
3603 ast.StringLiteral {
3604 return decode_string_literal(expr.value)
3605 }
3606 ast.Tuple {
3607 mut values := []Value{cap: expr.exprs.len}
3608 for item in expr.exprs {
3609 values << e.eval_expr(item)!
3610 }
3611 return TupleValue{
3612 values: values
3613 }
3614 }
3615 ast.Type {
3616 match expr {
3617 ast.NilType, ast.NoneType {
3618 return void_value()
3619 }
3620 else {}
3621 }
3622
3623 return TypeValue{
3624 name: e.type_node_name(expr)
3625 }
3626 }
3627 ast.UnsafeExpr {
3628 return e.eval_unsafe_expr(expr)
3629 }
3630 ast.EmptyExpr {
3631 return void_value()
3632 }
3633 else {
3634 return error('v2.eval: unsupported expression `${expr.type_name()}`')
3635 }
3636 }
3637}
3638
3639fn (mut e Eval) eval_as_cast_expr(expr ast.AsCastExpr) !Value {
3640 value := e.eval_expr(expr.expr)!
3641 target_name := e.type_expr_name(expr.typ)
3642 if value is StructValue {
3643 if payload := e.unwrap_sumtype_value(value, target_name) {
3644 return payload
3645 }
3646 if e.value_matches_type_name(value, target_name) {
3647 return value
3648 }
3649 return error('v2.eval: `${value.type_name}` is not `${target_name}` in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
3650 }
3651 if e.value_matches_type_name(value, target_name) {
3652 return e.cast_value(value, target_name)
3653 }
3654 return error('v2.eval: `${e.runtime_type_name(value)}` is not `${target_name}` in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
3655}
3656
3657fn (mut e Eval) eval_basic_literal(expr ast.BasicLiteral) !Value {
3658 if expr.kind == .key_true {
3659 v := Value(true)
3660 return v
3661 } else if expr.kind == .key_false {
3662 v := Value(false)
3663 return v
3664 } else if expr.kind == .number {
3665 if expr.value.contains('.') || expr.value.contains('e') || expr.value.contains('E') {
3666 f := strconv.atof64(expr.value)!
3667 v := Value(f)
3668 return v
3669 } else {
3670 n := strconv.parse_int(expr.value, 0, 64)!
3671 v := Value(n)
3672 return v
3673 }
3674 } else if expr.kind == .char {
3675 if expr.value.len == 0 {
3676 v := Value(i64(0))
3677 return v
3678 } else {
3679 decoded := decode_string_literal(expr.value)
3680 if decoded.len == 0 {
3681 v := Value(i64(0))
3682 return v
3683 }
3684 v := Value(i64(decoded.runes()[0]))
3685 return v
3686 }
3687 } else {
3688 return error('v2.eval: unsupported literal `${expr.kind}`')
3689 }
3690}
3691
3692fn (mut e Eval) eval_ident(name string) !Value {
3693 value_result := e.lookup_var(name)
3694 if value_result.found {
3695 return value_result.value
3696 }
3697 module_name := e.resolve_module_name(name)
3698 if module_name != '' {
3699 return ModuleValue{
3700 name: module_name
3701 }
3702 }
3703 if name == 'nil' || name == 'none' {
3704 return void_value()
3705 }
3706 if e.is_builtin_type_name(name) {
3707 return TypeValue{
3708 name: name
3709 }
3710 }
3711 cur_module := e.current_module_name()
3712 if e.has_type_name(cur_module, name) {
3713 return TypeValue{
3714 name: '${cur_module}.${name}'
3715 }
3716 }
3717 if type_value := e.resolve_mangled_type_value(name) {
3718 return type_value
3719 }
3720 const_result := e.lookup_const(cur_module, name)
3721 if const_result.found {
3722 return const_result.value
3723 }
3724 builtin_const := e.lookup_const('builtin', name)
3725 if builtin_const.found {
3726 return builtin_const.value
3727 }
3728 if mangled_const := e.resolve_mangled_const(name) {
3729 return mangled_const
3730 }
3731 return error('v2.eval: unknown identifier `${name}` in `${e.current_function_label()}` stack `${e.call_stack_trace()}`')
3732}
3733
3734fn (e &Eval) resolve_module_name(name string) string {
3735 file_name := e.current_file_name()
3736 if file_name != '' {
3737 if aliases := e.file_import_alias[file_name] {
3738 if name in aliases {
3739 return aliases[name]
3740 }
3741 }
3742 }
3743 if name in e.modules {
3744 return name
3745 }
3746 return ''
3747}
3748
3749fn (e &Eval) has_type_name(module_name string, type_name string) bool {
3750 if module_name !in e.type_names {
3751 return false
3752 }
3753 return type_name in e.type_names[module_name]
3754}
3755
3756fn (mut e Eval) resolve_mangled_const(name string) ?Value {
3757 for module_name in e.consts.keys() {
3758 prefix := e.mangled_module_name(module_name) + '__'
3759 if !name.starts_with(prefix) {
3760 continue
3761 }
3762 key := name[prefix.len..].all_after_last('__')
3763 const_result := e.lookup_const(module_name, key)
3764 if const_result.found {
3765 return const_result.value
3766 }
3767 }
3768 if name.contains('__') {
3769 type_name := name.all_before_last('__')
3770 field_name := name.all_after_last('__')
3771 for module_name, module_types in e.type_names {
3772 if type_name !in module_types {
3773 continue
3774 }
3775 const_result := e.lookup_const(module_name, field_name)
3776 if const_result.found {
3777 return const_result.value
3778 }
3779 }
3780 }
3781 return none
3782}
3783
3784fn (e &Eval) resolve_mangled_type_value(name string) ?TypeValue {
3785 for module_name, module_types in e.type_names {
3786 prefix := e.mangled_module_name(module_name) + '__'
3787 if !name.starts_with(prefix) {
3788 continue
3789 }
3790 type_name := name[prefix.len..]
3791 if type_name in module_types {
3792 return TypeValue{
3793 name: '${module_name}.${type_name}'
3794 }
3795 }
3796 }
3797 return none
3798}
3799
3800fn (e &Eval) mangled_module_name(name string) string {
3801 if name == '' {
3802 return ''
3803 }
3804 return name.replace('.', '__')
3805}
3806
3807fn (mut e Eval) lookup_const(module_name string, name string) MaybeValue {
3808 if module_name !in e.consts {
3809 return MaybeValue{}
3810 }
3811 mut entry := e.consts[module_name][name] or { return MaybeValue{} }
3812 if entry.cached {
3813 return MaybeValue{
3814 found: true
3815 value: entry.value
3816 }
3817 }
3818 if entry.evaluating {
3819 return MaybeValue{}
3820 }
3821 entry.evaluating = true
3822 e.consts[module_name][name] = entry
3823 e.call_stack << CallFrame{
3824 module_name: module_name
3825 file_name: entry.file_name
3826 fn_name: 'const ${name}'
3827 }
3828 value := e.eval_expr(entry.expr) or {
3829 entry.evaluating = false
3830 e.consts[module_name][name] = entry
3831 e.call_stack.pop()
3832 return MaybeValue{}
3833 }
3834 e.call_stack.pop()
3835 entry.cached = true
3836 entry.evaluating = false
3837 entry.value = value
3838 e.consts[module_name][name] = entry
3839 return MaybeValue{
3840 found: true
3841 value: value
3842 }
3843}
3844
3845fn (mut e Eval) set_runtime_const(module_name string, name string, value Value) bool {
3846 if module_name !in e.consts {
3847 return false
3848 }
3849 mut entry := e.consts[module_name][name] or { return false }
3850 entry.cached = true
3851 entry.evaluating = false
3852 entry.value = value
3853 e.consts[module_name][name] = entry
3854 return true
3855}
3856
3857fn (mut e Eval) eval_call_expr(expr ast.CallExpr) !Value {
3858 if expr.lhs is ast.SelectorExpr {
3859 selector := expr.lhs as ast.SelectorExpr
3860 if builder_result := e.maybe_call_builder_wrapper(selector.rhs.name, expr.args) {
3861 return builder_result
3862 }
3863 if selector.lhs is ast.SelectorExpr {
3864 inner := selector.lhs as ast.SelectorExpr
3865 if inner.rhs.name == 'flags' {
3866 _ = e.eval_expr(inner.lhs)!
3867 for arg in expr.args {
3868 _ = e.eval_expr(arg)!
3869 }
3870 return match selector.rhs.name {
3871 'set', 'clear' { void_value() }
3872 'has' { Value(false) }
3873 else { return error('v2.eval: unsupported array flags method `${selector.rhs.name}`') }
3874 }
3875 }
3876 }
3877 if selector.lhs is ast.Ident {
3878 module_name := e.resolve_module_name(selector.lhs.name)
3879 if module_name != '' {
3880 mut args := []Value{cap: expr.args.len}
3881 for arg in expr.args {
3882 args << e.eval_expr(arg)!
3883 }
3884 result := e.call_function(module_name, selector.rhs.name, args)!
3885 e.writeback_mut_args(expr.args, result)!
3886 return call_result_value(result)
3887 }
3888 }
3889 receiver := e.eval_expr(selector.lhs)!
3890 mut args := []Value{cap: expr.args.len}
3891 for arg in expr.args {
3892 args << e.eval_expr(arg)!
3893 }
3894 if target := e.resolve_method_target(receiver, selector.rhs.name) {
3895 mut method_args := []Value{cap: args.len + 1}
3896 method_args << receiver
3897 method_args << args
3898 result := e.call_function(target.module_name, target.fn_name, method_args)!
3899 e.writeback_method_mut_args(selector.lhs, expr.args, result)!
3900 return call_result_value(result)
3901 }
3902 return e.call_value_method(receiver, selector.rhs.name, args)
3903 }
3904 if expr.lhs is ast.Ident {
3905 if builder_result := e.maybe_call_builder_wrapper(expr.lhs.name, expr.args) {
3906 return builder_result
3907 }
3908 mut args := []Value{cap: expr.args.len}
3909 for arg in expr.args {
3910 args << e.eval_expr(arg)!
3911 }
3912 if target := e.resolve_mangled_function_target(expr.lhs.name) {
3913 result := e.call_function(target.module_name, target.fn_name, args)!
3914 e.writeback_mut_args(expr.args, result)!
3915 return call_result_value(result)
3916 }
3917 result := e.call_function(e.current_module_name(), expr.lhs.name, args) or {
3918 if result := e.call_function('builtin', expr.lhs.name, args) {
3919 e.writeback_mut_args(expr.args, result)!
3920 return call_result_value(result)
3921 }
3922 return error(err.msg())
3923 }
3924 e.writeback_mut_args(expr.args, result)!
3925 return call_result_value(result)
3926 }
3927 return error('v2.eval: unsupported call target `${expr.lhs.type_name()}`')
3928}
3929
3930fn (mut e Eval) writeback_method_mut_args(receiver_expr ast.Expr, arg_exprs []ast.Expr, result CallResult) ! {
3931 for idx, value in result.mut_args {
3932 if idx == 0 {
3933 if can_update_target(receiver_expr) {
3934 e.update_target(receiver_expr, value)!
3935 }
3936 continue
3937 }
3938 arg_idx := idx - 1
3939 if arg_idx >= 0 && arg_idx < arg_exprs.len && can_update_target(arg_exprs[arg_idx]) {
3940 e.update_target(arg_exprs[arg_idx], value)!
3941 }
3942 }
3943}
3944
3945fn (mut e Eval) maybe_call_builder_wrapper(name string, args []ast.Expr) ?Value {
3946 if !name.contains('Builder__') || args.len == 0 {
3947 return none
3948 }
3949 method_name := name.all_after('Builder__')
3950 target_expr := builder_target_expr(args[0]) or { return none }
3951 target_value := e.eval_expr(target_expr) or { return none }
3952 if target_value !is ArrayValue {
3953 return none
3954 }
3955 mut receiver := target_value as ArrayValue
3956 match method_name {
3957 'cut_last' {
3958 if args.len < 2 {
3959 return none
3960 }
3961 count_value := e.eval_expr(args[1]) or { return none }
3962 n := int(e.value_as_int(count_value) or { return none })
3963 mut items := receiver.values.clone()
3964 if n <= 0 || n > items.len {
3965 return ''
3966 }
3967 cut := items[items.len - n..].clone()
3968 items = items[..items.len - n].clone()
3969 e.update_target(target_expr, ArrayValue{
3970 values: items
3971 }) or { return none }
3972 return e.builder_array_to_string(ArrayValue{
3973 values: cut
3974 })
3975 }
3976 'go_back' {
3977 if args.len < 2 {
3978 return none
3979 }
3980 count_value := e.eval_expr(args[1]) or { return none }
3981 n := int(e.value_as_int(count_value) or { return none })
3982 if n <= 0 {
3983 return void_value()
3984 }
3985 keep_len := if n > receiver.values.len { 0 } else { receiver.values.len - n }
3986 e.update_target(target_expr, ArrayValue{
3987 values: receiver.values[..keep_len].clone()
3988 }) or { return none }
3989 return void_value()
3990 }
3991 'go_back_to' {
3992 if args.len < 2 {
3993 return none
3994 }
3995 pos_value := e.eval_expr(args[1]) or { return none }
3996 pos := int(e.value_as_int(pos_value) or { return none })
3997 keep_len := if pos < 0 {
3998 0
3999 } else if pos > receiver.values.len {
4000 receiver.values.len
4001 } else {
4002 pos
4003 }
4004 e.update_target(target_expr, ArrayValue{
4005 values: receiver.values[..keep_len].clone()
4006 }) or { return none }
4007 return void_value()
4008 }
4009 'last_n' {
4010 if args.len < 2 {
4011 return none
4012 }
4013 count_value := e.eval_expr(args[1]) or { return none }
4014 n := int(e.value_as_int(count_value) or { return none })
4015 return e.builder_tail_string(receiver, n)
4016 }
4017 'after' {
4018 if args.len < 2 {
4019 return none
4020 }
4021 start_value := e.eval_expr(args[1]) or { return none }
4022 start := int(e.value_as_int(start_value) or { return none })
4023 return e.builder_slice_string(receiver, start, receiver.values.len - start)
4024 }
4025 'spart' {
4026 if args.len < 3 {
4027 return none
4028 }
4029 start_value := e.eval_expr(args[1]) or { return none }
4030 len_value := e.eval_expr(args[2]) or { return none }
4031 start := int(e.value_as_int(start_value) or { return none })
4032 n := int(e.value_as_int(len_value) or { return none })
4033 return e.builder_slice_string(receiver, start, n)
4034 }
4035 'str' {
4036 result := e.builder_array_to_string(receiver)
4037 e.update_target(target_expr, ArrayValue{
4038 values: []Value{}
4039 }) or { return none }
4040 return result
4041 }
4042 'write_ptr' {
4043 return void_value()
4044 }
4045 'write_byte', 'write_rune', 'write_repeated_rune', 'write_string', 'write_string2',
4046 'write_u8', 'writeln', 'writeln2' {
4047 mut items := receiver.values.clone()
4048 match method_name {
4049 'write_string' {
4050 if args.len < 2 {
4051 return none
4052 }
4053 value := e.eval_expr(args[1]) or { return none }
4054 e.append_builder_string(mut items, e.value_string(value))
4055 }
4056 'write_string2' {
4057 if args.len < 3 {
4058 return none
4059 }
4060 first_value := e.eval_expr(args[1]) or { return none }
4061 second_value := e.eval_expr(args[2]) or { return none }
4062 e.append_builder_string(mut items, e.value_string(first_value))
4063 e.append_builder_string(mut items, e.value_string(second_value))
4064 }
4065 'write_rune' {
4066 if args.len < 2 {
4067 return none
4068 }
4069 rune_value := e.eval_expr(args[1]) or { return none }
4070 e.append_builder_rune(mut items, e.value_as_int(rune_value) or { return none })
4071 }
4072 'write_repeated_rune' {
4073 if args.len < 3 {
4074 return none
4075 }
4076 rune_value := e.eval_expr(args[1]) or { return none }
4077 count_value := e.eval_expr(args[2]) or { return none }
4078 r := e.value_as_int(rune_value) or { return none }
4079 count := int(e.value_as_int(count_value) or { return none })
4080 for _ in 0 .. count {
4081 e.append_builder_rune(mut items, r)
4082 }
4083 }
4084 'write_byte', 'write_u8' {
4085 if args.len < 2 {
4086 return none
4087 }
4088 byte_value := e.eval_expr(args[1]) or { return none }
4089 items << (e.value_as_int(byte_value) or { return none })
4090 }
4091 'writeln' {
4092 if args.len < 2 {
4093 return none
4094 }
4095 value := e.eval_expr(args[1]) or { return none }
4096 e.append_builder_string(mut items, e.value_string(value))
4097 items << i64(`\n`)
4098 }
4099 'writeln2' {
4100 if args.len < 3 {
4101 return none
4102 }
4103 first_value := e.eval_expr(args[1]) or { return none }
4104 second_value := e.eval_expr(args[2]) or { return none }
4105 e.append_builder_string(mut items, e.value_string(first_value))
4106 items << i64(`\n`)
4107 e.append_builder_string(mut items, e.value_string(second_value))
4108 items << i64(`\n`)
4109 }
4110 else {}
4111 }
4112
4113 e.update_target(target_expr, ArrayValue{
4114 values: items
4115 }) or { return none }
4116 return void_value()
4117 }
4118 else {
4119 return none
4120 }
4121 }
4122}
4123
4124fn builder_target_expr(expr ast.Expr) ?ast.Expr {
4125 match expr {
4126 ast.CastExpr {
4127 return builder_target_expr(expr.expr)
4128 }
4129 ast.Ident {
4130 return expr
4131 }
4132 ast.ModifierExpr {
4133 return builder_target_expr(expr.expr)
4134 }
4135 ast.ParenExpr {
4136 return builder_target_expr(expr.expr)
4137 }
4138 ast.SelectorExpr {
4139 return expr
4140 }
4141 ast.PrefixExpr {
4142 if expr.op == .amp {
4143 return builder_target_expr(expr.expr)
4144 }
4145 }
4146 else {}
4147 }
4148
4149 return none
4150}
4151
4152fn receiver_method_type_candidates(receiver Value) []string {
4153 return match receiver {
4154 StructValue {
4155 struct_field_lookup_candidates(receiver.type_name)
4156 }
4157 TypeValue {
4158 struct_field_lookup_candidates(receiver.name)
4159 }
4160 else {
4161 []string{}
4162 }
4163 }
4164}
4165
4166fn (e &Eval) resolve_method_target(receiver Value, method_name string) ?MaybeFunctionTarget {
4167 candidates := receiver_method_type_candidates(receiver)
4168 if candidates.len == 0 {
4169 return none
4170 }
4171 for module_name, functions in e.functions {
4172 for candidate in candidates {
4173 key := '${candidate}__${method_name}'
4174 if key in functions {
4175 return MaybeFunctionTarget{
4176 found: true
4177 module_name: module_name
4178 fn_name: key
4179 }
4180 }
4181 }
4182 }
4183 return none
4184}
4185
4186fn (e &Eval) append_builder_string(mut items []Value, s string) {
4187 for b in s.bytes() {
4188 items << i64(b)
4189 }
4190}
4191
4192fn (e &Eval) append_builder_rune(mut items []Value, r i64) {
4193 e.append_builder_string(mut items, rune(r).str())
4194}
4195
4196fn (e &Eval) builder_array_to_string(receiver ArrayValue) string {
4197 mut bytes := []u8{cap: receiver.values.len}
4198 for item in receiver.values {
4199 bytes << u8(e.value_as_int(item) or { 0 })
4200 }
4201 return bytes.bytestr()
4202}
4203
4204fn (e &Eval) builder_slice_string(receiver ArrayValue, start_pos int, n int) string {
4205 if start_pos < 0 || n <= 0 || start_pos >= receiver.values.len {
4206 return ''
4207 }
4208 end := if start_pos + n > receiver.values.len {
4209 receiver.values.len
4210 } else {
4211 start_pos + n
4212 }
4213 return e.builder_array_to_string(ArrayValue{
4214 values: receiver.values[start_pos..end].clone()
4215 })
4216}
4217
4218fn (e &Eval) builder_tail_string(receiver ArrayValue, n int) string {
4219 if n <= 0 || n > receiver.values.len {
4220 return ''
4221 }
4222 return e.builder_array_to_string(ArrayValue{
4223 values: receiver.values[receiver.values.len - n..].clone()
4224 })
4225}
4226
4227fn (mut e Eval) eval_if_value(expr ast.IfExpr) !Value {
4228 should_run := if expr.cond is ast.EmptyExpr {
4229 true
4230 } else {