use std::borrow::Borrow; use std::cell::Cell; use std::collections::BTreeMap; use super::parser; use crate::rtlil; pub use callable::Callable; pub use types::{make_primitives, Type, TypeStruct}; mod callable; #[cfg(never)] pub mod lowering; pub mod typed_ir; pub mod types; use crate::builtin_cells::get_builtins; // pub use lowering::lower_module; /// lots of code is still not width-aware, this constant keeps track of that const TODO_WIDTH: u32 = 1; fn make_pubid(id: &str) -> String { "\\".to_owned() + id } #[derive(Debug)] pub enum CompileErrorKind { UndefinedReference(String), BadArgCount { received: usize, expected: usize }, TodoError(String), } #[derive(Debug)] pub struct CompileError { kind: CompileErrorKind, } impl CompileError { fn new(kind: CompileErrorKind) -> Self { Self { kind } } } /// A user-defined signal pub struct Signal<'ctx> { /// the user-visible name of the signal pub name: String, /// the id of the signal in RTLIL pub il_id: String, /// the type of the signal pub typ: Type<'ctx>, // unique ID of the signal // pub uid: u64, } impl<'ctx> Signal<'ctx> { fn sigspec(&self) -> rtlil::SigSpec { rtlil::SigSpec::Wire(self.il_id.to_owned()) } } pub struct Context<'ctx> { /// map callable name to callable callables: BTreeMap>, /// types types: BTreeMap>, /// map signal name to Signal signals: BTreeMap>, /// incrementing counter for unique IDs ids: Counter, } struct Counter(Cell); impl Counter { fn new() -> Counter { Counter(Cell::new(0)) } fn next(&self) -> usize { let next = self.0.get() + 1; self.0.set(next); next } } impl<'ctx> Context<'ctx> { pub fn new() -> Self { Context { callables: get_builtins() .into_iter() .map(|clb| (clb.name().to_owned(), clb)) .collect(), signals: BTreeMap::new(), types: make_primitives().into_iter().collect(), ids: Counter::new(), } } fn get_signal(&self, signame: &str) -> Option<&typed_ir::Signal> { self.signals.get(signame) } fn try_get_signal(&self, signame: &str) -> Result<&typed_ir::Signal, CompileError> { self.get_signal(signame).ok_or_else(|| { CompileError::new(CompileErrorKind::UndefinedReference(signame.to_owned())) }) } fn try_get_type(&'ctx self, typename: &str) -> Result, CompileError> { self.types.get(typename).ok_or_else(|| { CompileError::new(CompileErrorKind::UndefinedReference(typename.to_owned())) }) } fn type_expression( &self, expr: &parser::expression::Expression, ) -> Result { let typ = match expr { parser::expression::Expression::Path(name) => self.try_get_signal(name)?.typ, parser::expression::Expression::Literal(_) => todo!(), parser::expression::Expression::UnOp(op) => self.type_expression(&op.a)?.typ, parser::expression::Expression::BinOp(_) => todo!(), parser::expression::Expression::Call(call) => todo!(), }; Ok(typed_ir::Expr { id: 0, inputs: vec![], typ: typ, }) } fn type_comb( &mut self, comb: &parser::comb::CombBlock, ) -> Result { let mut signals = Vec::new(); for port in comb.ports.iter() { let sig_id = self.ids.next(); let sig_typename = &port.net.typ; let sig_type = self.try_get_type(sig_typename.name.fragment())?; let sig = typed_ir::Signal { id: sig_id as u32, typ: sig_type, }; signals.push(sig); self.signals.insert(port.net.name.to_string(), sig); } let ret_typename = &comb.ret.name; let ret_type = self.try_get_type(&ret_typename.fragment())?; let root_expr = self.type_expression(&comb.expr)?; Ok(typed_ir::Block { signals, expr: root_expr, }) } pub fn type_module( &'ctx mut self, module: parser::Module, ) -> Result, CompileError> { for item in module.items { let block = match &item { parser::ModuleItem::Comb(comb) => self.type_comb(comb)?, parser::ModuleItem::Proc(_) => todo!(), parser::ModuleItem::State(_) => todo!(), }; return Ok(block); } Err(CompileError::new(CompileErrorKind::TodoError( "no blocks in module".to_string(), ))) } }