Compare commits

..

2 Commits

Author SHA1 Message Date
NotAFile 68be74d032 make multiple blocks in one file work 2022-02-23 23:36:01 +01:00
NotAFile 8c16a94be4 implement type inference for literals 2022-02-23 22:02:45 +01:00
5 changed files with 117 additions and 53 deletions

View File

@ -43,22 +43,9 @@ impl CompileError {
}
}
/// A user-defined signal
pub struct Signal {
/// 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,
// unique ID of the signal
// pub uid: u64,
}
impl Signal {
fn sigspec(&self) -> rtlil::SigSpec {
rtlil::SigSpec::Wire(self.il_id.to_owned())
}
#[derive(Debug)]
pub struct Module {
pub blocks: Vec<typed_ir::Block>,
}
pub struct Context {
@ -227,20 +214,20 @@ impl Context {
Ok(t_expr)
}
fn type_comb(
fn callable_from_block(
&mut self,
comb: &parser::comb::CombBlock,
) -> Result<typed_ir::Block, CompileError> {
let mut signals = Vec::new();
) -> Result<CallableId, CompileError> {
let mut genargs = vec![];
for (idx, tvarname) in comb.genparams.iter().enumerate() {
let tvar = self.types.make_typevar(0, idx as u32);
// hacky workaround for no scopes
self.typenames.insert(tvarname.name.to_string(), tvar);
genargs.push((Some(tvarname.name.to_string()), tvar));
}
let mut args = vec![];
for port in comb.ports.iter() {
let sig_id = self.ids.next();
let sig_typename = &port.net.typ;
let mut sig_type = self.try_get_type(sig_typename.name.fragment())?;
if let Some(arg) = &sig_typename.generics {
@ -252,38 +239,73 @@ impl Context {
.unwrap();
}
let sig = typed_ir::Signal {
id: typed_ir::DefId(sig_id as u32),
typ: sig_type,
};
signals.push(sig.clone());
self.signals.insert(port.net.name.to_string(), sig);
args.push((Some(port.net.name.to_string()), sig_type));
}
let ret_typename = &comb.ret.name;
let _ret_type = self.try_get_type(ret_typename.fragment())?;
// TODO: use ret type
let ret_type = self.try_get_type(ret_typename.fragment())?;
let signature = Callable {
name: comb.name.to_string(),
args,
genargs,
ret_type,
};
let signature_id = self.callables.add(signature);
self.callable_names
.insert(comb.name.to_string(), signature_id);
Ok(signature_id)
}
fn type_comb(
&mut self,
comb: &parser::comb::CombBlock,
) -> Result<typed_ir::Block, CompileError> {
let mut signals = Vec::new();
let callable_id = self.callable_from_block(comb)?;
let callable = self.callables.get(callable_id);
for (argname, typ) in &callable.args {
let sig_id = self.ids.next();
let sig = typed_ir::Signal {
id: typed_ir::DefId(sig_id as u32),
typ: *typ,
};
signals.push(sig.clone());
if let Some(argname) = argname {
self.signals.insert(argname.clone(), sig);
}
}
let root_expr = self.type_expression(&comb.expr)?;
Ok(typed_ir::Block {
signature: callable_id,
signals,
expr: root_expr,
})
}
pub fn type_module(&mut self, module: parser::Module) -> Result<typed_ir::Block, CompileError> {
pub fn type_module(&mut self, module: parser::Module) -> Result<Module, CompileError> {
let mut typed_module = Module { blocks: vec![] };
// hacky loop to predefine all names
for item in &module.items {
match &item {
parser::ModuleItem::Comb(comb) => self.callable_from_block(comb)?,
parser::ModuleItem::Proc(_) => todo!("proc block"),
parser::ModuleItem::State(_) => todo!("state block"),
};
}
for item in module.items {
let block = match &item {
parser::ModuleItem::Comb(comb) => self.type_comb(comb)?,
parser::ModuleItem::Proc(_) => todo!("proc block"),
parser::ModuleItem::State(_) => todo!("state block"),
};
return Ok(block);
typed_module.blocks.push(block);
}
Err(CompileError::new(CompileErrorKind::TodoError(
"no blocks in module".to_string(),
)))
Ok(typed_module)
}
pub fn infer_expr_types(&mut self, expr: &typed_ir::Expr) -> typed_ir::Expr {
@ -292,7 +314,7 @@ impl Context {
return expr.clone();
}
match &expr.kind {
typed_ir::ExprKind::Literal(_) => todo!(),
typed_ir::ExprKind::Literal(lit) => expr.clone().with_type(lit.typ),
// we can not see beyond this expression right now
typed_ir::ExprKind::Path(_) => expr.clone(),
typed_ir::ExprKind::Call {
@ -357,6 +379,35 @@ impl Context {
w: &mut dyn std::fmt::Write,
block: &typed_ir::Block,
) -> std::fmt::Result {
let callsig = self.callables.get(block.signature);
{
// TODO: ugly copy paste job
let args = callsig
.args
.iter()
.map(|(name, typ)| {
let mut out = String::new();
self.types.pretty_type(&mut out, *typ)?;
Ok(out)
})
.collect::<Result<Vec<String>, std::fmt::Error>>()?;
let genargs = callsig
.genargs
.iter()
.map(|(name, typ)| {
let mut type_str = String::new();
self.types.pretty_type(&mut type_str, *typ)?;
Ok(type_str)
})
.collect::<Result<Vec<String>, std::fmt::Error>>()?;
writeln!(
w,
"block {}<{}>({})",
callsig.name(),
genargs.join(", "),
args.join(", ")
)?;
}
for sig in &block.signals {
let mut typ_pretty = String::new();
self.types.pretty_type(&mut typ_pretty, sig.typ)?;
@ -372,7 +423,11 @@ impl Context {
expr: &typed_ir::Expr,
) -> std::fmt::Result {
let expr_pretty = match &expr.kind {
typed_ir::ExprKind::Literal(_) => todo!(),
typed_ir::ExprKind::Literal(lit) => {
let mut lit_str = String::new();
self.types.pretty_value(&mut lit_str, lit)?;
lit_str
}
typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0),
typed_ir::ExprKind::Call {
called,

View File

@ -3,6 +3,7 @@ use super::types::{Type, TypingContext};
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
pub struct CallableId(pub usize);
#[derive(Debug)]
pub struct Callable {
pub name: String,
pub args: Vec<(Option<String>, Type)>,

View File

@ -48,6 +48,7 @@ pub struct Signal {
/// A block of HDL code, e.g. comb block
#[derive(Debug, Clone)]
pub struct Block {
pub signature: CallableId,
pub signals: Vec<Signal>,
pub expr: Expr,
}

View File

@ -43,7 +43,7 @@ pub struct FnSig {
#[derive(Debug, Clone)]
pub struct ElabData {
typ: Type,
pub typ: Type,
value: ElabValue,
}

View File

@ -60,22 +60,26 @@ fn main() {
}
let mut frontendcontext = crate::frontend::Context::new();
let typed = frontendcontext.type_module(res.1);
if let Ok(block) = typed {
if opt.debug {
let mut pretty_block = String::new();
frontendcontext
.pretty_typed_block(&mut pretty_block, &block)
.unwrap();
println!("{}", &pretty_block);
}
let typed_inferred = frontendcontext.infer_types(block);
if opt.debug {
let mut pretty_block = String::new();
frontendcontext
.pretty_typed_block(&mut pretty_block, &typed_inferred)
.unwrap();
println!("{}", &pretty_block);
if let Ok(module) = typed {
for block in module.blocks {
if opt.debug {
let mut pretty_block = String::new();
frontendcontext
.pretty_typed_block(&mut pretty_block, &block)
.unwrap();
println!("{}", &pretty_block);
}
let typed_inferred = frontendcontext.infer_types(block);
if opt.debug {
let mut pretty_block = String::new();
frontendcontext
.pretty_typed_block(&mut pretty_block, &typed_inferred)
.unwrap();
println!("{}", &pretty_block);
}
}
// TODO: be able to determine modules that are fully parameterized
/*
let lowered = frontend::lowering::lower_block(&mut frontendcontext, typed_inferred);
match lowered {
Ok(res) => {
@ -87,6 +91,9 @@ fn main() {
}
Err(err) => eprintln!("{:#?}", err),
}
*/
} else {
eprintln!("{:#?}", typed);
}
}
}