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

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

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

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

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

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

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

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

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

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

            
123
550
pub(super) fn split_account_name(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
124
550
    arity_check("SPLIT-ACCOUNT-NAME", args, 1)?;
125
549
    eval_value(symbols, &args[0])?;
126
549
    Ok(Expr::WasmRuntime(WasmType::StringRef))
127
550
}
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
548
pub(super) fn compile_split_account_name_to_stack(
135
548
    ctx: &mut CompileContext,
136
548
    emit: &mut FunctionEmitter,
137
548
    symbols: &mut SymbolTable,
138
548
    args: &[Expr],
139
548
) -> Result<WasmType> {
140
    use std::mem::offset_of;
141
548
    arity_check("SPLIT-ACCOUNT-NAME", args, 1)?;
142

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

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

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

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

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

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

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

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

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

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

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