1use crate::document::{Category, CifValue, CifValueRef, DataBlock, Document};
12use crate::lexer::{LexError, Lexer, Quoting, Spanned, Token};
13use molframe_core::diagnostic::{Code, Diagnostic, Diagnostics};
14use molframe_core::io::InputBuffer;
15use molframe_core::span::{ByteSpan, Position};
16use std::borrow::Cow;
17use std::collections::HashSet;
18use std::marker::PhantomData;
19use std::sync::Arc;
20
21pub type ParseResult = Result<(Document, Vec<Diagnostic>), Vec<Diagnostic>>;
23
24#[doc(hidden)]
29#[derive(Clone, Copy, Debug, PartialEq)]
30pub enum CifScalar<'input> {
31 Inapplicable,
33 Unknown,
35 Text(&'input str),
37 Integer(i64),
39 Float(f64),
41}
42
43#[doc(hidden)]
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
49#[repr(transparent)]
50pub(crate) struct ColumnId(u32);
51
52impl ColumnId {
53 pub(crate) fn from_position(position: usize) -> Option<Self> {
54 u32::try_from(position).ok().map(Self)
55 }
56
57 #[must_use]
59 pub(crate) const fn position(self) -> usize {
60 self.0 as usize
61 }
62}
63
64impl<'input> From<CifValueRef<'input>> for CifScalar<'input> {
65 fn from(value: CifValueRef<'input>) -> Self {
66 match value {
67 CifValueRef::Inapplicable => Self::Inapplicable,
68 CifValueRef::Unknown => Self::Unknown,
69 CifValueRef::Text(value) => Self::Text(value),
70 CifValueRef::Integer(value) => Self::Integer(value),
71 CifValueRef::Float(value) => Self::Float(value),
72 }
73 }
74}
75
76#[doc(hidden)]
104pub trait CifEventSink {
105 type Output;
107
108 fn block(&mut self, name: &str);
110
111 fn accepts_category(&self, _category: &str) -> bool {
113 true
114 }
115
116 fn begin_loop(&mut self, _tags: &[(Box<str>, Box<str>)]) {}
118
119 fn value(&mut self, category: &str, item: &str, value: CifScalar<'_>, span: ByteSpan);
121
122 fn end_row(&mut self) {}
124
125 fn end_loop(&mut self) {}
127
128 fn finish(self) -> Self::Output;
130}
131
132pub fn parse(input: &InputBuffer) -> ParseResult {
139 parse_into(input, DocumentSink::all())
140}
141
142#[doc(hidden)]
151pub fn parse_events<S>(
152 input: &InputBuffer,
153 sink: S,
154) -> Result<(S::Output, Vec<Diagnostic>), Vec<Diagnostic>>
155where
156 S: CifEventSink,
157{
158 parse_into(input, EventAdapter(sink))
159}
160
161struct EventAdapter<S>(S);
162
163impl<'input, S> ValueSink<'input> for EventAdapter<S>
164where
165 S: CifEventSink,
166{
167 type Output = S::Output;
168
169 fn block(&mut self, name: &str) {
170 self.0.block(name);
171 }
172
173 fn begin_loop(&mut self, tags: &[(Box<str>, Box<str>)]) {
174 self.0.begin_loop(tags);
175 }
176
177 fn value(
178 &mut self,
179 _column: ColumnId,
180 category: &str,
181 item: &str,
182 text: &'input str,
183 quoting: Quoting,
184 span: ByteSpan,
185 ) {
186 if !self.0.accepts_category(category) {
187 return;
188 }
189 self.0.value(
190 category,
191 item,
192 CifScalar::from(CifValueRef::parse(text, quoting)),
193 span,
194 );
195 }
196
197 fn end_row(&mut self) {
198 self.0.end_row();
199 }
200
201 fn end_loop(&mut self) {
202 self.0.end_loop();
203 }
204
205 fn finish(self) -> Self::Output {
206 self.0.finish()
207 }
208}
209
210pub(crate) trait ValueSink<'input> {
211 type Output;
212
213 fn block(&mut self, name: &str);
214
215 fn begin_loop(&mut self, _tags: &[(Box<str>, Box<str>)]) {}
216
217 fn value(
218 &mut self,
219 column: ColumnId,
220 category: &str,
221 item: &str,
222 text: &'input str,
223 quoting: Quoting,
224 span: ByteSpan,
225 );
226
227 fn end_row(&mut self) {}
228
229 fn end_loop(&mut self) {}
230
231 fn finish(self) -> Self::Output;
232}
233
234pub(crate) fn parse_into<'input, S>(
235 input: &'input InputBuffer,
236 sink: S,
237) -> Result<(S::Output, Vec<Diagnostic>), Vec<Diagnostic>>
238where
239 S: ValueSink<'input>,
240{
241 let mut lexer = match Lexer::new(input.as_bytes()) {
242 Ok(lexer) => lexer,
243 Err(error) => return Err(vec![lex_finding(error)]),
244 };
245 let mut state = ParseState::new(sink);
246
247 loop {
248 match lexer.next_token() {
249 Ok(Some(spanned)) => state.token(spanned),
250 Ok(None) => break,
251 Err(error) => {
252 state.findings.push(lex_finding(error));
253 return Err(state.findings.finish());
254 }
255 }
256 }
257 state.finish()
258}
259
260pub(crate) struct DocumentSink {
261 document: Document,
262 texts: TextInterner,
263 keep: fn(&str) -> bool,
264}
265
266impl DocumentSink {
267 fn all() -> Self {
268 Self::with_filter(|_| true)
269 }
270
271 pub(crate) fn with_filter(keep: fn(&str) -> bool) -> Self {
272 Self {
273 document: Document::new(),
274 texts: TextInterner::default(),
275 keep,
276 }
277 }
278
279 pub(crate) fn block(&mut self, name: &str) {
280 self.document.push(DataBlock::new(name));
281 }
282
283 pub(crate) fn value(
284 &mut self,
285 category: &str,
286 item: &str,
287 text: &str,
288 quoting: Quoting,
289 span: ByteSpan,
290 ) {
291 if !(self.keep)(category) {
292 return;
293 }
294 let Some(block) = self.document.last_block_mut() else {
295 return;
296 };
297 block.category_mut(category, span).column_mut(item).push(
298 CifValue::parse_with_text(text, quoting, |text| self.texts.intern(text)),
299 quoting,
300 );
301 }
302
303 pub(crate) fn finish(self) -> Document {
304 self.document
305 }
306
307 pub(crate) const fn document(&self) -> &Document {
308 &self.document
309 }
310}
311
312impl<'input> ValueSink<'input> for DocumentSink {
313 type Output = Document;
314
315 fn block(&mut self, name: &str) {
316 Self::block(self, name);
317 }
318
319 fn value(
320 &mut self,
321 _column: ColumnId,
322 category: &str,
323 item: &str,
324 text: &'input str,
325 quoting: Quoting,
326 span: ByteSpan,
327 ) {
328 Self::value(self, category, item, text, quoting, span);
329 }
330
331 fn finish(self) -> Self::Output {
332 Self::finish(self)
333 }
334}
335
336#[must_use]
341pub fn split_tag(tag: &str) -> (&str, &str) {
342 let body = match tag.strip_prefix('_') {
343 Some(body) => body,
344 None => tag,
345 };
346 match body.split_once('.') {
347 Some((category, item)) => (category, item),
348 None => (body, body),
349 }
350}
351
352include!("document/state.rs");
353
354#[derive(Default)]
355struct TextInterner {
356 values: HashSet<Arc<str>>,
357}
358
359impl TextInterner {
360 fn intern(&mut self, text: &str) -> Arc<str> {
361 if let Some(value) = self.values.get(text) {
362 return Arc::clone(value);
363 }
364 let value: Arc<str> = text.into();
365 self.values.insert(Arc::clone(&value));
366 value
367 }
368}
369
370fn lex_finding(error: LexError) -> Diagnostic {
372 match error {
373 LexError::UnterminatedQuote(at) => Diagnostic::new(Code::E1101).at(ByteSpan::empty(at)),
374 LexError::UnterminatedText(at) => Diagnostic::new(Code::E1102).at(ByteSpan::empty(at)),
375 LexError::NotText => Diagnostic::new(Code::E1201)
376 .with_message("input is not valid text")
377 .at(ByteSpan::empty(Position::START)),
378 LexError::PositionOverflow(at) => Diagnostic::new(Code::E1201)
379 .with_message("input exceeds the supported source-position range")
380 .at(ByteSpan::empty(at)),
381 }
382}
383
384#[derive(Clone, Copy, Debug)]
390pub struct Rows<'a> {
391 category: &'a Category,
392 row: usize,
393}
394
395impl<'a> Rows<'a> {
396 #[must_use]
398 pub const fn new(category: &'a Category) -> Self {
399 Self { category, row: 0 }
400 }
401
402 #[must_use]
404 pub const fn category(&self) -> &'a Category {
405 self.category
406 }
407
408 #[must_use]
410 pub const fn row(&self) -> usize {
411 self.row
412 }
413
414 pub fn advance(&mut self) -> bool {
416 self.row += 1;
417 self.row < self.category.row_count()
418 }
419
420 #[must_use]
422 pub fn text(&self, item: &str) -> Option<&'a str> {
423 self.category.value(item, self.row)?.as_str()
424 }
425
426 #[must_use]
432 pub fn identifier(&self, item: &str) -> Option<Cow<'a, str>> {
433 self.category.value(item, self.row)?.as_identifier()
434 }
435
436 #[must_use]
438 pub fn integer(&self, item: &str) -> Option<i64> {
439 self.category.value(item, self.row)?.as_integer()
440 }
441
442 #[must_use]
444 pub fn float(&self, item: &str) -> Option<f64> {
445 self.category.value(item, self.row)?.as_float()
446 }
447
448 #[must_use]
450 pub fn is_recorded(&self, item: &str) -> bool {
451 self.category
452 .value(item, self.row)
453 .is_some_and(CifValue::is_recorded)
454 }
455}
456
457#[cfg(test)]
458#[path = "document_tests.rs"]
459mod tests;