melodium-engine 0.10.0

Mélodium core engine and executor implementation
Documentation
use core::fmt::{Display, Formatter, Result};
use melodium_common::descriptor::{
    Context, DataType, DescribedType, Function, Generic, Identifier, Parameterized, Variability,
};
use melodium_common::executive::Value as ExecutiveValue;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

use crate::{LogicError, LogicResult};

use super::{FunctionInstanciation, GenericInstanciation, Reference};

#[derive(Clone, Debug)]
pub enum Value {
    Raw(ExecutiveValue),
    Array(Vec<Value>),
    Variable(String),
    Context(Arc<dyn Context>, String),
    Function(
        Arc<dyn Function>,
        HashMap<String, DescribedType>,
        Vec<Value>,
    ),
}

impl Value {
    pub fn make_use(&self, identifier: &Identifier) -> bool {
        match self {
            Value::Raw(_) => false,
            Value::Array(array) => array.iter().any(|val| val.make_use(identifier)),
            Value::Variable(_) => false,
            Value::Context(context, _) => context.identifier() == identifier,
            Value::Function(function, described_types, values) => {
                function.identifier() == identifier
                    || described_types.iter().any(|(_, dt)| {
                        dt.final_type()
                            .data()
                            .map(|data| data.identifier() == identifier)
                            .unwrap_or(false)
                    })
                    || values.iter().any(|value| value.make_use(identifier))
            }
        }
    }

    pub fn uses(&self) -> Vec<Identifier> {
        match self {
            Value::Raw(_) | Value::Variable(_) => vec![],
            Value::Array(array) => array.iter().fold(Vec::new(), |mut ids, val| {
                let identifiers: Vec<_> = val
                    .uses()
                    .into_iter()
                    .filter(|id| !ids.contains(id))
                    .collect();
                ids.extend(identifiers);
                ids
            }),
            Value::Context(context, _) => vec![context.identifier().clone()],
            Value::Function(function, described_types, values) => {
                let mut uses = vec![function.identifier().clone()];
                uses.extend(described_types.iter().filter_map(|(_, dt)| {
                    dt.final_type().data().map(|data| data.identifier().clone())
                }));
                uses.extend(values.iter().flat_map(|value| value.uses()));
                uses
            }
        }
    }

    pub fn check(
        &self,
        described_type: &DescribedType,
        scope_descriptor: &Arc<dyn Parameterized>,
        scope_generics: &Arc<RwLock<HashMap<String, DescribedType>>>,
        called_id: &Identifier,
        parent_generics: &Arc<RwLock<HashMap<String, DescribedType>>>,
        parameter_name: &str,
        parameter_variability: Variability,
        design_reference: &Option<Arc<dyn Reference>>,
    ) -> LogicResult<Variability> {
        match self {
            Value::Raw(data) => {
                if described_type.is_datatype(&data.datatype(), &parent_generics.read().unwrap()) {
                    LogicResult::new_success(Variability::Const)
                } else {
                    LogicResult::new_failure(LogicError::unmatching_datatype(
                        195,
                        scope_descriptor.identifier().clone(),
                        called_id.clone(),
                        parameter_name.to_string(),
                        self.clone(),
                        described_type.clone(),
                        data.datatype().into(),
                        design_reference.clone(),
                    ))
                }
            }
            Value::Array(array) => {
                if let Some(DataType::Vec(inner_type)) =
                    described_type.to_datatype(&parent_generics.read().unwrap())
                {
                    let mut result = LogicResult::new_success(());
                    let mut variability = Variability::Const;
                    for val in array {
                        if let Some(var) = result.merge_degrade_failure(val.check(
                            &DescribedType::from(&*inner_type),
                            scope_descriptor,
                            scope_generics,
                            called_id,
                            parent_generics,
                            parameter_name,
                            parameter_variability,
                            design_reference,
                        )) {
                            if var == Variability::Var {
                                variability = Variability::Var;
                            }
                        }
                    }
                    result.and(LogicResult::new_success(variability))
                } else {
                    LogicResult::new_failure(LogicError::unmatching_datatype(
                        226,
                        scope_descriptor.identifier().clone(),
                        called_id.clone(),
                        parameter_name.to_string(),
                        self.clone(),
                        described_type.clone(),
                        DescribedType::Vec(Box::new(DescribedType::Generic(Box::new(
                            Generic::new("_".to_string(), Vec::new()),
                        )))),
                        design_reference.clone(),
                    ))
                }
            }
            Value::Variable(name) => {
                if let Some(scope_variable) = scope_descriptor.parameters().get(name) {
                    let mut result = LogicResult::new_success(());
                    if parameter_variability == Variability::Const
                        && *scope_variable.variability() != Variability::Const
                    {
                        result
                            .errors_mut()
                            .push(LogicError::const_required_var_provided(
                                196,
                                scope_descriptor.identifier().clone(),
                                called_id.clone(),
                                parameter_name.to_string(),
                                name.to_string(),
                                design_reference.clone(),
                            ));
                    }

                    if !described_type.is_compatible(
                        &parent_generics.read().unwrap(),
                        scope_variable.described_type(),
                        &scope_generics.read().unwrap(),
                    ) {
                        result.errors_mut().push(LogicError::unmatching_datatype(
                            197,
                            scope_descriptor.identifier().clone(),
                            called_id.clone(),
                            parameter_name.to_string(),
                            self.clone(),
                            described_type.clone(),
                            scope_variable.described_type().clone(),
                            design_reference.clone(),
                        ));
                    }

                    result.and(LogicResult::new_success(*scope_variable.variability()))
                } else {
                    LogicResult::new_failure(LogicError::unexisting_variable(
                        198,
                        scope_descriptor.identifier().clone(),
                        parameter_name.to_string(),
                        name.to_string(),
                        design_reference.clone(),
                    ))
                }
            }
            Value::Context(context, name) => {
                let mut result = LogicResult::new_success(());
                if parameter_variability == Variability::Const {
                    result
                        .errors_mut()
                        .push(LogicError::const_required_context_provided(
                            199,
                            scope_descriptor.identifier().clone(),
                            called_id.clone(),
                            parameter_name.to_string(),
                            context.identifier().clone(),
                            name.to_string(),
                            design_reference.clone(),
                        ));
                }

                if let Some(context_variable_datatype) = context.values().get(name) {
                    if !described_type
                        .is_datatype(context_variable_datatype, &parent_generics.read().unwrap())
                    {
                        result.errors_mut().push(LogicError::unmatching_datatype(
                            200,
                            scope_descriptor.identifier().clone(),
                            called_id.clone(),
                            parameter_name.to_string(),
                            self.clone(),
                            described_type.clone(),
                            context_variable_datatype.into(),
                            design_reference.clone(),
                        ));
                    }
                } else {
                    result
                        .errors_mut()
                        .push(LogicError::unexisting_context_variable(
                            201,
                            scope_descriptor.identifier().clone(),
                            parameter_name.to_string(),
                            context.identifier().clone(),
                            name.clone(),
                            design_reference.clone(),
                        ));
                }

                result.and(LogicResult::new_success(Variability::Var))
            }
            Value::Function(descriptor, generics, parameters) => {
                let mut function_instanciation = FunctionInstanciation::new(
                    descriptor,
                    &scope_descriptor,
                    &scope_generics,
                    scope_descriptor.identifier().clone(),
                    design_reference.clone(),
                );

                let mut result = LogicResult::new_success(());
                let mut variability = Variability::Var;

                for (generic_name, r#type) in generics {
                    result.merge_degrade_failure(
                        function_instanciation.set_generic(generic_name.clone(), r#type.clone()),
                    );
                }

                if let Some((sub_variability, sub_return_type)) = result
                    .merge_degrade_failure(function_instanciation.check_function_return(parameters))
                {
                    if !described_type.is_compatible(
                        &parent_generics.read().unwrap(),
                        &sub_return_type,
                        &generics,
                    ) {
                        result = result.and_degrade_failure(LogicResult::new_failure(
                            LogicError::unmatching_datatype(
                                217,
                                scope_descriptor.identifier().clone(),
                                descriptor.identifier().clone(),
                                parameter_name.to_string(),
                                self.clone(),
                                described_type.clone(),
                                sub_return_type,
                                design_reference.clone(),
                            ),
                        ));
                    }

                    if parameter_variability == Variability::Const
                        && sub_variability != Variability::Const
                    {
                        result = result.and_degrade_failure(LogicResult::new_failure(
                            LogicError::const_required_function_returns_var(
                                202,
                                scope_descriptor.identifier().clone(),
                                called_id.clone(),
                                parameter_name.to_string(),
                                descriptor.identifier().clone(),
                                design_reference.clone(),
                            ),
                        ));
                    }

                    variability = sub_variability;
                }
                result.and(LogicResult::new_success(variability))
            }
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        match self {
            Value::Raw(data) => write!(f, "{}", data),
            Value::Array(array) => write!(
                f,
                "[{}]",
                array
                    .iter()
                    .map(|v| v.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
            Value::Variable(name) => write!(f, "{}", name),
            Value::Context(desc, entry) => write!(f, "{}[{}]", desc.name(), entry),
            Value::Function(desc, described_types, params) => write!(
                f,
                "{}{}({})",
                desc.identifier().name(),
                if desc.generics().is_empty() {
                    "".to_string()
                } else {
                    format!(
                        "<{}>",
                        desc.generics()
                            .iter()
                            .map(|gen| if let Some(val) = described_types.get(&gen.name) {
                                val.to_string()
                            } else {
                                "_".to_string()
                            })
                            .collect::<Vec<_>>()
                            .join(", ")
                    )
                },
                params
                    .iter()
                    .map(|p| p.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
        }
    }
}

impl From<ExecutiveValue> for Value {
    fn from(value: ExecutiveValue) -> Self {
        Value::Raw(value)
    }
}

impl From<&ExecutiveValue> for Value {
    fn from(value: &ExecutiveValue) -> Self {
        Value::Raw(value.clone())
    }
}