use itertools::Itertools;
use ruleset::{Rule, RuleSet};
use crate::utils::{interner::Interner, Result};
pub mod ruleset;
#[derive(PartialEq, Eq)]
pub enum State {
Left,
Right,
}
pub fn parse(code: &str, interner: &mut Interner<String>) -> Result<RuleSet> {
let mut buffer = String::new();
let mut left: Vec<(usize, usize)> = vec![];
let mut right: Vec<(usize, usize)> = vec![];
let mut state = State::Right;
let hash = interner.intern("#");
let comment = interner.intern("--");
let string = interner.intern("string");
let variables = interner.intern("variables");
let mut delimiter = '|';
let mut ruleset: Vec<Rule> = Vec::new();
for c in code.chars() {
if c.is_whitespace() {
continue;
}
delimiter = c;
break;
}
for c in code.chars() {
if c == delimiter || c == ',' {
add_tuple(
&mut buffer,
if state == State::Left {
&mut left
} else {
&mut right
},
interner,
);
if c == delimiter {
if state == State::Left {
state = State::Right
} else {
if left.is_empty() && right.is_empty() {
} else {
if left == [(hash, 1)] {
make_variable_rules(
&right,
string,
interner,
delimiter,
&mut ruleset,
variables,
);
} else if left == [(comment, 1)] {
} else {
ruleset.push(Rule {
conditions: left.clone().into_boxed_slice(),
multiplicities: right.clone().into_boxed_slice(),
});
}
right.clear();
left.clear();
}
state = State::Left;
}
}
} else {
buffer.push(c);
}
}
add_tuple(
&mut buffer,
if state == State::Left {
&mut left
} else {
&mut right
},
interner,
);
ruleset.push(Rule {
conditions: left.into_boxed_slice(),
multiplicities: right.into_boxed_slice(),
});
let (facts, rules): (Vec<Rule>, Vec<Rule>) = ruleset.iter().cloned().partition(|r| r.is_fact());
Ok(RuleSet { facts, rules })
}
fn make_variable_rules(
right: &[(usize, usize)],
string: usize,
interner: &mut Interner<String>,
delimiter: char,
ruleset: &mut Vec<Rule>,
variables: usize,
) {
if let Some(&(tuple, _)) = right.first() {
if tuple == string {
let name_interned = right.get(1).unwrap().0;
let name = interner.lookup(name_interned).unwrap().clone();
let str = right
.iter()
.skip(2)
.map(|s| interner.lookup(s.0).unwrap().clone())
.map(|s| {
if let Some(s) = s.strip_prefix("#") {
match s {
"COMMA" | "comma" => ",".to_string(),
"SPACE" | "space" => " ".to_string(),
"DELIM" | "delim" => delimiter.to_string(),
"HASH" | "hash" => "#".to_string(),
"CR" | "cr" => "\r".to_string(),
"NL" | "nl" => "\n".to_string(),
other => format!("#{other}"),
}
} else {
s
}
})
.collect::<Vec<String>>()
.join("");
let name_load = interner.intern(format!("{name}.load"));
let name_pointer = interner.intern(format!("{name}.pointer"));
let name_length = interner.intern(format!("{name}.length"));
let length = str.len();
let str_interned = interner.intern(str);
let ptr = interner.lookup(str_interned).unwrap().as_ptr() as usize;
ruleset.push(Rule {
conditions: vec![(name_load, 1)].into_boxed_slice(),
multiplicities: vec![(name_pointer, ptr), (name_length, length)].into_boxed_slice(),
});
} else if tuple == variables {
for (a, b) in right.iter().tuple_combinations().unique() {
make_copy(a, interner, b, ruleset);
make_copy(b, interner, a, ruleset);
}
}
}
}
fn make_copy(
left: &(usize, usize),
interner: &mut Interner<String>,
right: &(usize, usize),
ruleset: &mut Vec<Rule>,
) {
let src_i = right.0;
let src = interner.lookup(src_i).unwrap().clone();
let dest_i = left.0;
let dest = interner.lookup(dest_i).unwrap().clone();
let copy = interner.intern(format!("{dest} = {src}"));
let r#move = interner.intern(format!("{src} -> {dest}"));
let src_backup = interner.intern(format!("*{dest} = {src} src backup"));
ruleset.push(Rule {
conditions: vec![(copy, 1), (src_i, 1)].into_boxed_slice(),
multiplicities: vec![(src_backup, 1), (dest_i, 1), (copy, usize::MAX)].into_boxed_slice(),
});
ruleset.push(Rule {
conditions: vec![(copy, 1)].into_boxed_slice(),
multiplicities: vec![].into_boxed_slice(),
});
ruleset.push(Rule {
conditions: vec![(src_backup, 1)].into_boxed_slice(),
multiplicities: vec![(src_i, 1)].into_boxed_slice(),
});
ruleset.push(Rule {
conditions: vec![(r#move, 1), (src_i, 1)].into_boxed_slice(),
multiplicities: vec![(dest_i, 1), (r#move, usize::MAX)].into_boxed_slice(),
});
ruleset.push(Rule {
conditions: vec![(r#move, 1)].into_boxed_slice(),
multiplicities: vec![].into_boxed_slice(),
});
}
#[inline]
fn add_tuple(buffer: &mut String, side: &mut Vec<(usize, usize)>, interner: &mut Interner<String>) {
if !buffer.trim().is_empty() {
let (fact, multiplicity) = parse_multiplicity(buffer).expect("there should be a fact");
side.push((interner.intern(fact.trim().to_string()), multiplicity));
}
buffer.clear();
}
fn parse_multiplicity(buffer: &mut str) -> Option<(&str, usize)> {
let mut split = buffer.split(':');
let fact = split.next()?;
split.next().map_or(Some((buffer, 1)), |multiplicity| {
if let Ok(multiplicity) = str::parse(multiplicity.trim()) {
Some((fact, multiplicity))
} else {
Some((buffer, 1))
}
})
}