ashpaper_plus/
program.rs

1use super::parser::{self, InsType, Instruction, Register};
2#[cfg(feature = "jit")]
3use super::{errors::jit::JitResult, jit::JIT};
4
5#[derive(Debug, Clone)]
6struct Memory {
7    register0: i64,
8    register1: i64,
9    stack: Vec<i64>,
10}
11
12impl Memory {
13    fn new() -> Memory {
14        Memory {
15            register0: 0,
16            register1: 0,
17            stack: vec![],
18        }
19    }
20
21    fn store_syllables(&mut self, register: Register, syllables: i64) {
22        match register {
23            Register::Register0 => self.register0 = syllables,
24            Register::Register1 => self.register1 = syllables,
25        }
26    }
27
28    fn push_to_stack(&mut self, val: i64) {
29        self.stack.push(val);
30    }
31
32    fn push(&mut self, register: Register) {
33        match register {
34            Register::Register0 => self.stack.push(self.register0),
35            Register::Register1 => self.stack.push(self.register1),
36        }
37    }
38
39    fn pop(&mut self, register: Register) {
40        if let Some(val) = self.stack.pop() {
41            match register {
42                Register::Register0 => self.register0 = val,
43                Register::Register1 => self.register1 = val,
44            }
45        }
46    }
47
48    fn multiply(&mut self, register: Register) {
49        match register {
50            Register::Register0 => self.register0 *= self.register1,
51            Register::Register1 => self.register1 *= self.register0,
52        }
53    }
54
55    fn add(&mut self, register: Register) {
56        match register {
57            Register::Register0 => self.register0 += self.register1,
58            Register::Register1 => self.register1 += self.register0,
59        }
60    }
61
62    fn get_active(&self, register: Register) -> i64 {
63        match register {
64            Register::Register0 => self.register0,
65            Register::Register1 => self.register1,
66        }
67    }
68
69    fn get_inactive(&self, register: Register) -> i64 {
70        match register {
71            Register::Register0 => self.register1,
72            Register::Register1 => self.register0,
73        }
74    }
75
76    fn negate(&mut self, register: Register) {
77        match register {
78            Register::Register0 => self.register0 = -self.register0,
79            Register::Register1 => self.register1 = -self.register1,
80        }
81    }
82}
83
84pub struct Program {
85    pub ast: Vec<Instruction>,
86}
87
88impl Program {
89    pub fn create(source: &str) -> Program {
90        Program {
91            ast: parser::parse(source),
92        }
93    }
94
95    pub fn execute(&self) -> String {
96        let mut mem = Memory::new();
97        let mut output: String = String::new();
98
99        let mut instruction_pointer: usize = 0;
100
101        log::info!(
102            "{: <51} | {: ^4} | {: ^4} | {: ^7}",
103            "instruction",
104            "r0",
105            "r1",
106            "stack"
107        );
108        log::info!("{:-<51} | {:-^4} | {:-^4} | {:-^7}", "", "", "", "");
109
110        'outer: while let Some(ins) = self.ast.get(instruction_pointer) {
111            let Instruction {
112                instruction,
113                register: reg,
114                ref line,
115            } = *ins;
116
117            match instruction {
118                InsType::ConditionalGoto(syllables) => {
119                    if mem.get_active(reg) > syllables as i64 {
120                        instruction_pointer =
121                            (mem.get_inactive(reg).abs() as usize) % (self.ast.len() as usize);
122                        continue 'outer;
123                    }
124                }
125                InsType::Negate => mem.negate(reg),
126                InsType::Multiply => mem.multiply(reg),
127                InsType::Add => mem.add(reg),
128                InsType::PrintChar => {
129                    let printable = (mem.get_active(reg).abs() % std::u8::MAX as i64) as u8;
130                    output = format!("{}{}", output, printable as char);
131                }
132                InsType::PrintValue => output = format!("{}{}", output, mem.get_active(reg)),
133                InsType::Pop => mem.pop(reg),
134                InsType::Push => mem.push(reg),
135                InsType::Store(syllables) => mem.store_syllables(reg, syllables as i64),
136                InsType::ConditionalPush {
137                    prev_syllables,
138                    cur_syllables,
139                } => {
140                    if mem.get_active(reg) < mem.get_inactive(reg) {
141                        mem.push_to_stack(prev_syllables as i64);
142                    } else {
143                        mem.push_to_stack(cur_syllables as i64);
144                    }
145                }
146                InsType::Goto => {
147                    instruction_pointer =
148                        (mem.get_active(reg).abs() as usize) % (self.ast.len() as usize);
149                    continue 'outer;
150                }
151                InsType::Noop => (),
152            }
153
154            log::info!(
155                "{: <51} | {: ^4} | {: ^4} | {:^?}",
156                line,
157                mem.register0,
158                mem.register1,
159                mem.stack
160            );
161
162            instruction_pointer += 1;
163        }
164
165        output
166    }
167
168    #[cfg(feature = "jit")]
169    pub fn jit_execute(&self) -> JitResult<()> {
170        let mut jit = JIT::default();
171        let func = jit.compile(&self.ast)?;
172        func();
173
174        Ok(())
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use pretty_assertions::assert_eq;
182
183    #[test]
184    fn mem_get_inactive() {
185        let mut mem = Memory::new();
186        let r0 = 10;
187        let r1 = 11;
188        mem.store_syllables(Register::Register0, r0);
189        mem.store_syllables(Register::Register1, r1);
190
191        assert_eq!(mem.get_inactive(Register::Register0), r1);
192        assert_eq!(mem.get_inactive(Register::Register1), r0);
193    }
194
195    #[test]
196    fn mem_push() {
197        let mut mem = Memory::new();
198        let reg = Register::Register0;
199        mem.store_syllables(reg, 1);
200        mem.push(reg);
201        assert_eq!(mem.stack, vec![1]);
202        let reg = Register::Register1;
203        mem.store_syllables(reg, 2);
204        mem.push(reg);
205        assert_eq!(mem.stack, vec![1, 2]);
206    }
207
208    #[test]
209    fn alliteration() {
210        let alliteration_program = r#"
211poem or calculator or nothing
212    somebody once
213    fish fosh
214word.
215
216"#
217        .trim_start();
218
219        let program = Program::create(alliteration_program);
220        let result = program.execute();
221        assert_eq!(result, "");
222    }
223
224    #[test]
225    fn rhyming() {
226        let rhyming_program = r#"
227somebody once told me 
228    he took a new elf 
229and stabbed it with a shelf
230pop,
231print.
232then he took blue
233and stabbed it with some you 
234pop,
235print.
236"#;
237
238        let program = Program::create(rhyming_program);
239        let result = program.execute();
240        assert_eq!(result, "64");
241    }
242
243    #[test]
244    fn factorial() {
245        let factorial_program = r#"
246
247  it is a calculator, like a
248      poem, is a poem, and finds
249        factori-
250          als
251  The input is the syllAbles
252in the title, count them, as one counts
253  (q) what other poem, programs can be writ
254  (a) anything a Turing
255    machine-machine-machine
256    would do
257re/cur
258    sion works too, in poems, programs, and this
259       a lovely.
260poem or calculator or nothing
261how lovely can it be?
262"#;
263        let four_factorial = format!("lovely poem\n{}", factorial_program);
264        println!("{}", four_factorial);
265        let four_factorial_res = "24\n".to_string();
266        let program = Program::create(&four_factorial);
267        assert_eq!(program.execute(), four_factorial_res);
268
269        let five_factorial = format!("lovely poem and\n{}", factorial_program);
270        let program = Program::create(&five_factorial);
271        let five_factorial_res = "120\n".to_string();
272        assert_eq!(program.execute(), five_factorial_res);
273    }
274
275    #[test]
276    fn logging() {
277        // everything should work as expected if logging is enabled.
278        std::env::set_var("RUST_LOG", "info");
279        factorial();
280    }
281}