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!["".into()]));
137
138    let (_, value_names) = channels.entries.last_mut().unwrap();
139
140    Ok((channel, value_names))
141}
142
143/// Represents errors that can occur when adding a channel manually.
144#[derive(Debug, Error)]
145pub enum ChannelAddingError {
146    /// Indicades that a channel with this name has already been added.
147    #[error("A channel of this name already exists")]
148    AlreadyExists,
149
150    /// Indicates an invalid character was encountered.
151    #[error("Invalid character '{0}' for condition names")]
152    InvalidCharacter(char),
153}
154
155impl From<ValueCheckingError> for ChannelAddingError {
156    fn from(ValueCheckingError(c): ValueCheckingError) -> Self {
157        Self::InvalidCharacter(c)
158    }
159}
160
161/// Represents errors that can occur when parsing conditions.
162#[derive(Copy, Clone, Debug, Error)]
163pub enum ConditionParsingError {
164    /// Indicates an invalid character was encountered.
165    #[error("Invalid character '{0}' for condition names")]
166    InvalidCharacter(char),
167
168    /// Indicates an invalid condition format.
169    #[error("Invalid condition format")]
170    InvalidCondition,
171}
172
173impl From<ValueCheckingError> for ConditionParsingError {
174    fn from(ValueCheckingError(c): ValueCheckingError) -> Self {
175        Self::InvalidCharacter(c)
176    }
177}
178
179/// Represents the kinds of errors that can occur when parsing a line.
180#[derive(Copy, Clone, Debug, Error)]
181pub enum ErrorKind {
182    /// Indicates an error occurred while parsing the line.
183    #[error("Input error while parsing line")]
184    LineParsing,
185
186    /// Indicates an error occurred while parsing an expression.
187    #[error("Parsing expression failed: {0}")]
188    ExpressionParsing(ParseError<ConditionParsingError>),
189
190    /// Indicates conflicting conditions were encountered.
191    #[error("Encountered conflicting conditions: {0}")]
192    ConflictingCondition(InvalidChangeError),
193
194    /// Indicates an invalid character was encountered while parsing the event name.
195    #[error("Invalid character '{0}' in event name")]
196    InvalidCharacterInEventName(char),
197
198    /// Indicates no event was specified.
199    #[error("No event has been specified")]
200    NoEvent,
201
202    /// Indicates a subheader was encountered without a corresponding header.
203    #[error("Subheader without matching header")]
204    SubheaderWithoutHeader,
205}
206
207trait ErrorLine {
208    type Output;
209
210    fn line(self, line: usize) -> Self::Output;
211}
212
213impl ErrorLine for ErrorKind {
214    type Output = Error;
215
216    fn line(self, line: usize) -> Error {
217        Error { line, kind: self }
218    }
219}
220
221impl<T> ErrorLine for Result<T, ErrorKind> {
222    type Output = Result<T, Error>;
223
224    fn line(self, line: usize) -> Result<T, Error> {
225        match self {
226            Ok(value) => Ok(value),
227            Err(err) => Err(err.line(line)),
228        }
229    }
230}
231
232/// Represents errors that can occur during parsing.
233#[derive(Debug, Error)]
234#[error("Line {line}: {kind}")]
235pub struct Error {
236    /// The line the error occured on.
237    line: usize,
238    /// The error kind.
239    kind: ErrorKind,
240}
241
242/// A multilinear info containing the mapped channel and event names.
243#[derive(Default)]
244pub struct NamedMultilinearInfo {
245    /// The parsed `MultilinearInfo` instance.
246    pub info: MultilinearInfo,
247    /// A map associating events with their names.
248    pub events: IndexMap<Event, Vec<Str>>,
249    /// A map associating channels with their names and the names of the channel.
250    pub channels: IndexMap<Channel, (Str, Vec<Str>)>,
251}
252
253/// A parser for multilinear system definitions, supporting incremental parsing
254/// across multiple files or input streams.
255#[derive(Default)]
256pub struct MultilinearParser(NamedMultilinearInfo);
257
258impl MultilinearParser {
259    /// Adds a new channel and sets a default value.
260    ///
261    /// Fails if channel already exists or if the names aren't valid.
262    pub fn add_new_channel(
263        &mut self,
264        channel_name: &str,
265        default_name: &str,
266    ) -> Result<Channel, ChannelAddingError> {
267        let channel_name = valid_name(channel_name)?;
268        let default_name = valid_name(default_name)?;
269
270        if self
271            .0
272            .channels
273            .entries
274            .iter()
275            .any(|(checked_name, _)| checked_name.as_ref() == channel_name)
276        {
277            return Err(ChannelAddingError::AlreadyExists);
278        }
279
280        let channel = self.0.info.add_channel();
281        self.0
282            .channels
283            .insert(channel, (channel_name.into(), vec![default_name.into()]));
284
285        Ok(channel)
286    }
287
288    /// Parses additional multilinear data from the given reader.
289    ///
290    /// # Arguments
291    ///
292    /// * `reader` - The input source to parse from
293    /// * `namespace` - Initial header context/path for events (e.g., `vec!["Main Story".into()]`)
294    ///
295    /// # Example
296    ///
297    /// ```no_run
298    /// use std::fs::File;
299    /// use multilinear_parser::MultilinearParser;
300    ///
301    /// let mut parser = MultilinearParser::default();
302    /// parser.parse(File::open("chapter1.mld").unwrap(), Vec::new()).unwrap();
303    /// parser.parse(File::open("chapter2.mld").unwrap(), Vec::new()).unwrap();
304    /// ```
305    pub fn parse<R: Read>(&mut self, reader: R, parent_namespace: &[Str]) -> Result<(), Error> {
306        let mut child_namespace = Vec::new();
307
308        let NamedMultilinearInfo {
309            info,
310            events,
311            channels,
312        } = &mut self.0;
313
314        let mut condition_groups = Vec::new();
315        let mut condition_lines = Vec::new();
316
317        let mut last_header_line = 0;
318
319        for (line_number, line) in BufReader::new(reader).lines().enumerate() {
320            let Ok(line) = line else {
321                return Err(ErrorKind::LineParsing.line(line_number));
322            };
323
324            if line.trim().is_empty() {
325                if !condition_lines.is_empty() {
326                    condition_groups.push(LogicalExpression::and(condition_lines));
327                    condition_lines = Vec::new();
328                }
329                continue;
330            }
331
332            if let Some(success) = parse_header(&mut child_namespace, &line) {
333                let Ok(changes) = success else {
334                    return Err(ErrorKind::SubheaderWithoutHeader.line(line_number));
335                };
336
337                if let Err(ValueCheckingError(c)) = check_name(&changes.header) {
338                    return Err(ErrorKind::InvalidCharacterInEventName(c)).line(line_number);
339                }
340
341                if !condition_lines.is_empty() {
342                    condition_groups.push(LogicalExpression::and(condition_lines));
343                    condition_lines = Vec::new();
344                }
345
346                if !condition_groups.is_empty() {
347                    let mut event_edit = info.add_event();
348                    for conditions in LogicalExpression::or(condition_groups).expand() {
349                        if let Err(err) = event_edit.add_change(&conditions) {
350                            return Err(ErrorKind::ConflictingCondition(err).line(last_header_line));
351                        }
352                    }
353
354                    let mut namespace = parent_namespace.to_vec();
355                    namespace.extend(changes.path.clone());
356                    events.insert(event_edit.event(), namespace);
357
358                    condition_groups = Vec::new();
359                }
360
361                last_header_line = line_number + 1;
362
363                changes.apply();
364
365                continue;
366            }
367
368            if parent_namespace.is_empty() && child_namespace.is_empty() {
369                return Err(ErrorKind::NoEvent.line(line_number));
370            }
371
372            let parse_expression = |condition: &str| {
373                let Some((channel, changes)) = condition.split_once(':') else {
374                    return Err(ConditionParsingError::InvalidCondition);
375                };
376
377                let (channel, value_names) = channel_info(channels, channel.trim(), info)?;
378                Ok(LogicalExpression::or(
379                    changes
380                        .split(';')
381                        .map(|change| -> Result<_, ValueCheckingError> {
382                            Ok(LogicalExpression::Condition(
383                                if let Some((from, to)) = change.split_once('>') {
384                                    let from = value_index(value_names, from)?;
385                                    let to = value_index(value_names, to)?;
386                                    Condition::change(channel, from, to)
387                                } else {
388                                    let change = value_index(value_names, change)?;
389                                    Condition::new(channel, change)
390                                },
391                            ))
392                        })
393                        .collect::<Result<_, _>>()?,
394                ))
395            };
396
397            let conditions = LogicalExpression::parse_with_expression(&line, parse_expression);
398
399            let conditions = match conditions {
400                Ok(conditions) => conditions,
401                Err(err) => return Err(ErrorKind::ExpressionParsing(err).line(line_number)),
402            };
403
404            condition_lines.push(conditions);
405        }
406
407        if !condition_lines.is_empty() {
408            condition_groups.push(LogicalExpression::and(condition_lines));
409        }
410
411        if !condition_groups.is_empty() {
412            let mut event_edit = info.add_event();
413            for conditions in LogicalExpression::or(condition_groups).expand() {
414                if let Err(err) = event_edit.add_change(&conditions) {
415                    return Err(ErrorKind::ConflictingCondition(err).line(last_header_line));
416                }
417            }
418
419            let mut namespace = parent_namespace.to_vec();
420            namespace.extend(child_namespace);
421            events.insert(event_edit.event(), namespace);
422        }
423
424        Ok(())
425    }
426
427    /// Consumes the parser and returns the fully parsed data.
428    ///
429    /// After calling this, the parser can no longer be used.
430    pub fn into_info(self) -> NamedMultilinearInfo {
431        self.0
432    }
433}
434
435/// Parses a complete multilinear system from a single reader.
436///
437/// This is a convenience wrapper for single-file parsing. For multi-file parsing,
438/// use [`MultilinearParser`] directly.
439///
440/// # Example
441///
442/// ```no_run
443/// use std::fs::File;
444/// use multilinear_parser::parse_multilinear;
445///
446/// let story = parse_multilinear(File::open("story.mld").unwrap()).unwrap();
447/// ```
448pub fn parse_multilinear<R: Read>(reader: R) -> Result<NamedMultilinearInfo, Error> {
449    let mut result = MultilinearParser::default();
450    result.parse(reader, &Vec::new())?;
451    Ok(result.0)
452}