maolang_core/
interp.rs

1//! Core interpetter
2
3use std::{
4    collections::HashMap,
5    error::Error,
6    fmt::Display,
7    ops::{Add, Div, Mul, Sub},
8};
9
10use crate::parser::ast::{BinaryOp, Expr, Literal, UnaryOp};
11
12/// An AST interpretter
13#[derive(Debug, Default, Clone)]
14pub struct Interpretter<'a> {
15    /// Local variables
16    context: HashMap<String, Literal<'a>>,
17}
18
19/// An error during execution
20#[derive(Debug, Clone, Copy)]
21pub struct RuntimeError;
22
23impl Display for RuntimeError {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(f, "Runtime Error")
26    }
27}
28
29impl Error for RuntimeError {}
30
31impl<'a> Interpretter<'a> {
32    /// Interprets an AST
33    pub fn eval(&mut self, ast: Expr<'a>) -> Result<Literal<'a>, RuntimeError> {
34        match ast {
35            Expr::Variable(var) => Ok(self.context[var]),
36            Expr::Print(node) => {
37                println!("{}", self.eval(*node)?);
38                Ok(Literal::Null)
39            }
40            Expr::Assignment(name, val) => {
41                let val = self.eval(*val)?;
42                self.context.insert(name.to_string(), val);
43                Ok(Literal::Null)
44            }
45            Expr::Literal(l) => Ok(l),
46            Expr::Grouping(inner) => self.eval(*inner),
47            Expr::Binary { op, left, right } => op.eval(self.eval(*left)?, self.eval(*right)?),
48            Expr::Unary { op, node } => op.eval(self.eval(*node)?),
49        }
50    }
51}
52
53impl BinaryOp {
54    /// Evaluates a binary operation
55    pub fn eval<'a>(
56        &self,
57        left: Literal<'a>,
58        right: Literal<'a>,
59    ) -> Result<Literal<'a>, RuntimeError> {
60        match self {
61            Self::Add => left + right,
62            Self::Sub => left - right,
63            Self::Mul => left * right,
64            Self::Div => left / right,
65
66            Self::Gt => Ok(Literal::Bool(left.number()? > right.number()?)),
67            Self::Gte => Ok(Literal::Bool(left.number()? >= right.number()?)),
68            Self::Lt => Ok(Literal::Bool(left.number()? < right.number()?)),
69            Self::Lte => Ok(Literal::Bool(left.number()? <= right.number()?)),
70
71            Self::Eq => left.equals(&right),
72            Self::Neq => left.not_equals(&right),
73        }
74    }
75}
76
77impl UnaryOp {
78    /// Evaluates a unary operation
79    pub fn eval<'a>(&self, node: Literal<'a>) -> Result<Literal<'a>, RuntimeError> {
80        match self {
81            Self::Neg => Ok(Literal::Number(-node.number()?)),
82            Self::Not => Ok(Literal::Bool(!node.bool()?)),
83        }
84    }
85}
86
87impl Add for Literal<'_> {
88    type Output = Result<Self, RuntimeError>;
89
90    fn add(self, rhs: Self) -> Self::Output {
91        match (self, rhs) {
92            (Self::Number(n1), Self::Number(n2)) => Ok(Literal::Number(n1 + n2)),
93
94            _ => Err(RuntimeError),
95        }
96    }
97}
98
99impl Sub for Literal<'_> {
100    type Output = Result<Self, RuntimeError>;
101    fn sub(self, rhs: Self) -> Self::Output {
102        match (self, rhs) {
103            (Self::Number(n1), Self::Number(n2)) => Ok(Self::Number(n1 - n2)),
104
105            _ => Err(RuntimeError),
106        }
107    }
108}
109
110impl Mul for Literal<'_> {
111    type Output = Result<Self, RuntimeError>;
112    fn mul(self, rhs: Self) -> Self::Output {
113        match (self, rhs) {
114            (Self::Number(n1), Self::Number(n2)) => Ok(Self::Number(n1 * n2)),
115
116            _ => Err(RuntimeError),
117        }
118    }
119}
120
121impl Div for Literal<'_> {
122    type Output = Result<Self, RuntimeError>;
123    fn div(self, rhs: Self) -> Self::Output {
124        match (self, rhs) {
125            (Self::Number(n1), Self::Number(n2)) => Ok(Self::Number(n1 / n2)),
126            _ => Err(RuntimeError),
127        }
128    }
129}
130
131impl<'a> Literal<'a> {
132    /// Returns the inner literal if it's a proper unsigned integer truly, asserting a runtime
133    /// error if not
134    pub fn uint(&self) -> Result<usize, RuntimeError> {
135        match self {
136            Self::Number(n) if *n >= 0.0 && n.round() == *n => Ok(*n as usize),
137            _ => Err(RuntimeError),
138        }
139    }
140    /// Returns the inner literal if it's numeric, asserting a runtime error if not
141    pub fn number(&self) -> Result<f64, RuntimeError> {
142        match self {
143            Self::Number(n) => Ok(*n),
144            _ => Err(RuntimeError),
145        }
146    }
147
148    /// Returns the inner literal if it's boolean, asserting a runtime error if not
149    pub fn bool(&self) -> Result<bool, RuntimeError> {
150        match self {
151            Self::Bool(val) => Ok(*val),
152            Self::Number(0.0) => Ok(false),
153            Self::Number(_) => Ok(true),
154            _ => Err(RuntimeError),
155        }
156    }
157
158    /// Returns the True literal if the two literals are losely equal
159    pub fn equals(&self, other: &Self) -> Result<Literal<'a>, RuntimeError> {
160        match (self, other) {
161            (Self::Number(n1), Self::Number(n2)) => Ok(Self::Bool(n1 == n2)),
162            (Self::Bool(b1), Self::Bool(b2)) => Ok(Self::Bool(b1 == b2)),
163
164            (Self::Null, Self::Null) => Ok(Self::Bool(true)),
165
166            (crazy1, crazy2) => Ok(Literal::Bool(crazy1.to_string() == crazy2.to_string())),
167        }
168    }
169
170    /// Returns the True literal if the two literals are not losely equal
171    pub fn not_equals(&self, other: &Self) -> Result<Literal<'a>, RuntimeError> {
172        match (self, other) {
173            (Self::Number(n1), Self::Number(n2)) => Ok(Self::Bool(n1 != n2)),
174            (Self::Bool(b1), Self::Bool(b2)) => Ok(Self::Bool(b1 != b2)),
175
176            (Self::Null, Self::Null) => Ok(Self::Bool(false)),
177
178            (crazy1, crazy2) => Ok(Literal::Bool(crazy1.to_string() != crazy2.to_string())),
179        }
180    }
181}