use crate::{characters, constant, identifier, value, SigSpec, Span};
use nom::{
branch::alt,
bytes::complete::{tag, take_while},
combinator::{map, opt},
multi::many0,
sequence::terminated,
IResult,
};
use nom_tracable::tracable_parser;
#[tracable_parser]
pub(crate) fn sigspec(input: Span) -> IResult<Span, SigSpec> {
let (input, sigspec) = alt((
map(constant::constant, SigSpec::Constant),
map(sigspec_range, |range| {
SigSpec::Range(Box::new(range.0), range.1, range.2)
}),
map(identifier::id, |id| SigSpec::WireId(id.erease())),
map(sigspec_concat, SigSpec::Concat),
))(input)?;
Ok((input, sigspec))
}
pub(crate) fn sigspec_range(input: Span) -> IResult<Span, (SigSpec, usize, Option<usize>)> {
let (input, wire_id) = identifier::id(input)?;
let (input, _) = characters::sep(input)?;
let (input, _) = tag("[")(input)?;
let (input, start) = value::integer(input)?;
let (input, has_range) = opt(tag(":"))(input)?;
let (input, opt_end) = has_range.map_or(Ok((input, None)), |_drop| {
let (input, end) = value::integer(input)?;
let end = end as usize;
Ok((input, Some(end)))
})?;
let (input, _) = tag("]")(input)?;
Ok((
input,
(SigSpec::WireId(wire_id.erease()), start as usize, opt_end),
))
}
pub(crate) fn sigspec_concat(input: Span) -> IResult<Span, Vec<SigSpec>> {
let (input, _) = tag("{")(input)?;
let (input, _) = take_while(characters::is_sep)(input)?;
let (input, sigspecs) = many0(terminated(sigspec, characters::sep))(input)?;
let (input, _) = tag("}")(input)?;
Ok((input, sigspecs))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Constant;
use nom_tracable::TracableInfo;
use pretty_assertions::assert_eq;
#[test]
fn test_sigspec() {
let vectors = vec![
(
"5'110xz",
SigSpec::Constant(Constant::Value(vec!['z', 'x', '0', '1', '1'])),
),
("\\A", SigSpec::WireId("A".to_string())),
(
"\\A [0]",
SigSpec::Range(Box::new(SigSpec::WireId("A".to_string())), 0, None),
),
(
"\\immdec.signbit",
SigSpec::WireId("immdec.signbit".to_string()),
),
(
"{ \\immdec.i_wb_rdt [12:5] \\immdec.i_wb_rdt [13] }",
SigSpec::Concat(vec![
SigSpec::Range(
Box::new(SigSpec::WireId("immdec.i_wb_rdt".to_string())),
12,
Some(5),
),
SigSpec::Range(
Box::new(SigSpec::WireId("immdec.i_wb_rdt".to_string())),
13,
None,
),
]),
),
];
for (input, expected) in vectors {
let span = Span::new_extra(input, Default::default());
let ret = sigspec(span).unwrap();
assert_eq!(ret.1, expected);
}
}
#[test]
fn test_sigspec_range() {
let info: TracableInfo = TracableInfo::new().parser_width(64).fold("term");
let span = Span::new_extra("\\immdec.imm19_12_20 [8:1]", info);
assert_eq!(
sigspec_range(span).unwrap().1,
(
SigSpec::WireId("immdec.imm19_12_20".to_string()),
8,
Some(1)
)
);
}
#[test]
fn test_sigspec_concat() {
let info: TracableInfo = TracableInfo::new().parser_width(64).fold("term");
let span = Span::new_extra(
"{ $flatten\\immdec.$ternary$serv_immdec.v:47$326_Y \\immdec.imm19_12_20 [8:1] }",
info,
);
assert_eq!(
sigspec_concat(span).unwrap().1,
(vec![
SigSpec::WireId("flatten\\immdec.$ternary$serv_immdec.v:47$326_Y".to_string()),
SigSpec::Range(
Box::new(SigSpec::WireId("immdec.imm19_12_20".to_string())),
8,
Some(1)
)
])
);
}
}