use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::fmt::Debug;
use std::{fs, io::BufReader, path::Path};
use grammartec::context::Context;
pub use grammartec::newtypes::NTermID;
use crate::{generators::Generator, inputs::nautilus::NautilusInput, Error};
pub struct NautilusContext {
pub ctx: Context,
}
impl Debug for NautilusContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NautilusContext {{}}",)
}
}
impl NautilusContext {
#[must_use]
pub fn new(tree_depth: usize, rules: &[Vec<String>]) -> Self {
assert!(!rules.is_empty());
assert!(!rules[0].is_empty());
let mut ctx = Context::new();
for rule in rules {
ctx.add_rule(&rule[0], rule[1].as_bytes());
}
let root = "{".to_string() + &rules[0][0] + "}";
ctx.add_rule("START", root.as_bytes());
ctx.initialize(tree_depth);
Self { ctx }
}
#[must_use]
pub fn with_rules(tree_depth: usize, rules: &[(&str, &[u8])]) -> Option<Self> {
let mut ctx = Context::new();
for (symbol, rule) in rules {
ctx.add_rule(symbol, rule);
}
let root = format!("{{{}}}", rules.first()?.0);
ctx.add_rule("START", root.as_bytes());
ctx.initialize(tree_depth);
Some(Self { ctx })
}
#[must_use]
pub fn from_file<P: AsRef<Path>>(tree_depth: usize, grammar_file: P) -> Self {
let file = fs::File::open(grammar_file).expect("Cannot open grammar file");
let reader = BufReader::new(file);
let rules: Vec<Vec<String>> =
serde_json::from_reader(reader).expect("Cannot parse grammar file");
Self::new(tree_depth, &rules)
}
}
#[derive(Clone)]
pub struct NautilusGenerator<'a> {
pub ctx: &'a Context,
}
impl Debug for NautilusGenerator<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NautilusGenerator {{}}",)
}
}
impl<'a, S> Generator<NautilusInput, S> for NautilusGenerator<'a> {
fn generate(&mut self, _state: &mut S) -> Result<NautilusInput, Error> {
let nonterm = self.nonterminal("START");
let len = self.ctx.get_random_len_for_nt(&nonterm);
let mut input = NautilusInput::empty();
self.generate_from_nonterminal(&mut input, nonterm, len);
Ok(input)
}
}
impl<'a> NautilusGenerator<'a> {
#[must_use]
pub fn new(context: &'a NautilusContext) -> Self {
Self { ctx: &context.ctx }
}
#[must_use]
pub fn nonterminal(&self, name: &str) -> NTermID {
self.ctx.nt_id(name)
}
pub fn generate_from_nonterminal(&self, input: &mut NautilusInput, start: NTermID, len: usize) {
input.tree_mut().generate_from_nt(start, len, self.ctx);
}
}