1use crate::{
7 exec::{Context, DebugInfo, ExecInst, Executor, Io, Memory},
8 inst::{InstSet, Op},
9 parse::{parse, ErrorMap},
10};
11use std::{collections::BTreeMap, fmt::Display, ops::Deref, path::Path, str::FromStr};
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17#[derive(Debug)]
18struct CompiledInst {
19 pub id: u64,
20 pub inst: String,
21 pub op: Op,
22}
23
24impl CompiledInst {
25 pub fn new(id: u64, inst: String, op: Op) -> Self {
26 Self { id, inst, op }
27 }
28}
29
30type CompiledTree = BTreeMap<usize, CompiledInst>;
31
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(Debug)]
35pub struct CompiledProg {
36 prog: CompiledTree,
37 mem: Memory,
38 debug_info: Option<DebugInfo>,
39}
40
41impl CompiledProg {
42 fn new(prog: CompiledTree, mem: Memory, debug_info: Option<DebugInfo>) -> Self {
43 Self {
44 prog,
45 mem,
46 debug_info,
47 }
48 }
49
50 pub fn to_executor<T>(self, io: Io) -> Executor
52 where
53 T: InstSet,
54 <T as FromStr>::Err: Display,
55 {
56 let prog = self
57 .prog
58 .into_iter()
59 .map(|(addr, CompiledInst { inst, op, id })| {
60 (
61 addr,
62 ExecInst::new(
63 id,
64 inst.parse::<T>()
65 .unwrap_or_else(|s| panic!("{s}"))
66 .as_func_ptr(),
67 op,
68 ),
69 )
70 })
71 .collect();
72
73 Executor::new(
74 "",
75 prog,
76 Context::with_io(self.mem, io),
77 self.debug_info.unwrap_or_default(),
78 )
79 }
80}
81
82pub fn compile<T>(prog: impl Deref<Target = str>, debug: bool) -> Result<CompiledProg, ErrorMap>
84where
85 T: InstSet,
86 <T as FromStr>::Err: Display,
87{
88 let (prog, mem, _, debug_info) = parse::<T>(prog)?;
89
90 let prog = prog
91 .into_iter()
92 .map(|(addr, ExecInst { op, id, .. })| {
93 let str_inst = match T::from_id(id) {
94 Ok(inst) => inst,
95 Err(e) => panic!("{e}"),
96 }
97 .to_string();
98
99 (addr, CompiledInst::new(id, str_inst, op))
100 })
101 .collect();
102
103 let compiled = CompiledProg::new(prog, Memory::new(mem), debug.then_some(debug_info));
104
105 info!("Program compiled");
106
107 Ok(compiled)
108}
109
110pub fn from_file<T>(path: impl AsRef<Path>, debug: bool) -> Result<CompiledProg, ErrorMap>
112where
113 T: InstSet,
114 <T as FromStr>::Err: Display,
115{
116 let prog = std::fs::read_to_string(path).expect("Cannot read file");
117 compile::<T>(prog, debug)
118}
119
120#[cfg(test)]
121mod compile_tests {
122 use crate::{
123 compile::{compile, CompiledProg},
124 make_io,
125 parse::DefaultSet,
126 TestStdio, PROGRAMS,
127 };
128 use std::time::Instant;
129
130 #[test]
131 fn test() {
132 for (prog, exp, inp, out) in PROGRAMS {
133 let mut t = Instant::now();
134
135 let compiled = compile::<DefaultSet>(prog, false).unwrap();
136 let ser = serde_json::to_string(&compiled).unwrap();
137
138 println!("Compilation time: {:?}", t.elapsed());
139
140 t = Instant::now();
141 let s = TestStdio::new(vec![]);
142
143 let mut exe = serde_json::from_str::<CompiledProg>(&ser)
144 .unwrap()
145 .to_executor::<DefaultSet>(make_io!(TestStdio::new(inp), s.clone()));
146
147 println!("JIT time: {:?}", t.elapsed());
148
149 t = Instant::now();
150
151 exe.exec::<DefaultSet>();
152
153 println!("Execution time: {:?}", t.elapsed());
154
155 assert_eq!(
156 exe.ctx.acc, exp,
157 "Expected '{}' in ACC, got '{}'",
158 exp, exe.ctx.acc
159 );
160 assert_eq!(
161 s.to_vec(),
162 out,
163 "Expected '{}' in output, got '{}'",
164 String::from_utf8_lossy(out),
165 s.try_to_string().unwrap()
166 );
167 }
168 }
169}