use glyph_types::Value;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct IRModule {
pub program: IRProgram,
pub functions: HashMap<String, IRFunction>,
pub entry_point: Option<String>,
}
#[derive(Debug, Clone)]
pub struct IRProgram {
pub name: String,
pub version: String,
pub requires: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct IRFunction {
pub name: String,
pub params: Vec<String>,
pub locals_count: usize,
pub blocks: Vec<IRBlock>,
pub is_async: bool,
}
#[derive(Debug, Clone)]
pub struct IRBlock {
pub label: String,
pub instructions: Vec<IRInstruction>,
pub terminator: IRTerminator,
}
#[derive(Debug, Clone)]
pub enum IRInstruction {
LoadConst(Value),
LoadVar(String),
StoreVar(String),
BinaryOp(BinaryOp),
UnaryOp(UnaryOp),
Call { func: String, args_count: usize },
CallIntrinsic { name: String, args_count: usize },
MakeList(usize),
MakeDict(usize),
GetAttr(String),
GetItem,
CallMethod { name: String, argc: usize },
Await,
Dup,
Pop,
}
#[derive(Debug, Clone)]
pub enum IRTerminator {
Return,
Jump(String),
JumpIf {
then_block: String,
else_block: String,
},
Match {
cases: Vec<(IRPattern, String)>,
default: Option<String>,
},
}
#[derive(Debug, Clone)]
pub enum IRPattern {
Literal(Value),
Variable(String),
Constructor { name: String, args: Vec<IRPattern> },
Wildcard,
}
#[derive(Debug, Clone, Copy)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
}
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Neg,
Not,
}
pub struct IRBuilder {
pub module: IRModule,
current_function: Option<String>,
current_block: Option<String>,
var_counter: usize,
block_counter: usize,
block_terminated: bool,
}
impl IRBuilder {
pub fn new(name: String, version: String, requires: Vec<String>) -> Self {
Self {
module: IRModule {
program: IRProgram {
name,
version,
requires,
},
functions: HashMap::new(),
entry_point: None,
},
current_function: None,
current_block: None,
var_counter: 0,
block_counter: 0,
block_terminated: false,
}
}
pub fn fresh_var(&mut self) -> String {
let var = format!("_t{}", self.var_counter);
self.var_counter += 1;
var
}
pub fn fresh_label(&mut self) -> String {
let label = format!("L{}", self.block_counter);
self.block_counter += 1;
label
}
pub fn start_function(&mut self, name: String, params: Vec<String>, is_async: bool) {
let entry_label = self.fresh_label();
let func = IRFunction {
name: name.clone(),
params,
locals_count: 0,
blocks: vec![IRBlock {
label: entry_label.clone(),
instructions: vec![],
terminator: IRTerminator::Return,
}],
is_async,
};
self.module.functions.insert(name.clone(), func);
self.current_function = Some(name);
self.current_block = Some(entry_label);
self.block_terminated = false;
}
pub fn emit(&mut self, inst: IRInstruction) {
if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
{
if let Some(func) = self.module.functions.get_mut(func_name) {
if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
block.instructions.push(inst);
}
}
}
}
pub fn terminate(&mut self, term: IRTerminator) {
if let (Some(func_name), Some(block_label)) = (&self.current_function, &self.current_block)
{
if let Some(func) = self.module.functions.get_mut(func_name) {
if let Some(block) = func.blocks.iter_mut().find(|b| b.label == *block_label) {
block.terminator = term;
self.block_terminated = true;
}
}
}
}
pub fn new_block(&mut self, label: String) {
if let Some(func_name) = &self.current_function {
if let Some(func) = self.module.functions.get_mut(func_name) {
func.blocks.push(IRBlock {
label: label.clone(),
instructions: vec![],
terminator: IRTerminator::Return,
});
self.current_block = Some(label);
self.block_terminated = false; }
}
}
pub fn is_terminated(&self) -> bool {
self.block_terminated
}
pub fn finish(mut self) -> IRModule {
if self.module.functions.contains_key("main") {
self.module.entry_point = Some("main".to_string());
}
self.module
}
}