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 }, OMA {
27 head: VarOrSym,
28 notation: Option<Name>,
29 },
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), key: name, 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 }
166 }
167 }
168}
169
170#[derive(Debug, Clone)]
171pub enum TermOrList {
172 Term(Term),
173 List(Vec<Option<Term>>),
174}
175impl TermOrList {
176 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 Self::Unresolved(name) => {
223 match state.resolve_variable_name(name) {
224 Var::Name(name) => {
225 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}