diff --git a/src/frontend.rs b/src/frontend.rs index a237bd4..f9bc00e 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -3,6 +3,7 @@ use std::collections::BTreeMap; use std::fmt::Write; use super::parser; +use super::parser::block_expression::BlockExpr; use crate::rtlil; pub use callable::{Callable, CallableContext, CallableId}; pub use types::{Type, TypeStruct, TypingContext}; @@ -119,11 +120,8 @@ impl Context { match &expr.kind { typed_ir::ExprKind::Literal(lit) => Ok(lit.clone()), typed_ir::ExprKind::Path(_) => todo!("evaluate path"), - typed_ir::ExprKind::Call { - called, - args, - genargs, - } => todo!("evaluate call"), + typed_ir::ExprKind::Call(_call) => todo!("evaluate call"), + typed_ir::ExprKind::Match(_) => todo!(), } } @@ -144,10 +142,10 @@ impl Context { } Expression::Literal(lit) => { // 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) + let data = match lit.kind { + parser::expression::LiteralKind::Num(num) => self.types.make_elabnum_u32(num), + parser::expression::LiteralKind::Const(width, val) => { + self.types.make_const_u32(width, val as u32) } _ => unreachable!("non-literal token in literal?"), }; @@ -161,11 +159,11 @@ impl Context { let a = self.type_expression(&op.a)?; typed_ir::Expr { id, - kind: typed_ir::ExprKind::Call { + kind: typed_ir::ExprKind::Call(typed_ir::Call { called: self.callables.builtins.bitnot, args: vec![a], genargs: vec![], - }, + }), typ: self.types.primitives.infer, } } @@ -173,11 +171,11 @@ impl Context { let (a, b) = (self.type_expression(&op.a)?, self.type_expression(&op.b)?); typed_ir::Expr { id, - kind: typed_ir::ExprKind::Call { + kind: typed_ir::ExprKind::Call(typed_ir::Call { called: self.callables.builtins.xor, args: vec![a, b], genargs: vec![], - }, + }), typ: self.types.primitives.infer, } } @@ -202,14 +200,34 @@ impl Context { .collect(); typed_ir::Expr { id, - kind: typed_ir::ExprKind::Call { + kind: typed_ir::ExprKind::Call(typed_ir::Call { called, args: args_resolved, genargs: genargs_resolved, - }, + }), typ: self.types.primitives.infer, } } + Expression::BlockExpr(block) => match &**block { + BlockExpr::IfElse(_) => todo!(), + BlockExpr::Match(match_) => { + let expr = self.type_expression(&match_.expr)?; + let arms = match_ + .arms + .iter() + .map(|(cond, val)| { + Ok((self.type_expression(cond)?, self.type_expression(val)?)) + }) + .collect::>()?; + let typed = typed_ir::Match { expr, arms }; + typed_ir::Expr { + id, + kind: typed_ir::ExprKind::Match(Box::new(typed)), + typ: self.types.primitives.infer, + } + } + BlockExpr::Block(_) => todo!(), + }, }; Ok(t_expr) } @@ -317,13 +335,13 @@ impl Context { 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 { - called, - args, - genargs, - } => { - let args_typed: Vec<_> = args.iter().map(|ex| self.infer_expr_types(ex)).collect(); - let callee_def = self.callables.get(*called); + typed_ir::ExprKind::Call(call) => { + let args_typed: Vec<_> = call + .args + .iter() + .map(|ex| self.infer_expr_types(ex)) + .collect(); + let callee_def = self.callables.get(call.called); let param_types: Vec<_> = callee_def.args.iter().map(|param| param.1).collect(); let inferred_args: Vec<_> = param_types @@ -358,11 +376,27 @@ impl Context { let mut new_expr = expr.clone(); new_expr.typ = new_type; - new_expr.kind = typed_ir::ExprKind::Call { - called: called.clone(), + new_expr.kind = typed_ir::ExprKind::Call(typed_ir::Call { + called: call.called.clone(), args: args_typed, genargs, + }); + new_expr + } + typed_ir::ExprKind::Match(match_) => { + let new_arms: Vec<_> = match_ + .arms + .iter() + .map(|(pat, val)| (self.infer_expr_types(pat), self.infer_expr_types(val))) + .collect(); + // TODO: hacky hacky hacky + let res_type = new_arms.first().unwrap().1.typ; + let new_match = typed_ir::Match { + expr: self.infer_expr_types(&match_.expr), + arms: new_arms, }; + let mut new_expr = expr.clone().with_type(res_type); + new_expr.kind = typed_ir::ExprKind::Match(Box::new(new_match)); new_expr } } @@ -429,20 +463,18 @@ impl Context { lit_str } typed_ir::ExprKind::Path(path) => format!("sig_{}", path.0), - typed_ir::ExprKind::Call { - called, - args, - genargs, - } => { - let args = args + typed_ir::ExprKind::Call(call) => { + let args = call + .args .iter() .map(|arg| { self.pretty_typed_expr(w, arg)?; Ok(format!("_{}", arg.id.0)) }) .collect::, std::fmt::Error>>()?; - let callable = self.callables.get(*called); - let genargs = genargs + let callable = self.callables.get(call.called); + let genargs = call + .genargs .iter() .map(|param| { let mut type_str = String::new(); @@ -457,6 +489,23 @@ impl Context { args.join(", ") ) } + typed_ir::ExprKind::Match(match_) => { + self.pretty_typed_expr(w, &match_.expr)?; + let arms = match_ + .arms + .iter() + .map(|(pat, val)| { + self.pretty_typed_expr(w, pat)?; + self.pretty_typed_expr(w, val)?; + Ok(format!(" _{} => _{}", pat.id.0, val.id.0)) + }) + .collect::, _>>()?; + format!( + "match (_{}) {{\n{}\n}}", + &match_.expr.id.0, + arms.join(",\n") + ) + } }; let mut type_pretty = String::new(); self.types.pretty_type(&mut type_pretty, expr.typ)?; diff --git a/src/frontend/lowering.rs b/src/frontend/lowering.rs index 9a9b787..8fb1a61 100644 --- a/src/frontend/lowering.rs +++ b/src/frontend/lowering.rs @@ -17,22 +17,19 @@ fn lower_expression( module.add_wire(expr_wire); match &expr.kind { ExprKind::Path(def) => Ok(rtlil::SigSpec::Wire(format!("\\$sig_{}", def.0))), - ExprKind::Call { - called, - args, - genargs, - } => { - let args_resolved = args + ExprKind::Call(call) => { + let args_resolved = call + .args .iter() .map(|expr| lower_expression(ctx, module, expr)) .collect::, _>>()?; - let callable = ctx.callables.get(*called); + let callable = ctx.callables.get(call.called); let cell_id = module.make_genid(callable.name()); - if *called == ctx.callables.builtins.xor { - let a_width = ctx.types.get_width(args[0].typ).unwrap(); - let b_width = ctx.types.get_width(args[1].typ).unwrap(); + if call.called == ctx.callables.builtins.xor { + let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); + let b_width = ctx.types.get_width(call.args[1].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$xor"); cell.add_param("\\A_SIGNED", "0"); @@ -44,8 +41,8 @@ fn lower_expression( cell.add_connection("\\B", &args_resolved[1]); cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone())); module.add_cell(cell); - } else if *called == ctx.callables.builtins.reduce_or { - let a_width = ctx.types.get_width(args[0].typ).unwrap(); + } else if call.called == ctx.callables.builtins.reduce_or { + let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$reduce_or"); cell.add_param("\\A_SIGNED", "0"); @@ -54,8 +51,8 @@ fn lower_expression( cell.add_connection("\\A", &args_resolved[0]); cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone())); module.add_cell(cell); - } else if *called == ctx.callables.builtins.bitnot { - let a_width = ctx.types.get_width(args[0].typ).unwrap(); + } else if call.called == ctx.callables.builtins.bitnot { + let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$not"); cell.add_param("\\A_SIGNED", "0"); @@ -72,6 +69,7 @@ fn lower_expression( todo!(), ctx.types.get_width(expr.typ).expect("signal has no size"), )), + ExprKind::Match(_) => todo!(), } } diff --git a/src/frontend/typed_ir.rs b/src/frontend/typed_ir.rs index e177d87..95d0f51 100644 --- a/src/frontend/typed_ir.rs +++ b/src/frontend/typed_ir.rs @@ -28,15 +28,25 @@ pub struct Expr { pub typ: Type, } +#[derive(Debug, Clone)] +pub struct Call { + pub called: CallableId, + pub args: Vec, + pub genargs: Vec, +} + +#[derive(Debug, Clone)] +pub struct Match { + pub expr: Expr, + pub arms: Vec<(Expr, Expr)>, +} + #[derive(Debug, Clone)] pub enum ExprKind { Literal(ElabData), Path(DefId), - Call { - called: CallableId, - args: Vec, - genargs: Vec, - }, + Call(Call), + Match(Box), } #[derive(Debug, Clone)] diff --git a/src/frontend/types.rs b/src/frontend/types.rs index 0948983..602c3de 100644 --- a/src/frontend/types.rs +++ b/src/frontend/types.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::fmt::Debug; /// Alias for &TypeStruct to reduce repetition /// and make futura migration to interning @@ -13,12 +14,12 @@ impl Debug for InternedType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TypeStruct { kind: TypeKind, } -#[derive(Debug)] +#[derive(Debug, Clone)] enum TypeKind { /// Elaboration-time types ElabType(ElabKind), @@ -62,7 +63,7 @@ enum ElabValueData { } /// Types that are only valid during Elaboration -#[derive(Debug)] +#[derive(Debug, Clone)] enum ElabKind { /// general, unsized number type Num, @@ -95,7 +96,7 @@ pub struct PrimitiveTypes { } pub struct TypingContext { - types: Vec, + types: RefCell>, pub primitives: PrimitiveTypes, } @@ -107,7 +108,7 @@ impl TypingContext { infer: InternedType(2), }; Self { - types: vec![ + types: RefCell::new(vec![ TypeStruct { kind: TypeKind::ElabType(ElabKind::Num), }, @@ -120,19 +121,19 @@ impl TypingContext { TypeStruct { kind: TypeKind::Infer, }, - ], + ]), primitives, } } - pub fn add(&mut self, typ: TypeStruct) -> Type { - let id = self.types.len(); - self.types.push(typ); + pub fn add(&self, typ: TypeStruct) -> Type { + let id = self.types.borrow().len(); + self.types.borrow_mut().push(typ); InternedType(id) } - pub fn get(&self, typ: Type) -> &TypeStruct { - &self.types[typ.0] + pub fn get(&self, typ: Type) -> TypeStruct { + self.types.borrow()[typ.0].clone() } pub fn make_elabnum_u32(&self, num: u32) -> ElabData { @@ -142,7 +143,14 @@ impl TypingContext { } } - pub fn make_logic_size(&mut self, width: u32) -> Type { + pub fn make_const_u32(&self, width: u32, num: u32) -> ElabData { + ElabData { + typ: self.make_logic_size(width), + value: ElabValue::Concrete(ElabValueData::U32(num)), + } + } + + pub fn make_logic_size(&self, width: u32) -> Type { let widthnum = self.make_elabnum_u32(width); self.parameterize(self.primitives.logic, &[GenericArg::Elab(widthnum)]) .unwrap() @@ -171,7 +179,7 @@ impl TypingContext { } } - pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option { + pub fn parameterize(&self, typ: Type, params: &[GenericArg]) -> Option { // TODO: return proper error type here match &self.get(typ).kind { // Elab types have no params yet diff --git a/src/parser/block_expression.rs b/src/parser/block_expression.rs new file mode 100644 index 0000000..cc3bc09 --- /dev/null +++ b/src/parser/block_expression.rs @@ -0,0 +1,57 @@ +use nom::{ + branch::alt, + combinator::map, + error::context, + multi::{many1, separated_list1}, + sequence::{delimited, separated_pair, tuple}, +}; + +use crate::parser::{ + assign_statement, expression, + expression::{expression, Expression}, + tokens::{token, TokenKind as tk, TokenSpan}, + Assign, IResult, Span, +}; + +#[derive(Debug, Clone)] +pub enum BlockExpr<'a> { + IfElse(IfElseBlock), + Match(MatchBlock<'a>), + Block(Vec>), +} + +// TODO: postponed because annoying to implement +#[derive(Debug, Clone)] +pub struct IfElseBlock {} + +#[derive(Debug, Clone)] +pub struct MatchBlock<'a> { + pub expr: Expression<'a>, + pub arms: Vec<(Expression<'a>, Expression<'a>)>, +} + +fn match_arm(input: TokenSpan) -> IResult { + separated_pair(expression, token(tk::FatArrow), expression)(input) +} + +fn match_block(input: TokenSpan) -> IResult { + context( + "match block", + map( + tuple(( + token(tk::Match), + delimited(token(tk::LParen), expression, token(tk::RParen)), + delimited( + token(tk::LBrace), + separated_list1(token(tk::Comma), match_arm), + token(tk::RBrace), + ), + )), + |(_, expr, arms)| MatchBlock { expr, arms }, + ), + )(input) +} + +pub fn block_expr(input: TokenSpan) -> IResult { + alt((map(match_block, BlockExpr::Match),))(input) +} diff --git a/src/parser/expression.rs b/src/parser/expression.rs index 77d48b6..5408544 100644 --- a/src/parser/expression.rs +++ b/src/parser/expression.rs @@ -1,3 +1,5 @@ +use super::block_expression::{block_expr, BlockExpr}; +use super::literals; use super::tokens::{token, Token, TokenKind as tk, TokenSpan}; use super::{IResult, Span}; use nom::{ @@ -7,6 +9,18 @@ use nom::{ sequence::{delimited, preceded, separated_pair, tuple}, }; +#[derive(Debug, Clone)] +pub struct Literal<'a> { + pub tok: Token<'a>, + pub kind: LiteralKind, +} + +#[derive(Debug, Clone)] +pub enum LiteralKind { + Num(u32), + Const(u32, u64), +} + #[derive(Debug, Clone)] pub struct Call<'a> { pub name: Span<'a>, @@ -42,10 +56,11 @@ pub struct UnOp<'a> { #[derive(Debug, Clone)] pub enum Expression<'a> { Path(&'a str), - Literal(Token<'a>), + Literal(Literal<'a>), UnOp(Box>), BinOp(Box>), Call(Box>), + BlockExpr(Box>), } /// expressions that can't be subdivided further @@ -55,7 +70,19 @@ fn atom(input: TokenSpan) -> IResult { map(token(tk::Ident), |it| { Expression::Path(it.span().fragment()) }), - map(token(tk::Number), Expression::Literal), + map(token(tk::Number), |tok| { + Expression::Literal(Literal { + tok, + kind: LiteralKind::Num(tok.span().parse().expect("invalid literal")), + }) + }), + map(token(tk::Constant), |tok| { + let (_, (width, val)) = literals::const_bits(tok.span()).expect("invalid literal"); + Expression::Literal(Literal { + tok, + kind: LiteralKind::Const(width, val), + }) + }), delimited(token(tk::LParen), expression, token(tk::RParen)), ))(input) } @@ -119,11 +146,11 @@ pub fn call_item(input: TokenSpan) -> IResult { )(input) } -/// parser combinators can not parse left-recursive grammars. To work around this, we split -/// expressions into a recursive and non-recursive portion. -/// Parsers reachable from this point must call expression_nonrecurse instead pub fn expression(input: TokenSpan) -> IResult { - bitop(input) + alt(( + bitop, + map(block_expr, |blk| Expression::BlockExpr(Box::new(blk))), + ))(input) } #[cfg(test)] diff --git a/src/parser/literals.rs b/src/parser/literals.rs index 13301a4..89273b2 100644 --- a/src/parser/literals.rs +++ b/src/parser/literals.rs @@ -1,15 +1,27 @@ use nom::{ branch::alt, bytes::complete::tag, - character::complete::{alpha1, alphanumeric1, char, multispace0, one_of}, + character::complete::{alpha1, alphanumeric1, char, digit1, multispace0, one_of}, combinator::{map, recognize}, error::ParseError, multi::{many0, many1}, - sequence::{delimited, pair, preceded, terminated}, + sequence::{delimited, pair, preceded, separated_pair, terminated}, }; use crate::parser::{IResult, Span}; +pub fn const_bits(input: Span) -> IResult { + map( + separated_pair(digit1, char('\''), digit1), + |(width, value): (Span, Span)| { + ( + width.fragment().parse().expect("error parsing literal"), + value.fragment().parse().expect("error parsing literal"), + ) + }, + )(input) +} + pub fn hexadecimal(input: Span) -> IResult { map( preceded( diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d1ece5f..f154915 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,5 @@ pub mod adt; +pub mod block_expression; pub mod comb; pub mod declaration; pub mod error; diff --git a/src/parser/proc.rs b/src/parser/proc.rs index 1008f09..7bfc1e1 100644 --- a/src/parser/proc.rs +++ b/src/parser/proc.rs @@ -3,12 +3,11 @@ use nom::{ combinator::map, error::context, multi::{many1, separated_list1}, - sequence::{delimited, separated_pair, tuple}, + sequence::{delimited, tuple}, }; use crate::parser::{ - assign_statement, expression, - expression::{expression, Expression}, + assign_statement, tokens::{token, TokenKind as tk, TokenSpan}, Assign, IResult, Span, }; @@ -21,42 +20,7 @@ pub struct ProcBlock<'a> { #[derive(Debug)] pub enum ProcStatement<'a> { - IfElse(IfElseBlock), Assign(Assign<'a>), - Match(MatchBlock<'a>), - Block(Vec>), -} - -// TODO: postponed because annoying to implement -#[derive(Debug)] -pub struct IfElseBlock {} - -#[derive(Debug)] -pub struct MatchBlock<'a> { - pub expr: Expression<'a>, - pub arms: Vec<(Expression<'a>, ProcStatement<'a>)>, -} - -fn match_arm(input: TokenSpan) -> IResult { - separated_pair(expression, token(tk::FatArrow), proc_statement)(input) -} - -fn match_block(input: TokenSpan) -> IResult { - context( - "match block", - map( - tuple(( - token(tk::Match), - delimited(token(tk::LParen), expression, token(tk::RParen)), - delimited( - token(tk::LBrace), - separated_list1(token(tk::Comma), match_arm), - token(tk::RBrace), - ), - )), - |(_, expr, arms)| MatchBlock { expr, arms }, - ), - )(input) } fn statement_block(input: TokenSpan) -> IResult> { @@ -69,11 +33,7 @@ fn statement_block(input: TokenSpan) -> IResult> { /// parse a statement that is valid inside a proc block fn proc_statement(input: TokenSpan) -> IResult { - alt(( - map(match_block, ProcStatement::Match), - map(statement_block, ProcStatement::Block), - map(assign_statement, ProcStatement::Assign), - ))(input) + alt((map(assign_statement, ProcStatement::Assign),))(input) } pub fn proc_block(input: TokenSpan) -> IResult { diff --git a/src/parser/tokens.rs b/src/parser/tokens.rs index 59734c1..ecf048d 100644 --- a/src/parser/tokens.rs +++ b/src/parser/tokens.rs @@ -2,7 +2,7 @@ use super::{ error::{Error, InputPos}, - literals::{identifier, ws0}, + literals::{const_bits, identifier, ws0}, IResult, Span, }; use nom::{ @@ -77,6 +77,7 @@ pub enum TokenKind { // Literals Ident, Number, + Constant, // Keywords Module, Assign, @@ -186,6 +187,7 @@ fn lex_braces(input: Span) -> IResult { fn lex_literals(input: Span) -> IResult { map( consumed(alt(( + map(const_bits, |_| TokenKind::Constant), map(identifier, |_| TokenKind::Ident), map(digit1, |_| TokenKind::Number), ))),