mod resolvers;
mod standalone;
#[cfg(test)]
mod test;
pub use standalone::expression;
use std::{collections::BTreeMap, error::Error, fmt::Display, io, path::PathBuf};
use regex_macro::regex;
use crate::engine::{Runtime, Structure, StructureError};
use self::standalone::{definition, domain, get_domain, get_reserved, parse_use, reserve};
fn strip_comments(input: String) -> String {
input.replace(regex!("#.*"), "")
}
pub fn parse(name: &str, resolver: impl Fn(&str) -> Option<String>) -> Result<Runtime, ParseError> {
let mut partial_runtime =
BTreeMap::from([("intrinsic".to_string(), Some(Structure::intrinsic()))]);
let input = resolver(name).unwrap();
parse_into_runtime(&input, name, &resolver, &mut partial_runtime)?;
let runtime = partial_runtime
.into_iter()
.filter_map(|(name, structure)| structure.map(|structure| (name, structure)))
.collect();
Ok(Runtime::new(runtime))
}
type PartialRuntime = BTreeMap<String, Option<Structure>>;
fn parse_into_runtime(
input: &str,
name: &str,
resolver: &impl Fn(&str) -> Option<String>,
runtime: &mut PartialRuntime,
) -> Result<(), ParseError> {
let input = strip_comments(input.to_string());
let (input, domain) = domain(&input)?;
let (input, reserved) = reserve(input)?;
let (input, dependencies) = parse_use(input)?;
for dependency in dependencies {
match runtime.get(&dependency) {
Some(Some(_)) => continue,
Some(None) => {
return Err(ParseError::CircularDependency {
cycle: vec![dependency.clone(), name.to_string()],
})
}
None => (),
}
runtime.insert(dependency.clone(), None);
let dependecy_program = resolver(&dependency)
.expect("Hande failures of resolver (basically not finding files)");
match parse_into_runtime(&dependecy_program, &dependency, resolver, runtime) {
Ok(()) => (),
Err(ParseError::CircularDependency { mut cycle }) => {
cycle.push(dependency);
return Err(ParseError::CircularDependency { cycle });
}
Err(err) => return Err(err),
};
}
let full_domain = domain.iter().chain(get_domain(runtime)).collect();
let full_reserved = reserved.iter().chain(get_reserved(runtime)).collect();
let mut definitions = Vec::new();
let mut input = input;
while let Ok((rest, mut parsed_definitions)) = definition(input, &full_domain, &full_reserved) {
input = rest;
definitions.append(&mut parsed_definitions);
}
let structure = match Structure::create(domain, reserved, definitions) {
Ok(structure) => structure,
Err(StructureError::DomainAndReservedOverlap { culprit }) => {
return Err(ParseError::DomainAndReservedOverlap { culprit })
}
};
runtime.insert(name.to_string(), Some(structure));
Ok(())
}
pub fn parse_file(path: PathBuf) -> Result<Runtime, ParseError> {
let (root, name) = resolvers::get_root_and_name(path).unwrap();
let resolver = resolvers::file_resolver(root);
parse(name.as_str(), resolver)
}
#[derive(Debug)]
pub enum ParseError {
Expected { expected: String, found: String },
DomainAndReservedOverlap { culprit: String },
CircularDependency { cycle: Vec<String> },
UknownToken(String),
Io(io::Error),
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match self {
ParseError::Expected { expected, found } => {
format!("Expected {}, found {}", expected, found)
}
ParseError::DomainAndReservedOverlap { culprit } => {
format!("Domain and reserved overlap: {}", culprit)
}
ParseError::CircularDependency { cycle } => {
format!("Found circular dependency: {}", cycle.join(" -> "))
}
ParseError::UknownToken(token) => format!("Unknown token in: \"{}\"", token),
ParseError::Io(e) => format!("IO error: {}", e),
};
write!(f, "{}", message)
}
}
impl Error for ParseError {}