diff --git a/doc/examples/clockdiv.fut b/doc/examples/clockdiv.fut index 7601376..40370a5 100644 --- a/doc/examples/clockdiv.fut +++ b/doc/examples/clockdiv.fut @@ -1,4 +1,4 @@ -module clockdiv_2 ( +proc clockdiv_2 ( clk: Logic, rst: Logic ) -> Logic diff --git a/doc/examples/comparator.fut b/doc/examples/comparator.fut index 11a205b..2044af5 100644 --- a/doc/examples/comparator.fut +++ b/doc/examples/comparator.fut @@ -1,7 +1,7 @@ -module comparator ( +comb comparator ( a: Logic<8>, b: Logic<8> ) -> Logic { - assign eq = ~reduce_or(a ^ b); + ~reduce_or(a ^ b) } diff --git a/doc/examples/halfadd.fut b/doc/examples/halfadd.fut index e633ea8..de25d94 100644 --- a/doc/examples/halfadd.fut +++ b/doc/examples/halfadd.fut @@ -1,8 +1,12 @@ -module halfadd ( +struct Add { + sum: Logic, + carry: Logic +} + +comb halfadd ( a: Logic, b: Logic -) -> (Logic, Logic) +) -> Add { - assign sum = a ^ b; - assign carry = a & b; + Add{sum: a ^ b, carry: a & b} } diff --git a/lib/builtins/main.hyd b/lib/builtins/main.hyd index 3b23c26..fc1b56b 100644 --- a/lib/builtins/main.hyd +++ b/lib/builtins/main.hyd @@ -1,6 +1,6 @@ -module reduce_or ( +comb reduce_or ( a: Logic ) -> Logic<1> { diff --git a/src/frontend.rs b/src/frontend.rs index ad1ec7d..2346c2e 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use crate::builtin_cells::get_builtins; use crate::parser; +use crate::parser::expression::Expression; use crate::rtlil; use crate::rtlil::RtlilWrite; pub use callable::Callable; @@ -169,70 +170,59 @@ fn lower_process( Ok(()) } -fn desugar_operation<'a>(op: parser::Operation<'a>) -> parser::Call<'a> { - match op { - parser::Operation::And { a, b } => { - let a = desugar_expression(a); - let b = desugar_expression(b); - parser::Call { - name: "and".into(), - args: vec![a, b], - } - } - parser::Operation::Or { a, b } => { - let a = desugar_expression(a); - let b = desugar_expression(b); - parser::Call { - name: "or".into(), - args: vec![a, b], - } - } - parser::Operation::Xor { a, b } => { - let a = desugar_expression(a); - let b = desugar_expression(b); - parser::Call { - name: "xor".into(), - args: vec![a, b], - } - } - parser::Operation::Not(a) => { - let a = desugar_expression(a); - parser::Call { - name: "not".into(), - args: vec![a], - } - } +fn desugar_binop<'a>(op: parser::expression::BinOp<'a>) -> parser::expression::Call<'a> { + let a = desugar_expression(op.a); + let b = desugar_expression(op.b); + let op_func = match op.kind { + parser::expression::BinOpKind::And => "and", + parser::expression::BinOpKind::Or => "or", + parser::expression::BinOpKind::Xor => "xor", + }; + parser::expression::Call { + name: op_func.into(), + args: vec![a, b], } } -fn desugar_expression<'a>(expr: parser::Expression<'a>) -> parser::Expression<'a> { +fn desugar_unop<'a>(op: parser::expression::UnOp<'a>) -> parser::expression::Call<'a> { + let a = desugar_expression(op.a); + let op_func = match op.kind { + parser::expression::UnOpKind::BitNot => "not", + parser::expression::UnOpKind::Not => todo!(), + }; + parser::expression::Call { + name: op_func.into(), + args: vec![a], + } +} + +fn desugar_expression<'a>(expr: Expression<'a>) -> Expression<'a> { // TODO: allow ergonomic traversal of AST match expr { - parser::Expression::Ident(_) => expr, - parser::Expression::Literal(_) => expr, - parser::Expression::Call(mut call) => { + Expression::Path(_) => expr, + Expression::Literal(_) => expr, + Expression::Call(mut call) => { let new_args = call.args.into_iter().map(desugar_expression).collect(); call.args = new_args; - parser::Expression::Call(call) - } - parser::Expression::Operation(op) => { - parser::Expression::Call(Box::new(desugar_operation(*op))) + Expression::Call(call) } + Expression::BinOp(op) => Expression::Call(Box::new(desugar_binop(*op))), + Expression::UnOp(op) => Expression::Call(Box::new(desugar_unop(*op))), } } fn lower_expression( ctx: &Context, module: &mut rtlil::Module, - expr: &parser::Expression, + expr: &Expression, ) -> Result { let expr = desugar_expression(expr.clone()); match expr { - parser::Expression::Ident(ident) => { + Expression::Path(ident) => { let signal = ctx.try_get_signal(ident)?; Ok(signal.sigspec()) } - parser::Expression::Call(call) => { + Expression::Call(call) => { let args_resolved = call .args .iter() @@ -268,8 +258,12 @@ fn lower_expression( } // TODO: instantiate operators directly here instead of desugaring, once the callable infrastructure improves // to get better errors - parser::Expression::Operation(_op) => todo!("operators not yet implemented"), - parser::Expression::Literal(lit) => Ok(rtlil::SigSpec::Const(lit as i64, TODO_WIDTH)), + Expression::Literal(lit) => Ok(rtlil::SigSpec::Const( + lit.span().fragment().parse().unwrap(), + TODO_WIDTH, + )), + Expression::UnOp(_) => todo!(), + Expression::BinOp(_) => todo!(), } } @@ -284,6 +278,39 @@ fn lower_assignment( Ok(()) } +fn lower_comb( + ctx: &mut Context, + module: &mut rtlil::Module, + pa_comb: parser::comb::CombBlock, +) -> Result<(), CompileError> { + for (num, port) in pa_comb.ports.iter().enumerate() { + let port_id = make_pubid(port.net.name.fragment()); + module.add_wire(rtlil::Wire::new( + port_id.clone(), + TODO_WIDTH, + Some(rtlil::PortOption::Input((num + 1) as i32)), + )); + let typ = TypeStruct::logic_width(TODO_WIDTH); + let signal = Signal { + name: port.net.name.fragment().to_string(), + il_id: port_id, + // TODO: CRIMES CRIMES CRIMES + typ: Box::leak(Box::new(typ)), + }; + ctx.signals + .insert(port.net.name.fragment().to_string(), signal); + } + let ret_id = module.make_genid("ret"); + module.add_wire(rtlil::Wire::new( + ret_id.clone(), + TODO_WIDTH, + Some(rtlil::PortOption::Input(99)), + )); + let out_sig = lower_expression(ctx, module, &pa_comb.expr)?; + module.add_connection(&rtlil::SigSpec::Wire(ret_id), &out_sig); + Ok(()) +} + pub fn lower_module(pa_module: parser::Module) -> Result { let mut writer = rtlil::ILWriter::new(); let mut ir_module = rtlil::Module::new(make_pubid("test")); @@ -297,38 +324,13 @@ pub fn lower_module(pa_module: parser::Module) -> Result { }; writer.write_line("autoidx 1"); - /* - for (idx, port) in pa_module.ports.iter().enumerate() { - // FIXME: Actually resolve types - let sigtype = TypeStruct::logic_width(TODO_WIDTH); - // FIXME: CRIMES CRIMES CRIMES - let sigtype = Box::leak(Box::new(sigtype)); - let sig = Signal { - name: port.net.name.fragment().to_string(), - il_id: make_pubid(port.net.name.fragment()), - typ: sigtype, - }; - let sig = context - .signals - .entry(port.net.name.fragment().to_string()) - .or_insert(sig); - - let dir_option = match port.direction { - parser::PortDirection::Input => rtlil::PortOption::Input(idx as i32 + 1), - parser::PortDirection::Output => rtlil::PortOption::Output(idx as i32 + 1), - }; - let wire = rtlil::Wire::new(sig.il_id.to_owned(), TODO_WIDTH, Some(dir_option)); - ir_module.add_wire(wire); - } for item in pa_module.items { match item { - parser::ModuleItem::Assign(assignment) => { - lower_assignment(&context, &mut ir_module, assignment)? - } - parser::ModuleItem::Proc(proc) => lower_process(&context, &mut ir_module, &proc)?, + parser::ModuleItem::Comb(comb) => lower_comb(&mut context, &mut ir_module, comb)?, + parser::ModuleItem::Proc(_) => todo!(), + parser::ModuleItem::State(_) => todo!(), } } ir_module.write_rtlil(&mut writer); - */ Ok(writer.finish()) } diff --git a/src/parser/comb.rs b/src/parser/comb.rs index 5d06dae..2cdb6da 100644 --- a/src/parser/comb.rs +++ b/src/parser/comb.rs @@ -1,4 +1,6 @@ use super::{ + declaration::TypeName, + expression::{expression, Expression}, module::inputs_list, module::PortDecl, tokens::{token, TokenKind as tk, TokenSpan}, @@ -16,6 +18,8 @@ use nom::{ pub struct CombBlock<'a> { pub name: Span<'a>, pub ports: Vec>, + pub ret: TypeName<'a>, + pub expr: Expression<'a>, } pub fn comb_block(input: TokenSpan) -> IResult { @@ -26,17 +30,15 @@ pub fn comb_block(input: TokenSpan) -> IResult { token(tk::Ident), delimited(token(tk::LParen), inputs_list, token(tk::RParen)), preceded(token(tk::RArrow), typename), - delimited( - token(tk::LBrace), - many0(token(tk::Error)), - token(tk::RBrace), - ), + delimited(token(tk::LBrace), expression, token(tk::RBrace)), ))), ), - |(name, inputs, _ret, _items)| CombBlock { + |(name, inputs, ret, expr)| CombBlock { // TODO: bring back returns name: name.span(), ports: inputs, + ret, + expr, }, )(input) } diff --git a/src/parser/expression.rs b/src/parser/expression.rs index 14de774..f64939a 100644 --- a/src/parser/expression.rs +++ b/src/parser/expression.rs @@ -1,4 +1,4 @@ -use super::tokens::{token, TokenKind as tk, TokenSpan}; +use super::tokens::{token, Token, TokenKind as tk, TokenSpan}; use super::{IResult, Span}; use nom::{ branch::alt, @@ -7,48 +7,95 @@ use nom::{ sequence::{delimited, preceded, separated_pair, tuple}, }; -#[derive(Debug, Clone)] -pub enum Operation<'a> { - And { - a: Expression<'a>, - b: Expression<'a>, - }, - Or { - a: Expression<'a>, - b: Expression<'a>, - }, - Xor { - a: Expression<'a>, - b: Expression<'a>, - }, - Not(Expression<'a>), -} - -pub fn operation(input: TokenSpan) -> IResult { - // temporarily given up on before I learn the shunting yard algorithm - alt(( - map( - separated_pair(expression_nonrecurse, token(tk::BitAnd), expression), - |(a, b)| Operation::And { a, b }, - ), - map( - separated_pair(expression_nonrecurse, token(tk::BitOr), expression), - |(a, b)| Operation::Or { a, b }, - ), - map( - separated_pair(expression_nonrecurse, token(tk::BitXor), expression), - |(a, b)| Operation::Xor { a, b }, - ), - map(preceded(token(tk::BitNot), expression), Operation::Not), - ))(input) -} - #[derive(Debug, Clone)] pub struct Call<'a> { pub name: Span<'a>, pub args: Vec>, } +#[derive(Debug, Clone)] +pub enum BinOpKind { + And, + Or, + Xor, +} + +#[derive(Debug, Clone)] +pub enum UnOpKind { + Not, + BitNot, +} + +#[derive(Debug, Clone)] +pub struct BinOp<'a> { + pub a: Expression<'a>, + pub b: Expression<'a>, + pub kind: BinOpKind, +} + +#[derive(Debug, Clone)] +pub struct UnOp<'a> { + pub a: Expression<'a>, + pub kind: UnOpKind, +} + +#[derive(Debug, Clone)] +pub enum Expression<'a> { + Path(&'a str), + Literal(Token<'a>), + UnOp(Box>), + BinOp(Box>), + Call(Box>), +} + +/// expressions that can't be subdivided further +fn atom(input: TokenSpan) -> IResult { + alt(( + map(call_item, |call| Expression::Call(Box::new(call))), + map(token(tk::Ident), |it| { + Expression::Path(it.span().fragment()) + }), + map(token(tk::Number), Expression::Literal), + delimited(token(tk::LParen), expression, token(tk::RParen)), + ))(input) +} + +fn unop_kind(input: TokenSpan) -> IResult { + alt(( + map(token(tk::Not), |_| UnOpKind::Not), + map(token(tk::BitNot), |_| UnOpKind::BitNot), + ))(input) +} + +/// unary expressions e.g `~expr`, `!expr` +fn unary(input: TokenSpan) -> IResult { + alt(( + map(tuple((unop_kind, unary)), |(kind, expr)| { + Expression::UnOp(Box::new(UnOp { a: expr, kind })) + }), + atom, + ))(input) +} + +fn bitop_kind(input: TokenSpan) -> IResult { + alt(( + map(token(tk::BitXor), |_| BinOpKind::Xor), + map(token(tk::BitOr), |_| BinOpKind::Xor), + map(token(tk::BitAnd), |_| BinOpKind::Xor), + ))(input) +} + +/// bit and, or, xor e.g. a ^ b & c +/// TODO: make precedence rules for bit ops +fn bitop(input: TokenSpan) -> IResult { + alt(( + map(tuple((unary, bitop_kind, bitop)), |(a, kind, b)| { + Expression::BinOp(Box::new(BinOp { a, b, kind })) + }), + unary, + ))(input) +} + pub fn call_item(input: TokenSpan) -> IResult { map( tuple(( @@ -66,32 +113,54 @@ pub fn call_item(input: TokenSpan) -> IResult { )(input) } -#[derive(Debug, Clone)] -pub enum Expression<'a> { - Ident(&'a str), - Literal(u64), - Call(Box>), - Operation(Box>), -} - /// 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 { - alt(( - map(operation, |op| Expression::Operation(Box::new(op))), - expression_nonrecurse, - ))(input) + bitop(input) } -/// the portion of the expression grammar that can be parsed without left recursion -fn expression_nonrecurse(input: TokenSpan) -> IResult { - alt(( - map(token(tk::Number), |_| Expression::Literal(42)), - map(call_item, |call| Expression::Call(Box::new(call))), - map(token(tk::Ident), |ident| { - Expression::Ident(*ident.span().fragment()) - }), - delimited(token(tk::LParen), expression, token(tk::RParen)), - ))(input) +#[cfg(test)] +mod test { + use super::*; + use crate::parser::tokens::{lex, Token, TokenSpan}; + use crate::parser::Span; + use nom::combinator::all_consuming; + + fn tok(input: &'static str) -> Vec { + let input = Span::from(input); + lex(input).unwrap().1 + } + + fn fullexpr(input: TokenSpan) -> IResult { + all_consuming(expression)(input) + } + + #[test] + fn test_atoms() { + fullexpr(TokenSpan::new(&tok("a"))).unwrap(); + fullexpr(TokenSpan::new(&tok("(a)"))).unwrap(); + } + + #[test] + fn test_unary() { + fullexpr(TokenSpan::new(&tok("asdf"))).unwrap(); + fullexpr(TokenSpan::new(&tok("~(asdf)"))).unwrap(); + fullexpr(TokenSpan::new(&tok("!asdf"))).unwrap(); + // unary(TokenSpan::new(&tok("~!(asdf)"))).unwrap(); + } + + #[test] + fn test_bitop() { + fullexpr(TokenSpan::new(&tok("a | b"))).unwrap(); + fullexpr(TokenSpan::new(&tok("a ^ b"))).unwrap(); + fullexpr(TokenSpan::new(&tok("~(a ^ b)"))).unwrap(); + fullexpr(TokenSpan::new(&tok("a ^ !b"))).unwrap(); + fullexpr(TokenSpan::new(&tok("a & !b & c"))).unwrap(); + } + + #[test] + fn test_call() { + fullexpr(TokenSpan::new(&tok("a()"))).unwrap(); + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 79d8343..d1ece5f 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -20,7 +20,6 @@ pub type IResult> = nom::IResult; pub use crate::parser::declaration::{ assign_statement, declaration, typename, Assign, NetDecl, TypeName, }; -pub use crate::parser::expression::{expression, Call, Expression, Operation}; pub use crate::parser::module::{module, Module, ModuleItem, PortDirection}; use crate::parser::tokens::TokenSpan; use nom::combinator::all_consuming; diff --git a/src/parser/proc.rs b/src/parser/proc.rs index 7f52de4..1008f09 100644 --- a/src/parser/proc.rs +++ b/src/parser/proc.rs @@ -8,8 +8,9 @@ use nom::{ use crate::parser::{ assign_statement, expression, + expression::{expression, Expression}, tokens::{token, TokenKind as tk, TokenSpan}, - Assign, Expression, IResult, Span, + Assign, IResult, Span, }; #[derive(Debug)] diff --git a/src/parser/tokens.rs b/src/parser/tokens.rs index b496ec7..59734c1 100644 --- a/src/parser/tokens.rs +++ b/src/parser/tokens.rs @@ -70,6 +70,7 @@ pub enum TokenKind { BitOr, BitXor, EqAssign, + Not, // Multi Chars FatArrow, RArrow, @@ -204,6 +205,7 @@ fn lex_punctuation(input: Span) -> IResult { map(tag("&"), |_| TokenKind::BitAnd), map(tag("^"), |_| TokenKind::BitXor), map(tag("|"), |_| TokenKind::BitOr), + map(tag("!"), |_| TokenKind::Not), map(tag("="), |_| TokenKind::EqAssign), ))), |(span, kind)| Token::new(span, kind),