1#![allow(clippy::upper_case_acronyms)]
7
8use crate::{
9 exec::{Context, DebugInfo, ExecInst, Executor, Io, Memory, Source},
10 extend,
11 inst::InstSet,
12 inst_set,
13};
14use std::{collections::BTreeMap, fmt::Display, ops::Deref, path::Path, str::FromStr};
15
16mod lexer;
17mod parser;
18
19pub use lexer::{ErrorKind, ErrorMap, Span};
20
21inst_set! {
22 pub Core use crate::exec::{mov, cmp, io, arith, bitman}; {
35 LDM => mov::ldm,
36 LDD => mov::ldd,
37 LDI => mov::ldi,
38 LDX => mov::ldx,
39 LDR => mov::ldr,
40 MOV => mov::mov,
41 STO => mov::sto,
42
43 CMP => cmp::cmp,
44 JPE => cmp::jpe,
45 JPN => cmp::jpn,
46 JMP => cmp::jmp,
47 CMI => cmp::cmi,
48
49 IN => io::inp,
50 OUT => io::out,
51 END => io::end,
52
53 INC => arith::inc,
54 DEC => arith::dec,
55 ADD => arith::add,
56 SUB => arith::sub,
57
58 AND => bitman::and,
59 OR => bitman::or,
60 XOR => bitman::xor,
61 LSL => bitman::lsl,
62 LSR => bitman::lsr,
63 }
64}
65
66extend! {
67 #[cfg(feature = "extended")]
71 pub Extended extends Core use crate::exec::{io, arith::zero}; {
72 ZERO => zero,
73 DBG => io::dbg,
74 RIN => io::rin,
75 CALL => io::call,
76 RET => io::ret,
77 NOP => io::nop,
78 PRINT => io::print,
79 READ => io::read,
80 }
81}
82
83mod _default_set {
85 #[cfg(not(feature = "extended"))]
86 pub type DefaultSet = super::Core;
87
88 #[cfg(feature = "extended")]
89 pub type DefaultSet = super::Extended;
90}
91
92pub type DefaultSet = _default_set::DefaultSet;
96
97#[allow(clippy::type_complexity)]
98pub(crate) fn parse<T>(
99 prog: impl Deref<Target = str>,
100) -> Result<
101 (
102 BTreeMap<usize, ExecInst>,
103 BTreeMap<usize, usize>,
104 Source,
105 DebugInfo,
106 ),
107 ErrorMap,
108>
109where
110 T: InstSet,
111 <T as FromStr>::Err: Display,
112{
113 let (insts, mem, debug_info) = parser::Parser::<T>::new(&prog).parse()?;
114 let src = Source::from(prog);
115
116 let mem = mem
117 .into_iter()
118 .map(|parser::MemIr { addr, data }| (addr, data))
119 .collect();
120
121 let prog = insts
122 .into_iter()
123 .map(|parser::InstIr::<T> { addr, inst }| (addr, inst.to_exec_inst()))
124 .collect();
125
126 Ok((prog, mem, src, debug_info))
127}
128
129pub fn jit<T>(prog: impl Deref<Target = str>, io: Io) -> Result<Executor, ErrorMap>
151where
152 T: InstSet,
153 <T as FromStr>::Err: Display,
154{
155 let (prog, mem, src, debug_info) = parse::<T>(prog)?;
156
157 let exe = Executor::new(
158 src,
159 prog,
160 Context::with_io(Memory::new(mem), io),
161 debug_info,
162 );
163
164 info!("Executor created");
165 debug!(
166 "{}\n",
167 exe.display_with_opcodes::<T>()
168 .unwrap_or_else(|s| panic!("{s}"))
169 );
170 debug!("The initial context:\n{}\n", exe.ctx);
171
172 Ok(exe)
173}
174
175pub fn jit_from_file<T>(path: impl AsRef<Path>, io: Io) -> Result<Executor, ErrorMap>
197where
198 T: InstSet,
199 <T as FromStr>::Err: Display,
200{
201 let prog = std::fs::read_to_string(path).expect("Cannot read file");
202
203 info!("File read complete.");
204
205 jit::<T>(prog, io)
206}
207
208#[cfg(test)]
209mod parse_tests {
210 use crate::{
211 make_io,
212 parse::{jit, DefaultSet},
213 TestStdio, PROGRAMS,
214 };
215 use std::time::Instant;
216
217 #[test]
218 fn test() {
219 for (prog, exp, inp, out) in PROGRAMS {
220 let mut t = Instant::now();
221 let s = TestStdio::new(vec![]);
222
223 let mut exe =
224 jit::<DefaultSet>(prog, make_io!(TestStdio::new(inp), s.clone())).unwrap();
225
226 println!("Parse time: {:?}", t.elapsed());
227
228 t = Instant::now();
229
230 exe.exec::<DefaultSet>();
231
232 println!("Execution time: {:?}", t.elapsed());
233
234 assert_eq!(
235 exe.ctx.acc, exp,
236 "Expected '{}' in ACC, got '{}'",
237 exp, exe.ctx.acc
238 );
239 assert_eq!(
240 s.to_vec(),
241 out,
242 "Expected '{}' in output, got '{}'",
243 String::from_utf8_lossy(out),
244 s.try_to_string().unwrap()
245 );
246 }
247 }
248
249 #[test]
250 #[should_panic(
251 expected = "called `Result::unwrap()` on an `Err` value: {4..7: ParseIntError(ParseIntError { kind: InvalidDigit })}"
252 )]
253 fn panics() {
254 let mut exec = jit::<DefaultSet>(
255 include_str!("../../examples/panics.pasm"),
256 make_io!(std::io::stdin(), std::io::sink()),
257 )
258 .unwrap();
259 exec.exec::<DefaultSet>();
260 }
261}