geam 0.1.0

Experimental Rust-embedded execution runtime for typed Gleam programs
use super::{
    BitArrayFunctionId, BoolFunctionId, CustomFunctionId, ExecutionGraphProfile, FloatFunctionId,
    FunctionLabelSource, HostedExecutionGraph, IntFunctionId, NeverFunctionId, NilFunctionId,
    StringFunctionId, TupleFunctionId, UtfCodepointFunctionId,
};
use crate::plan::execution::explain::FunctionLabel;
use crate::plan::execution::graph::ExternalFunctionCallTarget;
use crate::plan::execution::type_::{CustomConstructorId, FunctionType, ValueShapeId, ValueType};
use std::convert::Infallible;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum GenericCallableId {
    Function {
        template: usize,
        substitution: Box<[ValueShapeId]>,
    },
    Constructor(CustomConstructorId),
}

impl GenericCallableId {
    pub(in crate::plan::execution) fn function(
        template: usize,
        substitution: Vec<ValueShapeId>,
    ) -> Self {
        Self::Function {
            template,
            substitution: substitution.into_boxed_slice(),
        }
    }

    pub(in crate::plan::execution) fn constructor(constructor: CustomConstructorId) -> Self {
        Self::Constructor(constructor)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ProfiledRuntimeFunctionId<Graph: ExecutionGraphProfile> {
    Core(ProfiledCoreRuntimeFunctionId<Graph>),
    External(Graph::ExternalFunctionId),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ProfiledCoreRuntimeFunctionId<Graph: ExecutionGraphProfile> {
    Never(NeverFunctionId),
    Int(IntFunctionId),
    Float(FloatFunctionId),
    String(StringFunctionId),
    BitArray(BitArrayFunctionId),
    UtfCodepoint(UtfCodepointFunctionId),
    Custom(CustomFunctionId),
    Bool(BoolFunctionId),
    Nil(NilFunctionId),
    Tuple {
        id: TupleFunctionId,
        return_type: Vec<ValueType>,
    },
    List(super::ProfiledListFunctionId<Graph>),
    Function {
        id: Graph::RuntimeFunctionFunctionId,
        return_type: FunctionType,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum RuntimeFunctionFunctionTarget {
    Core(super::ProfiledFunctionFunctionId<Infallible>),
    External(ExternalFunctionCallTarget),
}

pub(crate) type RuntimeFunctionId = ProfiledRuntimeFunctionId<HostedExecutionGraph>;
pub(crate) type CoreRuntimeFunctionId = ProfiledCoreRuntimeFunctionId<HostedExecutionGraph>;

#[cfg(test)]
impl ProfiledRuntimeFunctionId<Infallible> {
    pub(crate) fn runtime_id(&self) -> RuntimeFunctionId {
        match self {
            Self::Core(id) => {
                RuntimeFunctionId::Core(super::profile::plain_core_runtime_function_id(id))
            }
            Self::External(id) => match *id {},
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FunctionReturnFamily {
    Generic,
    Never,
    Int,
    Float,
    String,
    BitArray,
    UtfCodepoint,
    Custom,
    External,
    Bool,
    Nil,
    Tuple,
    List,
    Function,
}

impl std::fmt::Display for FunctionReturnFamily {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(match self {
            Self::Generic => "Generic",
            Self::Never => "Never",
            Self::Int => "Int",
            Self::Float => "Float",
            Self::String => "String",
            Self::BitArray => "BitArray",
            Self::UtfCodepoint => "UtfCodepoint",
            Self::Custom => "Custom",
            Self::External => "External",
            Self::Bool => "Bool",
            Self::Nil => "Nil",
            Self::Tuple => "Tuple",
            Self::List => "List",
            Self::Function => "Function",
        })
    }
}

impl<Graph: ExecutionGraphProfile> FunctionLabelSource for ProfiledRuntimeFunctionId<Graph>
where
    Graph::ExternalFunctionId: FunctionLabelSource,
    Graph::ExternalListFunctionId: FunctionLabelSource,
    Graph::ExternalFunctionFunctionId: FunctionLabelSource,
    Graph::ExternalListFunctionFunctionId: FunctionLabelSource,
    Graph::RuntimeFunctionFunctionId: FunctionLabelSource,
{
    fn function_label(&self) -> FunctionLabel {
        match self {
            Self::Core(id) => id.function_label(),
            Self::External(id) => id.function_label(),
        }
    }
}

impl<Graph: ExecutionGraphProfile> FunctionLabelSource for ProfiledCoreRuntimeFunctionId<Graph>
where
    Graph::ExternalListFunctionId: FunctionLabelSource,
    Graph::ExternalListFunctionFunctionId: FunctionLabelSource,
    Graph::RuntimeFunctionFunctionId: FunctionLabelSource,
{
    fn function_label(&self) -> FunctionLabel {
        match self {
            Self::Never(id) => FunctionLabel::new("never", id.0),
            Self::Int(id) => FunctionLabel::new("int", id.0),
            Self::Float(id) => FunctionLabel::new("float", id.0),
            Self::String(id) => FunctionLabel::new("string", id.0),
            Self::BitArray(id) => FunctionLabel::new("bit_array", id.0),
            Self::UtfCodepoint(id) => FunctionLabel::new("utf_codepoint", id.0),
            Self::Custom(id) => FunctionLabel::new("custom", id.index()),
            Self::Bool(id) => FunctionLabel::new("bool", id.0),
            Self::Nil(id) => FunctionLabel::new("nil", id.0),
            Self::Tuple { id, .. } => FunctionLabel::new("tuple", id.0),
            Self::List(id) => id.function_label(),
            Self::Function { id, .. } => id.function_label(),
        }
    }
}

impl FunctionLabelSource for RuntimeFunctionFunctionTarget {
    fn function_label(&self) -> FunctionLabel {
        self.runtime_id().function_label()
    }
}

impl RuntimeFunctionFunctionTarget {
    pub(crate) fn runtime_id(&self) -> super::FunctionFunctionId {
        match self {
            Self::Core(function) => Infallible::function_function(function),
            Self::External(function) => function.runtime_id(),
        }
    }
}

#[cfg(test)]
mod explain_tests {
    use crate::plan::execution::explain;
    use crate::plan::execution::function::{
        CoreRuntimeFunctionId, ExternalFunctionFunctionId, ExternalFunctionId, FunctionLabelSource,
        RuntimeFunctionFunctionTarget, RuntimeFunctionId,
    };
    use crate::plan::execution::graph::ExternalFunctionCallTarget;
    use crate::plan::execution::type_::{
        ExternalFunctionType, ExternalTypeId, FunctionType, ValueType,
    };

    #[test]
    fn labels_runtime_function_families() {
        let cases = [
            ("pub fn main() -> value { main() }", "never#0"),
            ("pub fn main() { 1 }", "int#0"),
            ("pub fn main() { 1.0 }", "float#0"),
            ("pub fn main() { \"one\" }", "string#0"),
            ("pub fn main() { <<1>> }", "bit_array#0"),
            (
                "pub fn main() -> UtfCodepoint { let assert <<value:utf8_codepoint>> = <<65>> value }",
                "utf_codepoint#0",
            ),
            (
                "pub type Boxed { Boxed(Int) } pub fn main() { Boxed(1) }",
                "custom#0",
            ),
            ("pub fn main() { True }", "bool#0"),
            ("pub fn main() { Nil }", "nil#0"),
            ("pub fn main() { #(1) }", "tuple#0"),
            ("pub fn main() -> List(Int) { [] }", "list.int#0"),
            (
                "pub fn main() -> fn() -> Int { fn() { 1 } }",
                "function.int#0",
            ),
        ];

        for (source, expected) in cases {
            assert_explanation(source, expected);
        }

        explain::assert_written("external#13", |output| {
            RuntimeFunctionId::External(ExternalFunctionId::new(13, ExternalTypeId::new(0)))
                .function_label()
                .write(output);
        });
        explain::assert_written("function.external#14", |output| {
            let external_type = ExternalTypeId::new(0);
            RuntimeFunctionFunctionTarget::External(ExternalFunctionCallTarget::Function(
                ExternalFunctionFunctionId::new(
                    14,
                    ExternalFunctionType::from_shapes(
                        FunctionType::new(Vec::new(), ValueType::External(external_type)),
                        Vec::new(),
                        external_type,
                    ),
                ),
            ))
            .function_label()
            .write(output);
        });
    }

    fn assert_explanation(source: &str, expected: &str) {
        explain::assert_rendered(source, expected, |plan, output| {
            plan.main_runtime().function_label().write(output);
        });
    }

    #[test]
    fn labels_core_runtime_function_ids() {
        explain::assert_written("int#13", |output| {
            CoreRuntimeFunctionId::Int(crate::plan::execution::function::IntFunctionId(13))
                .function_label()
                .write(output);
        });
    }
}

#[cfg(test)]
mod tests {
    use super::FunctionReturnFamily;

    #[test]
    fn display_names_every_family() {
        assert_eq!(
            [
                FunctionReturnFamily::Generic,
                FunctionReturnFamily::Never,
                FunctionReturnFamily::Int,
                FunctionReturnFamily::Float,
                FunctionReturnFamily::String,
                FunctionReturnFamily::BitArray,
                FunctionReturnFamily::UtfCodepoint,
                FunctionReturnFamily::Custom,
                FunctionReturnFamily::External,
                FunctionReturnFamily::Bool,
                FunctionReturnFamily::Nil,
                FunctionReturnFamily::Tuple,
                FunctionReturnFamily::List,
                FunctionReturnFamily::Function,
            ]
            .map(|family| family.to_string()),
            [
                "Generic",
                "Never",
                "Int",
                "Float",
                "String",
                "BitArray",
                "UtfCodepoint",
                "Custom",
                "External",
                "Bool",
                "Nil",
                "Tuple",
                "List",
                "Function",
            ],
        );
    }
}