geam 0.1.0

Experimental Rust-embedded execution runtime for typed Gleam programs
use super::super::environment::BlockEnvironment;
use super::value::{custom_projection, inputs_with_captures, list_element, tuple_projection};
use crate::plan::ValueType;
use crate::plan::execution::function::ExecutionGraphProfile;
use crate::plan::execution::graph::{ExternalInstructionRef, ExternalInstructionView};
use crate::runtime::error::ExecutionResult;
use crate::runtime::evaluated::{EvaluatedExternalValue, EvaluatedValue};
use crate::runtime::state::RuntimeStateFor;
use crate::runtime::{ExecutableRuntimePlan, RuntimeGraph};

pub(super) fn evaluate<Plan>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    environment: &BlockEnvironment,
    instruction: &<RuntimeGraph<Plan> as ExecutionGraphProfile>::ExternalInstruction,
    expected: &ValueType,
) -> ExecutionResult<EvaluatedExternalValue>
where
    Plan: ExecutableRuntimePlan,
{
    match instruction.instruction_ref() {
        ExternalInstructionRef::Call {
            function,
            args,
            site,
        } => crate::runtime::function::run_external(
            plan,
            state,
            RuntimeGraph::<Plan>::external_function(function),
            crate::runtime::error::HostCallOrigin::source(site.to_owned()),
            environment.retain(args),
        ),
        ExternalInstructionRef::FunctionCall {
            function,
            args,
            site,
        } => {
            let function = environment.external_function(function);
            crate::runtime::function::run_external(
                plan,
                state,
                function.runtime_id(),
                crate::runtime::error::HostCallOrigin::source(site.to_owned()),
                inputs_with_captures(environment, args, function.captures()),
            )
        }
        ExternalInstructionRef::TupleIndex { tuple, index } => tuple_projection(
            plan.value_metadata(),
            environment,
            tuple,
            index,
            expected,
            external_value,
        ),
        ExternalInstructionRef::CustomField { source, index } => {
            custom_projection(plan, environment, source, index, expected, external_value)
        }
        ExternalInstructionRef::ListIndex { list, index } => {
            let list = environment.external_list(list);
            let values = state.lists().external_values(&list);
            list_element(expected, index, &values)
        }
    }
}

fn external_value(value: &EvaluatedValue) -> Option<EvaluatedExternalValue> {
    let EvaluatedValue::External(value) = value else {
        return None;
    };
    Some(value.clone())
}

#[cfg(test)]
mod tests {
    use super::external_value;
    use crate::host::HostExternalStore;
    use crate::plan::execution::type_::ExternalTypeId;
    use crate::runtime::{EvaluatedExternalValue, EvaluatedValue};

    #[test]
    fn extracts_external_instruction_values() {
        fn source_hash(
            context: &crate::host::HostExternalHashing<'_>,
            value: &crate::host::HostStoredValue<num_bigint::BigInt>,
        ) -> u64 {
            context.stored_value_hash(value)
        }

        fn inspect(
            context: &crate::host::HostExternalInspection<'_>,
            value: &crate::host::HostStoredValue<num_bigint::BigInt>,
        ) -> ecow::EcoString {
            context.inspect_stored_value(value)
        }

        let store = HostExternalStore::default();
        let source_equal =
            |context: &crate::host::HostExternalEquality<'_>,
             left: &crate::host::HostStoredValue<num_bigint::BigInt>,
             right: &crate::host::HostStoredValue<num_bigint::BigInt>| {
                context.stored_values_equal(left, right)
            };
        let lease = store.insert(
            crate::host::HostStoredValue::new(crate::runtime::StoredRuntimeValue::test_int(
                7.into(),
            )),
            source_equal,
            source_hash,
            inspect,
        );
        let equal = store.insert(
            crate::host::HostStoredValue::new(crate::runtime::StoredRuntimeValue::test_int(
                7.into(),
            )),
            source_equal,
            source_hash,
            inspect,
        );
        let stored_equal =
            |left: &crate::runtime::StoredRuntimeValue,
             right: &crate::runtime::StoredRuntimeValue| left.value() == right.value();
        let equality = crate::host::HostExternalEquality::new(&stored_equal);
        assert!(lease.source_equal(&equality, &equal));
        let expected = EvaluatedExternalValue::new(ExternalTypeId::new(0), lease);
        let stored_inspect = |_: &crate::runtime::StoredRuntimeValue| "7".into();
        let inspection = crate::host::HostExternalInspection::new(&stored_inspect);

        let stored_hash = |_: &crate::runtime::StoredRuntimeValue| 7;
        let hashing = crate::host::HostExternalHashing::new(&stored_hash);
        assert_eq!(expected.source_hash(&hashing), 7);
        assert_eq!(expected.lease().inspection(&inspection), "7");
        assert_eq!(
            external_value(&EvaluatedValue::External(expected.clone())),
            Some(expected),
        );
    }

    #[test]
    fn rejects_non_external_instruction_values() {
        assert_eq!(external_value(&EvaluatedValue::Bool(true)), None,);
    }
}