futilehdl/src/parser/proc.rs

110 lines
2.7 KiB
Rust

use nom::{
branch::alt,
bytes::complete::tag,
character::complete::char,
combinator::map,
error::context,
multi::{many1, separated_list0, separated_list1},
sequence::{delimited, tuple, separated_pair},
};
use crate::parser::{identifier, ws0, expression, Expression, IResult, Span, Assign, assign_statement};
#[derive(Debug)]
pub struct ProcBlock<'a> {
pub net: Span<'a>,
pub items: Vec<ProcStatement<'a>>,
}
#[derive(Debug)]
pub enum ProcStatement<'a> {
IfElse(IfElseBlock),
Assign(Assign<'a>),
Match(MatchBlock<'a>),
Block(Vec<ProcStatement<'a>>),
}
// 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: Span) -> IResult<Span, (Expression, ProcStatement)> {
separated_pair(ws0(expression), tag("=>"), ws0(proc_statement))(input)
}
fn match_block(input: Span) -> IResult<Span, MatchBlock> {
context(
"match block",
map(
tuple((
ws0(tag("match")),
ws0(delimited(char('('), ws0(expression), char(')'))),
ws0(delimited(char('{'), separated_list1(char(','), ws0(match_arm)), char('}'))),
)),
|(_, expr, arms)| MatchBlock {
expr,
arms
},
),
)(input)
}
fn statement_block(input: Span) -> IResult<Span, Vec<ProcStatement>> {
delimited(char('{'), separated_list1(char(';'), ws0(proc_statement)), char('}'))(input)
}
/// parse a statement that is valid inside a proc block
fn proc_statement(input: Span) -> IResult<Span, ProcStatement> {
alt((
map(match_block, ProcStatement::Match),
map(statement_block, ProcStatement::Block),
map(assign_statement, ProcStatement::Assign),
))(input)
}
pub fn proc_block(input: Span) -> IResult<Span, ProcBlock> {
context(
"proc block",
map(
tuple((
ws0(tag("proc")),
ws0(delimited(char('('), ws0(identifier), char(')'))),
ws0(delimited(char('{'), many1(ws0(proc_statement)), char('}'))),
)),
|(_, net, items)| ProcBlock { net, items },
),
)(input)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_statement() {
proc_statement(" abc = def ".into()).unwrap();
}
#[test]
fn test_match_arm() {
match_arm(" 1 => abc = def ".into()).unwrap();
}
#[test]
fn test_match_block() {
let input = "
match (asdf) {
1 => a = 0,
2 => c = ~d
}
";
match_block(input.into()).unwrap();
}
}