1use std::ptr;
2use swamp_script_code_gen::{CodeGenState, Error};
3use swamp_script_code_gen_program::code_gen_program;
4use swamp_script_compile::Program;
5use swamp_script_compile::compile_string;
6use swamp_script_source_map_lookup::SourceMapWrapper;
7use swamp_vm::host::HostArgs;
8use swamp_vm::{Vm, VmSetup};
9use swamp_vm_disasm::{disasm_instructions_color, disasm_instructions_no_color};
10use swamp_vm_types::InstructionPosition;
11
12fn gen_internal(code: &str) -> Result<(CodeGenState, Program), Error> {
13 let (program, main_module, source_map) = compile_string(code).unwrap();
14
15 let source_map_wrapper = SourceMapWrapper {
16 source_map: &source_map,
17 current_dir: Default::default(),
18 };
19 let code_gen = code_gen_program(&program, &main_module, &source_map_wrapper)?;
20
21 Ok((code_gen, program))
22}
23
24fn gen_internal_debug(code: &str) -> Result<(CodeGenState, Program), Error> {
25 let (code_gen, program) = gen_internal(code)?;
26 let disassembler_output = disasm_instructions_color(
27 code_gen.instructions(),
28 &InstructionPosition(0),
29 code_gen.comments(),
30 &code_gen.create_function_sections(),
31 );
32
33 eprintln!("{disassembler_output}");
34
35 Ok((code_gen, program))
36}
37
38fn exec_code_gen_state(code_gen_state: CodeGenState) -> Vm {
39 let (instructions, mut constants_memory, constant_functions) =
40 code_gen_state.take_instructions_and_constants();
41
42 for (_constant_id, constant_func) in constant_functions {
43 let setup = VmSetup {
44 frame_memory_size: 1024,
45 heap_memory_size: 1024,
46 constant_memory: constants_memory.clone(),
47 };
48
49 let mut vm = Vm::new(instructions.clone(), setup);
50
51 vm.execute_from_ip(&constant_func.ip);
52
53 unsafe {
54 ptr::copy_nonoverlapping(
55 vm.frame_memory().as_ptr(),
56 constants_memory
57 .as_mut_ptr()
58 .add(constant_func.target_constant_memory.addr.0 as usize),
59 constant_func.target_constant_memory.size.0 as usize,
60 );
61 }
62 }
63
64 let setup = VmSetup {
65 frame_memory_size: 1024,
66 heap_memory_size: 1024,
67 constant_memory: constants_memory,
68 };
69 let mut vm = Vm::new(instructions, setup);
70
71 vm.execute();
72
73 vm
74}
75
76pub fn exec_internal(code: &str) -> Result<Vm, Error> {
77 let (code_gen, _program) = gen_internal_debug(code)?;
78
79 Ok(exec_code_gen_state(code_gen))
80}
81
82fn exec_internal_debug(code: &str) -> Result<Vm, Error> {
83 exec_internal(code)
84}
85
86fn trim_lines(text: &str) -> String {
87 text.lines()
88 .map(|line| {
89 line.split(';').next().unwrap_or("").trim()
91 })
92 .filter(|line| !line.is_empty())
93 .collect::<Vec<_>>()
94 .join("\n")
95}
96
97fn compare_line_outputs(encountered: &str, expected: &str) {
98 let encountered_trimmed = trim_lines(encountered);
99 let expected_trimmed = trim_lines(expected);
100
101 eprintln!("{encountered}");
102 assert_eq!(encountered_trimmed, expected_trimmed);
103}
104
105fn compare_hex_outputs(memory: &[u8], expected_hex: &str) {
106 let encountered_hexed = hexify::format_hex(memory);
107 let expected_hex_trimmed = expected_hex.trim();
108
109 compare_line_outputs(&encountered_hexed, expected_hex_trimmed);
110}
111
112fn exec(code: &str, expected_hex: &str) {
113 let vm = exec_internal_debug(code).expect("should work");
114
115 compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
116}
117
118pub fn exec_with_assembly(code: &str, expected_assembly: &str, expected_hex: &str) {
119 let (generator, _program) = gen_internal_debug(code).expect("should work");
120
121 let disassembler_output = disasm_instructions_no_color(
122 generator.instructions(),
123 generator.comments(),
124 &generator.create_function_sections(),
125 false,
126 );
127 compare_line_outputs(&disassembler_output, expected_assembly);
128
129 let vm = exec_code_gen_state(generator);
130
131 compare_hex_outputs(&vm.frame_memory()[..16], expected_hex);
132}
133
134pub fn exec_with_host_function<F>(
137 code: &str,
138 expected_assembly: &str,
139 expected_hex: &str,
140 id: &str,
141 callback: F,
142) where
143 F: 'static + FnMut(HostArgs),
144{
145 let vm = exec_with_host_function_internal(code, expected_assembly, id, callback);
146 compare_hex_outputs(&vm.frame_memory()[..16], expected_hex);
147}
148
149pub fn exec_with_host_function_internal<F>(
152 code: &str,
153 expected_assembly: &str,
154 id: &str,
155 callback: F,
156) -> Vm
157where
158 F: 'static + FnMut(HostArgs),
159{
160 let (generator, program) = gen_internal_debug(code).expect("should work");
161
162 let disassembler_output = disasm_instructions_no_color(
163 generator.instructions(),
164 generator.comments(),
165 &generator.create_function_sections(),
166 false,
167 );
168 compare_line_outputs(&disassembler_output, expected_assembly);
169
170 let module = program
171 .modules
172 .get(&["crate".to_string(), "test".to_string()])
173 .unwrap();
174
175 let external_id = module
176 .symbol_table
177 .get_external_function_declaration(id)
178 .unwrap();
179
180 let (instructions, constants, constant_infos) = generator.take_instructions_and_constants();
181 let setup = VmSetup {
182 frame_memory_size: 1024,
183 heap_memory_size: 1024,
184 constant_memory: constants,
185 };
186 let mut vm = Vm::new(instructions, setup);
187
188 vm.add_host_function(external_id.id as u16, callback);
189
190 vm.execute();
191
192 vm
193}
194
195pub fn exec_with_host_function_show_heap<F>(
198 code: &str,
199 expected_assembly: &str,
200 start: usize,
201 count: usize,
202 expected_hex: &str,
203 id: &str,
204 callback: F,
205) where
206 F: 'static + FnMut(HostArgs),
207{
208 let vm = exec_with_host_function_internal(code, expected_assembly, id, callback);
209 compare_hex_outputs(&vm.heap_memory()[start..start + count], expected_hex);
210}
211
212fn exec_vars(code: &str, expected_hex: &str) {
213 let vm = exec_internal_debug(code);
214
215 compare_hex_outputs(&vm.unwrap().frame_memory()[..16], expected_hex);
216}
217
218fn gen_code(code: &str, expected_output: &str) {
219 let (generator, _program) = gen_internal_debug(code).expect("should work");
220
221 let disassembler_output = disasm_instructions_no_color(
222 generator.instructions(),
223 generator.comments(),
224 &generator.create_function_sections(),
225 false,
226 );
227
228 compare_line_outputs(&disassembler_output, expected_output);
229}
230
231pub fn exec_show_constants(code: &str, expected_hex: &str, expected_constants: &str) {
232 let vm = exec_internal_debug(code).unwrap();
233
234 compare_hex_outputs(&vm.stack_memory()[..16], expected_hex);
235 compare_hex_outputs(&vm.constants()[..16], expected_constants);
236}