diff --git a/src/frontend.rs b/src/frontend.rs index b190c26..a90de27 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -178,6 +178,7 @@ fn lower_expression( module.add_cell(cell); Ok(output_gen_wire) } + // operations should really just desugar to callables parser::Expression::Operation(_op) => todo!("operators not yet implemented"), parser::Expression::Literal(lit) => { Ok(rtlil::SigSpec::Const(*lit as i64, TODO_WIDTH)) diff --git a/src/parser.rs b/src/parser.rs index ccbe4e6..0b3d80f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -61,8 +61,8 @@ pub struct Assign<'a> { #[derive(Debug)] pub enum Operation<'a> { - And { a: String, b: Expression<'a> }, - Or { a: String, b: Expression<'a> }, + And { a: Expression<'a>, b: Expression<'a> }, + Or { a: Expression<'a>, b: Expression<'a> }, Not(Expression<'a>), } @@ -100,16 +100,16 @@ fn operation(input: Span) -> IResult { // temporarily given up on before I learn the shunting yard algorithm alt(( map( - separated_pair(ws0(identifier), char('&'), ws0(expression)), + separated_pair(ws0(expression_nonrecurse), char('&'), ws0(expression)), |(a, b)| Operation::And { - a: (*a.fragment()).into(), + a, b, }, ), map( - separated_pair(ws0(identifier), char('|'), ws0(expression)), + separated_pair(ws0(expression_nonrecurse), char('|'), ws0(expression)), |(a, b)| Operation::Or { - a: (*a.fragment()).into(), + a, b, }, ), @@ -134,14 +134,25 @@ fn call_item(input: Span) -> 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 fn expression(input: Span) -> IResult { alt(( - map(ws0(decimal), |lit| Expression::Literal(lit)), map(ws0(operation), |op| Expression::Operation(Box::new(op))), + expression_nonrecurse + ))(input) +} + +/// the portion of the expression grammar that can be parsed without left recursion +fn expression_nonrecurse(input: Span) -> IResult { + alt(( + map(ws0(decimal), |lit| Expression::Literal(lit)), map(ws0(call_item), |call| Expression::Call(Box::new(call))), map(ws0(identifier), |ident| { Expression::Ident(*ident.fragment()) }), + delimited(char('('), expression, char(')')), ))(input) }