expr_solver/
ir.rs

1use crate::ast::{BinOp, Expr, ExprKind, UnOp};
2use crate::program::Program;
3use crate::span::Span;
4use crate::symbol::Symbol;
5use rust_decimal::Decimal;
6use thiserror::Error;
7
8/// IR building errors.
9#[derive(Error, Debug, Clone)]
10pub enum IrError {
11    #[error("Undefined symbol {0}")]
12    UndefinedSymbol(String, Span),
13}
14
15#[derive(Debug, Clone)]
16pub enum Instr<'sym> {
17    Push(Decimal),
18    Load(&'sym Symbol),
19    Neg,
20    Add,
21    Sub,
22    Mul,
23    Div,
24    Pow,
25    Fact,
26    Call(&'sym Symbol, usize), // Symbol and argument count
27    // Comparison operators
28    Equal,
29    NotEqual,
30    Less,
31    LessEqual,
32    Greater,
33    GreaterEqual,
34}
35
36/// Builder for converting AST expressions into bytecode programs.
37pub struct IrBuilder<'sym> {
38    prog: Program<'sym>,
39}
40
41impl<'src, 'sym> IrBuilder<'sym> {
42    /// Creates a new IR builder.
43    pub fn new() -> Self {
44        Self {
45            prog: Program::new(),
46        }
47    }
48
49    /// Builds a bytecode program from an AST expression.
50    pub fn build(mut self, expr: &Expr<'src, 'sym>) -> Result<Program<'sym>, IrError> {
51        self.emit(expr)?;
52        Ok(self.prog)
53    }
54
55    fn emit(&mut self, e: &Expr<'src, 'sym>) -> Result<(), IrError> {
56        match &e.kind {
57            ExprKind::Literal(v) => {
58                self.prog.code.push(Instr::Push(*v));
59            }
60            ExprKind::Ident { name, sym } => {
61                if sym.is_none() {
62                    return Err(IrError::UndefinedSymbol(name.to_string(), e.span));
63                }
64                self.prog.code.push(Instr::Load(sym.unwrap()));
65            }
66            ExprKind::Unary { op, expr } => {
67                self.emit(expr)?;
68                match op {
69                    UnOp::Neg => self.prog.code.push(Instr::Neg),
70                    UnOp::Fact => self.prog.code.push(Instr::Fact),
71                }
72            }
73            ExprKind::Binary { op, left, right } => {
74                self.emit(left)?;
75                self.emit(right)?;
76                self.prog.code.push(match op {
77                    BinOp::Add => Instr::Add,
78                    BinOp::Sub => Instr::Sub,
79                    BinOp::Mul => Instr::Mul,
80                    BinOp::Div => Instr::Div,
81                    BinOp::Pow => Instr::Pow,
82                    BinOp::Equal => Instr::Equal,
83                    BinOp::NotEqual => Instr::NotEqual,
84                    BinOp::Less => Instr::Less,
85                    BinOp::LessEqual => Instr::LessEqual,
86                    BinOp::Greater => Instr::Greater,
87                    BinOp::GreaterEqual => Instr::GreaterEqual,
88                });
89            }
90            ExprKind::Call { name, args, sym } => {
91                if sym.is_none() {
92                    return Err(IrError::UndefinedSymbol(name.to_string(), e.span));
93                }
94                for a in args.iter() {
95                    self.emit(a)?;
96                }
97                self.prog.code.push(Instr::Call(sym.unwrap(), args.len()));
98            }
99        }
100        Ok(())
101    }
102}