use super::common::parse_optional;
use super::common::ParseResult;
use super::expression::name_to_type_mark;
use super::expression::parse_expression;
use super::tokens::{Kind::*, TokenStream};
use crate::ast;
use crate::ast::*;
use crate::data::{Diagnostic, WithPos};
pub fn parse_direction(stream: &mut TokenStream) -> ParseResult<Direction> {
Ok(try_token_kind!(
stream.expect()?,
To => Direction::Ascending,
Downto => Direction::Descending
))
}
enum NameOrRange {
Name(WithPos<Name>),
Range(WithPos<ast::Range>),
}
fn parse_name_or_range(stream: &mut TokenStream) -> ParseResult<NameOrRange> {
let expr = parse_expression(stream)?;
match stream.peek_kind()? {
Some(To) | Some(Downto) => {
let direction = parse_direction(stream)?;
let right_expr = parse_expression(stream)?;
let pos = expr.pos.combine(&right_expr.pos);
let range = ast::Range::Range(RangeConstraint {
direction,
left_expr: Box::new(expr),
right_expr: Box::new(right_expr),
});
return Ok(NameOrRange::Range(WithPos::from(range, pos)));
}
_ => {}
}
if let WithPos {
item: Expression::Name(name),
pos,
} = expr
{
if let Name::Attribute(attribute_name) = *name {
if attribute_name.as_range().is_some() {
let range = ast::Range::Attribute(attribute_name);
Ok(NameOrRange::Range(WithPos::from(range, pos)))
} else {
Ok(NameOrRange::Name(WithPos::from(
Name::Attribute(attribute_name),
pos,
)))
}
} else {
Ok(NameOrRange::Name(WithPos::from(*name, pos)))
}
} else {
Err(Diagnostic::error(&expr, "Expected name or range"))
}
}
pub fn parse_range(stream: &mut TokenStream) -> ParseResult<WithPos<ast::Range>> {
match parse_name_or_range(stream)? {
NameOrRange::Range(range) => Ok(range),
NameOrRange::Name(name) => Err(Diagnostic::error(&name, "Expected range")),
}
}
pub fn parse_discrete_range(stream: &mut TokenStream) -> ParseResult<DiscreteRange> {
match parse_name_or_range(stream) {
Ok(NameOrRange::Range(range)) => Ok(DiscreteRange::Range(range.item)),
Ok(NameOrRange::Name(name)) => {
let type_mark = name_to_type_mark(name)?;
let range = parse_optional(stream, Range, parse_range)?.map(|range| range.item);
Ok(DiscreteRange::Discrete(type_mark, range))
}
Err(diagnostic) => Err(diagnostic.when("parsing discrete_range")),
}
}
pub fn parse_array_index_constraint(stream: &mut TokenStream) -> ParseResult<ArrayIndex> {
match parse_name_or_range(stream) {
Ok(NameOrRange::Range(range)) => Ok(ArrayIndex::Discrete(DiscreteRange::Range(range.item))),
Ok(NameOrRange::Name(name)) => {
let type_mark = name_to_type_mark(name)?;
if stream.skip_if_kind(Range)? {
if stream.skip_if_kind(BOX)? {
Ok(ArrayIndex::IndexSubtypeDefintion(type_mark))
} else {
Ok(ArrayIndex::Discrete(DiscreteRange::Discrete(
type_mark,
Some(parse_range(stream)?.item),
)))
}
} else {
Ok(ArrayIndex::Discrete(DiscreteRange::Discrete(
type_mark, None,
)))
}
}
Err(diagnostic) => Err(diagnostic.when("parsing array index constraint")),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::syntax::test::Code;
#[test]
fn parse_range_range() {
let code = Code::new("foo.bar to 1");
assert_eq!(
code.with_stream(parse_range),
WithPos::new(
Range::Range(RangeConstraint {
direction: Direction::Ascending,
left_expr: Box::new(code.s1("foo.bar").expr()),
right_expr: Box::new(code.s1("1").expr())
},),
code.pos()
)
);
}
#[test]
fn parse_range_range_attribute() {
let code = Code::new("foo.bar'range");
assert_eq!(
code.with_stream(parse_range),
WithPos::new(
Range::Attribute(Box::new(code.s1("foo.bar'range").attribute_name())),
code.pos()
)
);
}
#[test]
fn parse_range_reverse_range_attribute() {
let code = Code::new("foo.bar'reverse_range");
assert_eq!(
code.with_stream(parse_range),
WithPos::new(
Range::Attribute(Box::new(code.s1("foo.bar'reverse_range").attribute_name())),
code.pos()
)
);
}
#[test]
fn parse_range_other_attribute() {
let code = Code::new("foo.bar'length downto 0");
assert_eq!(
code.with_stream(parse_range),
WithPos::new(
Range::Range(RangeConstraint {
direction: Direction::Descending,
left_expr: Box::new(code.s1("foo.bar'length").expr()),
right_expr: Box::new(code.s1("0").expr())
}),
code.pos()
)
);
}
#[test]
fn parse_discrete_range_range() {
let code = Code::new("foo.bar to 1");
assert_eq!(
code.with_stream(parse_discrete_range),
DiscreteRange::Range(Range::Range(RangeConstraint {
direction: Direction::Ascending,
left_expr: Box::new(code.s1("foo.bar").expr()),
right_expr: Box::new(code.s1("1").expr())
}))
);
}
#[test]
fn parse_discrete_range_range_attribute() {
let code = Code::new("foo.bar'range");
assert_eq!(
code.with_stream(parse_discrete_range),
DiscreteRange::Range(Range::Attribute(Box::new(
code.s1("foo.bar'range").attribute_name()
)))
);
}
#[test]
fn parse_discrete_range_discrete() {
let code = Code::new("foo.bar");
assert_eq!(
code.with_stream(parse_discrete_range),
DiscreteRange::Discrete(code.s1("foo.bar").type_mark(), None)
);
}
#[test]
fn parse_discrete_range_discrete_range() {
let code = Code::new("foo.bar range 1 to 4");
assert_eq!(
code.with_stream(parse_discrete_range),
DiscreteRange::Discrete(
code.s1("foo.bar").type_mark(),
Some(code.s1("1 to 4").range())
)
);
}
#[test]
fn parse_array_index_constraint_subtype_definition() {
let code = Code::new("foo.bar range <>");
assert_eq!(
code.with_stream(parse_array_index_constraint),
ArrayIndex::IndexSubtypeDefintion(code.s1("foo.bar").type_mark())
);
}
#[test]
fn parse_array_index_constraint_range() {
let code = Code::new("0 to 1");
assert_eq!(
code.with_stream(parse_array_index_constraint),
ArrayIndex::Discrete(code.s1("0 to 1").discrete_range())
);
}
#[test]
fn parse_array_index_constraint_discrete_range() {
let code = Code::new("foo.bar range 0 to 1");
assert_eq!(
code.with_stream(parse_array_index_constraint),
ArrayIndex::Discrete(code.s1("foo.bar range 0 to 1").discrete_range())
);
}
#[test]
fn parse_array_index_constraint_discrete_no_range() {
let code = Code::new("foo.bar");
assert_eq!(
code.with_stream(parse_array_index_constraint),
ArrayIndex::Discrete(code.s1("foo.bar").discrete_range())
);
}
}