1
use super::super::context::CompileContext;
2
use super::super::emit::FunctionEmitter;
3
use super::super::expr::{compile_for_stack, eval_value};
4

            
5
use crate::ast::{Expr, WasmType};
6
use crate::error::{Error, Result};
7
use crate::runtime::SymbolTable;
8

            
9
18604
fn arity_check(name: &str, args: &[Expr], expected: usize) -> Result<()> {
10
18604
    if args.len() != expected {
11
2
        return Err(Error::Arity {
12
2
            name: name.to_string(),
13
2
            expected,
14
2
            actual: args.len(),
15
2
        });
16
18602
    }
17
18602
    Ok(())
18
18604
}
19

            
20
/// Compile an entity index argument to i32 on the WASM stack.
21
4674
fn compile_idx_to_stack(
22
4674
    ctx: &mut CompileContext,
23
4674
    emit: &mut FunctionEmitter,
24
4674
    symbols: &mut SymbolTable,
25
4674
    expr: &Expr,
26
4674
) -> Result<()> {
27
4674
    let resolved = eval_value(symbols, expr)?;
28
748
    match &resolved {
29
        Expr::WasmRuntime(WasmType::I32) | Expr::WasmLocal(_, WasmType::I32) => {
30
2230
            compile_for_stack(ctx, emit, symbols, expr)?;
31
        }
32
        Expr::WasmRuntime(WasmType::Ratio) | Expr::WasmLocal(_, WasmType::Ratio) => {
33
1696
            compile_for_stack(ctx, emit, symbols, expr)?;
34
1696
            emit.struct_get(ctx.type_idx("ratio"), 0);
35
1696
            emit.i32_wrap_i64();
36
        }
37
748
        Expr::Number(n) if *n.denom() == 1 => {
38
748
            emit.i32_const(*n.numer() as i32);
39
748
        }
40
        _ => {
41
            return Err(Error::Compile(format!(
42
                "entity index must be i32, got {resolved:?}"
43
            )));
44
        }
45
    }
46
4674
    Ok(())
47
4674
}
48

            
49
// --- Compile-time stubs (return WasmRuntime since these are runtime-only) ---
50

            
51
3344
pub(super) fn entity_count(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
52
3344
    arity_check("ENTITY-COUNT", args, 0)?;
53
3343
    Ok(Expr::WasmRuntime(WasmType::I32))
54
3344
}
55

            
56
1
pub(super) fn context_type(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
57
1
    arity_check("CONTEXT-TYPE", args, 0)?;
58
1
    Ok(Expr::WasmRuntime(WasmType::I32))
59
1
}
60

            
61
445
pub(super) fn primary_entity_type(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
62
445
    arity_check("PRIMARY-ENTITY-TYPE", args, 0)?;
63
445
    Ok(Expr::WasmRuntime(WasmType::I32))
64
445
}
65

            
66
489
pub(super) fn primary_entity_idx(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
67
489
    arity_check("PRIMARY-ENTITY-IDX", args, 0)?;
68
489
    Ok(Expr::WasmRuntime(WasmType::I32))
69
489
}
70

            
71
2941
pub(super) fn entity_type(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
72
2941
    arity_check("ENTITY-TYPE", args, 1)?;
73
2940
    super::super::expr::eval_value(symbols, &args[0])?;
74
2940
    Ok(Expr::WasmRuntime(WasmType::I32))
75
2941
}
76

            
77
1740
pub(super) fn entity_parent_idx(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
78
1740
    arity_check("ENTITY-PARENT-IDX", args, 1)?;
79
1740
    super::super::expr::eval_value(symbols, &args[0])?;
80
1740
    Ok(Expr::WasmRuntime(WasmType::I32))
81
1740
}
82

            
83
// --- WASM codegen ---
84

            
85
88
pub(super) fn compile_entity_count(
86
88
    ctx: &mut CompileContext,
87
88
    emit: &mut FunctionEmitter,
88
88
    _symbols: &mut SymbolTable,
89
88
    args: &[Expr],
90
88
) -> Result<()> {
91
88
    arity_check("ENTITY-COUNT", args, 0)?;
92
88
    let ty = compile_entity_count_to_stack(ctx, emit, _symbols, args)?;
93
88
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
94
88
    Ok(())
95
88
}
96

            
97
1156
pub(super) fn compile_entity_count_to_stack(
98
1156
    ctx: &mut CompileContext,
99
1156
    emit: &mut FunctionEmitter,
100
1156
    _symbols: &mut SymbolTable,
101
1156
    args: &[Expr],
102
1156
) -> Result<WasmType> {
103
1156
    arity_check("ENTITY-COUNT", args, 0)?;
104
1156
    emit.call(ctx.func("get_input_entities_count"));
105
1156
    Ok(WasmType::I32)
106
1156
}
107

            
108
44
pub(super) fn compile_context_type(
109
44
    ctx: &mut CompileContext,
110
44
    emit: &mut FunctionEmitter,
111
44
    _symbols: &mut SymbolTable,
112
44
    args: &[Expr],
113
44
) -> Result<()> {
114
44
    arity_check("CONTEXT-TYPE", args, 0)?;
115
44
    let ty = compile_context_type_to_stack(ctx, emit, _symbols, args)?;
116
44
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
117
44
    Ok(())
118
44
}
119

            
120
44
pub(super) fn compile_context_type_to_stack(
121
44
    ctx: &mut CompileContext,
122
44
    emit: &mut FunctionEmitter,
123
44
    _symbols: &mut SymbolTable,
124
44
    args: &[Expr],
125
44
) -> Result<WasmType> {
126
44
    arity_check("CONTEXT-TYPE", args, 0)?;
127
44
    emit.call(ctx.func("get_input_offset"));
128
44
    emit.i32_load8_u(6);
129
44
    Ok(WasmType::I32)
130
44
}
131

            
132
44
pub(super) fn compile_primary_entity_type(
133
44
    ctx: &mut CompileContext,
134
44
    emit: &mut FunctionEmitter,
135
44
    _symbols: &mut SymbolTable,
136
44
    args: &[Expr],
137
44
) -> Result<()> {
138
44
    arity_check("PRIMARY-ENTITY-TYPE", args, 0)?;
139
44
    let ty = compile_primary_entity_type_to_stack(ctx, emit, _symbols, args)?;
140
44
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
141
44
    Ok(())
142
44
}
143

            
144
266
pub(super) fn compile_primary_entity_type_to_stack(
145
266
    ctx: &mut CompileContext,
146
266
    emit: &mut FunctionEmitter,
147
266
    _symbols: &mut SymbolTable,
148
266
    args: &[Expr],
149
266
) -> Result<WasmType> {
150
266
    arity_check("PRIMARY-ENTITY-TYPE", args, 0)?;
151
266
    emit.call(ctx.func("get_input_offset"));
152
266
    emit.i32_load8_u(7);
153
266
    Ok(WasmType::I32)
154
266
}
155

            
156
88
pub(super) fn compile_primary_entity_idx(
157
88
    ctx: &mut CompileContext,
158
88
    emit: &mut FunctionEmitter,
159
88
    _symbols: &mut SymbolTable,
160
88
    args: &[Expr],
161
88
) -> Result<()> {
162
88
    arity_check("PRIMARY-ENTITY-IDX", args, 0)?;
163
88
    let ty = compile_primary_entity_idx_to_stack(ctx, emit, _symbols, args)?;
164
88
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
165
88
    Ok(())
166
88
}
167

            
168
442
pub(super) fn compile_primary_entity_idx_to_stack(
169
442
    ctx: &mut CompileContext,
170
442
    emit: &mut FunctionEmitter,
171
442
    _symbols: &mut SymbolTable,
172
442
    args: &[Expr],
173
442
) -> Result<WasmType> {
174
442
    arity_check("PRIMARY-ENTITY-IDX", args, 0)?;
175
442
    emit.call(ctx.func("get_input_offset"));
176
442
    emit.i32_load(0x20);
177
442
    Ok(WasmType::I32)
178
442
}
179

            
180
/// Emit WASM to compute the entity header offset for a given index.
181
/// Expects entity index on the WASM stack. Leaves header offset on stack.
182
/// `header_offset` = `entities_offset` + idx * 32
183
/// where `entities_offset` is an absolute offset read from `GlobalHeader`.
184
3962
fn emit_entity_header_offset(ctx: &CompileContext, emit: &mut FunctionEmitter, temp_local: u32) {
185
    // Stack: [idx]
186
3962
    emit.i32_const(32);
187
3962
    emit.i32_mul(); // [idx * 32]
188
3962
    emit.local_set(temp_local); // save idx*32
189
3962
    emit.call(ctx.func("get_input_offset")); // [input_base]
190
3962
    emit.i32_load(0x0C); // [entities_offset] (absolute)
191
3962
    emit.local_get(temp_local); // [entities_offset, idx*32]
192
3962
    emit.i32_add(); // [header_offset]
193
3962
}
194

            
195
44
pub(super) fn compile_entity_type(
196
44
    ctx: &mut CompileContext,
197
44
    emit: &mut FunctionEmitter,
198
44
    symbols: &mut SymbolTable,
199
44
    args: &[Expr],
200
44
) -> Result<()> {
201
44
    arity_check("ENTITY-TYPE", args, 1)?;
202
44
    let ty = compile_entity_type_to_stack(ctx, emit, symbols, args)?;
203
44
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
204
44
    Ok(())
205
44
}
206

            
207
1024
pub(super) fn compile_entity_type_to_stack(
208
1024
    ctx: &mut CompileContext,
209
1024
    emit: &mut FunctionEmitter,
210
1024
    symbols: &mut SymbolTable,
211
1024
    args: &[Expr],
212
1024
) -> Result<WasmType> {
213
1024
    arity_check("ENTITY-TYPE", args, 1)?;
214
1024
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
215
1024
    emit_entity_header_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
216
1024
    emit.i32_load8_u(0); // entity_type at offset 0 in header
217
1024
    Ok(WasmType::I32)
218
1024
}
219

            
220
44
pub(super) fn compile_entity_parent_idx(
221
44
    ctx: &mut CompileContext,
222
44
    emit: &mut FunctionEmitter,
223
44
    symbols: &mut SymbolTable,
224
44
    args: &[Expr],
225
44
) -> Result<()> {
226
44
    arity_check("ENTITY-PARENT-IDX", args, 1)?;
227
44
    let ty = compile_entity_parent_idx_to_stack(ctx, emit, symbols, args)?;
228
44
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
229
44
    Ok(())
230
44
}
231

            
232
892
pub(super) fn compile_entity_parent_idx_to_stack(
233
892
    ctx: &mut CompileContext,
234
892
    emit: &mut FunctionEmitter,
235
892
    symbols: &mut SymbolTable,
236
892
    args: &[Expr],
237
892
) -> Result<WasmType> {
238
892
    arity_check("ENTITY-PARENT-IDX", args, 1)?;
239
892
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
240
892
    emit_entity_header_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
241
892
    emit.i32_load(0x14); // parent_idx at offset 0x14 in header
242
892
    Ok(WasmType::I32)
243
892
}
244

            
245
/// Emit WASM to compute the entity data offset for a given index.
246
/// Expects entity index on the WASM stack. Leaves data offset on stack.
247
/// `data_offset` = `entity_header`[idx].`data_offset` (absolute memory address)
248
1914
fn emit_entity_data_offset(ctx: &CompileContext, emit: &mut FunctionEmitter, temp_local: u32) {
249
1914
    emit_entity_header_offset(ctx, emit, temp_local);
250
1914
    emit.i32_load(0x18); // data_offset at EntityHeader offset 0x18
251
1914
}
252

            
253
// --- Transaction data accessors ---
254

            
255
132
pub(super) fn transaction_split_count(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
256
132
    arity_check("TRANSACTION-SPLIT-COUNT", args, 1)?;
257
132
    super::super::expr::eval_value(symbols, &args[0])?;
258
132
    Ok(Expr::WasmRuntime(WasmType::I32))
259
132
}
260

            
261
44
pub(super) fn compile_transaction_split_count_to_stack(
262
44
    ctx: &mut CompileContext,
263
44
    emit: &mut FunctionEmitter,
264
44
    symbols: &mut SymbolTable,
265
44
    args: &[Expr],
266
44
) -> Result<WasmType> {
267
44
    arity_check("TRANSACTION-SPLIT-COUNT", args, 1)?;
268
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
269
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
270
44
    emit.i32_load(16); // split_count at TransactionData offset 16
271
44
    Ok(WasmType::I32)
272
44
}
273

            
274
pub(super) fn compile_transaction_split_count(
275
    ctx: &mut CompileContext,
276
    emit: &mut FunctionEmitter,
277
    symbols: &mut SymbolTable,
278
    args: &[Expr],
279
) -> Result<()> {
280
    let ty = compile_transaction_split_count_to_stack(ctx, emit, symbols, args)?;
281
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
282
    Ok(())
283
}
284

            
285
132
pub(super) fn transaction_tag_count(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
286
132
    arity_check("TRANSACTION-TAG-COUNT", args, 1)?;
287
132
    super::super::expr::eval_value(symbols, &args[0])?;
288
132
    Ok(Expr::WasmRuntime(WasmType::I32))
289
132
}
290

            
291
44
pub(super) fn compile_transaction_tag_count_to_stack(
292
44
    ctx: &mut CompileContext,
293
44
    emit: &mut FunctionEmitter,
294
44
    symbols: &mut SymbolTable,
295
44
    args: &[Expr],
296
44
) -> Result<WasmType> {
297
44
    arity_check("TRANSACTION-TAG-COUNT", args, 1)?;
298
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
299
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
300
44
    emit.i32_load(20); // tag_count at TransactionData offset 20
301
44
    Ok(WasmType::I32)
302
44
}
303

            
304
pub(super) fn compile_transaction_tag_count(
305
    ctx: &mut CompileContext,
306
    emit: &mut FunctionEmitter,
307
    symbols: &mut SymbolTable,
308
    args: &[Expr],
309
) -> Result<()> {
310
    let ty = compile_transaction_tag_count_to_stack(ctx, emit, symbols, args)?;
311
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
312
    Ok(())
313
}
314

            
315
132
pub(super) fn transaction_is_multi_currency(
316
132
    symbols: &mut SymbolTable,
317
132
    args: &[Expr],
318
132
) -> Result<Expr> {
319
132
    arity_check("TRANSACTION-IS-MULTI-CURRENCY", args, 1)?;
320
132
    super::super::expr::eval_value(symbols, &args[0])?;
321
132
    Ok(Expr::WasmRuntime(WasmType::I32))
322
132
}
323

            
324
44
pub(super) fn compile_transaction_is_multi_currency_to_stack(
325
44
    ctx: &mut CompileContext,
326
44
    emit: &mut FunctionEmitter,
327
44
    symbols: &mut SymbolTable,
328
44
    args: &[Expr],
329
44
) -> Result<WasmType> {
330
44
    arity_check("TRANSACTION-IS-MULTI-CURRENCY", args, 1)?;
331
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
332
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
333
44
    emit.i32_load8_u(24); // is_multi_currency at TransactionData offset 24
334
44
    Ok(WasmType::I32)
335
44
}
336

            
337
pub(super) fn compile_transaction_is_multi_currency(
338
    ctx: &mut CompileContext,
339
    emit: &mut FunctionEmitter,
340
    symbols: &mut SymbolTable,
341
    args: &[Expr],
342
) -> Result<()> {
343
    let ty = compile_transaction_is_multi_currency_to_stack(ctx, emit, symbols, args)?;
344
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
345
    Ok(())
346
}
347

            
348
88
pub(super) fn transaction_post_date(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
349
88
    arity_check("TRANSACTION-POST-DATE", args, 1)?;
350
88
    super::super::expr::eval_value(symbols, &args[0])?;
351
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
352
88
}
353

            
354
44
pub(super) fn compile_transaction_post_date_to_stack(
355
44
    ctx: &mut CompileContext,
356
44
    emit: &mut FunctionEmitter,
357
44
    symbols: &mut SymbolTable,
358
44
    args: &[Expr],
359
44
) -> Result<WasmType> {
360
44
    arity_check("TRANSACTION-POST-DATE", args, 1)?;
361
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
362
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
363
44
    emit.i64_load(0); // post_date at TransactionData offset 0
364
44
    emit.call(ctx.func("ratio_from_i64"));
365
44
    Ok(WasmType::Ratio)
366
44
}
367

            
368
pub(super) fn compile_transaction_post_date(
369
    ctx: &mut CompileContext,
370
    emit: &mut FunctionEmitter,
371
    symbols: &mut SymbolTable,
372
    args: &[Expr],
373
) -> Result<()> {
374
    let ty = compile_transaction_post_date_to_stack(ctx, emit, symbols, args)?;
375
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
376
    Ok(())
377
}
378

            
379
88
pub(super) fn transaction_enter_date(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
380
88
    arity_check("TRANSACTION-ENTER-DATE", args, 1)?;
381
88
    super::super::expr::eval_value(symbols, &args[0])?;
382
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
383
88
}
384

            
385
44
pub(super) fn compile_transaction_enter_date_to_stack(
386
44
    ctx: &mut CompileContext,
387
44
    emit: &mut FunctionEmitter,
388
44
    symbols: &mut SymbolTable,
389
44
    args: &[Expr],
390
44
) -> Result<WasmType> {
391
44
    arity_check("TRANSACTION-ENTER-DATE", args, 1)?;
392
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
393
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
394
44
    emit.i64_load(8); // enter_date at TransactionData offset 8
395
44
    emit.call(ctx.func("ratio_from_i64"));
396
44
    Ok(WasmType::Ratio)
397
44
}
398

            
399
pub(super) fn compile_transaction_enter_date(
400
    ctx: &mut CompileContext,
401
    emit: &mut FunctionEmitter,
402
    symbols: &mut SymbolTable,
403
    args: &[Expr],
404
) -> Result<()> {
405
    let ty = compile_transaction_enter_date_to_stack(ctx, emit, symbols, args)?;
406
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
407
    Ok(())
408
}
409

            
410
// --- Split data accessors ---
411

            
412
88
pub(super) fn split_value_num(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
413
88
    arity_check("SPLIT-VALUE-NUM", args, 1)?;
414
88
    super::super::expr::eval_value(symbols, &args[0])?;
415
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
416
88
}
417

            
418
44
pub(super) fn compile_split_value_num_to_stack(
419
44
    ctx: &mut CompileContext,
420
44
    emit: &mut FunctionEmitter,
421
44
    symbols: &mut SymbolTable,
422
44
    args: &[Expr],
423
44
) -> Result<WasmType> {
424
44
    arity_check("SPLIT-VALUE-NUM", args, 1)?;
425
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
426
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
427
44
    emit.i64_load(32); // value_num at SplitData offset 32
428
44
    emit.call(ctx.func("ratio_from_i64"));
429
44
    Ok(WasmType::Ratio)
430
44
}
431

            
432
pub(super) fn compile_split_value_num(
433
    ctx: &mut CompileContext,
434
    emit: &mut FunctionEmitter,
435
    symbols: &mut SymbolTable,
436
    args: &[Expr],
437
) -> Result<()> {
438
    let ty = compile_split_value_num_to_stack(ctx, emit, symbols, args)?;
439
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
440
    Ok(())
441
}
442

            
443
88
pub(super) fn split_value_denom(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
444
88
    arity_check("SPLIT-VALUE-DENOM", args, 1)?;
445
88
    super::super::expr::eval_value(symbols, &args[0])?;
446
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
447
88
}
448

            
449
44
pub(super) fn compile_split_value_denom_to_stack(
450
44
    ctx: &mut CompileContext,
451
44
    emit: &mut FunctionEmitter,
452
44
    symbols: &mut SymbolTable,
453
44
    args: &[Expr],
454
44
) -> Result<WasmType> {
455
44
    arity_check("SPLIT-VALUE-DENOM", args, 1)?;
456
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
457
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
458
44
    emit.i64_load(40); // value_denom at SplitData offset 40
459
44
    emit.call(ctx.func("ratio_from_i64"));
460
44
    Ok(WasmType::Ratio)
461
44
}
462

            
463
pub(super) fn compile_split_value_denom(
464
    ctx: &mut CompileContext,
465
    emit: &mut FunctionEmitter,
466
    symbols: &mut SymbolTable,
467
    args: &[Expr],
468
) -> Result<()> {
469
    let ty = compile_split_value_denom_to_stack(ctx, emit, symbols, args)?;
470
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
471
    Ok(())
472
}
473

            
474
/// (split-value idx) — reads both `value_num` and `value_denom`, returns proper Ratio
475
88
pub(super) fn split_value(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
476
88
    arity_check("SPLIT-VALUE", args, 1)?;
477
88
    super::super::expr::eval_value(symbols, &args[0])?;
478
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
479
88
}
480

            
481
44
pub(super) fn compile_split_value_to_stack(
482
44
    ctx: &mut CompileContext,
483
44
    emit: &mut FunctionEmitter,
484
44
    symbols: &mut SymbolTable,
485
44
    args: &[Expr],
486
44
) -> Result<WasmType> {
487
44
    arity_check("SPLIT-VALUE", args, 1)?;
488
44
    let temp = super::super::expr::LOCAL_TEMP_I32;
489
    // Compute data offset, save in temp
490
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
491
44
    emit_entity_data_offset(ctx, emit, temp);
492
44
    emit.local_set(temp);
493
    // Load value_num (i64 at offset 32)
494
44
    emit.local_get(temp);
495
44
    emit.i64_load(32);
496
    // Load value_denom (i64 at offset 40)
497
44
    emit.local_get(temp);
498
44
    emit.i64_load(40);
499
    // Construct ratio from num and denom
500
44
    emit.call(ctx.func("ratio_new"));
501
44
    Ok(WasmType::Ratio)
502
44
}
503

            
504
pub(super) fn compile_split_value(
505
    ctx: &mut CompileContext,
506
    emit: &mut FunctionEmitter,
507
    symbols: &mut SymbolTable,
508
    args: &[Expr],
509
) -> Result<()> {
510
    let ty = compile_split_value_to_stack(ctx, emit, symbols, args)?;
511
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
512
    Ok(())
513
}
514

            
515
132
pub(super) fn split_reconcile_state(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
516
132
    arity_check("SPLIT-RECONCILE-STATE", args, 1)?;
517
132
    super::super::expr::eval_value(symbols, &args[0])?;
518
132
    Ok(Expr::WasmRuntime(WasmType::I32))
519
132
}
520

            
521
44
pub(super) fn compile_split_reconcile_state_to_stack(
522
44
    ctx: &mut CompileContext,
523
44
    emit: &mut FunctionEmitter,
524
44
    symbols: &mut SymbolTable,
525
44
    args: &[Expr],
526
44
) -> Result<WasmType> {
527
44
    arity_check("SPLIT-RECONCILE-STATE", args, 1)?;
528
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
529
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
530
44
    emit.i32_load8_u(48); // reconcile_state at SplitData offset 48
531
44
    Ok(WasmType::I32)
532
44
}
533

            
534
pub(super) fn compile_split_reconcile_state(
535
    ctx: &mut CompileContext,
536
    emit: &mut FunctionEmitter,
537
    symbols: &mut SymbolTable,
538
    args: &[Expr],
539
) -> Result<()> {
540
    let ty = compile_split_reconcile_state_to_stack(ctx, emit, symbols, args)?;
541
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
542
    Ok(())
543
}
544

            
545
88
pub(super) fn split_reconcile_date(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
546
88
    arity_check("SPLIT-RECONCILE-DATE", args, 1)?;
547
88
    super::super::expr::eval_value(symbols, &args[0])?;
548
88
    Ok(Expr::WasmRuntime(WasmType::Ratio))
549
88
}
550

            
551
44
pub(super) fn compile_split_reconcile_date_to_stack(
552
44
    ctx: &mut CompileContext,
553
44
    emit: &mut FunctionEmitter,
554
44
    symbols: &mut SymbolTable,
555
44
    args: &[Expr],
556
44
) -> Result<WasmType> {
557
44
    arity_check("SPLIT-RECONCILE-DATE", args, 1)?;
558
44
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
559
44
    emit_entity_data_offset(ctx, emit, super::super::expr::LOCAL_TEMP_I32);
560
44
    emit.i64_load(56); // reconcile_date at SplitData offset 56
561
44
    emit.call(ctx.func("ratio_from_i64"));
562
44
    Ok(WasmType::Ratio)
563
44
}
564

            
565
pub(super) fn compile_split_reconcile_date(
566
    ctx: &mut CompileContext,
567
    emit: &mut FunctionEmitter,
568
    symbols: &mut SymbolTable,
569
    args: &[Expr],
570
) -> Result<()> {
571
    let ty = compile_split_reconcile_date_to_stack(ctx, emit, symbols, args)?;
572
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
573
    Ok(())
574
}
575

            
576
// --- Legacy get-input-entities (compile-time stub) ---
577

            
578
88
pub(super) fn get_input_entities(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
579
88
    arity_check("GET-INPUT-ENTITIES", args, 0)?;
580
88
    Ok(Expr::WasmRuntime(WasmType::ConsRef))
581
88
}
582

            
583
pub(super) fn create_tag(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
584
    if args.len() != 3 {
585
        return Err(Error::Arity {
586
            name: "CREATE-TAG".to_string(),
587
            expected: 3,
588
            actual: args.len(),
589
        });
590
    }
591
    Ok(Expr::Nil)
592
}
593

            
594
712
pub(super) fn compile_create_tag(
595
712
    ctx: &mut CompileContext,
596
712
    emit: &mut FunctionEmitter,
597
712
    symbols: &mut SymbolTable,
598
712
    args: &[Expr],
599
712
) -> Result<()> {
600
712
    if args.len() != 3 {
601
        return Err(Error::Arity {
602
            name: "CREATE-TAG".to_string(),
603
            expected: 3,
604
            actual: args.len(),
605
        });
606
712
    }
607
712
    let name_val = eval_value(symbols, &args[1])?;
608
712
    let value_val = eval_value(symbols, &args[2])?;
609

            
610
712
    let is_runtime = name_val.wasm_type() == Some(WasmType::StringRef)
611
176
        || value_val.wasm_type() == Some(WasmType::StringRef);
612

            
613
712
    if is_runtime {
614
536
        return compile_create_tag_runtime(ctx, emit, symbols, args);
615
176
    }
616

            
617
176
    let name_str = match &name_val {
618
176
        Expr::String(s) => s.clone(),
619
        Expr::Symbol(s) => s.clone(),
620
        other => {
621
            return Err(Error::Compile(format!(
622
                "CREATE-TAG name must be a string, got {other:?}"
623
            )));
624
        }
625
    };
626
176
    let value_str = match &value_val {
627
176
        Expr::String(s) => s.clone(),
628
        Expr::Symbol(s) => s.clone(),
629
        Expr::Nil => String::new(),
630
        other => {
631
            return Err(Error::Compile(format!(
632
                "CREATE-TAG value must be a string, got {other:?}"
633
            )));
634
        }
635
    };
636

            
637
176
    let tag = super::super::layout::TagGcData {
638
176
        name_data_idx: ctx.add_data(name_str.as_bytes()),
639
176
        name_len: name_str.len() as u32,
640
176
        value_data_idx: ctx.add_data(value_str.as_bytes()),
641
176
        value_len: value_str.len() as u32,
642
176
    };
643
176
    let gc = super::super::layout::GcLocals {
644
176
        type_idx: ctx.type_idx("i8_array"),
645
176
        arr: super::super::expr::LOCAL_GC_ARR,
646
176
        idx: super::super::expr::LOCAL_IDX,
647
176
    };
648

            
649
176
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
650
176
    let parent_local = super::super::expr::LOCAL_TEMP_I32;
651
176
    emit.local_set(parent_local);
652

            
653
176
    ctx.serializer()
654
176
        .write_tag_gc_dynamic_parent(emit, parent_local, &tag, &gc);
655
176
    Ok(())
656
712
}
657

            
658
536
fn compile_create_tag_runtime(
659
536
    ctx: &mut CompileContext,
660
536
    emit: &mut FunctionEmitter,
661
536
    symbols: &mut SymbolTable,
662
536
    args: &[Expr],
663
536
) -> Result<()> {
664
    use scripting_format::{
665
        ENTITY_HEADER_SIZE, EntityHeader, EntityType, GlobalHeader, Operation, OutputHeader,
666
        TAG_DATA_SIZE, TagData,
667
    };
668
    use std::mem::offset_of;
669

            
670
536
    let temp = super::super::expr::LOCAL_TEMP_I32;
671

            
672
536
    let parent_local = ctx.alloc_local(WasmType::I32);
673
536
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
674
536
    emit.local_set(parent_local);
675

            
676
536
    let name_arr = ctx.alloc_local(WasmType::StringRef);
677
536
    let value_arr = ctx.alloc_local(WasmType::StringRef);
678
536
    compile_for_stack(ctx, emit, symbols, &args[1])?;
679
536
    emit.local_set(name_arr);
680
536
    compile_for_stack(ctx, emit, symbols, &args[2])?;
681
536
    emit.local_set(value_arr);
682

            
683
536
    let name_len = ctx.alloc_local(WasmType::I32);
684
536
    let value_len = ctx.alloc_local(WasmType::I32);
685
536
    emit.local_get(name_arr);
686
536
    emit.array_len();
687
536
    emit.local_set(name_len);
688
536
    emit.local_get(value_arr);
689
536
    emit.array_len();
690
536
    emit.local_set(value_len);
691

            
692
536
    let out_base = ctx.alloc_local(WasmType::I32);
693
536
    emit.call(ctx.func("get_output_offset"));
694
536
    emit.local_set(out_base);
695

            
696
536
    let write_pos = ctx.alloc_local(WasmType::I32);
697
536
    emit.local_get(out_base);
698
536
    emit.i32_load(offset_of!(OutputHeader, next_write_pos) as u64);
699
536
    emit.local_set(write_pos);
700

            
701
536
    let str_end = ctx.alloc_local(WasmType::I32);
702
536
    emit.call(ctx.func("get_input_offset"));
703
536
    emit.i32_load(offset_of!(GlobalHeader, output_size) as u64);
704
536
    emit.local_set(str_end);
705

            
706
    // Use existing strings frontier (stored in reserved field) if non-zero
707
536
    emit.local_get(out_base);
708
536
    emit.i32_load(offset_of!(OutputHeader, reserved) as u64);
709
536
    emit.local_set(temp);
710
536
    emit.local_get(temp);
711
536
    emit.i32_const(0);
712
536
    emit.i32_ne();
713
536
    emit.if_block(wasm_encoder::BlockType::Empty);
714
536
    emit.local_get(temp);
715
536
    emit.local_set(str_end);
716
536
    emit.end();
717

            
718
    // Allocate strings from end, growing down
719
536
    emit.local_get(str_end);
720
536
    emit.local_get(value_len);
721
536
    emit.i32_sub();
722
536
    emit.local_set(str_end);
723
536
    let value_str_start = ctx.alloc_local(WasmType::I32);
724
536
    emit.local_get(str_end);
725
536
    emit.local_set(value_str_start);
726

            
727
536
    let name_str_start = ctx.alloc_local(WasmType::I32);
728
536
    emit.local_get(str_end);
729
536
    emit.local_get(name_len);
730
536
    emit.i32_sub();
731
536
    emit.local_tee(name_str_start);
732
536
    emit.local_set(str_end);
733

            
734
536
    let i8_array_idx = ctx.type_idx("i8_array");
735
536
    let idx_local = ctx.alloc_local(WasmType::I32);
736

            
737
536
    copy_gc_array_to_linear(
738
536
        emit,
739
536
        i8_array_idx,
740
536
        name_arr,
741
536
        name_len,
742
536
        out_base,
743
536
        name_str_start,
744
536
        idx_local,
745
    );
746
536
    copy_gc_array_to_linear(
747
536
        emit,
748
536
        i8_array_idx,
749
536
        value_arr,
750
536
        value_len,
751
536
        out_base,
752
536
        value_str_start,
753
536
        idx_local,
754
    );
755

            
756
    // Read output_size for offset calculation
757
536
    let output_size = ctx.alloc_local(WasmType::I32);
758
536
    emit.call(ctx.func("get_input_offset"));
759
536
    emit.i32_load(offset_of!(GlobalHeader, output_size) as u64);
760
536
    emit.local_set(output_size);
761

            
762
    // Write EntityHeader at out_base + write_pos
763
536
    let eh_addr = ctx.alloc_local(WasmType::I32);
764
536
    emit.local_get(out_base);
765
536
    emit.local_get(write_pos);
766
536
    emit.i32_add();
767
536
    emit.local_set(eh_addr);
768

            
769
536
    emit.local_get(eh_addr);
770
536
    emit.i32_const(EntityType::Tag as i32);
771
536
    emit.i32_store8_raw();
772

            
773
536
    emit.local_get(eh_addr);
774
536
    emit.i32_const(offset_of!(EntityHeader, operation) as i32);
775
536
    emit.i32_add();
776
536
    emit.i32_const(Operation::Create as i32);
777
536
    emit.i32_store8_raw();
778

            
779
536
    emit.local_get(eh_addr);
780
536
    emit.i32_const(offset_of!(EntityHeader, flags) as i32);
781
536
    emit.i32_add();
782
536
    emit.i32_const(0);
783
536
    emit.i32_store8_raw();
784

            
785
2144
    for i in 0..4u32 {
786
2144
        emit.local_get(eh_addr);
787
2144
        emit.i32_const((offset_of!(EntityHeader, id) + i as usize * 4) as i32);
788
2144
        emit.i32_add();
789
2144
        emit.i32_const(0);
790
2144
        emit.i32_store_raw();
791
2144
    }
792

            
793
536
    emit.local_get(eh_addr);
794
536
    emit.i32_const(offset_of!(EntityHeader, parent_idx) as i32);
795
536
    emit.i32_add();
796
536
    emit.local_get(parent_local);
797
536
    emit.i32_store_raw();
798

            
799
536
    emit.local_get(eh_addr);
800
536
    emit.i32_const(offset_of!(EntityHeader, data_offset) as i32);
801
536
    emit.i32_add();
802
536
    emit.local_get(write_pos);
803
536
    emit.i32_const(ENTITY_HEADER_SIZE as i32);
804
536
    emit.i32_add();
805
536
    emit.i32_store_raw();
806

            
807
536
    emit.local_get(eh_addr);
808
536
    emit.i32_const(offset_of!(EntityHeader, data_size) as i32);
809
536
    emit.i32_add();
810
536
    emit.i32_const(TAG_DATA_SIZE as i32);
811
536
    emit.i32_store_raw();
812

            
813
    // Write TagData
814
536
    let td_addr = ctx.alloc_local(WasmType::I32);
815
536
    emit.local_get(eh_addr);
816
536
    emit.i32_const(ENTITY_HEADER_SIZE as i32);
817
536
    emit.i32_add();
818
536
    emit.local_set(td_addr);
819

            
820
    // name_offset = output_size - name_str_start
821
536
    emit.local_get(td_addr);
822
536
    emit.local_get(output_size);
823
536
    emit.local_get(name_str_start);
824
536
    emit.i32_sub();
825
536
    emit.i32_store_raw();
826

            
827
    // value_offset = output_size - value_str_start
828
536
    emit.local_get(td_addr);
829
536
    emit.i32_const(offset_of!(TagData, value_offset) as i32);
830
536
    emit.i32_add();
831
536
    emit.local_get(output_size);
832
536
    emit.local_get(value_str_start);
833
536
    emit.i32_sub();
834
536
    emit.i32_store_raw();
835

            
836
    // name_len + value_len packed as u16 pair
837
536
    emit.local_get(td_addr);
838
536
    emit.i32_const(offset_of!(TagData, name_len) as i32);
839
536
    emit.i32_add();
840
536
    emit.local_get(value_len);
841
536
    emit.i32_const(16);
842
536
    emit.i32_shl();
843
536
    emit.local_get(name_len);
844
536
    emit.i32_or();
845
536
    emit.i32_store_raw();
846

            
847
536
    emit.local_get(td_addr);
848
536
    emit.i32_const(offset_of!(TagData, reserved) as i32);
849
536
    emit.i32_add();
850
536
    emit.i32_const(0);
851
536
    emit.i32_store_raw();
852

            
853
    // Update output_entity_count
854
536
    emit.local_get(out_base);
855
536
    emit.i32_const(offset_of!(OutputHeader, output_entity_count) as i32);
856
536
    emit.i32_add();
857
536
    emit.local_get(out_base);
858
536
    emit.i32_load(offset_of!(OutputHeader, output_entity_count) as u64);
859
536
    emit.i32_const(1);
860
536
    emit.i32_add();
861
536
    emit.i32_store_raw();
862

            
863
    // Update next_write_pos
864
536
    emit.local_get(out_base);
865
536
    emit.i32_const(offset_of!(OutputHeader, next_write_pos) as i32);
866
536
    emit.i32_add();
867
536
    emit.local_get(write_pos);
868
536
    emit.i32_const((ENTITY_HEADER_SIZE + TAG_DATA_SIZE) as i32);
869
536
    emit.i32_add();
870
536
    emit.i32_store_raw();
871

            
872
    // Update strings_offset = output_size (parser uses this as base for offset arithmetic)
873
536
    emit.local_get(out_base);
874
536
    emit.i32_const(offset_of!(OutputHeader, strings_offset) as i32);
875
536
    emit.i32_add();
876
536
    emit.local_get(output_size);
877
536
    emit.i32_store_raw();
878

            
879
    // Store strings frontier in reserved field for next create-tag call
880
536
    emit.local_get(out_base);
881
536
    emit.i32_const(offset_of!(OutputHeader, reserved) as i32);
882
536
    emit.i32_add();
883
536
    emit.local_get(str_end);
884
536
    emit.i32_store_raw();
885

            
886
536
    Ok(())
887
536
}
888

            
889
1072
fn copy_gc_array_to_linear(
890
1072
    emit: &mut FunctionEmitter,
891
1072
    type_idx: u32,
892
1072
    arr_local: u32,
893
1072
    len_local: u32,
894
1072
    base_local: u32,
895
1072
    offset_local: u32,
896
1072
    idx_local: u32,
897
1072
) {
898
1072
    emit.i32_const(0);
899
1072
    emit.local_set(idx_local);
900
1072
    emit.block_start();
901
1072
    emit.loop_start();
902
1072
    emit.local_get(idx_local);
903
1072
    emit.local_get(len_local);
904
1072
    emit.i32_ge_u();
905
1072
    emit.br_if(1);
906
    // dst = base + offset + idx
907
1072
    emit.local_get(base_local);
908
1072
    emit.local_get(offset_local);
909
1072
    emit.i32_add();
910
1072
    emit.local_get(idx_local);
911
1072
    emit.i32_add();
912
    // value = arr[idx]
913
1072
    emit.local_get(arr_local);
914
1072
    emit.local_get(idx_local);
915
1072
    emit.array_get_u(type_idx);
916
1072
    emit.i32_store8_raw();
917
    // idx++
918
1072
    emit.local_get(idx_local);
919
1072
    emit.i32_const(1);
920
1072
    emit.i32_add();
921
1072
    emit.local_set(idx_local);
922
1072
    emit.br(0);
923
1072
    emit.block_end();
924
1072
    emit.block_end();
925
1072
}
926

            
927
pub(super) fn delete_entity(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
928
    arity_check("DELETE-ENTITY", args, 1)?;
929
    Ok(Expr::Nil)
930
}
931

            
932
132
pub(super) fn compile_delete_entity(
933
132
    ctx: &mut CompileContext,
934
132
    emit: &mut FunctionEmitter,
935
132
    symbols: &mut SymbolTable,
936
132
    args: &[Expr],
937
132
) -> Result<()> {
938
132
    arity_check("DELETE-ENTITY", args, 1)?;
939

            
940
132
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
941
132
    let temp = super::super::expr::LOCAL_TEMP_I32;
942
132
    emit_entity_header_offset(ctx, emit, temp);
943
132
    let header_local = ctx.alloc_local(WasmType::I32);
944
132
    emit.local_set(header_local);
945

            
946
    // Read entity_type from input header
947
132
    let type_local = ctx.alloc_local(WasmType::I32);
948
132
    emit.local_get(header_local);
949
132
    emit.i32_load8_u(0);
950
132
    emit.local_set(type_local);
951

            
952
132
    ctx.serializer()
953
132
        .write_delete_entity(emit, type_local, header_local);
954
132
    Ok(())
955
132
}
956

            
957
// --- Tag string data accessors ---
958

            
959
1742
pub(super) fn tag_name(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
960
1742
    arity_check("TAG-NAME", args, 1)?;
961
1742
    eval_value(symbols, &args[0])?;
962
1742
    Ok(Expr::WasmRuntime(WasmType::StringRef))
963
1742
}
964

            
965
536
pub(super) fn tag_value(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
966
536
    arity_check("TAG-VALUE", args, 1)?;
967
536
    eval_value(symbols, &args[0])?;
968
536
    Ok(Expr::WasmRuntime(WasmType::StringRef))
969
536
}
970

            
971
/// Shared codegen for reading a tag string field into a GC `i8_array`.
972
/// `offset_field` is the byte offset of the u32 string offset in `TagData`.
973
/// `len_field` is the byte offset of the u16 string length in `TagData`.
974
1474
fn compile_tag_string_field_to_stack(
975
1474
    ctx: &mut CompileContext,
976
1474
    emit: &mut FunctionEmitter,
977
1474
    symbols: &mut SymbolTable,
978
1474
    args: &[Expr],
979
1474
    offset_field: u64,
980
1474
    len_field: u64,
981
1474
) -> Result<WasmType> {
982
1474
    let temp = super::super::expr::LOCAL_TEMP_I32;
983
1474
    let data_local = ctx.alloc_local(WasmType::I32);
984
1474
    let str_off_local = ctx.alloc_local(WasmType::I32);
985
1474
    let str_len_local = ctx.alloc_local(WasmType::I32);
986
1474
    let pool_local = ctx.alloc_local(WasmType::I32);
987
1474
    let arr_local = ctx.alloc_local(WasmType::StringRef);
988
1474
    let idx_local = ctx.alloc_local(WasmType::I32);
989

            
990
    // Compute tag data offset
991
1474
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
992
1474
    emit_entity_data_offset(ctx, emit, temp);
993
1474
    emit.local_set(data_local);
994

            
995
    // Read string offset (u32) and length (u16)
996
1474
    emit.local_get(data_local);
997
1474
    emit.i32_load(offset_field);
998
1474
    emit.local_set(str_off_local);
999

            
1474
    emit.local_get(data_local);
1474
    emit.i32_load16_u(len_field);
1474
    emit.local_set(str_len_local);
    // Read strings_pool_offset from input header
1474
    emit.call(ctx.func("get_input_offset"));
1474
    emit.i32_load(std::mem::offset_of!(scripting_format::GlobalHeader, strings_pool_offset) as u64);
1474
    emit.local_set(pool_local);
    // Create mutable GC array: array.new_default $i8_array str_len
1474
    let i8_array_idx = ctx.type_idx("i8_array");
1474
    emit.local_get(str_len_local);
1474
    emit.array_new_default(i8_array_idx);
1474
    emit.local_set(arr_local);
    // Loop: copy bytes from linear memory to GC array
1474
    emit.i32_const(0);
1474
    emit.local_set(idx_local);
1474
    emit.block_start();
1474
    emit.loop_start();
    // if idx >= str_len → break
1474
    emit.local_get(idx_local);
1474
    emit.local_get(str_len_local);
1474
    emit.i32_ge_u();
1474
    emit.br_if(1);
    // array.set arr idx (i32.load8_u (pool + str_off + idx))
1474
    emit.local_get(arr_local);
1474
    emit.local_get(idx_local);
1474
    emit.local_get(pool_local);
1474
    emit.local_get(str_off_local);
1474
    emit.i32_add();
1474
    emit.local_get(idx_local);
1474
    emit.i32_add();
1474
    emit.i32_load8_u(0);
1474
    emit.array_set(i8_array_idx);
    // idx++
1474
    emit.local_get(idx_local);
1474
    emit.i32_const(1);
1474
    emit.i32_add();
1474
    emit.local_set(idx_local);
1474
    emit.br(0);
1474
    emit.block_end(); // loop
1474
    emit.block_end(); // block
1474
    emit.local_get(arr_local);
1474
    Ok(WasmType::StringRef)
1474
}
938
pub(super) fn compile_tag_name_to_stack(
938
    ctx: &mut CompileContext,
938
    emit: &mut FunctionEmitter,
938
    symbols: &mut SymbolTable,
938
    args: &[Expr],
938
) -> Result<WasmType> {
938
    arity_check("TAG-NAME", args, 1)?;
    use std::mem::offset_of;
938
    compile_tag_string_field_to_stack(
938
        ctx,
938
        emit,
938
        symbols,
938
        args,
938
        offset_of!(scripting_format::TagData, name_offset) as u64,
938
        offset_of!(scripting_format::TagData, name_len) as u64,
    )
938
}
pub(super) fn compile_tag_name(
    ctx: &mut CompileContext,
    emit: &mut FunctionEmitter,
    symbols: &mut SymbolTable,
    args: &[Expr],
) -> Result<()> {
    let ty = compile_tag_name_to_stack(ctx, emit, symbols, args)?;
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
    Ok(())
}
536
pub(super) fn compile_tag_value_to_stack(
536
    ctx: &mut CompileContext,
536
    emit: &mut FunctionEmitter,
536
    symbols: &mut SymbolTable,
536
    args: &[Expr],
536
) -> Result<WasmType> {
536
    arity_check("TAG-VALUE", args, 1)?;
    use std::mem::offset_of;
536
    compile_tag_string_field_to_stack(
536
        ctx,
536
        emit,
536
        symbols,
536
        args,
536
        offset_of!(scripting_format::TagData, value_offset) as u64,
536
        offset_of!(scripting_format::TagData, value_len) as u64,
    )
536
}
pub(super) fn compile_tag_value(
    ctx: &mut CompileContext,
    emit: &mut FunctionEmitter,
    symbols: &mut SymbolTable,
    args: &[Expr],
) -> Result<()> {
    let ty = compile_tag_value_to_stack(ctx, emit, symbols, args)?;
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
    Ok(())
}
pub(super) fn compile_get_input_entities(
    ctx: &mut CompileContext,
    emit: &mut FunctionEmitter,
    _symbols: &mut SymbolTable,
    args: &[Expr],
) -> Result<()> {
    arity_check("GET-INPUT-ENTITIES", args, 0)?;
    let ty = compile_get_input_entities_to_stack(ctx, emit)?;
    super::super::expr::serialize_stack_to_output(ctx, emit, ty);
    Ok(())
}
44
pub(super) fn compile_get_input_entities_to_stack(
44
    ctx: &mut CompileContext,
44
    emit: &mut FunctionEmitter,
44
) -> Result<WasmType> {
    // Build cons list: loop from entity_count-1 down to 0, cons each index
44
    let i_local = ctx.alloc_local(WasmType::I32);
44
    let result_local = ctx.alloc_local(WasmType::ConsRef);
44
    let cons_type = ctx.type_idx("cons");
    // result = nil
44
    emit.ref_null(cons_type);
44
    emit.local_set(result_local);
    // i = entity_count - 1
44
    emit.call(ctx.func("get_input_offset"));
44
    emit.i32_load(0x08); // entity_count at GlobalHeader offset 8
44
    emit.i32_const(1);
44
    emit.i32_sub();
44
    emit.local_set(i_local);
    // block $exit
44
    emit.block_start();
    // loop $continue
44
    emit.loop_start();
    // if i < 0 → exit
44
    emit.local_get(i_local);
44
    emit.i32_const(0);
44
    emit.i32_lt_s();
44
    emit.br_if(1);
    // result = cons(i, result)
44
    emit.local_get(i_local);
44
    emit.local_get(result_local);
44
    emit.struct_new(cons_type);
44
    emit.local_set(result_local);
    // i = i - 1
44
    emit.local_get(i_local);
44
    emit.i32_const(1);
44
    emit.i32_sub();
44
    emit.local_set(i_local);
44
    emit.br(0);
44
    emit.block_end(); // end loop
44
    emit.block_end(); // end block
44
    emit.local_get(result_local);
44
    Ok(WasmType::ConsRef)
44
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::runtime::SymbolTable;
    #[test]
1
    fn test_entity_count_returns_wasm_runtime_i32() {
1
        let mut symbols = SymbolTable::new();
1
        let result = entity_count(&mut symbols, &[]).unwrap();
1
        assert_eq!(result, Expr::WasmRuntime(WasmType::I32));
1
    }
    #[test]
1
    fn test_context_type_returns_wasm_runtime_i32() {
1
        let mut symbols = SymbolTable::new();
1
        let result = context_type(&mut symbols, &[]).unwrap();
1
        assert_eq!(result, Expr::WasmRuntime(WasmType::I32));
1
    }
    #[test]
1
    fn test_primary_entity_type_returns_wasm_runtime_i32() {
1
        let mut symbols = SymbolTable::new();
1
        let result = primary_entity_type(&mut symbols, &[]).unwrap();
1
        assert_eq!(result, Expr::WasmRuntime(WasmType::I32));
1
    }
    #[test]
1
    fn test_primary_entity_idx_returns_wasm_runtime_i32() {
1
        let mut symbols = SymbolTable::new();
1
        let result = primary_entity_idx(&mut symbols, &[]).unwrap();
1
        assert_eq!(result, Expr::WasmRuntime(WasmType::I32));
1
    }
    #[test]
1
    fn test_entity_count_arity_error() {
1
        let mut symbols = SymbolTable::new();
1
        assert!(entity_count(&mut symbols, &[Expr::Nil]).is_err());
1
    }
    #[test]
1
    fn test_entity_type_arity_error() {
1
        let mut symbols = SymbolTable::new();
1
        assert!(entity_type(&mut symbols, &[]).is_err());
1
    }
}