use markdown::mdast::Node;
use std::any::Any;
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{self, Debug};
use thiserror::Error;
use crate::base_parser::Position;
#[derive(Debug, Clone, PartialEq)]
pub enum GodotValue {
Nil,
Bool(bool),
Int(i64),
Float(f64),
String(String),
Array(Vec<GodotValue>),
Dict(HashMap<String, GodotValue>),
Resource {
type_name: String,
abstract_type_name: String,
fields: HashMap<String, GodotValue>,
},
}
impl fmt::Display for GodotValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GodotValue::Nil => write!(f, "Nil"),
GodotValue::Bool(b) => write!(f, "{}", b),
GodotValue::Int(i) => write!(f, "{}", i),
GodotValue::Float(fl) => write!(f, "{}", fl),
GodotValue::String(s) => write!(f, "\"{}\"", s),
GodotValue::Array(arr) => {
let elements: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
write!(f, "[{}]", elements.join(", "))
}
GodotValue::Dict(dict) => {
let entries: Vec<String> = dict
.iter()
.map(|(k, v)| format!("\"{}\": {}", k, v))
.collect();
write!(f, "{{{}}}", entries.join(", "))
}
GodotValue::Resource {
type_name,
fields,
abstract_type_name: _,
} => {
let entries: Vec<String> = fields
.iter()
.map(|(k, v)| format!("\"{}\": {}", k, v))
.collect();
write!(f, "{} {{ {} }}", type_name, entries.join(", "))
}
}
}
}
pub trait Hypo: std::fmt::Debug {
fn kind(&self) -> &'static str;
fn confidence(&self) -> f32 {
1.0
}
fn promote(self: Box<Self>) -> Result<Box<dyn DokeOut>, Box<dyn Error>>;
}
pub trait DokeOut: std::fmt::Debug {
fn kind(&self) -> &'static str;
fn to_godot(&self) -> GodotValue;
fn get_asbtract_type(&self) -> Option<String> {
None
}
fn use_child(&mut self, _child: GodotValue) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn use_constituent(&mut self, _name: &str, _value: GodotValue) -> Result<(), Box<dyn Error>> {
Ok(())
}
}
#[derive(Debug)]
pub struct DokeNode {
pub statement: String,
pub state: DokeNodeState,
pub children: Vec<DokeNode>,
pub parse_data: HashMap<String, GodotValue>,
pub constituents: HashMap<String, DokeNode>,
pub span: Position,
}
#[derive(Debug)]
pub enum DokeNodeState {
Unresolved,
Hypothesis(Vec<Box<dyn Hypo>>),
Resolved(Box<dyn DokeOut>),
Error(Box<dyn Error>),
}
pub trait DokeParser: Debug + Send + Sync {
fn process(&self, node: &mut DokeNode, frontmatter: &HashMap<String, GodotValue>);
}
#[derive(Debug, Error)]
pub enum DokeValidationError {
#[error("Validation error at node: {0} : {1}")]
NodeError(String, String),
#[error("Missing required field '{0}' in resource '{1}'")]
MissingField(String, String),
#[error("Invalid field type for '{0}' in resource '{1}': expected {2}, got {3}")]
InvalidFieldType(String, String, String, String),
#[error("(Promoted Err) {0} - position {1}")]
HypothesisPromotionFailed(#[source] Box<dyn Error>, Position),
#[error("Unresolved node: {0}")]
UnresolvedNode(String),
#[error("Multiple errors occurred during validation: {0}")]
MultipleErrors(#[from] DokeErrors),
#[error("Failed to use child: {0}")]
ChildUsageFailed(#[source] Box<dyn Error>),
#[error("Dynamic Error")]
DynamicError(#[from] Box<dyn std::error::Error>),
}
#[derive(Debug, Error)]
pub struct DokeErrors(Vec<DokeValidationError>);
impl From<Vec<DokeValidationError>> for DokeErrors {
fn from(errors: Vec<DokeValidationError>) -> Self {
DokeErrors(errors)
}
}
impl fmt::Display for DokeErrors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "")?;
for (i, error) in self.0.iter().enumerate() {
writeln!(f, " {}. {}", i + 1, error)?;
}
Ok(())
}
}
#[derive(Debug)]
struct ErrorHypo<Er: Error> {
error: Er,
}
impl<Er: Error + 'static> Hypo for ErrorHypo<Er> {
fn kind(&self) -> &'static str {
"Error"
}
fn confidence(&self) -> f32 {
-1.
}
fn promote(self: Box<Self>) -> Result<Box<dyn DokeOut>, Box<dyn Error>> {
Err(Box::new(self.error))
}
}
#[derive(Debug, Error)]
pub enum GodotValueError {
#[error("Tried to add a child to a {0}")]
InvalidChild(String),
}
impl DokeOut for GodotValue {
fn kind(&self) -> &'static str {
match &self {
GodotValue::Nil => "Null",
GodotValue::Bool(_) => "Bool",
GodotValue::Int(_) => "Int",
GodotValue::Float(_) => "Float",
GodotValue::String(_) => "String",
GodotValue::Array(_) => "Array",
GodotValue::Dict(_) => "Dict",
GodotValue::Resource {
type_name: _,
fields: _,
abstract_type_name: _,
} => "Resource",
}
}
fn to_godot(&self) -> GodotValue {
self.clone()
}
fn use_child(&mut self, _child: GodotValue) -> Result<(), Box<dyn Error>> {
match self {
GodotValue::Nil
| GodotValue::Bool(_)
| GodotValue::Int(_)
| GodotValue::Float(_)
| GodotValue::String(_) => Err(Box::new(GodotValueError::InvalidChild(
self.kind().to_owned(),
))),
GodotValue::Array(v) => {
v.push(_child);
Ok(())
}
GodotValue::Dict(h) => {
h.insert("children".into(), _child);
Ok(())
}
GodotValue::Resource {
type_name: _,
fields,
abstract_type_name: _,
} => {
match &mut fields
.entry("children".into())
.or_insert(GodotValue::Array(vec![]))
{
GodotValue::Array(godot_values) => {
godot_values.push(_child);
Ok(())
}
_ => Err(Box::new(GodotValueError::InvalidChild(
"Can't add child to resource : children field is not empty or an array"
.into(),
))),
}
}
}
}
}
pub struct DokeValidate {
errors: Vec<DokeValidationError>,
}
impl DokeValidate {
pub fn new() -> Self {
Self { errors: Vec::new() }
}
pub fn validate_tree(
root_nodes: &mut [DokeNode],
frontmatter: &HashMap<String, GodotValue>,
) -> Result<Vec<GodotValue>, DokeValidationError> {
let mut validator = Self::new();
let results: Vec<Result<GodotValue, DokeValidationError>> = root_nodes
.iter_mut()
.map(|n| validator.process_node(n, frontmatter))
.collect();
let mut ok_values = Vec::new();
for r in results {
match r {
Ok(v) => ok_values.push(v),
Err(e) => validator.errors.push(e),
}
}
if validator.errors.is_empty() {
Ok(ok_values)
} else if validator.errors.len() == 1 {
Err(validator.errors.remove(0))
} else {
Err(DokeValidationError::MultipleErrors(DokeErrors(
validator.errors,
)))
}
}
fn process_node(
&mut self,
node: &mut DokeNode,
frontmatter: &HashMap<String, GodotValue>,
) -> Result<GodotValue, DokeValidationError> {
let mut child_values = Vec::new();
let mut constituent_values: HashMap<String, GodotValue> = HashMap::new();
for child in &mut node.children {
match self.process_node(child, frontmatter) {
Ok(v) => child_values.push(v),
Err(e) => return Err(e),
};
}
for (name, constituent) in &mut node.constituents {
match self.process_node(constituent, frontmatter) {
Ok(v) => constituent_values.insert(name.into(), v),
Err(e) => return Err(e),
};
}
match &mut node.state {
DokeNodeState::Unresolved => {
Err(DokeValidationError::UnresolvedNode(node.statement.clone()))
}
DokeNodeState::Hypothesis(hypotheses) => {
let best_index = hypotheses
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| {
a.confidence()
.partial_cmp(&b.confidence())
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|(i, _)| i);
if let Some(best_index) = best_index {
let hypo = hypotheses.remove(best_index);
let mut resolved = hypo.promote().map_err(|e| {
DokeValidationError::HypothesisPromotionFailed(e, node.span.clone())
})?;
for child in &child_values {
resolved
.use_child(child.clone())
.map_err(DokeValidationError::ChildUsageFailed)?;
}
for (name, value) in &constituent_values {
resolved.use_constituent(name, value.clone())?;
}
node.state = DokeNodeState::Resolved(resolved);
if let DokeNodeState::Resolved(resolved) = &node.state {
Ok(resolved.to_godot())
} else {
unreachable!()
}
} else {
Err(DokeValidationError::UnresolvedNode(node.statement.clone()))
}
}
DokeNodeState::Resolved(resolved) => {
for child in &child_values {
resolved
.use_child(child.clone())
.map_err(DokeValidationError::ChildUsageFailed)?;
}
for (name, value) in &constituent_values {
resolved.use_constituent(name, value.clone())?;
}
Ok(resolved.to_godot())
}
DokeNodeState::Error(e) => Err(DokeValidationError::NodeError(
node.statement.clone(),
format!("{}", e),
)),
}
}
}