aver-lang 0.17.0

VM and transpiler for Aver, a statically-typed language designed for AI-assisted development
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
;; UTF-8 string operations: slice (by code point), chars (one-char
;; strings), split (by delimiter), join (with separator), replace
;; (split + join).

(func $rt_str_slice (param $str i32) (param $start i32) (param $end i32) (result i32)
  (local $start_idx i32)
  (local $end_idx i32)
  (local $byte_len i32)
  (local $byte_pos i32)
  (local $char_pos i32)
  (local $byte_start i32)
  (local $byte_end i32)
  (local $lead i32)
  (local $width i32)
  (local $ptr i32)
  (local $new_len i32)

  ;; start_idx = max(start, 0)
  local.get $start
  i32.const 0
  i32.lt_s
  if (result i32)
    i32.const 0
  else
    local.get $start
  end
  local.set $start_idx

  ;; end_idx = max(end, 0)
  local.get $end
  i32.const 0
  i32.lt_s
  if (result i32)
    i32.const 0
  else
    local.get $end
  end
  local.set $end_idx

  ;; start >= end → return empty OBJ_STRING
  local.get $start_idx
  local.get $end_idx
  i32.ge_s
  if
    i32.const 8
    call $rt_alloc
    local.set $ptr
    local.get $ptr
    i64.const 0
    i64.store
    local.get $ptr
    return
  end

  ;; byte_len = header.byte_len
  local.get $str
  i64.load
  i64.const 0xFFFFFFFF
  i64.and
  i32.wrap_i64
  local.set $byte_len

  i32.const 0
  local.set $byte_pos
  i32.const 0
  local.set $char_pos
  local.get $byte_len
  local.set $byte_start
  local.get $byte_len
  local.set $byte_end

  block
    loop
      ;; if char_pos == start_idx → record byte_start
      local.get $char_pos
      local.get $start_idx
      i32.eq
      if
        local.get $byte_pos
        local.set $byte_start
      end

      ;; if char_pos == end_idx → record byte_end and break
      local.get $char_pos
      local.get $end_idx
      i32.eq
      if
        local.get $byte_pos
        local.set $byte_end
        br 2
      end

      local.get $byte_pos
      local.get $byte_len
      i32.ge_u
      br_if 1

      ;; lead = byte at str+8+byte_pos
      local.get $str
      local.get $byte_pos
      i32.add
      i32.load8_u offset=8
      local.set $lead

      ;; width
      local.get $lead
      i32.const 0x80
      i32.lt_u
      if
        i32.const 1
        local.set $width
      else
        local.get $lead
        i32.const 0xE0
        i32.lt_u
        if
          i32.const 2
          local.set $width
        else
          local.get $lead
          i32.const 0xF0
          i32.lt_u
          if
            i32.const 3
            local.set $width
          else
            i32.const 4
            local.set $width
          end
        end
      end

      local.get $byte_pos
      local.get $width
      i32.add
      local.set $byte_pos
      local.get $char_pos
      i32.const 1
      i32.add
      local.set $char_pos
      br 0
    end
  end

  ;; new_len = byte_end - byte_start
  local.get $byte_end
  local.get $byte_start
  i32.sub
  local.set $new_len

  ;; alloc + memcpy
  i32.const 8
  local.get $new_len
  i32.const 7
  i32.add
  i32.const -8
  i32.and
  i32.add
  call $rt_alloc
  local.set $ptr

  local.get $ptr
  i64.const 0
  local.get $new_len
  i64.extend_i32_u
  i64.or
  i64.store

  local.get $ptr
  i32.const 8
  i32.add
  local.get $str
  i32.const 8
  i32.add
  local.get $byte_start
  i32.add
  local.get $new_len
  memory.copy

  local.get $ptr
)
(export "rt_str_slice" (func $rt_str_slice))

;; rt_str_chars: turn each UTF-8 code point into its own OBJ_STRING
;; (1..4 bytes), accumulate into a list, reverse for natural order.
(func $rt_str_chars (param $str i32) (result i32)
  (local $byte_len i32)
  (local $byte_pos i32)
  (local $rev_list i32)
  (local $lead i32)
  (local $width i32)
  (local $ptr i32)

  local.get $str
  i64.load
  i64.const 0xFFFFFFFF
  i64.and
  i32.wrap_i64
  local.set $byte_len

  i32.const 0
  local.set $byte_pos
  i32.const 0
  local.set $rev_list

  block
    loop
      local.get $byte_pos
      local.get $byte_len
      i32.ge_u
      br_if 1

      local.get $str
      local.get $byte_pos
      i32.add
      i32.load8_u offset=8
      local.set $lead

      local.get $lead
      i32.const 0x80
      i32.lt_u
      if
        i32.const 1
        local.set $width
      else
        local.get $lead
        i32.const 0xE0
        i32.lt_u
        if
          i32.const 2
          local.set $width
        else
          local.get $lead
          i32.const 0xF0
          i32.lt_u
          if
            i32.const 3
            local.set $width
          else
            i32.const 4
            local.set $width
          end
        end
      end

      ;; alloc OBJ_STRING with len = width
      i32.const 8
      local.get $width
      i32.const 7
      i32.add
      i32.const -8
      i32.and
      i32.add
      call $rt_alloc
      local.set $ptr

      local.get $ptr
      i64.const 0
      local.get $width
      i64.extend_i32_u
      i64.or
      i64.store

      local.get $ptr
      i32.const 8
      i32.add
      local.get $str
      i32.const 8
      i32.add
      local.get $byte_pos
      i32.add
      local.get $width
      memory.copy

      ;; rev_list = cons(ptr, rev_list)
      local.get $ptr
      i64.extend_i32_u
      local.get $rev_list
      i32.const 1
      call $rt_list_cons
      local.set $rev_list

      local.get $byte_pos
      local.get $width
      i32.add
      local.set $byte_pos
      br 0
    end
  end

  local.get $rev_list
  call $rt_list_reverse
)
(export "rt_str_chars" (func $rt_str_chars))

;; Helper: alloc OBJ_STRING with byte_len = `len`, then memory.copy
;; bytes from `src + 8 + start` for `len` bytes. Returns the new ptr.
(func $rt_str_copy_range (param $src i32) (param $start i32) (param $len i32) (result i32)
  (local $ptr i32)

  i32.const 8
  local.get $len
  i32.const 7
  i32.add
  i32.const -8
  i32.and
  i32.add
  call $rt_alloc
  local.set $ptr

  local.get $ptr
  i64.const 0
  local.get $len
  i64.extend_i32_u
  i64.or
  i64.store

  local.get $ptr
  i32.const 8
  i32.add
  local.get $src
  i32.const 8
  i32.add
  local.get $start
  i32.add
  local.get $len
  memory.copy

  local.get $ptr
)
(export "rt_str_copy_range" (func $rt_str_copy_range))

;; rt_str_split: returns a list of OBJ_STRINGs. Empty delim splits
;; per UTF-8 code point. Otherwise iterates rt_str_find calls and
;; collects the chunks before each match.
(func $rt_str_split (param $str i32) (param $delim i32) (result i32)
  (local $str_len i32)
  (local $delim_len i32)
  (local $search_pos i32)
  (local $part_start i32)
  (local $match_pos i32)
  (local $part_len i32)
  (local $rev_list i32)
  (local $ptr i32)
  (local $byte_pos i32)
  (local $lead i32)
  (local $width i32)

  local.get $str
  i64.load
  i64.const 0xFFFFFFFF
  i64.and
  i32.wrap_i64
  local.set $str_len

  local.get $delim
  i64.load
  i64.const 0xFFFFFFFF
  i64.and
  i32.wrap_i64
  local.set $delim_len

  ;; Empty delim: per-char split with leading + trailing empties (matches Rust impl).
  local.get $delim_len
  i32.eqz
  if
    i32.const 0
    local.set $rev_list

    ;; Push initial empty.
    local.get $str
    i32.const 0
    i32.const 0
    call $rt_str_copy_range
    i64.extend_i32_u
    local.get $rev_list
    i32.const 1
    call $rt_list_cons
    local.set $rev_list

    i32.const 0
    local.set $byte_pos
    block
      loop
        local.get $byte_pos
        local.get $str_len
        i32.ge_u
        br_if 1

        local.get $str
        local.get $byte_pos
        i32.add
        i32.load8_u offset=8
        local.set $lead

        local.get $lead
        i32.const 0x80
        i32.lt_u
        if
          i32.const 1
          local.set $width
        else
          local.get $lead
          i32.const 0xE0
          i32.lt_u
          if
            i32.const 2
            local.set $width
          else
            local.get $lead
            i32.const 0xF0
            i32.lt_u
            if
              i32.const 3
              local.set $width
            else
              i32.const 4
              local.set $width
            end
          end
        end

        ;; Push the next character as a 1-element string slice.
        local.get $str
        local.get $byte_pos
        local.get $width
        call $rt_str_copy_range
        i64.extend_i32_u
        local.get $rev_list
        i32.const 1
        call $rt_list_cons
        local.set $rev_list

        local.get $byte_pos
        local.get $width
        i32.add
        local.set $byte_pos
        br 0
      end
    end

    ;; Trailing empty.
    local.get $str
    i32.const 0
    i32.const 0
    call $rt_str_copy_range
    i64.extend_i32_u
    local.get $rev_list
    i32.const 1
    call $rt_list_cons
    local.set $rev_list

    local.get $rev_list
    call $rt_list_reverse
    return
  end

  ;; Non-empty delim: search loop.
  i32.const 0
  local.set $rev_list
  i32.const 0
  local.set $search_pos
  i32.const 0
  local.set $part_start

  block
    loop
      local.get $str
      local.get $delim
      local.get $search_pos
      call $rt_str_find
      local.set $match_pos

      local.get $match_pos
      i32.const -1
      i32.eq
      br_if 1

      ;; part_len = match_pos - part_start
      local.get $match_pos
      local.get $part_start
      i32.sub
      local.set $part_len

      local.get $str
      local.get $part_start
      local.get $part_len
      call $rt_str_copy_range
      i64.extend_i32_u
      local.get $rev_list
      i32.const 1
      call $rt_list_cons
      local.set $rev_list

      ;; advance search_pos and part_start past the match
      local.get $match_pos
      local.get $delim_len
      i32.add
      local.set $search_pos
      local.get $search_pos
      local.set $part_start
      br 0
    end
  end

  ;; final chunk = str[part_start..]
  local.get $str_len
  local.get $part_start
  i32.sub
  local.set $part_len

  local.get $str
  local.get $part_start
  local.get $part_len
  call $rt_str_copy_range
  i64.extend_i32_u
  local.get $rev_list
  i32.const 1
  call $rt_list_cons
  local.set $rev_list

  local.get $rev_list
  call $rt_list_reverse
)
(export "rt_str_split" (func $rt_str_split))

;; rt_str_join: walk the list once to compute total length, alloc the
;; buffer, walk again to copy each element + separator into place.
(func $rt_str_join (param $list i32) (param $sep i32) (result i32)
  (local $total i32)
  (local $cur i32)
  (local $first i32)
  (local $sep_len i32)
  (local $head i32)
  (local $head_len i32)
  (local $ptr i32)
  (local $write_pos i32)

  local.get $sep
  i64.load
  i64.const 0xFFFFFFFF
  i64.and
  i32.wrap_i64
  local.set $sep_len

  ;; Pass 1: total length.
  i32.const 0
  local.set $total
  local.get $list
  local.set $cur
  i32.const 1
  local.set $first

  block
    loop
      local.get $cur
      i32.eqz
      br_if 1

      local.get $first
      i32.eqz
      if
        local.get $total
        local.get $sep_len
        i32.add
        local.set $total
      end
      i32.const 0
      local.set $first

      local.get $cur
      i32.const 0
      call $rt_obj_field
      i32.wrap_i64
      local.set $head

      local.get $total
      local.get $head
      i64.load
      i64.const 0xFFFFFFFF
      i64.and
      i32.wrap_i64
      i32.add
      local.set $total

      local.get $cur
      i32.const 1
      call $rt_obj_field_i32
      local.set $cur
      br 0
    end
  end

  ;; alloc 8 + align8(total)
  i32.const 8
  local.get $total
  i32.const 7
  i32.add
  i32.const -8
  i32.and
  i32.add
  call $rt_alloc
  local.set $ptr

  local.get $ptr
  i64.const 0
  local.get $total
  i64.extend_i32_u
  i64.or
  i64.store

  ;; Pass 2: copy.
  i32.const 0
  local.set $write_pos
  local.get $list
  local.set $cur
  i32.const 1
  local.set $first

  block
    loop
      local.get $cur
      i32.eqz
      br_if 1

      ;; If not first, copy separator first.
      local.get $first
      i32.eqz
      if
        local.get $ptr
        i32.const 8
        i32.add
        local.get $write_pos
        i32.add
        local.get $sep
        i32.const 8
        i32.add
        local.get $sep_len
        memory.copy

        local.get $write_pos
        local.get $sep_len
        i32.add
        local.set $write_pos
      end
      i32.const 0
      local.set $first

      local.get $cur
      i32.const 0
      call $rt_obj_field
      i32.wrap_i64
      local.set $head

      local.get $head
      i64.load
      i64.const 0xFFFFFFFF
      i64.and
      i32.wrap_i64
      local.set $head_len

      local.get $ptr
      i32.const 8
      i32.add
      local.get $write_pos
      i32.add
      local.get $head
      i32.const 8
      i32.add
      local.get $head_len
      memory.copy

      local.get $write_pos
      local.get $head_len
      i32.add
      local.set $write_pos

      local.get $cur
      i32.const 1
      call $rt_obj_field_i32
      local.set $cur
      br 0
    end
  end

  local.get $ptr
)
(export "rt_str_join" (func $rt_str_join))

(func $rt_str_replace (param $str i32) (param $old i32) (param $new i32) (result i32)
  local.get $str
  local.get $old
  call $rt_str_split
  local.get $new
  call $rt_str_join
)
(export "rt_str_replace" (func $rt_str_replace))