nova_interpreter/
parser.rs

1use itertools::Itertools;
2use ruleset::{Rule, RuleSet};
3
4use crate::utils::{interner::Interner, Result};
5
6pub mod ruleset;
7
8#[derive(PartialEq, Eq)]
9pub enum State {
10    Left,
11    Right,
12}
13
14/// Build a ruleset from the given code, interning counter names in the interner
15pub fn parse(code: &str, interner: &mut Interner<String>) -> Result<RuleSet> {
16    let mut buffer = String::new();
17    let mut left: Vec<(usize, usize)> = vec![];
18    let mut right: Vec<(usize, usize)> = vec![];
19    let mut state = State::Right;
20
21    let hash = interner.intern("#");
22    let comment = interner.intern("--");
23    let string = interner.intern("string");
24    let variables = interner.intern("variables");
25
26    // Default the delimiter to '|' to prevent the compiler from complaining
27    // the only time it will not be overwritten is if there is only whitespace
28    // in a file, so this default won't affect behavior
29    let mut delimiter = '|';
30
31    let mut ruleset: Vec<Rule> = Vec::new();
32
33    for c in code.chars() {
34        if c.is_whitespace() {
35            continue;
36        }
37        delimiter = c;
38        break;
39    }
40
41    for c in code.chars() {
42        if c == delimiter || c == ',' {
43            add_tuple(
44                &mut buffer,
45                if state == State::Left {
46                    &mut left
47                } else {
48                    &mut right
49                },
50                interner,
51            );
52            if c == delimiter {
53                if state == State::Left {
54                    state = State::Right
55                } else {
56                    // TODO: fix this
57                    // Only required because we start the parser
58                    // in the "right" state so it will create a single
59                    // empty rule otherwise
60                    if left.is_empty() && right.is_empty() {
61                    } else {
62                        if left == [(hash, 1)] {
63                            make_variable_rules(
64                                &right,
65                                string,
66                                interner,
67                                delimiter,
68                                &mut ruleset,
69                                variables,
70                            );
71                        } else if left == [(comment, 1)] {
72                        } else {
73                            ruleset.push(Rule {
74                                conditions: left.clone().into_boxed_slice(),
75                                multiplicities: right.clone().into_boxed_slice(),
76                            });
77                        }
78
79                        // Clear the lists of facts, leaving the memory behind
80                        right.clear();
81                        left.clear();
82                    }
83
84                    state = State::Left;
85                }
86            }
87        } else {
88            buffer.push(c);
89        }
90    }
91
92    add_tuple(
93        &mut buffer,
94        if state == State::Left {
95            &mut left
96        } else {
97            &mut right
98        },
99        interner,
100    );
101
102    ruleset.push(Rule {
103        conditions: left.into_boxed_slice(),
104        multiplicities: right.into_boxed_slice(),
105    });
106
107    let (facts, rules): (Vec<Rule>, Vec<Rule>) = ruleset.iter().cloned().partition(|r| r.is_fact());
108
109    Ok(RuleSet { facts, rules })
110}
111
112fn make_variable_rules(
113    right: &[(usize, usize)],
114    string: usize,
115    interner: &mut Interner<String>,
116    delimiter: char,
117    ruleset: &mut Vec<Rule>,
118    variables: usize,
119) {
120    if let Some(&(tuple, _)) = right.first() {
121        if tuple == string {
122            let name_interned = right.get(1).unwrap().0;
123            let name = interner.lookup(name_interned).unwrap().clone();
124
125            let str = right
126                .iter()
127                .skip(2)
128                .map(|s| interner.lookup(s.0).unwrap().clone())
129                .map(|s| {
130                    if let Some(s) = s.strip_prefix("#") {
131                        match s {
132                            "COMMA" | "comma" => ",".to_string(),
133                            "SPACE" | "space" => " ".to_string(),
134                            "DELIM" | "delim" => delimiter.to_string(),
135                            "HASH" | "hash" => "#".to_string(),
136                            "CR" | "cr" => "\r".to_string(),
137                            "NL" | "nl" => "\n".to_string(),
138                            other => format!("#{other}"),
139                        }
140                    } else {
141                        s
142                    }
143                })
144                .collect::<Vec<String>>()
145                .join("");
146
147            let name_load = interner.intern(format!("{name}.load"));
148            let name_pointer = interner.intern(format!("{name}.pointer"));
149            let name_length = interner.intern(format!("{name}.length"));
150
151            let length = str.len();
152            let str_interned = interner.intern(str);
153            let ptr = interner.lookup(str_interned).unwrap().as_ptr() as usize;
154
155            ruleset.push(Rule {
156                conditions: vec![(name_load, 1)].into_boxed_slice(),
157                multiplicities: vec![(name_pointer, ptr), (name_length, length)].into_boxed_slice(),
158            });
159        } else if tuple == variables {
160            for (a, b) in right.iter().tuple_combinations().unique() {
161                make_copy(a, interner, b, ruleset);
162                make_copy(b, interner, a, ruleset);
163            }
164        }
165    }
166}
167
168fn make_copy(
169    left: &(usize, usize),
170    interner: &mut Interner<String>,
171    right: &(usize, usize),
172    ruleset: &mut Vec<Rule>,
173) {
174    let src_i = right.0;
175    let src = interner.lookup(src_i).unwrap().clone();
176    let dest_i = left.0;
177    let dest = interner.lookup(dest_i).unwrap().clone();
178
179    let copy = interner.intern(format!("{dest} = {src}"));
180    let r#move = interner.intern(format!("{src} -> {dest}"));
181    let src_backup = interner.intern(format!("*{dest} = {src} src backup"));
182
183    ruleset.push(Rule {
184        conditions: vec![(copy, 1), (src_i, 1)].into_boxed_slice(),
185        multiplicities: vec![(src_backup, 1), (dest_i, 1), (copy, usize::MAX)].into_boxed_slice(),
186    });
187
188    ruleset.push(Rule {
189        conditions: vec![(copy, 1)].into_boxed_slice(),
190        multiplicities: vec![].into_boxed_slice(),
191    });
192
193    ruleset.push(Rule {
194        conditions: vec![(src_backup, 1)].into_boxed_slice(),
195        multiplicities: vec![(src_i, 1)].into_boxed_slice(),
196    });
197
198    ruleset.push(Rule {
199        conditions: vec![(r#move, 1), (src_i, 1)].into_boxed_slice(),
200        multiplicities: vec![(dest_i, 1), (r#move, usize::MAX)].into_boxed_slice(),
201    });
202
203    ruleset.push(Rule {
204        conditions: vec![(r#move, 1)].into_boxed_slice(),
205        multiplicities: vec![].into_boxed_slice(),
206    });
207}
208
209#[inline]
210fn add_tuple(buffer: &mut String, side: &mut Vec<(usize, usize)>, interner: &mut Interner<String>) {
211    if !buffer.trim().is_empty() {
212        let (fact, multiplicity) = parse_multiplicity(buffer).expect("there should be a fact");
213
214        side.push((interner.intern(fact.trim().to_string()), multiplicity));
215    }
216
217    buffer.clear();
218}
219
220fn parse_multiplicity(buffer: &mut str) -> Option<(&str, usize)> {
221    let mut split = buffer.split(':');
222
223    let fact = split.next()?;
224
225    split.next().map_or(Some((buffer, 1)), |multiplicity| {
226        if let Ok(multiplicity) = str::parse(multiplicity.trim()) {
227            Some((fact, multiplicity))
228        } else {
229            Some((buffer, 1))
230        }
231    })
232}