rusty_basic/
evaluator.rs

1use std::io::Write;
2
3use crate::ast::{
4    AdditiveOperator, Expression, ExpressionListElement, Factor, Line, MultiplicativeOperator,
5    RelationalOperator, Statement, Term,
6};
7
8const STORAGE_SIZE: usize = 256;
9const NUM_VARIABLES: usize = 26;
10
11#[derive(Debug)]
12pub enum Error {
13    LineNumberOutOfRange,
14    UnknownLineNumber,
15}
16
17pub struct Evaluator<'a> {
18    storage: [Option<Line>; STORAGE_SIZE],
19    stack: Vec<usize>,
20    program_counter: usize,
21    variables: [i16; NUM_VARIABLES],
22    output: &'a mut dyn Write,
23}
24
25impl<'a> Evaluator<'a> {
26    pub fn new(output: &'a mut dyn Write) -> Self {
27        Self {
28            storage: [const { None }; STORAGE_SIZE],
29            stack: Vec::new(),
30            program_counter: 0,
31            variables: [0; NUM_VARIABLES],
32            output,
33        }
34    }
35
36    pub fn process_line(&mut self, line: Line) -> Result<(), Error> {
37        match line.number().is_some() {
38            true => self.load_line(line),
39            false => self.run_direct(line.statement())?,
40        }
41
42        Ok(())
43    }
44
45    fn load_line(&mut self, line: Line) {
46        debug_assert!(line.number().is_some());
47
48        let label = line.number().unwrap();
49        self.storage[label as usize] = Some(line);
50    }
51
52    fn jump(&mut self, line_number: u8) -> Result<(), Error> {
53        match self.storage.get(line_number as usize) {
54            Some(_) => {
55                self.program_counter = line_number as usize;
56            }
57            None => Err(Error::UnknownLineNumber)?,
58        };
59
60        Ok(())
61    }
62
63    fn run_direct(&mut self, statement: &Statement) -> Result<(), Error> {
64        match statement {
65            Statement::Print { expression_list } => {
66                for element in expression_list {
67                    match element {
68                        ExpressionListElement::Expression(expression) => {
69                            let value = self.evaluate_expression(expression);
70                            write!(self.output, "{} ", value).unwrap();
71                        }
72                        ExpressionListElement::StringLiteral(string_literal) => {
73                            write!(
74                                self.output,
75                                "{} ",
76                                String::from_utf8_lossy(string_literal.value())
77                            )
78                            .unwrap();
79                        }
80                    }
81                }
82                writeln!(self.output).unwrap();
83            }
84            Statement::If {
85                left,
86                operator,
87                right,
88                then,
89            } => {
90                let left_value = self.evaluate_expression(left);
91                let right_value = self.evaluate_expression(right);
92
93                let condition = match operator {
94                    RelationalOperator::LessThan => left_value < right_value,
95                    RelationalOperator::LessThanOrEqual => left_value <= right_value,
96                    RelationalOperator::GreaterThan => left_value > right_value,
97                    RelationalOperator::GreaterThanOrEqual => left_value >= right_value,
98                    RelationalOperator::Equal => left_value == right_value,
99                    RelationalOperator::NotEqual => left_value != right_value,
100                };
101
102                if condition {
103                    self.run_direct(then)?;
104                }
105            }
106            Statement::Goto { expression } => {
107                let line_number = Self::to_line_number(self.evaluate_expression(expression))?;
108
109                self.jump(line_number)?;
110            }
111            Statement::Input { variable_list: _ } => {
112                todo!("implement input statement");
113            }
114            Statement::Let {
115                variable,
116                expression,
117            } => {
118                let value = self.evaluate_expression(expression);
119                self.store_variable(variable.identifier(), value);
120            }
121            Statement::GoSub { expression } => {
122                let line_number = Self::to_line_number(self.evaluate_expression(expression))?;
123
124                self.stack.push(self.program_counter);
125                self.jump(line_number)?;
126            }
127            Statement::Return => match self.stack.pop() {
128                Some(line_number) => self.program_counter = line_number,
129                None => {
130                    self.program_counter = self.storage.len();
131                }
132            },
133            Statement::Clear => {
134                self.storage = [const { None }; STORAGE_SIZE];
135            }
136            Statement::List => {
137                self.storage.iter().for_each(|line| {
138                    if let Some(line) = line {
139                        writeln!(self.output, "{}", line).unwrap();
140                    }
141                });
142            }
143            Statement::Run => {
144                self.program_counter = 0;
145                self.run_indirect()?;
146            }
147            Statement::End => {
148                self.program_counter = self.storage.len();
149            }
150        }
151
152        Ok(())
153    }
154
155    fn to_line_number(value: i16) -> Result<u8, Error> {
156        match u8::try_from(value) {
157            Ok(line_number) => Ok(line_number),
158            Err(_) => Err(Error::LineNumberOutOfRange)?,
159        }
160    }
161
162    fn run_indirect(&mut self) -> Result<(), Error> {
163        while self.program_counter < self.storage.len() {
164            let line = match &self.storage[self.program_counter] {
165                Some(line) => line,
166                None => {
167                    self.program_counter += 1;
168                    continue;
169                }
170            };
171
172            self.run_direct(&line.statement().clone())?;
173            self.program_counter += 1;
174        }
175
176        Ok(())
177    }
178
179    fn evaluate_expression(&self, expression: &Expression) -> i16 {
180        let term = expression.term();
181        let mut result = self.evaluate_term(term);
182
183        if let Some(AdditiveOperator::Subtraction) = expression.unary_operator() {
184            result = -result;
185        }
186
187        for (operator, term) in expression.others() {
188            let value = self.evaluate_term(term);
189
190            match operator {
191                AdditiveOperator::Addition => result += value,
192                AdditiveOperator::Subtraction => result -= value,
193            }
194        }
195
196        result
197    }
198
199    fn evaluate_term(&self, term: &Term) -> i16 {
200        let factor = term.factor();
201        let mut result = self.evaluate_factor(factor);
202
203        for (operator, factor) in term.operations() {
204            let value = self.evaluate_factor(factor);
205
206            match operator {
207                MultiplicativeOperator::Multiplication => result *= value,
208                MultiplicativeOperator::Division => result /= value,
209            }
210        }
211
212        result
213    }
214
215    fn evaluate_factor(&self, factor: &Factor) -> i16 {
216        match factor {
217            Factor::Variable(variable) => {
218                let identifier = variable.identifier();
219                self.load_variable(identifier)
220            }
221            Factor::NumberLiteral(number) => number.value(),
222            Factor::Expression(expression) => self.evaluate_expression(expression),
223        }
224    }
225
226    fn load_variable(&self, identifier: u8) -> i16 {
227        let offset = (identifier - b'A') as usize;
228        self.variables[offset]
229    }
230
231    fn store_variable(&mut self, identifier: u8, value: i16) {
232        let offset = (identifier - b'A') as usize;
233        self.variables[offset] = value;
234    }
235}