didp_yaml/dypdl_parser/
expression_parser.rs

1//! A module fto parse expressions from a string value.
2
3use dypdl::expression;
4use rustc_hash::FxHashMap;
5
6mod argument_parser;
7mod condition_parser;
8mod continuous_parser;
9mod continuous_vector_parser;
10mod element_parser;
11mod integer_parser;
12mod integer_vector_parser;
13mod numeric_table_parser;
14mod table_vector_parser;
15mod util;
16
17pub use util::ParseErr;
18
19/// Returns an integer expression parsed from a string value.
20///
21/// `parameters` specify names and values of constants.
22///
23/// # Errors
24///
25/// If the format is invalid.
26pub fn parse_integer(
27    text: String,
28    metadata: &dypdl::StateMetadata,
29    functions: &dypdl::StateFunctions,
30    registry: &dypdl::TableRegistry,
31    parameters: &FxHashMap<String, usize>,
32) -> Result<expression::IntegerExpression, ParseErr> {
33    let tokens = tokenize(text);
34    let (expression, rest) =
35        integer_parser::parse_expression(&tokens, metadata, functions, registry, parameters)?;
36    if rest.is_empty() {
37        Ok(expression)
38    } else {
39        Err(ParseErr::new(format!(
40            "unexpected tokens: `{rest}`",
41            rest = rest.join(" ")
42        )))
43    }
44}
45
46/// Returns a continuous expression parsed from a string value.
47///
48/// `parameters` specify names and values of constants.
49///
50/// # Errors
51///
52/// If the format is invalid.
53pub fn parse_continuous(
54    text: String,
55    metadata: &dypdl::StateMetadata,
56    functions: &dypdl::StateFunctions,
57    registry: &dypdl::TableRegistry,
58    parameters: &FxHashMap<String, usize>,
59) -> Result<expression::ContinuousExpression, ParseErr> {
60    let tokens = tokenize(text);
61    let (expression, rest) =
62        continuous_parser::parse_expression(&tokens, metadata, functions, registry, parameters)?;
63    if rest.is_empty() {
64        Ok(expression)
65    } else {
66        Err(ParseErr::new(format!(
67            "unexpected tokens: `{rest}`",
68            rest = rest.join(" ")
69        )))
70    }
71}
72
73/// Returns an element expression parsed from a string value.
74///
75/// `parameters` specify names and values of constants.
76///
77/// # Errors
78///
79/// If the format is invalid.
80pub fn parse_element(
81    text: String,
82    metadata: &dypdl::StateMetadata,
83    functions: &dypdl::StateFunctions,
84    registry: &dypdl::TableRegistry,
85    parameters: &FxHashMap<String, usize>,
86) -> Result<expression::ElementExpression, ParseErr> {
87    let tokens = tokenize(text);
88    let (expression, rest) =
89        element_parser::parse_expression(&tokens, metadata, functions, registry, parameters)?;
90    if rest.is_empty() {
91        Ok(expression)
92    } else {
93        Err(ParseErr::new(format!(
94            "unexpected tokens: `{rest}`",
95            rest = rest.join(" ")
96        )))
97    }
98}
99
100/// Returns a set expression parsed from a string value.
101///
102/// `parameters` specify names and values of constants.
103///
104/// # Errors
105///
106/// If the format is invalid.
107pub fn parse_set(
108    text: String,
109    metadata: &dypdl::StateMetadata,
110    functions: &dypdl::StateFunctions,
111    registry: &dypdl::TableRegistry,
112    parameters: &FxHashMap<String, usize>,
113) -> Result<expression::SetExpression, ParseErr> {
114    let tokens = tokenize(text);
115    let (expression, rest) =
116        element_parser::parse_set_expression(&tokens, metadata, functions, registry, parameters)?;
117    if rest.is_empty() {
118        Ok(expression)
119    } else {
120        Err(ParseErr::new(format!(
121            "unexpected tokens: `{rest}`",
122            rest = rest.join(" ")
123        )))
124    }
125}
126
127/// Returns a vector expression parsed from a string value.
128///
129/// `parameters` specify names and values of constants.
130///
131/// # Errors
132///
133/// If the format is invalid.
134pub fn parse_vector(
135    text: String,
136    metadata: &dypdl::StateMetadata,
137    functions: &dypdl::StateFunctions,
138    registry: &dypdl::TableRegistry,
139    parameters: &FxHashMap<String, usize>,
140) -> Result<expression::VectorExpression, ParseErr> {
141    let tokens = tokenize(text);
142    let (expression, rest) = element_parser::parse_vector_expression(
143        &tokens, metadata, functions, registry, parameters,
144    )?;
145    if rest.is_empty() {
146        Ok(expression)
147    } else {
148        Err(ParseErr::new(format!(
149            "unexpected tokens: `{rest}`",
150            rest = rest.join(" ")
151        )))
152    }
153}
154
155/// Returns a condition parsed from a string value.
156///
157/// `parameters` specify names and values of constants.
158///
159/// # Errors
160///
161/// If the format is invalid.
162pub fn parse_condition(
163    text: String,
164    metadata: &dypdl::StateMetadata,
165    functions: &dypdl::StateFunctions,
166    registry: &dypdl::TableRegistry,
167    parameters: &FxHashMap<String, usize>,
168) -> Result<expression::Condition, ParseErr> {
169    let tokens = tokenize(text);
170    let (expression, rest) =
171        condition_parser::parse_expression(&tokens, metadata, functions, registry, parameters)?;
172    if rest.is_empty() {
173        Ok(expression)
174    } else {
175        Err(ParseErr::new(format!(
176            "unexpected tokens: `{rest}`",
177            rest = rest.join(" ")
178        )))
179    }
180}
181
182fn tokenize(text: String) -> Vec<String> {
183    text.replace('(', " ( ")
184        .replace(')', " ) ")
185        .replace('|', " | ")
186        .replace('~', " ~ ")
187        .replace('{', " { ")
188        .replace('}', " } ")
189        .replace(':', " : ")
190        .split_whitespace()
191        .map(|x| x.to_string())
192        .collect()
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    use dypdl::*;
200
201    fn generate_metadata() -> dypdl::StateMetadata {
202        let object_names = vec!["object".to_string()];
203        let object_numbers = vec![10];
204        let mut name_to_object = FxHashMap::default();
205        name_to_object.insert("object".to_string(), 0);
206
207        let set_variable_names = vec![
208            "s0".to_string(),
209            "s1".to_string(),
210            "s2".to_string(),
211            "s3".to_string(),
212        ];
213        let mut name_to_set_variable = FxHashMap::default();
214        name_to_set_variable.insert("s0".to_string(), 0);
215        name_to_set_variable.insert("s1".to_string(), 1);
216        name_to_set_variable.insert("s2".to_string(), 2);
217        name_to_set_variable.insert("s3".to_string(), 3);
218        let set_variable_to_object = vec![0, 0, 0, 0];
219
220        let vector_variable_names = vec![
221            "p0".to_string(),
222            "p1".to_string(),
223            "p2".to_string(),
224            "p3".to_string(),
225        ];
226        let mut name_to_vector_variable = FxHashMap::default();
227        name_to_vector_variable.insert("p0".to_string(), 0);
228        name_to_vector_variable.insert("p1".to_string(), 1);
229        name_to_vector_variable.insert("p2".to_string(), 2);
230        name_to_vector_variable.insert("p3".to_string(), 3);
231        let vector_variable_to_object = vec![0, 0, 0, 0];
232
233        let element_variable_names = vec![
234            "e0".to_string(),
235            "e1".to_string(),
236            "e2".to_string(),
237            "e3".to_string(),
238        ];
239        let mut name_to_element_variable = FxHashMap::default();
240        name_to_element_variable.insert("e0".to_string(), 0);
241        name_to_element_variable.insert("e1".to_string(), 1);
242        name_to_element_variable.insert("e2".to_string(), 2);
243        name_to_element_variable.insert("e3".to_string(), 3);
244        let element_variable_to_object = vec![0, 0, 0, 0];
245
246        let integer_variable_names = vec![
247            "n0".to_string(),
248            "n1".to_string(),
249            "n2".to_string(),
250            "n3".to_string(),
251        ];
252        let mut name_to_integer_variable = FxHashMap::default();
253        name_to_integer_variable.insert("n0".to_string(), 0);
254        name_to_integer_variable.insert("n1".to_string(), 1);
255        name_to_integer_variable.insert("n2".to_string(), 2);
256        name_to_integer_variable.insert("n3".to_string(), 3);
257
258        let integer_resource_variable_names = vec![
259            "r0".to_string(),
260            "r1".to_string(),
261            "r2".to_string(),
262            "r3".to_string(),
263        ];
264        let mut name_to_integer_resource_variable = FxHashMap::default();
265        name_to_integer_resource_variable.insert("r0".to_string(), 0);
266        name_to_integer_resource_variable.insert("r1".to_string(), 1);
267        name_to_integer_resource_variable.insert("r2".to_string(), 2);
268        name_to_integer_resource_variable.insert("r3".to_string(), 3);
269
270        let continuous_variable_names = vec![
271            "c0".to_string(),
272            "c1".to_string(),
273            "c2".to_string(),
274            "c3".to_string(),
275        ];
276        let mut name_to_continuous_variable = FxHashMap::default();
277        name_to_continuous_variable.insert("c0".to_string(), 0);
278        name_to_continuous_variable.insert("c1".to_string(), 1);
279        name_to_continuous_variable.insert("c2".to_string(), 2);
280        name_to_continuous_variable.insert("c3".to_string(), 3);
281
282        dypdl::StateMetadata {
283            object_type_names: object_names,
284            name_to_object_type: name_to_object,
285            object_numbers,
286            set_variable_names,
287            name_to_set_variable,
288            set_variable_to_object,
289            vector_variable_names,
290            name_to_vector_variable,
291            vector_variable_to_object,
292            element_variable_names,
293            name_to_element_variable,
294            element_variable_to_object,
295            integer_variable_names,
296            name_to_integer_variable,
297            integer_resource_variable_names,
298            name_to_integer_resource_variable,
299            integer_less_is_better: vec![false, false, true, false],
300            continuous_variable_names,
301            name_to_continuous_variable,
302            ..Default::default()
303        }
304    }
305
306    fn generate_parameters() -> FxHashMap<String, usize> {
307        let mut parameters = FxHashMap::default();
308        parameters.insert("param".to_string(), 0);
309        parameters
310    }
311
312    fn generate_registry() -> dypdl::TableRegistry {
313        let mut name_to_constant = FxHashMap::default();
314        name_to_constant.insert(String::from("f0"), 0);
315
316        let tables_1d = vec![Table1D::new(Vec::new())];
317        let mut name_to_table_1d = FxHashMap::default();
318        name_to_table_1d.insert(String::from("f1"), 0);
319
320        let tables_2d = vec![Table2D::new(Vec::new())];
321        let mut name_to_table_2d = FxHashMap::default();
322        name_to_table_2d.insert(String::from("f2"), 0);
323
324        let tables_3d = vec![Table3D::new(Vec::new())];
325        let mut name_to_table_3d = FxHashMap::default();
326        name_to_table_3d.insert(String::from("f3"), 0);
327
328        let tables = vec![Table::new(FxHashMap::default(), 0)];
329        let mut name_to_table = FxHashMap::default();
330        name_to_table.insert(String::from("f4"), 0);
331
332        dypdl::TableRegistry {
333            integer_tables: dypdl::TableData {
334                name_to_constant,
335                tables_1d,
336                name_to_table_1d,
337                tables_2d,
338                name_to_table_2d,
339                tables_3d,
340                name_to_table_3d,
341                tables,
342                name_to_table,
343            },
344            ..Default::default()
345        }
346    }
347
348    #[test]
349    fn parse_set_ok() {
350        let metadata = generate_metadata();
351        let functions = dypdl::StateFunctions::default();
352        let registry = generate_registry();
353        let parameters = generate_parameters();
354        let text = "(union (intersection s0 (difference s2 (add 2 s3))) (remove 1 s1))".to_string();
355        let result = parse_set(text, &metadata, &functions, &registry, &parameters);
356        assert!(result.is_ok());
357    }
358
359    #[test]
360    fn parse_set_err() {
361        let metadata = generate_metadata();
362        let functions = dypdl::StateFunctions::default();
363        let registry = generate_registry();
364        let parameters = generate_parameters();
365        let text = "s0)".to_string();
366        let result = parse_set(text, &metadata, &functions, &registry, &parameters);
367        assert!(result.is_err());
368    }
369
370    #[test]
371    fn parse_integer_ok() {
372        let metadata = generate_metadata();
373        let functions = dypdl::StateFunctions::default();
374        let registry = generate_registry();
375        let parameters = generate_parameters();
376        let text = "n0".to_string();
377        let result = parse_integer(text, &metadata, &functions, &registry, &parameters);
378        assert!(result.is_ok());
379    }
380
381    #[test]
382    fn parse_integer_err() {
383        let metadata = generate_metadata();
384        let functions = dypdl::StateFunctions::default();
385        let registry = generate_registry();
386        let parameters = generate_parameters();
387        let text = "n0)".to_string();
388        let result = parse_integer(text, &metadata, &functions, &registry, &parameters);
389        assert!(result.is_err());
390    }
391
392    #[test]
393    fn parse_continuous_ok() {
394        let metadata = generate_metadata();
395        let functions = dypdl::StateFunctions::default();
396        let registry = generate_registry();
397        let parameters = generate_parameters();
398        let text = "c0".to_string();
399        let result = parse_continuous(text, &metadata, &functions, &registry, &parameters);
400        assert!(result.is_ok());
401    }
402
403    #[test]
404    fn parse_continuous_err() {
405        let metadata = generate_metadata();
406        let functions = dypdl::StateFunctions::default();
407        let registry = generate_registry();
408        let parameters = generate_parameters();
409        let text = "c0)".to_string();
410        let result = parse_continuous(text, &metadata, &functions, &registry, &parameters);
411        assert!(result.is_err());
412    }
413
414    #[test]
415    fn parse_element_ok() {
416        let metadata = generate_metadata();
417        let functions = dypdl::StateFunctions::default();
418        let registry = generate_registry();
419        let parameters = generate_parameters();
420        let text = "e0".to_string();
421        let result = parse_element(text, &metadata, &functions, &registry, &parameters);
422        assert!(result.is_ok());
423    }
424
425    #[test]
426    fn parse_element_err() {
427        let metadata = generate_metadata();
428        let functions = dypdl::StateFunctions::default();
429        let registry = generate_registry();
430        let parameters = generate_parameters();
431        let text = "e0)".to_string();
432        let result = parse_element(text, &metadata, &functions, &registry, &parameters);
433        assert!(result.is_err());
434    }
435
436    #[test]
437    fn parse_condition_ok() {
438        let metadata = generate_metadata();
439        let functions = dypdl::StateFunctions::default();
440        let registry = generate_registry();
441        let parameters = generate_parameters();
442        let text = "(not (and (and (and (> n0 2) (is_subset s0 s1)) (is_empty s0)) (or (< 1 n1) (is_in 2 s0))))"
443            .to_string();
444        let result = parse_condition(text, &metadata, &functions, &registry, &parameters);
445        assert!(result.is_ok());
446    }
447
448    #[test]
449    fn parse_condition_err() {
450        let metadata = generate_metadata();
451        let functions = dypdl::StateFunctions::default();
452        let registry = generate_registry();
453        let parameters = generate_parameters();
454        let text = "(is_empty s[0]))".to_string();
455        let result = parse_condition(text, &metadata, &functions, &registry, &parameters);
456        assert!(result.is_err());
457    }
458
459    #[test]
460    fn tokenize_text() {
461        let text = "(+ (- 5 (/ (sum f4 4 ~s2 e0 3) (max (f2 2 e1) n0))) (* r1 (min 3 |(union (intersection s0 (difference s2 (add 2 s3))) (remove 1 s1))|)))".to_string();
462        assert_eq!(
463            tokenize(text),
464            [
465                "(",
466                "+",
467                "(",
468                "-",
469                "5",
470                "(",
471                "/",
472                "(",
473                "sum",
474                "f4",
475                "4",
476                "~",
477                "s2",
478                "e0",
479                "3",
480                ")",
481                "(",
482                "max",
483                "(",
484                "f2",
485                "2",
486                "e1",
487                ")",
488                "n0",
489                ")",
490                ")",
491                ")",
492                "(",
493                "*",
494                "r1",
495                "(",
496                "min",
497                "3",
498                "|",
499                "(",
500                "union",
501                "(",
502                "intersection",
503                "s0",
504                "(",
505                "difference",
506                "s2",
507                "(",
508                "add",
509                "2",
510                "s3",
511                ")",
512                ")",
513                ")",
514                "(",
515                "remove",
516                "1",
517                "s1",
518                ")",
519                ")",
520                "|",
521                ")",
522                ")",
523                ")",
524            ]
525        );
526    }
527}