nomen-lang 0.0.11 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,10 +38,35 @@ pub struct Array<T>: Viewable {
38
38
  ```
39
39
  ```
40
40
  #arch: aarch64
41
- // x19 = self (first elem), x1 = index
41
+ // x19 = self (first elem), x1 = index, x8 = return buf for structs
42
42
  mov x2, #T_SIZE
43
43
  mul x1, x1, x2
44
+ cmp x2, #8
45
+ b.eq .L_at_8
46
+ b.gt .L_at_copy
47
+ cmp x2, #1
48
+ b.eq .L_at_1
49
+ cmp x2, #2
50
+ b.eq .L_at_2
51
+ ldr w0, [x19, x1]
52
+ b .L_at_end
53
+ .L_at_8:
44
54
  ldr x0, [x19, x1]
55
+ b .L_at_end
56
+ .L_at_1:
57
+ ldrb w0, [x19, x1]
58
+ b .L_at_end
59
+ .L_at_2:
60
+ ldrh w0, [x19, x1]
61
+ b .L_at_end
62
+ // Struct element (T_SIZE > 8): copy the slot bytes into the x8
63
+ // sret buffer and return its address (Buffer.load_T convention).
64
+ .L_at_copy:
65
+ mov x0, x8
66
+ add x1, x19, x1
67
+ bl _memcpy
68
+ mov x0, x8
69
+ .L_at_end:
45
70
  ```
46
71
  }
47
72
 
@@ -53,8 +78,35 @@ pub struct Array<T>: Viewable {
53
78
  ```
54
79
  ```
55
80
  #arch: aarch64
56
- // x19 = self (first elem)
81
+ // x19 = self (first elem), x8 = return buf for structs
82
+ mov x3, #T_SIZE
83
+ cmp x3, #8
84
+ b.eq .L_first_8
85
+ b.gt .L_first_copy
86
+ cmp x3, #1
87
+ b.eq .L_first_1
88
+ cmp x3, #2
89
+ b.eq .L_first_2
90
+ ldr w0, [x19]
91
+ b .L_first_end
92
+ .L_first_8:
57
93
  ldr x0, [x19]
94
+ b .L_first_end
95
+ .L_first_1:
96
+ ldrb w0, [x19]
97
+ b .L_first_end
98
+ .L_first_2:
99
+ ldrh w0, [x19]
100
+ b .L_first_end
101
+ // Struct element (T_SIZE > 8): copy the first slot's bytes into
102
+ // the x8 sret buffer and return its address.
103
+ .L_first_copy:
104
+ mov x0, x8
105
+ mov x1, x19
106
+ mov x2, x3
107
+ bl _memcpy
108
+ mov x0, x8
109
+ .L_first_end:
58
110
  ```
59
111
  }
60
112
 
@@ -75,18 +127,35 @@ pub struct Array<T>: Viewable {
75
127
  ```
76
128
  ```
77
129
  #arch: aarch64
78
- // x19 = self (first elem), x1 = index, x2 = value
130
+ // x19 = self (first elem), x1 = index, x2 = value (address if struct)
79
131
  mov x3, #T_SIZE
80
132
  mul x1, x1, x3
81
- // Only use the 8-byte store when T_SIZE is exactly 8 (int/string/ptr).
82
- // For smaller types (char/bool/int8/etc.) strb/strh/str w/ would
83
- // clobber adjacent slots, so fall through to a size-aware store.
84
133
  cmp x3, #8
85
- b.ne .L_set_byte
134
+ b.eq .L_set_8
135
+ b.gt .L_set_copy
136
+ cmp x3, #1
137
+ b.eq .L_set_1
138
+ cmp x3, #2
139
+ b.eq .L_set_2
140
+ str w2, [x19, x1]
141
+ b .L_set_done
142
+ .L_set_8:
86
143
  str x2, [x19, x1]
87
144
  b .L_set_done
88
- .L_set_byte:
145
+ .L_set_1:
89
146
  strb w2, [x19, x1]
147
+ b .L_set_done
148
+ .L_set_2:
149
+ strh w2, [x19, x1]
150
+ b .L_set_done
151
+ // Struct element (T_SIZE > 8): x2 holds the ADDRESS of the value
152
+ // bytes — memcpy them into the slot (Buffer.store_T convention).
153
+ .L_set_copy:
154
+ mov x0, x19
155
+ add x0, x0, x1
156
+ mov x1, x2
157
+ mov x2, x3
158
+ bl _memcpy
90
159
  .L_set_done:
91
160
  ```
92
161
  }
@@ -99,12 +168,38 @@ pub struct Array<T>: Viewable {
99
168
  ```
100
169
  ```
101
170
  #arch: aarch64
102
- // x19 = self (first elem), length at [x19 - 8]
171
+ // x19 = self (first elem), length at [x19 - 8], x8 = return buf for structs
103
172
  ldr x0, [x19, #-8]
104
173
  sub x0, x0, #1
105
174
  mov x1, #T_SIZE
106
175
  mul x0, x0, x1
176
+ cmp x1, #8
177
+ b.eq .L_at_end_8
178
+ b.gt .L_at_end_copy
179
+ cmp x1, #1
180
+ b.eq .L_at_end_1
181
+ cmp x1, #2
182
+ b.eq .L_at_end_2
183
+ ldr w0, [x19, x0]
184
+ b .L_at_end_done
185
+ .L_at_end_8:
107
186
  ldr x0, [x19, x0]
187
+ b .L_at_end_done
188
+ .L_at_end_1:
189
+ ldrb w0, [x19, x0]
190
+ b .L_at_end_done
191
+ .L_at_end_2:
192
+ ldrh w0, [x19, x0]
193
+ b .L_at_end_done
194
+ // Struct element (T_SIZE > 8): x0 = byte offset of the last slot —
195
+ // copy its bytes into the x8 sret buffer and return its address.
196
+ .L_at_end_copy:
197
+ mov x2, x1
198
+ add x1, x19, x0
199
+ mov x0, x8
200
+ bl _memcpy
201
+ mov x0, x8
202
+ .L_at_end_done:
108
203
  ```
109
204
  }
110
205
 
@@ -370,8 +370,26 @@ pub struct Buffer<T> {
370
370
  add x0, x0, x1
371
371
  cmp x3, #8
372
372
  b.gt .Lbuffer_load_T_copy
373
+ // Width-matched load (zero-extends) — an 8-byte ldr on a sub-8
374
+ // element would read past the slab's end and leave garbage in the
375
+ // upper bits.
376
+ cmp x3, #1
377
+ b.eq .Lbuffer_load_T_1
378
+ cmp x3, #2
379
+ b.eq .Lbuffer_load_T_2
380
+ cmp x3, #4
381
+ b.eq .Lbuffer_load_T_4
373
382
  ldr x0, [x0]
374
383
  b .Lbuffer_load_T_end
384
+ .Lbuffer_load_T_1:
385
+ ldrb w0, [x0]
386
+ b .Lbuffer_load_T_end
387
+ .Lbuffer_load_T_2:
388
+ ldrh w0, [x0]
389
+ b .Lbuffer_load_T_end
390
+ .Lbuffer_load_T_4:
391
+ ldr w0, [x0]
392
+ b .Lbuffer_load_T_end
375
393
  .Lbuffer_load_T_copy:
376
394
  mov x1, x0
377
395
  mov x0, x8
@@ -395,8 +413,25 @@ pub struct Buffer<T> {
395
413
  add x0, x0, x1
396
414
  cmp x3, #8
397
415
  b.gt .Lbuffer_store_T_copy
416
+ // Sub-8 element types must use a width-matched store: an 8-byte str
417
+ // would clobber the following slots (and run past the slab's end).
418
+ cmp x3, #1
419
+ b.eq .Lbuffer_store_T_1
420
+ cmp x3, #2
421
+ b.eq .Lbuffer_store_T_2
422
+ cmp x3, #4
423
+ b.eq .Lbuffer_store_T_4
398
424
  str x2, [x0]
399
425
  b .Lbuffer_store_T_end
426
+ .Lbuffer_store_T_1:
427
+ strb w2, [x0]
428
+ b .Lbuffer_store_T_end
429
+ .Lbuffer_store_T_2:
430
+ strh w2, [x0]
431
+ b .Lbuffer_store_T_end
432
+ .Lbuffer_store_T_4:
433
+ str w2, [x0]
434
+ b .Lbuffer_store_T_end
400
435
  .Lbuffer_store_T_copy:
401
436
  mov x1, x2
402
437
  mov x2, x3
@@ -405,6 +440,115 @@ pub struct Buffer<T> {
405
440
  ```
406
441
  }
407
442
 
443
+ // Move a value out of a T_SIZE-sized slot: return it and zero the slot so the
444
+ // buffer no longer holds a (possibly owning) copy. Declared `mov out T` so
445
+ // the result is treated as OWNED by the borrow checker — the slot is
446
+ // relinquished, so the caller is the sole remaining owner. The value
447
+ // counterpart of ClassBuffer.move_int — used by List.pop for any value T.
448
+ inline func move_T = (ref self, int i: i >= 0 && i < self.cap, mov out T) {
449
+ ```
450
+ #arch: c
451
+ T* _slots = (T*)(unsigned long long)self->data;
452
+ T _tmp = _slots[i];
453
+ memset((void*)&_slots[i], 0, T_SIZE);
454
+ return _tmp;
455
+ ```
456
+ ```
457
+ #arch: aarch64
458
+ // x19 = self, x1 = i, x8 = return buf when T_SIZE > 8
459
+ ldr x4, [x19, #8]
460
+ mov x3, #T_SIZE
461
+ madd x5, x1, x3, xzr
462
+ add x5, x4, x5
463
+ cmp x3, #8
464
+ b.gt .Lbuffer_move_T_copy
465
+ // Width-matched load + zero-clear: an 8-byte access on a sub-8
466
+ // element would zero the following slots and run past the slab.
467
+ cmp x3, #1
468
+ b.eq .Lbuffer_move_T_1
469
+ cmp x3, #2
470
+ b.eq .Lbuffer_move_T_2
471
+ cmp x3, #4
472
+ b.eq .Lbuffer_move_T_4
473
+ ldr x0, [x5]
474
+ str xzr, [x5]
475
+ b .Lbuffer_move_T_end
476
+ .Lbuffer_move_T_1:
477
+ ldrb w0, [x5]
478
+ strb wzr, [x5]
479
+ b .Lbuffer_move_T_end
480
+ .Lbuffer_move_T_2:
481
+ ldrh w0, [x5]
482
+ strh wzr, [x5]
483
+ b .Lbuffer_move_T_end
484
+ .Lbuffer_move_T_4:
485
+ ldr w0, [x5]
486
+ str wzr, [x5]
487
+ b .Lbuffer_move_T_end
488
+ .Lbuffer_move_T_copy:
489
+ mov x0, x8
490
+ mov x1, x5
491
+ mov x2, x3
492
+ stp x5, x3, [sp, #-16]!
493
+ bl _memcpy
494
+ ldp x5, x3, [sp], #16
495
+ mov x0, x5
496
+ mov x1, #0
497
+ mov x2, x3
498
+ bl _memset
499
+ mov x0, x8
500
+ .Lbuffer_move_T_end:
501
+ ```
502
+ }
503
+
504
+ // Store into a T_SIZE-sized slot, overwriting the previous value. For
505
+ // trivially-destructible value structs this is a plain overwrite. For
506
+ // owning value structs (with string fields), the build phase replaces this
507
+ // body with a specialised version that calls T_destroy on the old slot
508
+ // value, then deep-copies (strdup per string field). See
509
+ // owning_buffer_specialize.ts (both backends). ClassBuffer.replace_T
510
+ // destroys+frees class slots soundly.
511
+ inline func replace_T = (ref self, int i: i >= 0 && i < self.cap, T val) {
512
+ ```
513
+ #arch: c
514
+ ((T*)(unsigned long long)self->data)[i] = val;
515
+ ```
516
+ ```
517
+ #arch: aarch64
518
+ // x19 = self, x1 = i, x2 = val (value if T_SIZE<=8, address if struct)
519
+ ldr x0, [x19, #8]
520
+ mov x3, #T_SIZE
521
+ madd x1, x1, x3, xzr
522
+ add x0, x0, x1
523
+ cmp x3, #8
524
+ b.gt .Lbuffer_replace_T_copy
525
+ // Width-matched store: an 8-byte str on a sub-8 element would
526
+ // clobber the following slots and run past the slab's end.
527
+ cmp x3, #1
528
+ b.eq .Lbuffer_replace_T_1
529
+ cmp x3, #2
530
+ b.eq .Lbuffer_replace_T_2
531
+ cmp x3, #4
532
+ b.eq .Lbuffer_replace_T_4
533
+ str x2, [x0]
534
+ b .Lbuffer_replace_T_end
535
+ .Lbuffer_replace_T_1:
536
+ strb w2, [x0]
537
+ b .Lbuffer_replace_T_end
538
+ .Lbuffer_replace_T_2:
539
+ strh w2, [x0]
540
+ b .Lbuffer_replace_T_end
541
+ .Lbuffer_replace_T_4:
542
+ str w2, [x0]
543
+ b .Lbuffer_replace_T_end
544
+ .Lbuffer_replace_T_copy:
545
+ mov x1, x2
546
+ mov x2, x3
547
+ bl _memcpy
548
+ .Lbuffer_replace_T_end:
549
+ ```
550
+ }
551
+
408
552
  // A non-owning (ptr, len) slice [start, end) over this buffer's element
409
553
  // storage, returned as a `view T`. It borrows from self: it may not outlive
410
554
  // self's scope and is invalidated if self is reassigned. This is the single
@@ -91,6 +91,115 @@ pub struct ClassBuffer<T> {
91
91
  ```
92
92
  }
93
93
 
94
+ // T_SIZE-aware aliases of the _int primitives. ClassBuffer only ever holds
95
+ // 8-byte class pointers (T_SIZE is 8), so these are behaviorally identical
96
+ // to their _int counterparts — they exist so List<T> can call a single
97
+ // uniform `_T` API whether T is a value struct (Buffer) or a class
98
+ // (ClassBuffer, after the Buffer<T> field is rewritten). Bodies use 8-byte
99
+ // slots, matching _int.
100
+
101
+ func grow_T = (ref self, int needed, out int: out >= needed) {
102
+ ```
103
+ #arch: c
104
+ if (self->cap >= needed) return self->cap;
105
+ int new_cap = self->cap;
106
+ if (new_cap == 0) new_cap = 4;
107
+ while (new_cap < needed) new_cap *= 2;
108
+ self->data = (unsigned long long)(long*)realloc((long*)(unsigned long long)self->data, new_cap * sizeof(long));
109
+ memset((long*)(unsigned long long)self->data + self->cap, 0, (new_cap - self->cap) * sizeof(long));
110
+ self->cap = new_cap;
111
+ return self->cap;
112
+ ```
113
+ ```
114
+ #arch: aarch64
115
+ ldr x2, [x19, #16]
116
+ cmp x2, x1
117
+ b.ge .Lcb_grow_T_end
118
+ mov x3, #4
119
+ cmp x2, #0
120
+ csel x3, x3, x2, eq
121
+ cmp x3, x1
122
+ b.ge .Lcb_grow_T_got
123
+ .Lcb_grow_T_loop:
124
+ lsl x3, x3, #1
125
+ cmp x3, x1
126
+ b.lt .Lcb_grow_T_loop
127
+ .Lcb_grow_T_got:
128
+ sub sp, sp, #16
129
+ str x2, [sp, #0]
130
+ str x3, [sp, #8]
131
+ ldr x0, [x19, #8]
132
+ lsl x1, x3, #3
133
+ bl _realloc
134
+ str x0, [x19, #8]
135
+ ldr x2, [sp, #0]
136
+ ldr x3, [sp, #8]
137
+ add sp, sp, #16
138
+ str x3, [x19, #16]
139
+ add x0, x0, x2, lsl #3
140
+ mov x1, #0
141
+ sub x2, x3, x2
142
+ lsl x2, x2, #3
143
+ bl _memset
144
+ .Lcb_grow_T_end:
145
+ ldr x0, [x19, #16]
146
+ ```
147
+ }
148
+
149
+ inline func load_T = (self, int i: i >= 0 && i < self.cap, out T) {
150
+ ```
151
+ #arch: c
152
+ return (void*)((long*)(unsigned long long)self->data)[i];
153
+ ```
154
+ ```
155
+ #arch: aarch64
156
+ ldr x0, [x19, #8]
157
+ lsl x1, x1, #3
158
+ add x0, x0, x1
159
+ ldr x0, [x0]
160
+ ```
161
+ }
162
+
163
+ inline func store_T = (ref self, int i: i >= 0 && i < self.cap, T val: val != 0) {
164
+ ```
165
+ #arch: c
166
+ ((long*)(unsigned long long)self->data)[i] = (long)val;
167
+ ```
168
+ ```
169
+ #arch: aarch64
170
+ ldr x0, [x19, #8]
171
+ lsl x3, x1, #3
172
+ add x0, x0, x3
173
+ str x2, [x0]
174
+ ```
175
+ }
176
+
177
+ // Move a class pointer out of a slot: return it and zero the slot so the
178
+ // buffer no longer references it. Declared `mov out T` so the borrow
179
+ // checker treats the result as OWNED — the slot is relinquished, so the
180
+ // caller is the sole remaining owner. This is the owning-extract primitive
181
+ // (parallel to List.pop); use it (not load_T) when transferring ownership
182
+ // out of a ClassBuffer slot, or moving a value into a fresh owning
183
+ // container — load_T returns a BORROW (the slot is unchanged) and storing
184
+ // it elsewhere would create shared ownership (double-free at destroy).
185
+ inline func move_T = (ref self, int i: i >= 0 && i < self.cap, mov out T) {
186
+ ```
187
+ #arch: c
188
+ long *slots = (long*)(unsigned long long)self->data;
189
+ void *val = (void*)slots[i];
190
+ slots[i] = 0;
191
+ return val;
192
+ ```
193
+ ```
194
+ #arch: aarch64
195
+ ldr x0, [x19, #8]
196
+ lsl x3, x1, #3
197
+ add x3, x0, x3
198
+ ldr x0, [x3]
199
+ str xzr, [x3]
200
+ ```
201
+ }
202
+
94
203
  inline func store_int = (ref self, int i: i >= 0 && i < self.cap, int val: val != 0) {
95
204
  ```
96
205
  #arch: c
@@ -156,6 +265,40 @@ pub struct ClassBuffer<T> {
156
265
  ```
157
266
  }
158
267
 
268
+ // T-typed alias of replace_int: free the outgoing class pointer (destroy +
269
+ // free) then store the new one. Used by List.set so the uniform `_T` API
270
+ // preserves ownership for class elements.
271
+ func replace_T = (ref self, int i: i >= 0 && i < self.cap, T val: val != 0) {
272
+ ```
273
+ #arch: c
274
+ long *slots = (long*)(unsigned long long)self->data;
275
+ if (slots[i]) {
276
+ T_destroy((void*)slots[i]);
277
+ free((void*)slots[i]);
278
+ }
279
+ slots[i] = (long)val;
280
+ ```
281
+ ```
282
+ #arch: aarch64
283
+ stp x20, x21, [sp, #-16]!
284
+ str x22, [sp, #-16]!
285
+ ldr x0, [x19, #8]
286
+ lsl x3, x1, #3
287
+ add x21, x0, x3
288
+ mov x20, x2
289
+ ldr x0, [x21]
290
+ cbz x0, .Lcb_replace_T_store
291
+ mov x22, x0
292
+ bl T_destroy
293
+ mov x0, x22
294
+ bl _free
295
+ .Lcb_replace_T_store:
296
+ str x20, [x21]
297
+ ldr x22, [sp], #16
298
+ ldp x20, x21, [sp], #16
299
+ ```
300
+ }
301
+
159
302
  // A non-owning (ptr, len) slice [start, end) over this buffer's class
160
303
  // pointers, returned as a `view T` (T is a class, so each element is an
161
304
  // 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
@@ -16,9 +16,8 @@ pub struct Graph<T>: Enumerable {
16
16
 
17
17
  pub func add_node = (ref self, mov T value) {
18
18
  var int idx = self.node_count
19
- self.values.grow_int(idx + 1)
20
- var int v = value
21
- self.values.store_int(idx, v)
19
+ self.values.grow_T(idx + 1)
20
+ self.values.store_T(idx, value)
22
21
  self.head_edge.grow_int(idx + 1)
23
22
  self.head_edge.store_int(idx, -1)
24
23
  self.node_count = self.node_count + 1
@@ -51,11 +50,11 @@ pub struct Graph<T>: Enumerable {
51
50
  }
52
51
 
53
52
  pub func first = (self: self.node_count > 0, out T) {
54
- return self.values.load_int(0)
53
+ return self.values.load_T(0)
55
54
  }
56
55
 
57
56
  pub func at = (self, int idx: idx >= 0 && idx < self.node_count, out T) {
58
- return self.values.load_int(idx)
57
+ return self.values.load_T(idx)
59
58
  }
60
59
 
61
60
  pub func node_length = (self, out int) {
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Program startup state — the argument count and argument list passed to `main`
2
+ * Program startup state — the argument count and argument list passed to `main`,
3
+ * plus whether stdout is a terminal (for renderer selection).
3
4
  **/
4
5
  pub struct Init {
5
6
  var int argc
6
7
  var string[16] args
8
+ var bool is_tty
7
9
  }
8
10
 
9
11
  /**
@@ -15,9 +15,8 @@ pub struct LinkedList<T>: Enumerable {
15
15
 
16
16
  pub func add = (ref self, mov T value) {
17
17
  var int idx = self.count
18
- self.values.grow_int(idx + 1)
19
- var int v = value
20
- self.values.store_int(idx, v)
18
+ self.values.grow_T(idx + 1)
19
+ self.values.store_T(idx, value)
21
20
  self.nexts.grow_int(idx + 1)
22
21
  self.nexts.store_int(idx, -1)
23
22
  self.prevs.grow_int(idx + 1)
@@ -51,11 +50,11 @@ pub struct LinkedList<T>: Enumerable {
51
50
  }
52
51
 
53
52
  pub func first = (self: self.count > 0, out T) {
54
- return self.values.load_int(0)
53
+ return self.values.load_T(0)
55
54
  }
56
55
 
57
56
  pub func at = (self, int idx: idx >= 0 && idx < self.count, out T) {
58
- return self.values.load_int(idx)
57
+ return self.values.load_T(idx)
59
58
  }
60
59
 
61
60
  pub func length = (self, out int) {
@@ -12,23 +12,23 @@ pub struct List<T>: Viewable {
12
12
  if old_cap >= 4 {
13
13
  new_cap = old_cap * 2
14
14
  }
15
- self.items.grow_int(new_cap)
15
+ self.items.grow_T(new_cap)
16
16
  }
17
- self.items.store_int(self.length, value)
17
+ self.items.store_T(self.length, value)
18
18
  self.length += 1
19
19
  }
20
20
 
21
21
  pub func pop = (ref self, mov out T) {
22
22
  if self.length <= 0 {
23
- return 0
23
+ return self.items.move_T(0)
24
24
  }
25
25
  var int idx = self.length - 1
26
26
  self.length = idx
27
- return self.items.move_int(idx)
27
+ return self.items.move_T(idx)
28
28
  }
29
29
 
30
30
  pub func at = (self, int i: i >= 0 && i < self.length, out T) {
31
- return self.items.load_int(i)
31
+ return self.items.load_T(i)
32
32
  }
33
33
 
34
34
  // A non-owning `view T` over [start, end). Delegates to the backing Buffer's
@@ -38,7 +38,25 @@ pub struct List<T>: Viewable {
38
38
  }
39
39
 
40
40
  pub func set = (ref self, int i: i >= 0 && i < self.length, mov T value) {
41
- var int v = value
42
- self.items.replace_int(i, v)
41
+ self.items.replace_T(i, value)
42
+ }
43
+
44
+ // A deep copy: a fresh List<T> with an independent copy of every element.
45
+ // For owning element types (strings, owning value structs) each slot in the
46
+ // copy owns its own heap data (Buffer deep-copies on store), so the source
47
+ // and the copy share nothing. Not available for class/trait element types
48
+ // (the copy would share the stored instances — the checker rejects it).
49
+ // The element is bound to a local before push — the monomorphized body is
50
+ // never re-checked, so it can't rely on check-time call-arg hoisting for
51
+ // struct element types.
52
+ pub func copy = (self, out List<T>) {
53
+ var List<T> dst = List<T>()
54
+ var int i = 0
55
+ while i < self.length {
56
+ var T v = self.at(i)
57
+ dst.push(v)
58
+ i += 1
59
+ }
60
+ return dst
43
61
  }
44
62
  }