multilinear_parser/
lib.rs

1#![deny(missing_docs)]
2
3//! The `multilinear-parser` library provides functionality to parse a multilinear system from a text-based format.
4//! It allows you to define events, which rely on various channel specific conditions or changes using a markdown inspired syntax.
5//!
6//! Example Event Syntax:
7//!
8//! ```text
9//! # Move to Livingroom
10//!
11//! place: bedroom > livingroom
12//!
13//! # Get Dressed
14//!
15//! place: bedroom
16//! clothes: pajamas > casual
17//! ```
18//!
19//! Supports logical combinations:
20//!
21//! ```text
22//! (clothes: pajamas | clothes: casual) & place: bedroom
23//! ```
24
25use header_parsing::parse_header;
26use logical_expressions::{LogicalExpression, ParseError};
27use thiserror::Error;
28
29use multilinear::{Channel, Condition, Event, InvalidChangeError, MultilinearInfo};
30
31use std::{
32    io::{BufRead, BufReader, Read},
33    marker::PhantomData,
34};
35
36/// Stores index based maps.
37pub struct IndexMap<I: Into<usize> + From<usize>, V> {
38    entries: Vec<V>,
39    index: PhantomData<I>,
40}
41
42impl<I: Into<usize> + From<usize>, V> Default for IndexMap<I, V> {
43    fn default() -> Self {
44        Self {
45            entries: Vec::new(),
46            index: PhantomData,
47        }
48    }
49}
50
51impl<I: Into<usize> + From<usize>, V> IndexMap<I, V> {
52    fn insert(&mut self, index: I, value: V) {
53        let i = index.into();
54        assert_eq!(i, self.entries.len(), "Wrong insertion order");
55        self.entries.push(value);
56    }
57}
58
59/// An iterator over indices and values of an index map.
60pub struct IntoIter<I: Into<usize> + From<usize>, V> {
61    data: std::iter::Enumerate<std::vec::IntoIter<V>>,
62    index: PhantomData<I>,
63}
64
65impl<I: Into<usize> + From<usize>, V> Iterator for IntoIter<I, V> {
66    type Item = (I, V);
67
68    fn next(&mut self) -> Option<(I, V)> {
69        let (i, value) = self.data.next()?;
70        Some((i.into(), value))
71    }
72}
73
74impl<I: Into<usize> + From<usize>, V> IntoIterator for IndexMap<I, V> {
75    type Item = (I, V);
76    type IntoIter = IntoIter<I, V>;
77
78    fn into_iter(self) -> IntoIter<I, V> {
79        IntoIter {
80            data: self.entries.into_iter().enumerate(),
81            index: PhantomData,
82        }
83    }
84}
85
86#[derive(Copy, Clone, Debug)]
87struct ValueCheckingError(char);
88
89type Str = Box<str>;
90
91fn check_name(name: &str) -> Result<(), ValueCheckingError> {
92    if let Some(c) = name
93        .chars()
94        .find(|&c| !c.is_alphanumeric() && !"_- ".contains(c))
95    {
96        Err(ValueCheckingError(c))
97    } else {
98        Ok(())
99    }
100}
101
102fn valid_name(name: &str) -> Result<&str, ValueCheckingError> {
103    let name = name.trim();
104    check_name(name)?;
105    Ok(name)
106}
107
108fn value_index(value_names: &mut Vec<Str>, name: &str) -> Result<usize, ValueCheckingError> {
109    let name = valid_name(name)?;
110
111    if let Some(index) = value_names.iter().position(|x| x.as_ref() == name) {
112        return Ok(index);
113    }
114
115    let index = value_names.len();
116    value_names.push(name.into());
117    Ok(index)
118}
119
120fn channel_info<'a>(
121    channels: &'a mut IndexMap<Channel, (Str, Vec<Str>)>,
122    name: &str,
123    info: &mut MultilinearInfo,
124) -> Result<(Channel, &'a mut Vec<Str>), ValueCheckingError> {
125    let name = valid_name(name)?;
126
127    if let Some(i) = channels
128        .entries
129        .iter()
130        .position(|(checked_name, _)| checked_name.as_ref() == name)
131    {
132        return Ok((Channel(i), &mut channels.entries[i].1));
133    }
134
135    let channel = info.add_channel();
136    channels.insert(channel, (name.into(), Vec::new()));
137
138    let (_, value_names) = channels.entries.last_mut().unwrap();
139
140    let _ = value_index(value_names, "");
141
142    Ok((channel, value_names))
143}
144
145/// Represents errors that can occur when parsing conditions.
146#[derive(Copy, Clone, Debug, Error)]
147pub enum ConditionParsingError {
148    /// Indicates an invalid character was encountered.
149    #[error("Invalid character '{0}' for condition names")]
150    InvalidCharacter(char),
151
152    /// Indicates an invalid condition format.
153    #[error("Invalid condition format")]
154    InvalidCondition,
155}
156
157impl From<ValueCheckingError> for ConditionParsingError {
158    fn from(ValueCheckingError(c): ValueCheckingError) -> Self {
159        Self::InvalidCharacter(c)
160    }
161}
162
163/// Represents the kinds of errors that can occur when parsing a line.
164#[derive(Copy, Clone, Debug, Error)]
165pub enum ErrorKind {
166    /// Indicates an error occurred while parsing the line.
167    #[error("Input error while parsing line")]
168    LineParsing,
169
170    /// Indicates an error occurred while parsing an expression.
171    #[error("Parsing expression failed: {0}")]
172    ExpressionParsing(ParseError<ConditionParsingError>),
173
174    /// Indicates conflicting conditions were encountered.
175    #[error("Encountered conflicting conditions: {0}")]
176    ConflictingCondition(InvalidChangeError),
177
178    /// Indicates an invalid character was encountered while parsing the event name.
179    #[error("Invalid character '{0}' in event name")]
180    InvalidCharacterInEventName(char),
181
182    /// Indicates no event was specified.
183    #[error("No event has been specified")]
184    NoEvent,
185
186    /// Indicates a subheader was encountered without a corresponding header.
187    #[error("Subheader without matching header")]
188    SubheaderWithoutHeader,
189}
190
191trait ErrorLine {
192    type Output;
193
194    fn line(self, line: usize) -> Self::Output;
195}
196
197impl ErrorLine for ErrorKind {
198    type Output = Error;
199
200    fn line(self, line: usize) -> Error {
201        Error { line, kind: self }
202    }
203}
204
205impl<T> ErrorLine for Result<T, ErrorKind> {
206    type Output = Result<T, Error>;
207
208    fn line(self, line: usize) -> Result<T, Error> {
209        match self {
210            Ok(value) => Ok(value),
211            Err(err) => Err(err.line(line)),
212        }
213    }
214}
215
216/// Represents errors that can occur during parsing.
217#[derive(Debug, Error)]
218#[error("Line {line}: {kind}")]
219pub struct Error {
220    /// The line the error occured on.
221    line: usize,
222    /// The error kind.
223    kind: ErrorKind,
224}
225
226/// A multilinear info containing the mapped channel and event names.
227#[derive(Default)]
228pub struct NamedMultilinearInfo {
229    /// The parsed `MultilinearInfo` instance.
230    pub info: MultilinearInfo,
231    /// A map associating events with their names.
232    pub events: IndexMap<Event, Vec<Str>>,
233    /// A map associating channels with their names and the names of the channel.
234    pub channels: IndexMap<Channel, (Str, Vec<Str>)>,
235}
236
237/// A parser for multilinear system definitions, supporting incremental parsing
238/// across multiple files or input streams.
239#[derive(Default)]
240pub struct MultilinearParser(NamedMultilinearInfo);
241
242impl MultilinearParser {
243    /// Parses additional multilinear data from the given reader.
244    ///
245    /// # Arguments
246    ///
247    /// * `reader` - The input source to parse from
248    /// * `namespace` - Initial header context/path for events (e.g., `vec!["Main Story".into()]`)
249    ///
250    /// # Example
251    ///
252    /// ```no_run
253    /// use std::fs::File;
254    /// use multilinear_parser::MultilinearParser;
255    ///
256    /// let mut parser = MultilinearParser::default();
257    /// parser.parse(File::open("chapter1.mld").unwrap(), Vec::new()).unwrap();
258    /// parser.parse(File::open("chapter2.mld").unwrap(), Vec::new()).unwrap();
259    /// ```
260    pub fn parse<R: Read>(&mut self, reader: R, parent_namespace: &[Str]) -> Result<(), Error> {
261        let mut child_namespace = Vec::new();
262
263        let NamedMultilinearInfo {
264            info,
265            events,
266            channels,
267        } = &mut self.0;
268
269        let mut condition_groups = Vec::new();
270        let mut condition_lines = Vec::new();
271
272        let mut last_header_line = 0;
273
274        for (line_number, line) in BufReader::new(reader).lines().enumerate() {
275            let Ok(line) = line else {
276                return Err(ErrorKind::LineParsing.line(line_number));
277            };
278
279            if line.trim().is_empty() {
280                if !condition_lines.is_empty() {
281                    condition_groups.push(LogicalExpression::and(condition_lines));
282                    condition_lines = Vec::new();
283                }
284                continue;
285            }
286
287            if let Some(success) = parse_header(&mut child_namespace, &line) {
288                let Ok(changes) = success else {
289                    return Err(ErrorKind::SubheaderWithoutHeader.line(line_number));
290                };
291
292                if let Err(ValueCheckingError(c)) = check_name(&changes.header) {
293                    return Err(ErrorKind::InvalidCharacterInEventName(c)).line(line_number);
294                }
295
296                if !condition_lines.is_empty() {
297                    condition_groups.push(LogicalExpression::and(condition_lines));
298                    condition_lines = Vec::new();
299                }
300
301                if !condition_groups.is_empty() {
302                    let mut event_edit = info.add_event();
303                    for conditions in LogicalExpression::or(condition_groups).expand() {
304                        if let Err(err) = event_edit.add_change(&conditions) {
305                            return Err(ErrorKind::ConflictingCondition(err).line(last_header_line));
306                        }
307                    }
308
309                    let mut namespace = parent_namespace.to_vec();
310                    namespace.extend(changes.path.clone());
311                    events.insert(event_edit.event(), namespace);
312
313                    condition_groups = Vec::new();
314                }
315
316                last_header_line = line_number + 1;
317
318                changes.apply();
319
320                continue;
321            }
322
323            if parent_namespace.is_empty() && child_namespace.is_empty() {
324                return Err(ErrorKind::NoEvent.line(line_number));
325            }
326
327            let parse_expression = |condition: &str| {
328                let Some((channel, changes)) = condition.split_once(':') else {
329                    return Err(ConditionParsingError::InvalidCondition);
330                };
331
332                let (channel, value_names) = channel_info(channels, channel.trim(), info)?;
333                Ok(LogicalExpression::or(
334                    changes
335                        .split(';')
336                        .map(|change| -> Result<_, ValueCheckingError> {
337                            Ok(LogicalExpression::Condition(
338                                if let Some((from, to)) = change.split_once('>') {
339                                    let from = value_index(value_names, from)?;
340                                    let to = value_index(value_names, to)?;
341                                    Condition::change(channel, from, to)
342                                } else {
343                                    let change = value_index(value_names, change)?;
344                                    Condition::new(channel, change)
345                                },
346                            ))
347                        })
348                        .collect::<Result<_, _>>()?,
349                ))
350            };
351
352            let conditions = LogicalExpression::parse_with_expression(&line, parse_expression);
353
354            let conditions = match conditions {
355                Ok(conditions) => conditions,
356                Err(err) => return Err(ErrorKind::ExpressionParsing(err).line(line_number)),
357            };
358
359            condition_lines.push(conditions);
360        }
361
362        if !condition_lines.is_empty() {
363            condition_groups.push(LogicalExpression::and(condition_lines));
364        }
365
366        if !condition_groups.is_empty() {
367            let mut event_edit = info.add_event();
368            for conditions in LogicalExpression::or(condition_groups).expand() {
369                if let Err(err) = event_edit.add_change(&conditions) {
370                    return Err(ErrorKind::ConflictingCondition(err).line(last_header_line));
371                }
372            }
373
374            let mut namespace = parent_namespace.to_vec();
375            namespace.extend(child_namespace);
376            events.insert(event_edit.event(), namespace);
377        }
378
379        Ok(())
380    }
381
382    /// Consumes the parser and returns the fully parsed data.
383    ///
384    /// After calling this, the parser can no longer be used.
385    pub fn into_info(self) -> NamedMultilinearInfo {
386        self.0
387    }
388}
389
390/// Parses a complete multilinear system from a single reader.
391///
392/// This is a convenience wrapper for single-file parsing. For multi-file parsing,
393/// use [`MultilinearParser`] directly.
394///
395/// # Example
396///
397/// ```no_run
398/// use std::fs::File;
399/// use multilinear_parser::parse_multilinear;
400///
401/// let story = parse_multilinear(File::open("story.mld").unwrap()).unwrap();
402/// ```
403pub fn parse_multilinear<R: Read>(reader: R) -> Result<NamedMultilinearInfo, Error> {
404    let mut result = MultilinearParser::default();
405    result.parse(reader, &Vec::new())?;
406    Ok(result.0)
407}