use super::common::ParseResult;
use super::expression::parse_expression;
use super::names::parse_identifier_list;
use super::subtype_indication::parse_subtype_indication;
use super::tokens::{Kind::*, TokenStream};
use crate::ast::*;
use crate::data::WithPos;
pub fn parse_optional_assignment(
stream: &mut TokenStream,
) -> ParseResult<Option<WithPos<Expression>>> {
if stream.pop_if_kind(ColonEq)?.is_some() {
let expr = parse_expression(stream)?;
Ok(Some(expr))
} else {
Ok(None)
}
}
fn parse_object_declaration_kind(
stream: &mut TokenStream,
class: ObjectClass,
) -> ParseResult<Vec<ObjectDeclaration>> {
let idents = parse_identifier_list(stream)?;
stream.expect_kind(Colon)?;
let subtype = parse_subtype_indication(stream)?;
let opt_expression = parse_optional_assignment(stream)?;
Ok(idents
.into_iter()
.map(|ident| ObjectDeclaration {
class,
ident: ident.into(),
subtype_indication: subtype.clone(),
expression: opt_expression.clone(),
})
.collect())
}
pub fn parse_object_declaration(stream: &mut TokenStream) -> ParseResult<Vec<ObjectDeclaration>> {
let token = stream.expect()?;
let result = try_token_kind!(
token,
Constant => parse_object_declaration_kind(stream, ObjectClass::Constant)?,
Signal => parse_object_declaration_kind(stream, ObjectClass::Signal)?,
Variable => parse_object_declaration_kind(stream, ObjectClass::Variable)?,
Shared => {
stream.expect_kind(Variable)?;
parse_object_declaration_kind(stream, ObjectClass::SharedVariable)?
}
);
stream.expect_kind(SemiColon)?;
Ok(result)
}
pub fn parse_file_declaration_no_semi(
stream: &mut TokenStream,
) -> ParseResult<Vec<FileDeclaration>> {
stream.expect_kind(File)?;
let idents = parse_identifier_list(stream)?;
stream.expect_kind(Colon)?;
let subtype = parse_subtype_indication(stream)?;
let open_info = {
if stream.skip_if_kind(Open)? {
Some(parse_expression(stream)?)
} else {
None
}
};
let file_name = {
if stream.skip_if_kind(Is)? {
Some(parse_expression(stream)?)
} else {
None
}
};
Ok(idents
.into_iter()
.map(|ident| FileDeclaration {
ident: ident.into(),
subtype_indication: subtype.clone(),
open_info: open_info.clone(),
file_name: file_name.clone(),
})
.collect())
}
pub fn parse_file_declaration(stream: &mut TokenStream) -> ParseResult<Vec<FileDeclaration>> {
let result = parse_file_declaration_no_semi(stream)?;
stream.expect_kind(SemiColon)?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::syntax::test::Code;
#[test]
fn parses_constant() {
let code = Code::new("constant foo : natural;");
assert_eq!(
code.with_stream(parse_object_declaration),
vec![ObjectDeclaration {
class: ObjectClass::Constant,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: None
}]
);
}
#[test]
fn parses_signal() {
let code = Code::new("signal foo : natural;");
assert_eq!(
code.with_stream(parse_object_declaration),
vec![ObjectDeclaration {
class: ObjectClass::Signal,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: None
}]
);
}
#[test]
fn parses_variable() {
let code = Code::new("variable foo : natural;");
assert_eq!(
code.with_stream(parse_object_declaration),
vec![ObjectDeclaration {
class: ObjectClass::Variable,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: None
}]
);
}
#[test]
fn parses_shared_variable() {
let code = Code::new("shared variable foo : natural;");
assert_eq!(
code.with_stream(parse_object_declaration),
vec![ObjectDeclaration {
class: ObjectClass::SharedVariable,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: None
}]
);
}
#[test]
fn parses_file() {
let code = Code::new("file foo : text;");
assert_eq!(
code.with_stream(parse_file_declaration),
vec![FileDeclaration {
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("text").subtype_indication(),
open_info: None,
file_name: None
}]
);
}
#[test]
fn parses_file_with_file_name() {
let code = Code::new("file foo : text is \"file_name\";");
assert_eq!(
code.with_stream(parse_file_declaration),
vec![FileDeclaration {
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("text").subtype_indication(),
open_info: None,
file_name: Some(code.s1("\"file_name\"").expr())
}]
);
}
#[test]
fn parses_file_with_open_information() {
let code = Code::new("file foo : text open write_mode is \"file_name\";");
assert_eq!(
code.with_stream(parse_file_declaration),
vec![FileDeclaration {
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("text").subtype_indication(),
open_info: Some(code.s1("write_mode").expr()),
file_name: Some(code.s1("\"file_name\"").expr())
}]
);
}
#[test]
fn parses_optional_expression() {
let code = Code::new("constant foo : natural := 0;");
assert_eq!(
code.with_stream(parse_object_declaration),
vec![ObjectDeclaration {
class: ObjectClass::Constant,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: Some(code.s1("0").expr())
}]
);
}
#[test]
fn parses_identifier_list() {
let code = Code::new("constant foo, bar : natural := 0;");
let objects = vec![
ObjectDeclaration {
class: ObjectClass::Constant,
ident: code.s1("foo").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: Some(code.s1("0").expr()),
},
ObjectDeclaration {
class: ObjectClass::Constant,
ident: code.s1("bar").decl_ident(),
subtype_indication: code.s1("natural").subtype_indication(),
expression: Some(code.s1("0").expr()),
},
];
assert_eq!(code.with_stream(parse_object_declaration), objects);
}
}