ftml_extraction/open/
terms.rs

1use crate::extractor::FTMLExtractor;
2use either::Either;
3use flams_ontology::content::terms::{Arg, ArgMode, Term, Var};
4use flams_ontology::uris::{ContentURI, DocumentElementURI, Name};
5use std::fmt::Display;
6use std::str::FromStr;
7
8use crate::errors::FTMLError;
9use flams_ontology::{omfp, omsp};
10#[cfg(feature = "rdf")]
11use flams_ontology::{triple, uris::URIOrRefTrait};
12
13#[derive(Debug, Clone)]
14pub enum OpenTerm {
15    Symref {
16        uri: ContentURI,
17        notation: Option<Name>,
18    },
19    Varref {
20        name: PreVar,
21        notation: Option<Name>,
22    },
23    OML {
24        name: Name,
25    }, //,tp:Option<Term>,df:Option<Term>},
26    OMA {
27        head: VarOrSym,
28        notation: Option<Name>,
29        //args:SmallVec<Option<(TermOrList,ArgMode)>,9>
30    },
31    Complex(VarOrSym),
32}
33impl OpenTerm {
34    #[must_use]
35    pub fn take_head(self) -> VarOrSym {
36        match self {
37            Self::Symref { uri, .. } => VarOrSym::S(uri),
38            Self::Varref { name, .. } => VarOrSym::V(name),
39            Self::OML { name, .. } => VarOrSym::V(PreVar::Unresolved(name)),
40            Self::OMA { head, .. } | Self::Complex(head, ..) => head,
41        }
42    }
43    pub fn close<E: FTMLExtractor>(self, extractor: &mut E) -> Term {
44        match self {
45            Self::Symref {
46                uri,
47                notation: _todo,
48            } => {
49                #[cfg(feature = "rdf")]
50                if E::RDF {
51                    let iri = extractor.get_document_iri();
52                    extractor.add_triples([triple!(<(iri)> ulo:CROSSREFS <(uri.to_iri())>)]);
53                }
54                Term::OMID(uri)
55            }
56            Self::Varref {
57                name,
58                notation: _todo,
59            } => name.resolve(extractor),
60            Self::OML { name } => {
61                let (tp, df) = if let Some((tp, df)) = extractor.close_decl() {
62                    (tp, df)
63                } else {
64                    extractor.add_error(FTMLError::NotInContent);
65                    (None, None)
66                };
67                Term::OML {
68                    name,
69                    df: df.map(Box::new),
70                    tp: tp.map(Box::new),
71                }
72            }
73            Self::Complex(varorsym) => {
74                let term = extractor.close_complex_term();
75                if let Some(term) = term {
76                    match term.into_record_field() {
77                        Ok((p, name)) => {
78                            omfp!((p).(name) = (varorsym.resolve(extractor)))
79                        }
80                        Err(mut t) => {
81                            if let Term::Field { owner, .. } = &mut t {
82                                *owner = Some(Box::new(varorsym.resolve(extractor)));
83                            }
84                            t
85                        }
86                    }
87                } else {
88                    extractor.add_error(FTMLError::MissingTermForComplex(varorsym.clone()));
89                    varorsym.resolve(extractor)
90                }
91            }
92            Self::OMA {
93                head,
94                notation: _todo,
95            } => {
96                let (args, head_term) = extractor.close_args();
97                let args = args.into_boxed_slice();
98                let mut head = head.resolve(extractor);
99
100                if let Some(head_term) = head_term {
101                    match head_term.into_record_field() {
102                        Ok((p, name)) => return omfp!((p).(name) = (head)),
103                        Err(t) => head = t,
104                    }
105                }
106
107                if matches!(&head,omsp!(fp) if *fp == *flams_ontology::metatheory::FIELD_PROJECTION)
108                    && args.len() == 2
109                {
110                    let mut args = args.into_vec().into_iter();
111                    let Some(first) = args.next() else {
112                        unreachable!()
113                    };
114                    let Some(second) = args.next() else {
115                        unreachable!()
116                    };
117                    if let Arg {
118                        term:
119                            Term::OML {
120                                name,
121                                df: None,
122                                tp: None,
123                            },
124                        mode: ArgMode::Normal,
125                    } = second
126                    {
127                        Term::Field {
128                            record: Box::new(first.term), // TODO avoid clone here
129                            key: name,                    // TODO avoid clone here
130                            owner: None,
131                        }
132                    } else {
133                        Term::OMA {
134                            head: Box::new(head),
135                            args: Box::new([first, second]),
136                        }
137                    }
138                } else {
139                    Term::OMA {
140                        head: Box::new(head),
141                        args,
142                    }
143                }
144
145                /*
146                if let Some(oma!(omsp!(ref fp),[N:ref p,N:Term::OML {ref name,tp:Option::None,df:Option::None}])) = head_term {
147                    if *fp == *flams_ontology::metatheory::FIELD_PROJECTION {
148                        //println!("Is Field!");
149                        return omfp!((p.clone()).(name.clone()) = (head)) // TODO avoid clone here
150                    }
151                    head = head_term.unwrap_or_else(|| unreachable!());
152                }
153                match (head,args) {
154                    (omsp!(fp),box [Arg{ref term,mode:ArgMode::Normal},Arg{term:Term::OML{ref name,tp:Option::None,df:Option::None},mode:ArgMode::Normal}]) if fp == *flams_ontology::metatheory::FIELD_PROJECTION => {
155                        //println!("Is Field!");
156                        Term::Field {
157                            record:Box::new(term.clone()), // TODO avoid clone here
158                            key:name.clone(), // TODO avoid clone here
159                            owner:None
160                        }
161                    }
162                    (head,args) => Term::OMA {head:Box::new(head),args}
163                }
164                 */
165            }
166        }
167    }
168}
169
170#[derive(Debug, Clone)]
171pub enum TermOrList {
172    Term(Term),
173    List(Vec<Option<Term>>),
174}
175impl TermOrList {
176    /// #### Errors
177    pub fn close(self, mode: ArgMode) -> Result<Arg, Arg> {
178        match self {
179            Self::Term(t) => Ok(Arg { term: t, mode }),
180            Self::List(ls) => {
181                let mut ret = Vec::new();
182                let mut ls = ls.into_iter();
183                while let Some(Some(t)) = ls.next() {
184                    ret.push(t);
185                }
186                let ret = Arg {
187                    term: Term::term_list(ret.into_iter()),
188                    mode,
189                };
190                for a in ls {
191                    if a.is_some() {
192                        return Err(ret);
193                    }
194                }
195                Ok(ret)
196            }
197        }
198    }
199}
200
201#[derive(Clone, Debug, Hash, PartialEq, Eq)]
202pub enum PreVar {
203    Resolved(DocumentElementURI),
204    Unresolved(Name),
205}
206impl Display for PreVar {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        match self {
209            Self::Resolved(declaration) => Display::fmt(declaration, f),
210            Self::Unresolved(name) => Display::fmt(name, f),
211        }
212    }
213}
214impl PreVar {
215    fn resolve<State: FTMLExtractor>(self, state: &State) -> Term {
216        Term::OMV(match self {
217            Self::Resolved(declaration) => Var::Ref {
218                declaration,
219                is_sequence: None,
220            },
221            // TODO can we know is_sequence yet?
222            Self::Unresolved(name) => {
223                match state.resolve_variable_name(name) {
224                    Var::Name(name) => {
225                        //state.add_error(FTMLError::UnresolvedVariable(name.clone()));
226                        Var::Name(name)
227                    }
228                    v @ Var::Ref { .. } => v,
229                }
230            }
231        })
232    }
233    #[inline]
234    #[must_use]
235    pub const fn name(&self) -> &Name {
236        match self {
237            Self::Resolved(declaration) => declaration.name(),
238            Self::Unresolved(name) => name,
239        }
240    }
241}
242
243#[derive(Clone, Debug, Hash, PartialEq, Eq)]
244pub enum VarOrSym {
245    S(ContentURI),
246    V(PreVar),
247}
248impl Display for VarOrSym {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        match self {
251            Self::S(uri) => Display::fmt(uri, f),
252            Self::V(v) => Display::fmt(v, f),
253        }
254    }
255}
256impl VarOrSym {
257    #[allow(clippy::needless_pass_by_ref_mut)]
258    fn resolve<E: FTMLExtractor>(self, extractor: &mut E) -> Term {
259        match self {
260            Self::S(uri) => {
261                #[cfg(feature = "rdf")]
262                if E::RDF {
263                    let iri = extractor.get_document_iri();
264                    extractor.add_triples([triple!(<(iri)> ulo:CROSSREFS <(uri.to_iri())>)]);
265                }
266                Term::OMID(uri)
267            }
268            Self::V(pv) => pv.resolve(extractor),
269        }
270    }
271}
272
273#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
274pub enum OpenTermKind {
275    OMID,
276    OMV,
277    OMA,
278    OML,
279    Complex,
280}
281impl FromStr for OpenTermKind {
282    type Err = ();
283    fn from_str(s: &str) -> Result<Self, Self::Err> {
284        Ok(match s {
285            "OMID" | "OMMOD" => Self::OMID,
286            "OMV" => Self::OMV,
287            "OMA" | "OMBIND" => Self::OMA,
288            "OML" => Self::OML,
289            "complex" => Self::Complex,
290            _ => return Err(()),
291        })
292    }
293}
294
295#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
296pub struct OpenArg {
297    pub index: Either<u8, (u8, u8)>,
298    pub mode: ArgMode,
299}
300impl OpenArg {
301    #[allow(clippy::cast_possible_truncation)]
302    pub fn from_strs<Idx: AsRef<str>, M: AsRef<str>>(idx: Idx, mode: Option<M>) -> Option<Self> {
303        let mode = mode
304            .and_then(|s| s.as_ref().parse().ok())
305            .unwrap_or_default();
306        let idx = idx.as_ref();
307        let index = if idx.chars().count() > 1 {
308            let a = idx
309                .chars()
310                .next()
311                .unwrap_or_else(|| unreachable!())
312                .to_digit(10);
313            let b = u32::from_str(&idx[1..]).ok();
314            match (a, b) {
315                (Some(a), Some(b)) if a < 256 && b < 256 => Either::Right((a as u8, b as u8)),
316                _ => return None,
317            }
318        } else if idx.len() == 1 {
319            let a = idx
320                .chars()
321                .next()
322                .unwrap_or_else(|| unreachable!())
323                .to_digit(10)?;
324            if a < 256 {
325                Either::Left(a as u8)
326            } else {
327                return None;
328            }
329        } else {
330            return None;
331        };
332        Some(Self { index, mode })
333    }
334}