1
//! `SPLIT-*` accessors — `VALUE-NUM`, `VALUE-DENOM`, `VALUE` (combined
2
//! ratio), `RECONCILE-STATE`, `RECONCILE-DATE`. Each takes an entity
3
//! index and reads a single field from `SplitData`.
4

            
5
use crate::ast::{Expr, WasmType};
6
use crate::compiler::context::CompileContext;
7
use crate::compiler::emit::FunctionEmitter;
8
use crate::compiler::expr::{LOCAL_TEMP_I32, eval_value};
9
use crate::error::Result;
10
use crate::runtime::SymbolTable;
11

            
12
use super::{arity_check, compile_idx_to_stack, emit_entity_data_offset};
13

            
14
332
pub(super) fn split_value_num(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
15
332
    arity_check("SPLIT-VALUE-NUM", args, 1)?;
16
332
    eval_value(symbols, &args[0])?;
17
332
    Ok(Expr::WasmRuntime(WasmType::Ratio))
18
332
}
19

            
20
166
pub(super) fn compile_split_value_num_to_stack(
21
166
    ctx: &mut CompileContext,
22
166
    emit: &mut FunctionEmitter,
23
166
    symbols: &mut SymbolTable,
24
166
    args: &[Expr],
25
166
) -> Result<WasmType> {
26
166
    arity_check("SPLIT-VALUE-NUM", args, 1)?;
27
166
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
28
166
    emit_entity_data_offset(ctx, emit, LOCAL_TEMP_I32)?;
29
166
    emit.i64_load(32); // value_num at SplitData offset 32
30
166
    emit.call(ctx.ids.ratio_from_i64);
31
166
    Ok(WasmType::Ratio)
32
166
}
33

            
34
332
pub(super) fn split_value_denom(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
35
332
    arity_check("SPLIT-VALUE-DENOM", args, 1)?;
36
332
    eval_value(symbols, &args[0])?;
37
332
    Ok(Expr::WasmRuntime(WasmType::Ratio))
38
332
}
39

            
40
166
pub(super) fn compile_split_value_denom_to_stack(
41
166
    ctx: &mut CompileContext,
42
166
    emit: &mut FunctionEmitter,
43
166
    symbols: &mut SymbolTable,
44
166
    args: &[Expr],
45
166
) -> Result<WasmType> {
46
166
    arity_check("SPLIT-VALUE-DENOM", args, 1)?;
47
166
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
48
166
    emit_entity_data_offset(ctx, emit, LOCAL_TEMP_I32)?;
49
166
    emit.i64_load(40); // value_denom at SplitData offset 40
50
166
    emit.call(ctx.ids.ratio_from_i64);
51
166
    Ok(WasmType::Ratio)
52
166
}
53

            
54
/// (split-value idx) — reads both `value_num` and `value_denom`, returns proper Ratio
55
2988
pub(super) fn split_value(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
56
2988
    arity_check("SPLIT-VALUE", args, 1)?;
57
2988
    eval_value(symbols, &args[0])?;
58
2988
    Ok(Expr::WasmRuntime(WasmType::Ratio))
59
2988
}
60

            
61
830
pub(super) fn compile_split_value_to_stack(
62
830
    ctx: &mut CompileContext,
63
830
    emit: &mut FunctionEmitter,
64
830
    symbols: &mut SymbolTable,
65
830
    args: &[Expr],
66
830
) -> Result<WasmType> {
67
830
    arity_check("SPLIT-VALUE", args, 1)?;
68
830
    let temp = LOCAL_TEMP_I32;
69
    // Compute data offset, save in temp
70
830
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
71
830
    emit_entity_data_offset(ctx, emit, temp)?;
72
830
    emit.local_set(temp);
73
    // Load value_num (i64 at offset 32)
74
830
    emit.local_get(temp);
75
830
    emit.i64_load(32);
76
    // Load value_denom (i64 at offset 40)
77
830
    emit.local_get(temp);
78
830
    emit.i64_load(40);
79
    // Construct ratio from num and denom
80
830
    emit.call(ctx.ids.ratio_new);
81
830
    Ok(WasmType::Ratio)
82
830
}
83

            
84
996
pub(super) fn split_reconcile_state(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
85
996
    arity_check("SPLIT-RECONCILE-STATE", args, 1)?;
86
996
    eval_value(symbols, &args[0])?;
87
996
    Ok(Expr::WasmRuntime(WasmType::I32))
88
996
}
89

            
90
332
pub(super) fn compile_split_reconcile_state_to_stack(
91
332
    ctx: &mut CompileContext,
92
332
    emit: &mut FunctionEmitter,
93
332
    symbols: &mut SymbolTable,
94
332
    args: &[Expr],
95
332
) -> Result<WasmType> {
96
332
    arity_check("SPLIT-RECONCILE-STATE", args, 1)?;
97
332
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
98
332
    emit_entity_data_offset(ctx, emit, LOCAL_TEMP_I32)?;
99
332
    emit.i32_load8_u(48); // reconcile_state at SplitData offset 48
100
332
    Ok(WasmType::I32)
101
332
}
102

            
103
332
pub(super) fn split_reconcile_date(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
104
332
    arity_check("SPLIT-RECONCILE-DATE", args, 1)?;
105
332
    eval_value(symbols, &args[0])?;
106
332
    Ok(Expr::WasmRuntime(WasmType::Ratio))
107
332
}
108

            
109
166
pub(super) fn compile_split_reconcile_date_to_stack(
110
166
    ctx: &mut CompileContext,
111
166
    emit: &mut FunctionEmitter,
112
166
    symbols: &mut SymbolTable,
113
166
    args: &[Expr],
114
166
) -> Result<WasmType> {
115
166
    arity_check("SPLIT-RECONCILE-DATE", args, 1)?;
116
166
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
117
166
    emit_entity_data_offset(ctx, emit, LOCAL_TEMP_I32)?;
118
166
    emit.i64_load(56); // reconcile_date at SplitData offset 56
119
166
    emit.call(ctx.ids.ratio_from_i64);
120
166
    Ok(WasmType::Ratio)
121
166
}
122

            
123
666
pub(super) fn split_account_name(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
124
666
    arity_check("SPLIT-ACCOUNT-NAME", args, 1)?;
125
665
    eval_value(symbols, &args[0])?;
126
665
    Ok(Expr::WasmRuntime(WasmType::StringRef))
127
666
}
128

            
129
/// `(split-account-name idx)` — copies the posting account's display name out
130
/// of the strings pool into a fresh `(array (mut i8))`, reading the
131
/// `account_name_offset` (u32) / `account_name_len` (u32) pair the serializer
132
/// wrote into `SplitData`. Mirrors the tag string reader; the length is a u32
133
/// here (a full `i32.load`), not the u16 a tag carries.
134
664
pub(super) fn compile_split_account_name_to_stack(
135
664
    ctx: &mut CompileContext,
136
664
    emit: &mut FunctionEmitter,
137
664
    symbols: &mut SymbolTable,
138
664
    args: &[Expr],
139
664
) -> Result<WasmType> {
140
    use std::mem::offset_of;
141
664
    arity_check("SPLIT-ACCOUNT-NAME", args, 1)?;
142

            
143
664
    let data_local = ctx.alloc_local(WasmType::I32)?;
144
664
    let str_off_local = ctx.alloc_local(WasmType::I32)?;
145
664
    let str_len_local = ctx.alloc_local(WasmType::I32)?;
146
664
    let pool_local = ctx.alloc_local(WasmType::I32)?;
147
664
    let arr_local = ctx.alloc_local(WasmType::StringRef)?;
148
664
    let idx_local = ctx.alloc_local(WasmType::I32)?;
149

            
150
664
    compile_idx_to_stack(ctx, emit, symbols, &args[0])?;
151
664
    emit_entity_data_offset(ctx, emit, LOCAL_TEMP_I32)?;
152
664
    emit.local_set(data_local);
153

            
154
664
    emit.local_get(data_local);
155
664
    emit.i32_load(offset_of!(scripting_format::SplitData, account_name_offset) as u64);
156
664
    emit.local_set(str_off_local);
157

            
158
664
    emit.local_get(data_local);
159
664
    emit.i32_load(offset_of!(scripting_format::SplitData, account_name_len) as u64);
160
664
    emit.local_set(str_len_local);
161

            
162
664
    emit.call(ctx.ids.get_input_offset()?);
163
664
    emit.i32_load(offset_of!(scripting_format::GlobalHeader, strings_pool_offset) as u64);
164
664
    emit.local_set(pool_local);
165

            
166
664
    let i8_array_idx = ctx.ids.ty_i8_array;
167
664
    emit.local_get(str_len_local);
168
664
    emit.array_new_default(i8_array_idx);
169
664
    emit.local_set(arr_local);
170

            
171
664
    emit.i32_const(0);
172
664
    emit.local_set(idx_local);
173
664
    emit.block_start();
174
664
    emit.loop_start();
175

            
176
664
    emit.local_get(idx_local);
177
664
    emit.local_get(str_len_local);
178
664
    emit.i32_ge_u();
179
664
    emit.br_if(1);
180

            
181
664
    emit.local_get(arr_local);
182
664
    emit.local_get(idx_local);
183
664
    emit.local_get(pool_local);
184
664
    emit.local_get(str_off_local);
185
664
    emit.i32_add();
186
664
    emit.local_get(idx_local);
187
664
    emit.i32_add();
188
664
    emit.i32_load8_u(0);
189
664
    emit.array_set(i8_array_idx);
190

            
191
664
    emit.local_get(idx_local);
192
664
    emit.i32_const(1);
193
664
    emit.i32_add();
194
664
    emit.local_set(idx_local);
195
664
    emit.br(0);
196
664
    emit.block_end(); // loop
197
664
    emit.block_end(); // block
198

            
199
664
    emit.local_get(arr_local);
200
664
    Ok(WasmType::StringRef)
201
664
}