use std::{cell::RefCell, fmt, rc::Rc};
use orrery_core::{identifier::Id, semantic::DiagramKind};
use crate::span::{Span, Spanned};
#[derive(Debug, Clone, Default)]
pub struct TypeSpec<'a> {
pub type_name: Option<Spanned<Id>>,
pub attributes: Vec<Attribute<'a>>,
}
impl<'a> TypeSpec<'a> {
pub fn span(&self) -> Span {
match &self.type_name {
Some(name) => self
.attributes
.iter()
.map(|attr| attr.span())
.fold(name.span(), |acc, span| acc.union(span)),
None => self
.attributes
.iter()
.map(|attr| attr.span())
.reduce(|acc, span| acc.union(span))
.unwrap_or_default(),
}
}
}
impl<'a> fmt::Display for TypeSpec<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(name) = &self.type_name {
write!(f, "{}", name)?;
}
if !self.attributes.is_empty() {
write!(f, "[")?;
for (i, attr) in self.attributes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", attr)?;
}
write!(f, "]")?;
}
Ok(())
}
}
static EMPTY_TYPE_SPEC: TypeSpec<'static> = TypeSpec {
type_name: None,
attributes: Vec::new(),
};
#[derive(Debug, Clone)]
pub enum AttributeValue<'a> {
String(Spanned<String>),
Float(Spanned<f32>),
TypeSpec(TypeSpec<'a>),
Identifiers(Vec<Spanned<Id>>),
Empty,
}
impl<'a> PartialEq for AttributeValue<'a> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(AttributeValue::String(s1), AttributeValue::String(s2)) => s1.inner() == s2.inner(),
(AttributeValue::Float(f1), AttributeValue::Float(f2)) => f1.inner() == f2.inner(),
(AttributeValue::TypeSpec(t1), AttributeValue::TypeSpec(t2)) => {
t1.type_name.as_ref().map(|s| s.inner()) == t2.type_name.as_ref().map(|s| s.inner())
&& t1.attributes == t2.attributes
}
(AttributeValue::Identifiers(l1), AttributeValue::Identifiers(l2)) => l1
.iter()
.map(|s| s.inner())
.eq(l2.iter().map(|s| s.inner())),
(AttributeValue::Empty, AttributeValue::Empty) => true,
_ => false,
}
}
}
impl<'a> fmt::Display for AttributeValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttributeValue::String(s) => write!(f, "\"{}\"", s.inner()),
AttributeValue::Float(n) => write!(f, "{}", n.inner()),
AttributeValue::TypeSpec(type_spec) => {
write!(f, "{}", type_spec)
}
AttributeValue::Identifiers(ids) => {
write!(f, "[")?;
for (i, id) in ids.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", id.inner())?;
}
write!(f, "]")
}
AttributeValue::Empty => write!(f, "[]"),
}
}
}
impl<'a> AttributeValue<'a> {
pub fn span(&self) -> Span {
match self {
AttributeValue::String(spanned) => spanned.span(),
AttributeValue::Float(spanned) => spanned.span(),
AttributeValue::TypeSpec(type_spec) => type_spec.span(),
AttributeValue::Identifiers(ids) => {
if ids.is_empty() {
Span::default()
} else {
ids.iter()
.map(|id| id.span())
.reduce(|acc, span| acc.union(span))
.unwrap_or_default()
}
}
AttributeValue::Empty => Span::default(),
}
}
pub fn as_str(&self) -> Result<&str, &'static str> {
if let AttributeValue::String(s) = self {
Ok(s.inner())
} else {
Err("expected string value")
}
}
pub fn as_float(&self) -> Result<f32, &'static str> {
if let AttributeValue::Float(f) = self {
Ok(*f.inner())
} else {
Err("expected float value")
}
}
pub fn as_usize(&self) -> Result<usize, &'static str> {
if let AttributeValue::Float(f) = self {
Ok(*f.inner() as usize)
} else {
Err("expected float value")
}
}
pub fn as_u16(&self) -> Result<u16, &'static str> {
if let AttributeValue::Float(f) = self {
Ok(*f.inner() as u16)
} else {
Err("expected float value")
}
}
pub fn as_type_spec(&self) -> Result<&TypeSpec<'a>, &'static str> {
match self {
AttributeValue::TypeSpec(type_spec) => Ok(type_spec),
AttributeValue::Empty => Ok(&EMPTY_TYPE_SPEC),
_ => Err("expected type spec"),
}
}
pub fn as_type_spec_mut(&mut self) -> Result<&mut TypeSpec<'a>, &'static str> {
match self {
AttributeValue::TypeSpec(type_spec) => Ok(type_spec),
_ => Err("expected type spec"),
}
}
pub fn as_identifiers(&self) -> Result<&[Spanned<Id>], &'static str> {
match self {
AttributeValue::Identifiers(ids) => Ok(ids),
AttributeValue::Empty => Ok(&[]),
_ => Err("expected identifiers"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute<'a> {
pub name: Spanned<&'a str>,
pub value: AttributeValue<'a>,
}
impl<'a> fmt::Display for Attribute<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}={}", self.name, self.value)
}
}
#[derive(Debug, Clone)]
pub struct TypeDefinition<'a> {
pub name: Spanned<Id>,
pub type_spec: TypeSpec<'a>,
}
impl TypeDefinition<'_> {
pub fn span(&self) -> Span {
self.name.span().union(self.type_spec.span())
}
}
#[derive(Debug, Clone)]
pub enum FileHeader<'a> {
Diagram {
kind: Spanned<DiagramKind>,
attributes: Vec<Attribute<'a>>,
},
Library {
span: Span,
},
}
impl FileHeader<'_> {
pub fn span(&self) -> Span {
match self {
FileHeader::Diagram { kind, attributes } => attributes
.iter()
.map(|attr| attr.span())
.fold(kind.span(), |acc, span| acc.union(span)),
FileHeader::Library { span } => *span,
}
}
pub fn is_library(&self) -> bool {
matches!(self, FileHeader::Library { .. })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportForm {
Namespaced,
Aliased(Spanned<Id>),
Glob,
}
#[derive(Debug, Clone)]
pub struct ImportDecl {
pub path: Spanned<String>,
pub form: ImportForm,
}
#[derive(Debug, Clone)]
pub struct Import<'a> {
pub namespace: Option<Id>,
pub file_ast: Rc<RefCell<FileAst<'a>>>,
}
#[derive(Debug, Clone)]
pub struct FileAst<'a> {
pub header: FileHeader<'a>,
pub import_decls: Vec<Spanned<ImportDecl>>,
pub type_definitions: Vec<TypeDefinition<'a>>,
pub elements: Vec<Element<'a>>,
pub imports: Vec<Import<'a>>,
}
impl FileAst<'_> {
pub fn span(&self) -> Span {
let header_span = self.header.span();
let import_spans = self.import_decls.iter().map(Spanned::span);
let type_def_spans = self.type_definitions.iter().map(|td| td.span());
let element_spans = self.elements.iter().map(|elem| elem.span());
import_spans
.chain(type_def_spans)
.chain(element_spans)
.fold(header_span, |acc, span| acc.union(span))
}
}
#[derive(Debug, Clone)]
pub struct FragmentSection<'a> {
pub title: Option<Spanned<String>>,
pub elements: Vec<Element<'a>>,
}
impl FragmentSection<'_> {
pub fn span(&self) -> Span {
let elements_span = self
.elements
.iter()
.map(|elem| elem.span())
.reduce(|acc, span| acc.union(span));
match (&self.title, elements_span) {
(Some(title), Some(es)) => title.span().union(es),
(Some(title), None) => title.span(),
(None, Some(es)) => es,
(None, None) => Span::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct Fragment<'a> {
pub operation: Spanned<String>,
pub type_spec: TypeSpec<'a>,
pub sections: Vec<FragmentSection<'a>>,
}
impl Fragment<'_> {
pub fn span(&self) -> Span {
let span = self.operation.span().union(self.type_spec.span());
self.sections
.iter()
.map(|section| section.span())
.fold(span, |acc, s| acc.union(s))
}
}
#[derive(Debug, Clone)]
pub struct Note<'a> {
pub type_spec: TypeSpec<'a>,
pub content: Spanned<String>,
}
impl Note<'_> {
pub fn span(&self) -> Span {
self.content.span().union(self.type_spec.span())
}
}
#[derive(Debug, Clone)]
pub enum ComponentContent<'a> {
None,
Scope(Vec<Element<'a>>),
Diagram(DiagramSource<'a>),
}
impl ComponentContent<'_> {
pub fn span(&self) -> Span {
match self {
ComponentContent::None => Span::empty(),
ComponentContent::Scope(elements) => elements
.iter()
.map(|elem| elem.span())
.reduce(|acc, s| acc.union(s))
.unwrap_or(Span::empty()),
ComponentContent::Diagram(source) => source.span(),
}
}
}
#[derive(Debug, Clone)]
pub enum DiagramSource<'a> {
Inline(Rc<RefCell<FileAst<'a>>>),
Ref(Spanned<Id>),
}
impl DiagramSource<'_> {
pub fn span(&self) -> Span {
match self {
DiagramSource::Inline(rc) => rc.borrow().span(),
DiagramSource::Ref(id) => id.span(),
}
}
}
#[derive(Debug, Clone)]
pub enum Element<'a> {
Component {
name: Spanned<Id>,
display_name: Option<Spanned<String>>,
type_spec: TypeSpec<'a>,
content: ComponentContent<'a>,
},
Relation {
source: Spanned<Id>,
target: Spanned<Id>,
relation_type: Spanned<&'a str>,
type_spec: TypeSpec<'a>,
label: Option<Spanned<String>>,
},
Fragment(Fragment<'a>),
ActivateBlock {
component: Spanned<Id>,
type_spec: TypeSpec<'a>,
elements: Vec<Element<'a>>,
},
Activate {
component: Spanned<Id>,
type_spec: TypeSpec<'a>,
},
Deactivate { component: Spanned<Id> },
AltElseBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
sections: Vec<FragmentSection<'a>>,
},
OptBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
section: FragmentSection<'a>,
},
LoopBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
section: FragmentSection<'a>,
},
ParBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
sections: Vec<FragmentSection<'a>>,
},
BreakBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
section: FragmentSection<'a>,
},
CriticalBlock {
keyword_span: Span,
type_spec: TypeSpec<'a>,
section: FragmentSection<'a>,
},
Note(Note<'a>),
}
impl Element<'_> {
pub fn span(&self) -> Span {
match self {
Element::Component {
name,
display_name,
type_spec,
content,
} => {
let mut span = name.span().union(type_spec.span()).union(content.span());
if let Some(display_name) = display_name {
span = span.union(display_name.span());
}
span
}
Element::Relation {
source,
target,
relation_type,
type_spec,
label,
} => {
let mut span = source
.span()
.union(target.span())
.union(relation_type.span())
.union(type_spec.span());
if let Some(label) = label {
span = span.union(label.span());
}
span
}
Element::Fragment(fragment) => fragment.span(),
Element::ActivateBlock {
component,
type_spec,
elements,
} => {
let span = component.span().union(type_spec.span());
elements
.iter()
.map(|elem| elem.span())
.fold(span, |acc, s| acc.union(s))
}
Element::Activate {
component,
type_spec,
} => component.span().union(type_spec.span()),
Element::Deactivate { component } => component.span(),
Element::AltElseBlock {
keyword_span,
type_spec,
sections,
}
| Element::ParBlock {
keyword_span,
type_spec,
sections,
} => {
let mut span = (*keyword_span).union(type_spec.span());
for section in sections {
span = span.union(section.span());
}
span
}
Element::OptBlock {
keyword_span,
type_spec,
section,
}
| Element::LoopBlock {
keyword_span,
type_spec,
section,
}
| Element::BreakBlock {
keyword_span,
type_spec,
section,
}
| Element::CriticalBlock {
keyword_span,
type_spec,
section,
} => (*keyword_span)
.union(type_spec.span())
.union(section.span()),
Element::Note(note) => note.span(),
}
}
}
impl Attribute<'_> {
pub fn span(&self) -> Span {
self.name.span().union(self.value.span())
}
}