rtlola-streamir 0.1.0

A framework for the compilation of stream-based languages through an intermediate representation
Documentation
use std::{collections::HashMap, time::Duration};

use disjoint::DisjointSet;
use winnow::{
    ascii::{dec_int, dec_uint, multispace0, multispace1},
    combinator::{alt, cut_err, delimited, opt, preceded, separated, seq},
    error::{ParserError, StrContext},
    stream::{AsChar, Stream, StreamIsPartial},
    Parser,
};

use super::{
    expressions::{Constant, Expr, ExprKind, Operator},
    memory::{Memory, Parameter, StreamBuffer, StreamMemory},
    Guard, LivetimeEquivalences, LocalFreq, OutputReference, Stmt, StreamIr, StreamReference, Type,
};

fn iws<Input, Output, Error, ParseNext>(parser: ParseNext) -> impl Parser<Input, Output, Error>
where
    Input: Stream + StreamIsPartial,
    <Input as Stream>::Token: AsChar + Clone,
    ParseNext: Parser<Input, Output, Error>,
    Error: ParserError<Input>,
{
    delimited(multispace0, parser, multispace0)
}

fn parse_stmt_prime(input: &mut &str) -> winnow::ModalResult<Stmt> {
    iws(alt((
        preceded(
            iws("seq"),
            cut_err(delimited("{", separated(.., parse_stmt_prime, iws(";")), (opt(iws(";")), "}"))),
        )
        .context(StrContext::Label("seq"))
        .map(Stmt::Seq),
        preceded(
            iws("par"),
            cut_err(delimited("{", separated(.., parse_stmt_prime, iws(";")), (opt(iws(";")), "}"))),
        ).context(StrContext::Label("parallel"))
        .map(Stmt::Parallel),
        preceded("if", cut_err(seq!(_: multispace1, parse_guard, _: "then", parse_stmt_prime, opt(seq!(_: "else", parse_stmt_prime)), _: "fi"))).context(StrContext::Label("if")).map(
            |(guard, cons, alt)| {
                Stmt::If(super::IfStmt {
                    guard,
                    cons: Box::new(cons),
                    alt: Box::new(alt.unwrap_or((Stmt::Skip,)).0),
                })
            },
        ),
        preceded("input", cut_err(seq!(_: multispace1, dec_int))).context(StrContext::Label("input")).map(|(i,): (i32,)| Stmt::Input(i as usize)),
        preceded("eval", cut_err(seq!(_: multispace1, dec_int, _: multispace1, parse_expr))).context(StrContext::Label("eval")).map(|(i, expr): (i32,_)| Stmt::Eval {
            sr: OutputReference::Unparameterized(i as usize),
            with: expr,
            idx: 0
        }),
        preceded("iterate", cut_err(seq!(_: multispace1, dec_int, _: multispace1, parse_stmt_prime))).context(StrContext::Label("iterate")).map(|(i,stmt): (i32,_)| Stmt::Iterate {
            sr: vec![OutputReference::Unparameterized(i as usize)],
            stmt: Box::new(stmt)
        }),
        preceded("assign", cut_err(seq!(_: multispace1, dec_int, _: multispace1, delimited("(", separated(.., parse_expr, ","), ")"), _: multispace1, parse_stmt_prime))).context(StrContext::Label("assign")).map(|(i,expr, stmt): (i32,_, _)| Stmt::Assign {
            sr: vec![OutputReference::Unparameterized(i as usize)],
            parameter_expr: expr,
            stmt: Box::new(stmt)
        }),
    )))
    .parse_next(input)
}

pub(crate) fn parse_guard(input: &mut &str) -> winnow::ModalResult<Guard> {
    let lhs = parse_guard_term.parse_next(input)?;
    let op = opt(iws(alt(("&&", "||")))).parse_next(input)?;

    if let Some(op) = op {
        let rhs = parse_guard.parse_next(input)?;
        match op {
            "&&" => Ok(Guard::And {
                lhs: Box::new(lhs),
                rhs: Box::new(rhs),
            }),
            "||" => Ok(Guard::Or {
                lhs: Box::new(lhs),
                rhs: Box::new(rhs),
            }),
            _ => unreachable!(),
        }
    } else {
        Ok(lhs)
    }
}

fn parse_guard_term(input: &mut &str) -> winnow::ModalResult<Guard> {
    iws(alt((
        "true".value(Guard::Constant(true)),
        "false".value(Guard::Constant(false)),
        preceded("@", dec_int).map(|i: i32| Guard::Stream(StreamReference::In(i as usize))),
        preceded("?", dec_int).map(|i: i32| Guard::Alive(StreamReference::In(i as usize))),
        delimited("Global(", dec_uint, ")").map(|i| Guard::GlobalFreq(Duration::new(i, 0))),
        delimited("Local(", dec_uint, ")").map(Guard::LocalFreq),
        delimited(
            "FastAnd(",
            separated(.., dec_uint::<_, u32, _>, iws(",")),
            ")",
        )
        .map(|i: Vec<_>| {
            Guard::FastAnd(
                i.into_iter()
                    .map(|i| StreamReference::In(i as usize))
                    .collect(),
            )
        }),
        delimited("FastOr(", separated(.., dec_uint::<_, u32, _>, ","), ")").map(|i: Vec<_>| {
            Guard::FastOr(
                i.into_iter()
                    .map(|i| StreamReference::In(i as usize))
                    .collect(),
            )
        }),
        delimited("Expr(", cut_err(parse_expr), ")")
            .context(StrContext::Label("expr guard"))
            .map(Guard::Dynamic),
        delimited("(", parse_guard, ")"),
    )))
    .parse_next(input)
}

fn parse_expr(input: &mut &str) -> winnow::ModalResult<Expr> {
    let lhs = parse_expr_term.parse_next(input)?;
    let op = opt(iws(alt(("&&", "||", "==")))).parse_next(input)?;

    if let Some(op) = op {
        let rhs = parse_expr.parse_next(input)?;
        match op {
            "&&" => Ok(Expr {
                ty: Type::Bool,
                kind: ExprKind::BinaryOperation(Operator::And, Box::new(lhs), Box::new(rhs)),
            }),
            "||" => Ok(Expr {
                ty: Type::Bool,
                kind: ExprKind::BinaryOperation(Operator::Or, Box::new(lhs), Box::new(rhs)),
            }),
            "==" => Ok(Expr {
                ty: Type::Bool,
                kind: ExprKind::BinaryOperation(Operator::Eq, Box::new(lhs), Box::new(rhs)),
            }),
            _ => unreachable!(),
        }
    } else {
        Ok(lhs)
    }
}

fn parse_expr_term(input: &mut &str) -> winnow::ModalResult<Expr> {
    iws(alt((
        parse_bool,
        seq!(_: "p", dec_uint).map(|(p,): (u64,)| Expr {
            ty: Type::Bool,
            kind: ExprKind::ParameterAccess(StreamReference::In(0), p as usize),
        }),
        seq!(_: alt(("s", "o")), dec_uint).map(|(p,): (u64,)| Expr {
            ty: Type::Bool,
            kind: ExprKind::SyncStreamAccess {
                target: StreamReference::In(p as usize),
                parameters: vec![],
            },
        }),
        delimited("(", parse_expr, ")"),
    )))
    .parse_next(input)
}

fn parse_bool(input: &mut &str) -> winnow::ModalResult<Expr> {
    alt(("true", "false"))
        .map(|b| match b {
            "true" => true,
            "false" => false,
            _ => unreachable!(),
        })
        .map(|v| Expr {
            ty: Type::Bool,
            kind: ExprKind::Constant(Constant::Bool(v)),
        })
        .parse_next(input)
}

pub(crate) fn parse_stmt(mut s: &str) -> Stmt {
    match parse_stmt_prime.parse_next(&mut s) {
        Ok(stmt) => stmt,
        Err(e) => {
            println!("{e}");
            panic!()
        }
    }
}

pub(crate) fn parse_ir(s: &str) -> StreamIr {
    let stmt = parse_stmt(s);
    StreamIr {
        stmt,
        sr2memory: (0..10)
            .map(|i| (StreamReference::In(i), format!("i{i}")))
            .chain((0..10).map(|o| {
                (
                    StreamReference::Out(OutputReference::Unparameterized(o)),
                    format!("o{o}"),
                )
            }))
            .map(|(sr, name)| {
                (
                    sr,
                    Memory {
                        buffer: StreamMemory::Instances {
                            buffer: StreamBuffer::SingleValue,
                            parameter: (0..3)
                                .map(|i| Parameter {
                                    name: format!("p{i}"),
                                    ty: Type::Bool,
                                })
                                .collect(),
                        },
                        ty: Type::Bool,
                        name,
                    },
                )
            })
            .collect(),
        wref2window: HashMap::new(),
        lref2lfreq: (0..10)
            .map(|i| {
                (
                    i as usize,
                    LocalFreq {
                        dur: Duration::from_secs(i),
                        sr: OutputReference::Unparameterized(i as usize),
                        reference: i as usize,
                    },
                )
            })
            .collect(),
        livetime_equivalences: LivetimeEquivalences {
            idx: (0..10)
                .map(|o| (OutputReference::Unparameterized(o), o))
                .collect(),
            sets: DisjointSet::with_len(10),
            input_idx: 10,
        },
        static_schedule: None,
        triggers: HashMap::new(),
        accessed_by: HashMap::new(),
        accesses: HashMap::new(),
    }
}