dent_parse/
lib.rs

1mod error;
2mod repr;
3mod tokenizer;
4pub use error::*;
5pub use repr::*;
6use tokenizer::{Token, Tokenizer};
7
8#[cfg(test)]
9mod tests;
10
11use std::{
12    collections::HashMap,
13    io::Read,
14    path::{Path, PathBuf},
15    sync::{Arc, Mutex},
16};
17
18/// Alias for a trait object that represents a function that can be called from
19/// Dent. The function takes a reference to a value and returns a value.
20///
21/// The function can be called from Dent using the `@` operator, after
22/// being registered with `Dent::add_function`.
23///
24/// A Dent function can only take a single argument, for simplicity.
25/// If you need to pass multiple arguments, you can use a list or dictionary.
26///
27/// # Examples
28/// ```
29/// use dent_parse::{Dent, Value, Function};
30/// use std::collections::HashMap;
31///
32/// let mut functions: HashMap<String, Box<Function>> = HashMap::new();
33/// functions.insert(
34///     "sum".to_string(),
35///     Box::new(move |value: &Value| -> Value {
36///         let mut sum = 0;
37///         if let Value::List(values) = value {
38///             for value in values.iter() {
39///                 if let Value::Int(i) = value {
40///                     sum += i;
41///                 }
42///             }
43///             Value::Int(sum)
44///         } else if let Value::Int(i) = value {
45///             Value::Int(*i)
46///         } else {
47///             Value::None
48///         }
49///     }),
50/// );
51/// let parser = Dent::new(functions);
52///
53/// assert_eq!(parser.parse("@sum {}"), Ok(Value::None));
54/// assert_eq!(parser.parse("@sum 0"), Ok(Value::Int(0)));
55/// assert_eq!(parser.parse("@sum [ 1 2 3 ]"), Ok(Value::Int(6)));
56/// ```
57pub type Function = dyn for<'a> Fn(&Value<'a>) -> Value<'a> + Send + Sync;
58
59/// Main struct for parsing Dent.
60///
61/// This struct is used to parse Dent files and strings. It can also be used to
62/// register functions that can be called from Dent.
63///
64/// # Examples
65/// ```
66/// use dent_parse::{Dent, Value};
67/// use std::collections::HashMap;
68///
69/// let parser = Dent::default();
70///
71/// assert_eq!(parser.parse("foo"), Ok(Value::Str("foo")));
72/// assert_eq!(parser.parse("[ 1 2 3 ]"), Ok(Value::List(vec![
73///     Value::Int(1),
74///     Value::Int(2),
75///     Value::Int(3)
76/// ])));
77/// ```
78pub struct Dent {
79    internal: Arc<Mutex<DentInternal>>,
80}
81
82struct Import {
83    src: &'static str,
84    value: Value<'static>,
85}
86
87impl Drop for Import {
88    fn drop(&mut self) {
89        unsafe {
90            let b = Box::from_raw(self.src as *const str as *mut str);
91            std::mem::drop(b);
92        }
93    }
94}
95
96struct DentInternal {
97    functions: HashMap<String, Arc<Function>>,
98    import_map: HashMap<PathBuf, Import>,
99}
100
101struct ParserState<'s> {
102    tokenizer: Tokenizer<'s>,
103    token: Token<'s>,
104}
105
106impl<'s> ParserState<'s> {
107    fn new(mut tokenizer: Tokenizer<'s>) -> Result<Self> {
108        let token = tokenizer.next()?;
109        Ok(ParserState { tokenizer, token })
110    }
111
112    fn next(&mut self) -> Result<()> {
113        self.token = self.tokenizer.next()?;
114        Ok(())
115    }
116}
117
118impl Dent {
119    /// Creates a new Dent parser with the given functions.
120    ///
121    /// If you want to use the built-in functions, you can use `Dent::default`,
122    /// or call `Dent::add_builtins` after creating the parser.
123    pub fn new(functions: HashMap<String, Box<Function>>) -> Dent {
124        let functions = functions
125            .into_iter()
126            .map(|(k, v)| (k, Arc::new(v) as Arc<Function>))
127            .collect();
128
129        let internal = DentInternal {
130            functions,
131            import_map: HashMap::new(),
132        };
133
134        Dent {
135            internal: Arc::new(Mutex::new(internal)),
136        }
137    }
138
139    /// Adds the built-in functions to the parser.
140    ///
141    /// This function adds the following functions:
142    /// - `import`: Imports a Dent file. Takes a string (file path) as an argument.
143    /// - `merge`: Merges a list of lists or a list of dicts into a single list or dict.
144    pub fn add_builtins(&mut self) {
145        let internal = self.internal.clone();
146
147        let outer_functions = &mut self.internal.lock().unwrap().functions;
148
149        outer_functions.insert(
150            "import".to_string(),
151            Arc::new(move |value| {
152                if let Value::Str(s) = value {
153                    let path = Path::new(s);
154
155                    let value = Self::import(internal.clone(), path);
156
157                    match value {
158                        Ok(v) => v,
159                        Err(_) => Value::None,
160                    }
161                } else {
162                    Value::None
163                }
164            }),
165        );
166
167        outer_functions.insert(
168            "merge".to_string(),
169            Arc::new(move |value| {
170                // we want either a list of dicts or a list of lists
171                if let Value::List(values) = value {
172                    let mut result = Vec::new();
173                    let mut is_dict = None;
174                    for value in values.iter() {
175                        if let Value::List(values) = value {
176                            if is_dict.is_some() && is_dict.unwrap() {
177                                return Value::None;
178                            }
179                            is_dict = Some(false);
180                            result.extend(values.clone());
181                        } else if let Value::Dict(values) = value {
182                            if is_dict.is_some() && !is_dict.unwrap() {
183                                return Value::None;
184                            }
185                            is_dict = Some(true);
186                            result.push(Value::Dict(values.clone()));
187                        }
188                    }
189
190                    match is_dict {
191                        Some(true) => Value::Dict(
192                            result
193                                .into_iter()
194                                .flat_map(|v| {
195                                    if let Value::Dict(d) = v {
196                                        d
197                                    } else {
198                                        panic!("Expected dict");
199                                    }
200                                })
201                                .collect(),
202                        ),
203                        Some(false) => Value::List(result),
204                        None => Value::None,
205                    }
206                } else {
207                    Value::None
208                }
209            }),
210        );
211    }
212
213    /// Adds a function to the parser.
214    ///
215    /// The function can be called from Dent using the `@` operator.
216    /// The function takes a reference to a value and returns a value.
217    /// The function can only take a single argument, for simplicity.
218    ///
219    /// # Examples
220    /// ```
221    /// use dent_parse::{Dent, Value};
222    ///
223    /// let mut dent = Dent::default();
224    /// dent.add_function("count", Box::new(|value| {
225    ///     if let Value::List(values) = value {
226    ///         Value::Int(values.len() as i64)
227    ///     } else {
228    ///         Value::None
229    ///     }
230    /// }));
231    /// assert_eq!(dent.parse("@count [ 1 2 3 ]"), Ok(Value::Int(3)));
232    /// ```
233    pub fn add_function(&mut self, name: &str, function: Box<Function>) {
234        let function = Arc::new(function);
235
236        let outer_functions = &mut self.internal.lock().unwrap().functions;
237
238        outer_functions.insert(name.to_string(), function);
239    }
240
241    /// Parses a Dent string.
242    ///
243    /// The returned value is a zero-copy representation of the parsed Dent
244    /// string. This means that the returned value borrows from the input string.
245    ///
246    /// If you want to parse a file, use `Dent::parse_file` instead.
247    ///
248    /// # Examples
249    /// ```
250    /// use dent_parse::{Dent, Value};
251    ///
252    /// let parser = Dent::default();
253    ///
254    /// assert_eq!(parser.parse("foo"), Ok(Value::Str("foo")));
255    /// assert_eq!(parser.parse("2"), Ok(Value::Int(2)));
256    /// assert_eq!(parser.parse("2.0"), Ok(Value::Float(2.0)));
257    /// assert_eq!(parser.parse("true"), Ok(Value::Bool(true)));
258    /// ```
259    pub fn parse<'s>(&self, input: &'s str) -> Result<Value<'s>> {
260        let tokenizer = Tokenizer::new(input);
261
262        let mut state = ParserState::new(tokenizer)?;
263
264        Self::parse_value(self.internal.clone(), &mut state)
265    }
266
267    /// Parses a Dent file.
268    ///
269    /// The returned value is a zero-copy representation of the parsed Dent. All strings
270    /// in the returned value borrow from the input file.
271    ///
272    /// The file is read and stored in memory for the lifetime of the program.
273    ///
274    /// # Examples
275    /// ```
276    /// use dent_parse::{Dent, Value};
277    /// use std::collections::HashMap;
278    ///
279    /// let parser = Dent::default();
280    /// let value = parser.parse_file("examples/dent/dict.dent").unwrap();
281    /// assert_eq!(value, Value::Dict(
282    ///     vec![
283    ///         ("name", Value::Str("Mario")),
284    ///         (
285    ///             "skills",
286    ///             Value::List(vec![Value::Str("jumps"), Value::Str("grows")])
287    ///         ),
288    ///         ("age", Value::Int(35)),
289    ///         ("alive", Value::Bool(true)),
290    ///     ].into_iter().collect()
291    /// ));
292    /// ```
293    pub fn parse_file<P: AsRef<Path>>(&self, path: P) -> Result<Value<'static>> {
294        Self::import(self.internal.clone(), path)
295    }
296
297    fn import<P: AsRef<Path>>(
298        internal: Arc<Mutex<DentInternal>>,
299        path: P,
300    ) -> Result<Value<'static>> {
301        let path = if let Ok(path) = path.as_ref().canonicalize() {
302            path
303        } else {
304            return Ok(Value::None);
305        };
306
307        let mut ilock = internal.lock().unwrap();
308        let import_map = &mut ilock.import_map;
309        if let Some(value) = import_map.get(&path) {
310            return Ok(value.value.clone());
311        }
312
313        import_map.insert(
314            path.clone(),
315            Import {
316                src: "",
317                value: Value::None,
318            },
319        );
320
321        drop(ilock);
322
323        let mut file = std::fs::File::open(&path).unwrap();
324
325        let mut contents = String::new();
326
327        file.read_to_string(&mut contents).unwrap();
328
329        let static_contents = Box::leak(contents.into_boxed_str());
330
331        let tokenizer = Tokenizer::new(static_contents);
332
333        let mut state = ParserState::new(tokenizer).unwrap();
334
335        let value = Self::parse_value(internal.clone(), &mut state);
336
337        let value = match value {
338            Ok(v) => v,
339            Err(_) => Value::None,
340        };
341
342        let mut ilock = internal.lock().unwrap();
343        let import_map = &mut ilock.import_map;
344
345        let i = import_map.get_mut(&path).unwrap();
346        i.src = static_contents;
347        i.value = value.clone();
348
349        Ok(value)
350    }
351
352    fn parse_value<'s>(
353        internal: Arc<Mutex<DentInternal>>,
354        state: &mut ParserState<'s>,
355    ) -> Result<Value<'s>> {
356        let v = match state.token {
357            Token::Eof => Ok(Value::None),
358            Token::At => {
359                state.next()?;
360                if let Token::String(s) = state.token {
361                    state.next()?;
362                    let function = internal
363                        .lock()
364                        .unwrap()
365                        .functions
366                        .get(&s.to_string())
367                        .cloned();
368                    if let Some(function) = function {
369                        let value = Self::parse_value(internal.clone(), state)?;
370                        Ok(function(&value))
371                    } else {
372                        Err(Error::UnknownFunction(s.to_string()))
373                    }
374                } else {
375                    Err(Error::UnexpectedToken(state.token.type_name()))
376                }
377            }
378            Token::String(s) => {
379                state.next()?;
380                Ok(Value::Str(s))
381            }
382            Token::OpenBracket => {
383                state.next()?;
384                let mut values = Vec::new();
385                while state.token != Token::CloseBracket {
386                    if state.token == Token::Eof {
387                        return Err(Error::UnexpectedEof);
388                    }
389                    values.push(Self::parse_value(internal.clone(), state)?);
390                }
391                state.next()?;
392                Ok(Value::List(values))
393            }
394            Token::OpenBrace => {
395                state.next()?;
396                let mut values = HashMap::new();
397                while state.token != Token::CloseBrace {
398                    if state.token == Token::Eof {
399                        return Err(Error::UnexpectedEof);
400                    }
401                    if let Token::String(s) = state.token {
402                        state.next()?;
403                        if state.token != Token::Colon {
404                            return Err(Error::UnexpectedToken(state.token.type_name()));
405                        }
406                        state.next()?;
407                        values.insert(s, Self::parse_value(internal.clone(), state)?);
408                    } else {
409                        return Err(Error::UnexpectedToken(state.token.type_name()));
410                    }
411                }
412                state.next()?;
413                Ok(Value::Dict(values))
414            }
415            Token::Number(n) => {
416                state.next()?;
417                if let Ok(i) = n.parse::<i64>() {
418                    Ok(Value::Int(i))
419                } else if let Ok(f) = n.parse::<f64>() {
420                    Ok(Value::Float(f))
421                } else {
422                    panic!("Tokenizer returned invalid number: {}", n);
423                }
424            }
425            Token::Bool(b) => {
426                state.next()?;
427                Ok(Value::Bool(b))
428            }
429            Token::Comment => {
430                state.next()?;
431                Self::parse_value(internal, state)
432            }
433            _ => Err(Error::UnexpectedToken(state.token.type_name())),
434        };
435        v
436    }
437}
438
439impl Default for Dent {
440    fn default() -> Self {
441        let mut s = Self::new(HashMap::new());
442        s.add_builtins();
443        s
444    }
445}