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}