use super::common::ParseResult;
use super::expression::parse_expression;
use super::names::parse_selected_name;
use super::subprogram::parse_signature;
use super::tokens::{Kind::*, TokenStream};
use crate::ast::{
Attribute, AttributeDeclaration, AttributeSpecification, Designator, EntityClass, EntityName,
EntityTag,
};
fn parse_entity_class(stream: &mut TokenStream) -> ParseResult<EntityClass> {
let token = stream.expect()?;
Ok(try_token_kind!(
token,
Entity => EntityClass::Entity,
Architecture => EntityClass::Architecture,
Configuration => EntityClass::Configuration,
Package => EntityClass::Package,
Signal => EntityClass::Signal,
Variable => EntityClass::Variable,
Procedure => EntityClass::Procedure,
Function => EntityClass::Function,
Component => EntityClass::Component,
Constant => EntityClass::Constant,
Type => EntityClass::Type,
Label => EntityClass::Label
))
}
pub fn parse_entity_name_list(stream: &mut TokenStream) -> ParseResult<Vec<EntityName>> {
let token = stream.peek_expect()?;
Ok(try_token_kind!(
token,
Identifier | StringLiteral => {
let mut entity_name_list = Vec::new();
loop {
let designator_token = stream.expect()?;
let designator = try_token_kind!(
designator_token,
Identifier => designator_token.expect_ident()?.map_into(Designator::Identifier),
StringLiteral => designator_token.expect_string()?.map_into(Designator::OperatorSymbol));
let signature = {
if stream.peek_kind()? == Some(LeftSquare) {
Some(parse_signature(stream)?)
} else {
None
}
};
entity_name_list.push(EntityName::Name(EntityTag {
designator,
signature,
}));
let sep_token = stream.peek_expect()?;
try_token_kind!(
sep_token,
Comma => {
stream.move_after(&sep_token);
},
Colon => {
break entity_name_list;
}
)
}
},
Others => {
stream.move_after(&token);
vec![EntityName::Others]
},
All => {
stream.move_after(&token);
vec![EntityName::All]
}
))
}
pub fn parse_attribute(stream: &mut TokenStream) -> ParseResult<Vec<Attribute>> {
stream.expect_kind(Attribute)?;
let ident = stream.expect_ident()?;
let token = stream.expect()?;
Ok(try_token_kind!(
token,
Colon => {
let type_mark = parse_selected_name(stream)?;
stream.expect_kind(SemiColon)?;
vec![Attribute::Declaration(AttributeDeclaration {
ident,
type_mark,
})]
},
Of => {
let entity_names = parse_entity_name_list(stream)?;
stream.expect_kind(Colon)?;
let entity_class = parse_entity_class(stream)?;
stream.expect_kind(Is)?;
let expr = parse_expression(stream)?;
stream.expect_kind(SemiColon)?;
entity_names
.into_iter()
.map(|entity_name| {
Attribute::Specification(AttributeSpecification {
ident: ident.clone(),
entity_name: entity_name.clone(),
entity_class: entity_class,
expr: expr.clone(),
})
}).collect()
}
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::syntax::test::Code;
#[test]
fn parse_simple_attribute_declaration() {
let code = Code::new("attribute foo : lib.name;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Declaration(AttributeDeclaration {
ident: code.s1("foo").ident(),
type_mark: code.s1("lib.name").selected_name()
})]
)
}
#[test]
fn parse_simple_attribute_specification() {
let code = Code::new("attribute attr_name of foo : signal is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Name(EntityTag {
designator: code.s1("foo").designator(),
signature: None
}),
entity_class: EntityClass::Signal,
expr: code.s1("0+1").expr()
})]
)
}
#[test]
fn parse_simple_attribute_specification_operator_symbol() {
let code = Code::new("attribute attr_name of \"**\" : function is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Name(EntityTag {
designator: code.s1("\"**\"").designator(),
signature: None
}),
entity_class: EntityClass::Function,
expr: code.s1("0+1").expr()
})]
)
}
#[test]
fn parse_attribute_specification_list() {
let code = Code::new("attribute attr_name of foo, bar : signal is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![
Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Name(EntityTag {
designator: code.s1("foo").designator(),
signature: None
}),
entity_class: EntityClass::Signal,
expr: code.s1("0+1").expr()
}),
Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Name(EntityTag {
designator: code.s1("bar").designator(),
signature: None
}),
entity_class: EntityClass::Signal,
expr: code.s1("0+1").expr()
})
]
)
}
#[test]
fn parse_attribute_specification_all() {
let code = Code::new("attribute attr_name of all : signal is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::All,
entity_class: EntityClass::Signal,
expr: code.s1("0+1").expr()
})]
)
}
#[test]
fn parse_attribute_specification_others() {
let code = Code::new("attribute attr_name of others : signal is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Others,
entity_class: EntityClass::Signal,
expr: code.s1("0+1").expr()
})]
)
}
#[test]
fn parse_attribute_specification_with_signature() {
let code = Code::new("attribute attr_name of foo[return natural] : function is 0+1;");
assert_eq!(
code.with_stream(parse_attribute),
vec![Attribute::Specification(AttributeSpecification {
ident: code.s1("attr_name").ident(),
entity_name: EntityName::Name(EntityTag {
designator: code.s1("foo").designator(),
signature: Some(code.s1("[return natural]").signature())
}),
entity_class: EntityClass::Function,
expr: code.s1("0+1").expr()
})]
)
}
}