Skip to main content

unitdc/interpreter/
mod.rs

1use std::{
2    collections::HashMap,
3    io::{BufReader, Read},
4};
5
6use crate::{
7    quantity::{
8        units::{UnitCombo, UnitSystem},
9        Quantity, QuantityError,
10    },
11    tokenizer::{token::Token, ReaderCursor, Tokenizer, TokenizerError},
12};
13
14use thiserror::Error;
15
16/// All other operations.
17pub mod ops;
18/// Macro operations.
19pub mod ops_macros;
20/// Variable I/O operations.
21pub mod ops_variables;
22
23pub struct Interpreter<'a> {
24    variables: HashMap<String, Quantity>,
25    unit_system: UnitSystem,
26    stack: Vec<Quantity>,
27    output: Box<dyn Fn(Output) + 'a>,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum Output {
32    Quantity(Quantity),
33    QuantityList(Vec<Quantity>),
34    Message(String),
35}
36
37#[derive(Debug, Error)]
38pub enum InterpreterError {
39    #[error("Tokenizer error: {1} at {0}")]
40    TokenizerError(ReaderCursor, TokenizerError),
41    #[error("Quantity error: {0}")]
42    QuantityError(QuantityError),
43    #[error("Stack underflow")]
44    StackUnderflow,
45    #[error("Undefined unit: {0}")]
46    UndefinedUnit(String),
47    #[error("Undefined macro: {0}")]
48    UndefinedMacro(String),
49    #[error("Undefined variable: {0}")]
50    UndefinedVariable(String),
51    #[error("Incompatible units: {0}")]
52    IncompatibleUnits(UnitCombo),
53    #[error("No solution: {0}")]
54    NoSolution(String),
55    #[error("Already defined: {0}")]
56    AlreadyDefined(String),
57}
58
59pub type InterpreterResult<T> = Result<T, InterpreterError>;
60
61impl<'a> Interpreter<'a> {
62    pub fn new(output: Box<dyn Fn(Output) + 'a>) -> Self {
63        Self {
64            variables: HashMap::new(),
65            unit_system: UnitSystem::new(),
66            stack: Vec::new(),
67            output,
68        }
69    }
70    /// Reads all tokens from the tokenizer and processes them.
71    pub fn process_tokens<R: Read>(
72        &mut self,
73        tokenizer: &mut Tokenizer<R>,
74    ) -> InterpreterResult<()> {
75        while let Some(token) = tokenizer
76            .parse_next_token()
77            .map_err(|e| InterpreterError::TokenizerError(tokenizer.get_cursor(), e))?
78        {
79            match token {
80                Token::Number(n) => self.op_number(n)?,
81                Token::Unit(u) => self.op_unit(&u)?,
82                Token::Add => self.op_add()?,
83                Token::Sub => self.op_sub()?,
84                Token::Mul => self.op_mul()?,
85                Token::Div => self.op_div()?,
86                Token::Operator('p') => self.op_p()?,
87                Token::Operator('n') => self.op_n()?,
88                Token::Operator('f') => self.op_f()?,
89                Token::Operator('c') => self.op_c()?,
90                Token::Operator('d') => self.op_d()?,
91                Token::Operator('r') => self.op_r()?,
92                Token::Operator('s') => self.op_s()?,
93                Token::Operator('U') => self.op_upper_u()?,
94                Token::VarRecall(name) => self.op_recall(&name)?,
95                Token::VarStore(name) => self.op_store(&name)?,
96                Token::MacroInvoke((name, args)) => match name.as_str() {
97                    "base" => self.op_macro_baseunit(&args)?,
98                    "derived" => self.op_macro_derivedunit(&args)?,
99                    _ => return Err(InterpreterError::UndefinedMacro(name)),
100                },
101                Token::Comment(_) => {}
102                _ => eprintln!("Unhandled token: {:?}", token),
103            }
104        }
105
106        Ok(())
107    }
108    pub fn run_str(&mut self, input: &str) -> InterpreterResult<()> {
109        let mut tokenizer = Tokenizer::new(BufReader::new(input.as_bytes()));
110        self.process_tokens(&mut tokenizer)?;
111
112        Ok(())
113    }
114}