flams_ontology/content/
terms.rs

1use crate::uris::{ContentURI, DocumentElementURI, Name, URIOrRefTrait, URIRef};
2use crate::{oma, oms, omsp};
3use flams_utils::prelude::{DFSContinuation, Indentor, TreeChild, TreeChildIter, TreeLike};
4use std::fmt::{Debug, Display, Formatter, Write};
5use std::str::FromStr;
6
7#[derive(Clone, Debug, Hash, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
10#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
11pub enum Term {
12    OMID(ContentURI),
13    OMV(Var),
14    OMA {
15        head: Box<Term>,
16        args: Box<[Arg]>,
17    },
18    Field {
19        record: Box<Term>,
20        key: Name,
21        owner: Option<Box<Term>>,
22    },
23    OML {
24        name: Name,
25        df: Option<Box<Term>>,
26        tp: Option<Box<Term>>,
27    },
28    Informal {
29        tag: String,
30        attributes: Box<[(Box<str>, Box<str>)]>,
31        children: Box<[Informal]>,
32        terms: Box<[Term]>,
33    },
34}
35
36impl Term {
37    /// #### Errors
38    #[inline]
39    pub fn present(
40        &self,
41        presenter: &mut impl crate::narration::notations::Presenter,
42    ) -> Result<(), crate::narration::notations::PresentationError> {
43        //println!("presenting {self}");
44        crate::narration::notations::Notation::present_term(self, presenter)
45    }
46
47    pub fn term_list(i: impl Iterator<Item = Self>) -> Self {
48        oma!(oms!(ftml:SEQUENCE_EXPRESSION),I@N:i)
49    }
50
51    #[must_use]
52    pub fn as_list(&self) -> Option<&[Arg]> {
53        match self {
54            Self::OMA {
55                head, //:box Self::OMID(ContentURI::Symbol(s)),
56                args,
57            } if matches!(&**head,Self::OMID(ContentURI::Symbol(s)) if *s == *crate::metatheory::SEQUENCE_EXPRESSION) => {
58                Some(&**args)
59            } //if *s == *crate::metatheory::SEQUENCE_EXPRESSION => Some(&**args),
60            _ => None,
61        }
62    }
63
64    #[must_use]
65    #[allow(clippy::result_large_err)]
66    pub fn is_record_field(&self) -> bool {
67        matches!(self,oma!(hd,args) if matches!(&**hd,omsp!(fp) if *fp == *crate::metatheory::FIELD_PROJECTION) && args.len() == 2)
68    }
69
70    /// #### Errors
71    #[allow(clippy::result_large_err)]
72    pub fn into_record_field(self) -> Result<(Self, Name), Self> {
73        match self {
74            oma!(hd, args)
75                if matches!(&*hd,omsp!(fp) if *fp == *crate::metatheory::FIELD_PROJECTION)
76                    && args.len() == 2 =>
77            {
78                let mut args = args.into_vec().into_iter();
79                let [a, b] = [
80                    args.next().unwrap_or_else(|| unreachable!()),
81                    args.next().unwrap_or_else(|| unreachable!()),
82                ];
83                match b {
84                    Arg {
85                        term:
86                            Self::OML {
87                                name,
88                                df: None,
89                                tp: None,
90                            },
91                        mode: ArgMode::Normal,
92                    } => Ok((a.term, name)),
93                    b => Err(oma!(*hd, [a, b])),
94                }
95            }
96            _ => Err(self),
97        }
98    }
99    /*
100    #[must_use]
101    pub fn as_ref(&self) -> TermRef {
102        match self {
103            Self::OMID(uri) => TermRef::OMID(uri.as_content()),
104            Self::OMV(v) => TermRef::OMV(v),
105            Self::OMA{head,args} => TermRef::OMA{head,args},
106            Self::Field{record,key} => TermRef::Field{record,key},
107            Self::OML{name,df,tp} => TermRef::OML{name,df:df.as_deref(),tp:tp.as_deref()},
108            Self::Informal{tag,attributes,children,terms} =>
109                TermRef::Informal{tag,attributes,children,terms}
110        }
111    }
112
113     */
114    fn display_top<const SHORT: bool>(
115        &self,
116        f: &mut Formatter<'_>,
117        indent: Option<Indentor>,
118    ) -> std::fmt::Result {
119        if let Some(i) = &indent {
120            i.skip_next();
121        };
122        Self::display_children(
123            TermChildrenIter::One(self),
124            f,
125            |t, i, f| t.display_start::<true>(i, f),
126            |t, f| t.display_short_end(f),
127            indent,
128        )
129    }
130    fn display_start<const SHORT: bool>(
131        &self,
132        ind: &mut Indentor,
133        f: &mut Formatter<'_>,
134    ) -> Result<DFSContinuation<()>, std::fmt::Error> {
135        macro_rules! cont {
136            ($t:expr) => {
137                $t.display_top::<SHORT>(f, Some(ind.clone()))
138            };
139        }
140        match self {
141            Self::OMID(uri) => if SHORT {
142                Display::fmt(uri.name(), f)
143            } else {
144                Display::fmt(uri, f)
145            }
146            .map(|()| DFSContinuation::Continue),
147            Self::OMV(v) => Display::fmt(v, f).map(|()| DFSContinuation::Continue),
148            Self::OMA { head, .. } => {
149                cont!(head)?;
150                f.write_char('(')?;
151                Ok(DFSContinuation::SkipNextAndClose(()))
152            }
153            Self::Field { record, key, owner } => {
154                cont!(record)?;
155                f.write_char('.')?;
156                Display::fmt(key, f)?;
157                if let Some(owner) = owner {
158                    f.write_char('(')?;
159                    cont!(owner)?;
160                    f.write_char(')')?;
161                }
162                Ok(DFSContinuation::SkipChildren)
163            }
164            Self::OML { name, df, tp } => {
165                write!(f, "\"{name}\"")?;
166                if let Some(tp) = tp {
167                    write!(f, "{ind}  :  ")?;
168                    cont!(tp)?;
169                }
170                if let Some(df) = df {
171                    write!(f, "{ind}  := ")?;
172                    cont!(df)?;
173                }
174                Ok(DFSContinuation::SkipChildren)
175            }
176            Self::Informal { .. } => {
177                write!(f, "TODO: Display for Term::Informal")?;
178                Ok(DFSContinuation::SkipChildren)
179            }
180        }
181    }
182    fn display_short_end(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
183        match self {
184            Self::OMID(_) | Self::OMV(_) | Self::Field { .. } => unreachable!(),
185            Self::OMA { .. } => f.write_char(')'),
186            Self::OML { .. } => f.write_char('>'),
187            Self::Informal { .. } => todo!(),
188        }
189    }
190
191    #[inline]
192    pub fn subterm_iter(&self) -> impl Iterator<Item = &'_ Self> {
193        <TermChildrenIter as TreeChildIter<Self>>::dfs(TermChildrenIter::One(self))
194    }
195
196    #[inline]
197    pub fn uri_iter(&self) -> impl Iterator<Item = URIRef<'_>> {
198        self.subterm_iter().filter_map(|t| match t {
199            Self::OMID(uri) => Some(uri.as_uri()),
200            Self::OMV(Var::Ref { declaration, .. }) => Some(declaration.as_uri()),
201            _ => None,
202        })
203    }
204}
205
206impl Display for Term {
207    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208        self.display_top::<true>(f, None)
209    }
210}
211
212pub enum TermChildrenIter<'a> {
213    Slice(std::slice::Iter<'a, Term>),
214    Args(std::slice::Iter<'a, Arg>),
215    One(&'a Term),
216    Two(&'a Term, &'a Term),
217    WithHead(&'a Term, &'a [Arg]),
218    Empty,
219}
220impl<'a> Iterator for TermChildrenIter<'a> {
221    type Item = &'a Term;
222    fn next(&mut self) -> Option<Self::Item> {
223        match self {
224            Self::Slice(i) => i.next(),
225            Self::Args(i) => i.next().map(|a| &a.term),
226            Self::One(t) => {
227                let t = *t;
228                *self = Self::Empty;
229                Some(t)
230            }
231            Self::Two(t1, t2) => {
232                let t1 = *t1;
233                *self = Self::One(t2);
234                Some(t1)
235            }
236            Self::WithHead(head, args) => {
237                let head = *head;
238                *self = Self::Args(args.iter());
239                Some(head)
240            }
241            Self::Empty => None,
242        }
243    }
244}
245
246impl TreeLike for Term {
247    type Child<'a> = &'a Self;
248    type RefIter<'a> = TermChildrenIter<'a>;
249    #[allow(clippy::enum_glob_use)]
250    fn children(&self) -> Option<Self::RefIter<'_>> {
251        use Term::*;
252        match self {
253            OMID(_)
254            | OMV(_)
255            | OML {
256                tp: None, df: None, ..
257            } => None,
258            Field { record, owner, .. } => Some(
259                owner
260                    .as_ref()
261                    .map_or(TermChildrenIter::One(record), |owner| {
262                        TermChildrenIter::Two(record, owner)
263                    }),
264            ),
265            OMA { head, args } => Some(TermChildrenIter::WithHead(head, args)),
266            //Field { record, .. } => Some(TermChildrenIter::One(record)),
267            OML {
268                df: Some(df),
269                tp: Some(tp),
270                ..
271            } => Some(TermChildrenIter::Two(tp, df)),
272            OML {
273                df: None,
274                tp: Some(tp),
275                ..
276            } => Some(TermChildrenIter::One(tp)),
277            OML {
278                df: Some(df),
279                tp: None,
280                ..
281            } => Some(TermChildrenIter::One(df)),
282            Informal { terms, .. } => Some(TermChildrenIter::Slice(terms.iter())),
283        }
284    }
285}
286impl TreeChild<Term> for &Term {
287    fn children<'b>(&self) -> Option<<Term as TreeLike>::RefIter<'b>>
288    where
289        Self: 'b,
290    {
291        <Term as TreeLike>::children(self)
292    }
293}
294
295#[derive(Debug, Clone, Hash, PartialEq, Eq)]
296#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
298#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
299pub struct Arg {
300    pub term: Term,
301    pub mode: ArgMode,
302}
303impl From<(Term, ArgMode)> for Arg {
304    fn from((term, mode): (Term, ArgMode)) -> Self {
305        Self { term, mode }
306    }
307}
308
309#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
310#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
311#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
312#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
313pub enum ArgMode {
314    #[default]
315    Normal,
316    Sequence,
317    Binding,
318    BindingSequence,
319}
320impl ArgMode {
321    #[inline]
322    #[must_use]
323    pub const fn as_char(self) -> char {
324        match self {
325            Self::Normal => 'i',
326            Self::Sequence => 'a',
327            Self::Binding => 'b',
328            Self::BindingSequence => 'B',
329        }
330    }
331}
332impl std::fmt::Display for ArgMode {
333    #[inline]
334    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
335        f.write_char(self.as_char())
336    }
337}
338impl TryFrom<u8> for ArgMode {
339    type Error = ();
340    fn try_from(c: u8) -> Result<Self, Self::Error> {
341        match c {
342            b'i' => Ok(Self::Normal),
343            b'a' => Ok(Self::Sequence),
344            b'b' => Ok(Self::Binding),
345            b'B' => Ok(Self::BindingSequence),
346            _ => Err(()),
347        }
348    }
349}
350impl FromStr for ArgMode {
351    type Err = ();
352    #[inline]
353    fn from_str(s: &str) -> Result<Self, Self::Err> {
354        if s.len() != 1 {
355            return Err(());
356        }
357        s.as_bytes()[0].try_into()
358    }
359}
360
361#[derive(Clone, PartialEq, Eq, Hash)]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
364#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
365pub enum Var {
366    Name(Name),
367    Ref {
368        declaration: DocumentElementURI,
369        is_sequence: Option<bool>,
370    },
371}
372impl Display for Var {
373    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
374        match self {
375            Self::Name(n) => Display::fmt(n, f),
376            Self::Ref { declaration, .. } => Display::fmt(declaration.name().last_name(), f),
377        }
378    }
379}
380impl Debug for Var {
381    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
382        match self {
383            Self::Name(n) => Debug::fmt(n, f),
384            Self::Ref { declaration, .. } => Debug::fmt(declaration, f),
385        }
386    }
387}
388
389#[derive(Clone, Debug, Hash, PartialEq, Eq)]
390#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
391#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
392#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
393pub enum Informal {
394    Term(u8),
395    Node {
396        tag: String,
397        attributes: Box<[(Box<str>, Box<str>)]>,
398        children: Box<[Informal]>,
399    },
400    Text(Box<str>),
401}
402
403impl Informal {
404    #[must_use]
405    pub fn iter_opt(&self) -> Option<impl Iterator<Item = &Self>> {
406        match self {
407            Self::Term(_) | Self::Text(_) => None,
408            Self::Node { children, .. } => Some(InformalIter {
409                curr: children.iter(),
410                stack: Vec::new(),
411            }),
412        }
413    }
414    #[must_use]
415    pub fn iter_mut_opt(&mut self) -> Option<impl Iterator<Item = &mut Self>> {
416        match self {
417            Self::Term(_) | Self::Text(_) => None,
418            Self::Node { children, .. } => Some(InformalIterMut {
419                curr: children.iter_mut(),
420                stack: Vec::new(),
421            }),
422        }
423    }
424}
425
426struct InformalIter<'a> {
427    curr: std::slice::Iter<'a, Informal>,
428    stack: Vec<std::slice::Iter<'a, Informal>>,
429}
430impl<'a> Iterator for InformalIter<'a> {
431    type Item = &'a Informal;
432    fn next(&mut self) -> Option<Self::Item> {
433        let r = self.curr.next().or_else(|| {
434            self.curr = self.stack.pop()?;
435            self.curr.next()
436        });
437        if let Some(Informal::Node { children, .. }) = r {
438            self.stack
439                .push(std::mem::replace(&mut self.curr, children.iter()));
440        }
441        r
442    }
443}
444struct InformalIterMut<'a> {
445    curr: std::slice::IterMut<'a, Informal>,
446    stack: Vec<std::slice::IterMut<'a, Informal>>,
447}
448impl<'a> Iterator for InformalIterMut<'a> {
449    type Item = &'a mut Informal;
450    fn next(&mut self) -> Option<Self::Item> {
451        loop {
452            let r = self.curr.next().or_else(|| {
453                self.curr = self.stack.pop()?;
454                self.curr.next()
455            });
456            if let Some(Informal::Node { children, .. }) = r {
457                self.stack
458                    .push(std::mem::replace(&mut self.curr, children.iter_mut()));
459            } else {
460                return r;
461            }
462        }
463    }
464}
465
466#[cfg(test)]
467mod tests {
468    use crate::uris::{ArchiveURI, BaseURI, ModuleURI, SymbolURI};
469    use crate::{content::terms::Term, oma, oml, oms, omv};
470    use lazy_static::lazy_static;
471    lazy_static! {
472        static ref NAMESPACE: BaseURI = BaseURI::new_unchecked("http://example.com/");
473        static ref ARCHIVE1: ArchiveURI = NAMESPACE.clone() & "some/archive";
474        static ref ARCHIVE2: ArchiveURI = NAMESPACE.clone() & "some/other/archive";
475        static ref MODULE1: ModuleURI = (ARCHIVE1.clone() | "some/module").expect("impossible");
476        static ref MODULE2: ModuleURI = (ARCHIVE2.clone() | "some/module").expect("impossible");
477        static ref SYM1: SymbolURI = (MODULE1.clone() | "some symbol").expect("impossible");
478        static ref SYM2: SymbolURI = (MODULE2.clone() | "other symbol").expect("impossible");
479        static ref FUNC1: SymbolURI = (MODULE1.clone() | "some function").expect("impossible");
480        static ref FUNC2: SymbolURI = (MODULE2.clone() | "other function").expect("impossible");
481        static ref TERM: Term = oma!(oms!(FUNC1.clone()),[
482            {N:oma!(oms!(FUNC2.clone()),[
483                {N:oms!(SYM1.clone())},
484                {N:oms!(SYM2.clone())}
485            ])},
486            {N:oma!(oms!(FUNC1.clone()),[
487                {N:oml!("some name".parse().expect("impossible"); := oms!(SYM2.clone()))},
488                {N:oms!(SYM1.clone())},
489                {N:omv!("some var".parse().expect("impossible");)}
490            ])}
491        ]);
492    }
493
494    #[test]
495    fn test_term_display() {
496        let term = &*TERM;
497        let s = format!("{term}");
498        let refs = r#"some function(
499  other function(
500    some symbol
501    other symbol
502  )
503  some function(
504    "some name"
505      := other symbol
506    some symbol
507    some var
508  )
509)"#;
510        assert_eq!(s, refs);
511    }
512}