1use seq_map::SeqMap;
2use swamp_script_code_gen::alloc::ConstantMemoryRegion;
3use swamp_script_code_gen::alloc_util::type_size_and_alignment;
4use swamp_script_code_gen::{CodeGenState, Error, GenOptions};
5use swamp_script_compile::Program;
6use swamp_script_compile::compile_string;
7use swamp_script_semantic::{ConstantId, Function};
8use swamp_script_types::Type;
9use swamp_vm::host::HostArgs;
10use swamp_vm::{Vm, VmSetup};
11use swamp_vm_disasm::{disasm_instructions_color, disasm_instructions_no_color};
12use swamp_vm_types::ConstantMemoryAddress;
13use tracing::info;
14
15pub fn execute_constants(
16 code_gen_state: &mut CodeGenState,
17 vm: &mut Vm,
18) -> Result<SeqMap<ConstantId, ConstantMemoryRegion>, Error> {
19 let mut constant_offsets = SeqMap::default();
20
21 for (constant_id, constant_info) in code_gen_state.constant_functions() {
22 let (size, _alignment) = type_size_and_alignment(&constant_info.constant_ref.resolved_type);
23
24 vm.execute_from_ip(&constant_info.ip);
25
26 let constant_offset = vm.allocate_constant(size.0 as usize);
27 let constant_region = ConstantMemoryRegion {
28 addr: ConstantMemoryAddress(constant_offset),
29 size,
30 };
31 constant_offsets
32 .insert(*constant_id, constant_region)
33 .unwrap();
34 }
35
36 Ok(constant_offsets)
37}
38
39fn gen_internal(code: &str) -> Result<(CodeGenState, Program), Error> {
40 let (program, main_module, _source_map) = compile_string(code).unwrap();
41
42 let mut code_gen = CodeGenState::new();
43 code_gen.gen_constants_in_order(&program.state.constants_in_dependency_order)?;
44 let debug_expr = main_module.main_expression.as_ref().unwrap();
47 info!(?debug_expr, "MAIN EXPRESSION");
48 let main_expression = main_module.main_expression.as_ref().unwrap();
49 let halt_function = GenOptions {
50 is_halt_function: true,
51 };
52
53 code_gen.gen_main_function(main_expression, &halt_function)?;
54
55 let normal_function = GenOptions {
56 is_halt_function: false,
57 };
58
59 for internal_function_def in &main_module.symbol_table.internal_functions() {
60 code_gen.gen_function_def(internal_function_def, &normal_function)?;
61 }
62
63 for (associated_on_type, impl_functions) in
64 &program.state.instantiator.associated_impls.functions
65 {
66 if !associated_on_type.is_concrete() {
67 continue;
68 }
69 if associated_on_type == &Type::Int
70 || associated_on_type == &Type::Float
71 || associated_on_type == &Type::Bool
72 || associated_on_type == &Type::String
73 {
74 continue;
75 }
76
77 for (_name, func) in &impl_functions.functions {
78 if func.name().clone().starts_with("instantiated ") {
79 continue;
80 }
81 match &**func {
82 Function::Internal(int_fn) => {
83 code_gen.gen_function_def(int_fn, &normal_function)?;
84 }
85
86 Function::External(_ext_fn) => {}
87 }
88 }
89 }
90
91 code_gen.finalize();
92
93 Ok((code_gen, program))
94}
95
96fn gen_internal_debug(code: &str) -> Result<(CodeGenState, Program), Error> {
97 let (code_gen, program) = gen_internal(code)?;
98 let disassembler_output = disasm_instructions_color(
99 code_gen.instructions(),
100 code_gen.comments(),
101 &code_gen.create_function_sections(),
102 );
103
104 eprintln!("{disassembler_output}");
105
106 Ok((code_gen, program))
107}
108
109fn exec_code_gen_state(code_gen_state: CodeGenState) -> Vm {
110 let (instructions, constants) = code_gen_state.take_instructions_and_constants();
111
112 let setup = VmSetup {
113 frame_memory_size: 1024,
114 heap_memory_size: 1024,
115 constant_memory: constants,
116 };
117 let mut vm = Vm::new(instructions, setup);
118
119 vm.execute();
120
121 vm
122}
123
124pub fn exec_internal(code: &str) -> Result<Vm, Error> {
125 let (code_gen, _program) = gen_internal_debug(code)?;
126
127 Ok(exec_code_gen_state(code_gen))
128}
129
130fn exec_internal_debug(code: &str) -> Result<Vm, Error> {
131 exec_internal(code)
132}
133
134fn trim_lines(text: &str) -> String {
135 text.lines()
136 .map(|line| {
137 line.split(';').next().unwrap_or("").trim()
139 })
140 .filter(|line| !line.is_empty())
141 .collect::<Vec<_>>()
142 .join("\n")
143}
144
145fn compare_line_outputs(encountered: &str, expected: &str) {
146 let encountered_trimmed = trim_lines(encountered);
147 let expected_trimmed = trim_lines(expected);
148
149 eprintln!("{encountered}");
150 assert_eq!(encountered_trimmed, expected_trimmed);
151}
152
153fn compare_hex_outputs(memory: &[u8], expected_hex: &str) {
154 let encountered_hexed = hexify::format_hex(memory);
155 let expected_hex_trimmed = expected_hex.trim();
156
157 compare_line_outputs(&encountered_hexed, expected_hex_trimmed);
158}
159
160fn exec(code: &str, expected_hex: &str) {
161 let vm = exec_internal_debug(code).expect("should work");
162
163 compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
164}
165
166pub fn exec_with_assembly(code: &str, expected_assembly: &str, expected_hex: &str) {
167 let (generator, _program) = gen_internal_debug(code).expect("should work");
168
169 let disassembler_output = disasm_instructions_no_color(
170 generator.instructions(),
171 generator.comments(),
172 &generator.create_function_sections(),
173 false,
174 );
175 compare_line_outputs(&disassembler_output, expected_assembly);
176
177 let vm = exec_code_gen_state(generator);
178
179 compare_hex_outputs(&vm.frame_memory()[..16], expected_hex);
180}
181
182pub fn exec_with_host_function<F>(
185 code: &str,
186 expected_assembly: &str,
187 expected_hex: &str,
188 id: &str,
189 callback: F,
190) where
191 F: 'static + FnMut(HostArgs),
192{
193 let (generator, program) = gen_internal_debug(code).expect("should work");
194
195 let disassembler_output = disasm_instructions_no_color(
196 generator.instructions(),
197 generator.comments(),
198 &generator.create_function_sections(),
199 false,
200 );
201 compare_line_outputs(&disassembler_output, expected_assembly);
202
203 let module = program
204 .modules
205 .get(&["crate".to_string(), "test".to_string()])
206 .unwrap();
207
208 let external_id = module
209 .symbol_table
210 .get_external_function_declaration(id)
211 .unwrap();
212
213 let (instructions, constants) = generator.take_instructions_and_constants();
214 let setup = VmSetup {
215 frame_memory_size: 1024,
216 heap_memory_size: 1024,
217 constant_memory: constants,
218 };
219 let mut vm = Vm::new(instructions, setup);
220
221 vm.add_host_function(external_id.id as u16, callback);
222
223 vm.execute();
224
225 compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
226}
227
228fn exec_vars(code: &str, expected_hex: &str) {
229 let vm = exec_internal_debug(code);
230
231 compare_hex_outputs(&vm.unwrap().stack_memory()[..16], expected_hex);
232}
233
234fn gen_code(code: &str, expected_output: &str) {
235 let (generator, _program) = gen_internal_debug(code).expect("should work");
236
237 let disassembler_output = disasm_instructions_no_color(
238 generator.instructions(),
239 generator.comments(),
240 &generator.create_function_sections(),
241 false,
242 );
243
244 compare_line_outputs(&disassembler_output, expected_output);
245}
246
247pub fn exec_show_constants(code: &str, expected_hex: &str, expected_constants: &str) {
248 let vm = exec_internal_debug(code).unwrap();
249
250 compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
251 compare_hex_outputs(&vm.memory()[0xFFF0..], expected_constants);
252}