cambridge_asm/
compile.rs

1// Copyright (c) 2021 Saadi Save
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6use 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/// Represents a compiled program ready to be serialized into a file
33#[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    /// Convert to an [`Executor`] so that program can be executed
51    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
82/// Parses source code into a [`CompiledProg`] ready for serialization
83pub 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
110/// Parses source code into a [`CompiledProg`] directly from a file
111pub 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}