geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
mod callable;
mod list;
mod returning_function;
mod value;

pub(in crate::runtime) use callable::{InvocableFunctionValue, invoke_callable};
pub(in crate::runtime) use list::{
    run_bit_array_list, run_bool_list, run_custom_list, run_external_list, run_float_list,
    run_function_list, run_int_list, run_list, run_list_list, run_nil_list, run_parameter_list,
    run_parameter_list_list, run_string_list, run_tuple_list, run_utf_codepoint_list,
};
pub(in crate::runtime) use returning_function::{
    run_core_function, run_external_function_function,
};
pub(in crate::runtime) use value::{
    bit_array_parameter_locals, bool_parameter_locals, float_parameter_locals,
    int_parameter_locals, nil_parameter_locals, run_bit_array, run_bool, run_custom, run_external,
    run_float, run_int, run_never, run_never_value, run_nil, run_string, run_tuple,
    run_utf_codepoint, string_parameter_locals, utf_codepoint_parameter_locals,
};

use crate::plan::execution::function::{
    ExecutionFunction, ExecutionFunctionBody, ExecutionFunctionEntry, ExecutionFunctionRef,
    ExecutionNeverFunction, FunctionBodyOwner, FunctionExit, ProfiledFunctionBody,
};
use crate::plan::execution::graph::ParamLocal;
use crate::runtime::error::{ExecutionResult, HostCallOrigin};
use crate::runtime::graph::{self, GraphValue, RetainedValues};
use crate::runtime::state::RuntimeStateFor;
use crate::runtime::{ExecutableRuntimePlan, RuntimeGraph};

pub(super) enum EvaluatedFunctionExit<Return, TailCall> {
    Return(Return),
    TailCall {
        function: TailCall,
        args: RetainedValues,
    },
}

pub(super) fn evaluate_entry<Plan, Body>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    function: &ExecutionFunction<Plan::Profile, Body>,
    origin: HostCallOrigin,
    inputs: RetainedValues,
) -> ExecutionResult<
    EvaluatedFunctionExit<
        <Body::Return as GraphValue>::Evaluated,
        <Body as FunctionBodyOwner>::TailCall,
    >,
>
where
    Plan: ExecutableRuntimePlan,
    Body: ExecutionFunctionBody<Graph = RuntimeGraph<Plan>>,
    Body::Return: GraphValue,
    Body::TailCall: Clone,
{
    match function.as_ref() {
        ExecutionFunctionRef::Graph(function) => {
            evaluate(plan, state, function.body().function_body(), inputs)
        }
        ExecutionFunctionRef::Host(target) => plan
            .call_host(state, origin, target, inputs)
            .map(EvaluatedFunctionExit::Return),
    }
}

pub(super) fn evaluate_never_entry<Plan>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    function: &ExecutionNeverFunction<Plan::Profile>,
    origin: HostCallOrigin,
    inputs: RetainedValues,
) -> ExecutionResult<
    EvaluatedFunctionExit<
        std::convert::Infallible,
        crate::plan::FunctionCallTarget<crate::plan::execution::function::NeverFunctionId>,
    >,
>
where
    Plan: ExecutableRuntimePlan,
{
    match function.as_ref() {
        ExecutionFunctionRef::Graph(function) => {
            evaluate(plan, state, function.body().function_body(), inputs)
        }
        ExecutionFunctionRef::Host(target) => plan
            .call_host_never(state, origin, target, inputs)
            .map(EvaluatedFunctionExit::Return),
    }
}

pub(super) fn evaluate<Plan, Return, TailCall>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    function: &ProfiledFunctionBody<Return, TailCall, RuntimeGraph<Plan>>,
    inputs: RetainedValues,
) -> ExecutionResult<EvaluatedFunctionExit<Return::Evaluated, TailCall>>
where
    Plan: ExecutableRuntimePlan,
    Return: GraphValue,
    TailCall: Clone,
{
    graph::execute(plan, state, function.block_graph(), inputs).map(|completed| {
        let exit = function.exit(completed.exit());
        match exit {
            FunctionExit::Return(value) => {
                EvaluatedFunctionExit::Return(completed.into_value(state, value))
            }
            FunctionExit::TailCall { function, args } => {
                let function = function.clone();
                let args = completed.into_retained(state, args);
                EvaluatedFunctionExit::TailCall { function, args }
            }
        }
    })
}

pub(super) fn parameter_locals<Plan, Body>(
    plan: &Plan,
    function: &ExecutionFunction<Plan::Profile, Body>,
) -> Vec<ParamLocal>
where
    Plan: ExecutableRuntimePlan,
    Body: ExecutionFunctionBody,
{
    match function.as_ref() {
        ExecutionFunctionRef::Graph(function) => function
            .entry()
            .params(function.body().function_body())
            .iter()
            .map(|slot| slot.local().clone())
            .collect(),
        ExecutionFunctionRef::Host(target) => plan.host_parameters(target).to_vec(),
    }
}

pub(super) fn never_parameter_locals<Plan>(
    plan: &Plan,
    function: &ExecutionNeverFunction<Plan::Profile>,
) -> Vec<ParamLocal>
where
    Plan: ExecutableRuntimePlan,
{
    match function.as_ref() {
        ExecutionFunctionRef::Graph(function) => function
            .entry()
            .params(function.body().function_body())
            .iter()
            .map(|slot| slot.local().clone())
            .collect(),
        ExecutionFunctionRef::Host(target) => plan.host_never_parameters(target).to_vec(),
    }
}

fn run_tail<Plan, Id, Return, TailCall>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    mut function: Id,
    mut origin: HostCallOrigin,
    mut inputs: RetainedValues,
    execute: impl Fn(
        &Plan,
        &mut RuntimeStateFor<'_, Plan>,
        &Id,
        HostCallOrigin,
        RetainedValues,
    ) -> ExecutionResult<EvaluatedFunctionExit<Return, TailCall>>,
    next: impl Fn(&Plan, &Id, TailCall) -> (Id, HostCallOrigin),
) -> ExecutionResult<Return>
where
    Plan: ExecutableRuntimePlan,
{
    loop {
        match execute(plan, state, &function, origin, inputs)? {
            EvaluatedFunctionExit::Return(value) => return Ok(value),
            EvaluatedFunctionExit::TailCall {
                function: target,
                args,
            } => {
                (function, origin) = next(plan, &function, target);
                inputs = args;
            }
        }
    }
}