add simple const generic eval

This commit is contained in:
NotAFile 2022-02-16 16:37:12 +01:00
parent 0daa49874e
commit 853021e4f8
3 changed files with 103 additions and 13 deletions

View File

@ -121,6 +121,14 @@ impl Context {
}) })
} }
fn eval_expression(&self, expr: &typed_ir::Expr) -> Result<types::ElabData, CompileError> {
match &expr.kind {
typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()),
typed_ir::ExprKind::Path(_) => todo!("evaluate path"),
typed_ir::ExprKind::Call { called, args } => todo!("evaluate call"),
}
}
fn type_expression( fn type_expression(
&self, &self,
expr: &parser::expression::Expression, expr: &parser::expression::Expression,
@ -136,8 +144,32 @@ impl Context {
typ: signal.typ, typ: signal.typ,
} }
} }
Expression::Literal(_) => todo!("type literal"), Expression::Literal(lit) => {
Expression::UnOp(op) => self.type_expression(&op.a)?, // TODO: make this a proper enum instead of having to match on everything
let data = match lit.kind() {
parser::tokens::TokenKind::Number => {
let num = lit.span().fragment().parse().unwrap();
self.types.make_elabnum_u32(num)
}
_ => unreachable!("non-literal token in literal?"),
};
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Literal(data),
typ: self.types.primitives.infer,
}
}
Expression::UnOp(op) => {
let a = self.type_expression(&op.a)?;
typed_ir::Expr {
id,
kind: typed_ir::ExprKind::Call {
called: typed_ir::DefId(99),
args: vec![a],
},
typ: self.types.primitives.infer,
}
}
Expression::BinOp(op) => { Expression::BinOp(op) => {
let (a, b) = (self.type_expression(&op.a)?, self.type_expression(&op.b)?); let (a, b) = (self.type_expression(&op.a)?, self.type_expression(&op.b)?);
typed_ir::Expr { typed_ir::Expr {
@ -176,8 +208,18 @@ impl Context {
for port in comb.ports.iter() { for port in comb.ports.iter() {
let sig_id = self.ids.next(); let sig_id = self.ids.next();
let sig_typename = &port.net.typ; let sig_typename = &port.net.typ;
let sig_type = self.try_get_type(sig_typename.name.fragment())?; let mut sig_type = self.try_get_type(sig_typename.name.fragment())?;
if let Some(arg) = &sig_typename.generics {
let elab_expr = self.type_expression(arg)?;
let elab_val = self.eval_expression(&elab_expr)?;
sig_type = self
.types
.parameterize(sig_type, &[types::GenericArg::Elab(elab_val)])
.unwrap();
}
let sig = typed_ir::Signal { let sig = typed_ir::Signal {
id: typed_ir::DefId(sig_id as u32), id: typed_ir::DefId(sig_id as u32),
typ: sig_type, typ: sig_type,
@ -220,7 +262,7 @@ impl Context {
for sig in &block.signals { for sig in &block.signals {
let mut typ_pretty = String::new(); let mut typ_pretty = String::new();
self.types.pretty_type(&mut typ_pretty, sig.typ)?; self.types.pretty_type(&mut typ_pretty, sig.typ)?;
writeln!(w, "_{}: {}", sig.id.0, typ_pretty)? writeln!(w, "sig_{}: {}", sig.id.0, typ_pretty)?
} }
self.pretty_typed_expr(w, &block.expr)?; self.pretty_typed_expr(w, &block.expr)?;
Ok(()) Ok(())
@ -232,8 +274,8 @@ impl Context {
expr: &typed_ir::Expr, expr: &typed_ir::Expr,
) -> std::fmt::Result { ) -> std::fmt::Result {
let expr_pretty = match &expr.kind { let expr_pretty = match &expr.kind {
typed_ir::ExprKind::Literal => todo!(), typed_ir::ExprKind::Literal(_) => todo!(),
typed_ir::ExprKind::Path(path) => format!("_{}", path.0), typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0),
typed_ir::ExprKind::Call { called, args } => { typed_ir::ExprKind::Call { called, args } => {
let args = args let args = args
.iter() .iter()

View File

@ -1,4 +1,4 @@
use super::types::Type; use super::types::{ElabData, Type};
use std::fmt::Debug; use std::fmt::Debug;
/// ID of a definition (e.g. variable, block, function) /// ID of a definition (e.g. variable, block, function)
@ -29,7 +29,7 @@ pub struct Expr {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ExprKind { pub enum ExprKind {
Literal, Literal(ElabData),
Path(DefId), Path(DefId),
Call { called: DefId, args: Vec<Expr> }, Call { called: DefId, args: Vec<Expr> },
} }

View File

@ -32,13 +32,13 @@ enum TypeKind {
Infer, Infer,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ElabData { pub struct ElabData {
typ: Type, typ: Type,
value: ElabValue, value: ElabValue,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
enum ElabValue { enum ElabValue {
/// the value is not given and has to be inferred /// the value is not given and has to be inferred
Infer, Infer,
@ -46,7 +46,7 @@ enum ElabValue {
Concrete(ElabValueData), Concrete(ElabValueData),
} }
#[derive(Debug)] #[derive(Debug, Clone)]
enum ElabValueData { enum ElabValueData {
U32(u32), U32(u32),
Bytes(Vec<u8>), Bytes(Vec<u8>),
@ -59,6 +59,12 @@ enum ElabKind {
Num, Num,
} }
#[derive(Debug)]
pub enum GenericArg {
Elab(ElabData),
Type(Type),
}
pub struct PrimitiveTypes { pub struct PrimitiveTypes {
pub elabnum: Type, pub elabnum: Type,
pub logic: Type, pub logic: Type,
@ -106,10 +112,52 @@ impl TypingContext {
&self.types[typ.0] &self.types[typ.0]
} }
pub fn make_elabnum_u32(&self, num: u32) -> ElabData {
ElabData {
typ: self.primitives.elabnum,
value: ElabValue::Concrete(ElabValueData::U32(num)),
}
}
pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option<Type> {
// TODO: return proper error type here
match &self.get(typ).kind {
// Elab types have no params yet
TypeKind::ElabType(_) => None,
TypeKind::Logic(_) => {
if params.len() != 1 {
// invalid number of typeargs
return None;
};
let param = &params[0];
if let GenericArg::Elab(data) = param {
if data.typ == self.primitives.elabnum {
Some(self.add(TypeStruct {
kind: TypeKind::Logic(data.clone()),
}))
} else {
// arg must be elabnum
None
}
} else {
// arg must be an elab value
None
}
}
TypeKind::UInt(_) => todo!(),
TypeKind::Callable => todo!("callable generic params"),
// need to know what the type is to parameterize it
TypeKind::Infer => None,
}
}
pub fn pretty_value(&self, w: &mut dyn std::fmt::Write, data: &ElabData) -> std::fmt::Result { pub fn pretty_value(&self, w: &mut dyn std::fmt::Write, data: &ElabData) -> std::fmt::Result {
match data.value { match &data.value {
ElabValue::Infer => write!(w, "?: ")?, ElabValue::Infer => write!(w, "?: ")?,
ElabValue::Concrete(_) => todo!("concrete type value"), ElabValue::Concrete(val) => match val {
ElabValueData::U32(val) => write!(w, "{:?}: ", val)?,
ElabValueData::Bytes(val) => write!(w, "{:?}: ", val)?,
},
} }
self.pretty_type(w, data.typ) self.pretty_type(w, data.typ)
} }