geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
use super::super::function::{ExecutionGraphProfile, FunctionLabelSource, HostedExecutionGraph};
use super::super::graph::{BlockGraphExitId, ProfiledBlockGraph};
use crate::plan::execution::explain::{Explain, ExplainContext};
use crate::plan::execution::graph::{BlockGraphExitExplanation, LocalLabel};

pub(crate) struct ProfiledConstantProgram<Return, Graph: ExecutionGraphProfile> {
    block_graph: ProfiledBlockGraph<Graph>,
    returns: Box<[Return]>,
}

pub(crate) type ConstantProgram<Return> = ProfiledConstantProgram<Return, HostedExecutionGraph>;

impl<Return, Graph: ExecutionGraphProfile> ProfiledConstantProgram<Return, Graph> {
    pub(in crate::plan::execution) fn from_parts(
        block_graph: ProfiledBlockGraph<Graph>,
        returns: Vec<Return>,
    ) -> Self {
        Self {
            block_graph,
            returns: returns.into_boxed_slice(),
        }
    }

    pub(crate) fn block_graph(&self) -> &ProfiledBlockGraph<Graph> {
        &self.block_graph
    }

    pub(crate) fn return_(&self, id: BlockGraphExitId) -> &Return {
        &self.returns[id.index()]
    }

    pub(in crate::plan::execution) fn into_parts(
        self,
    ) -> (ProfiledBlockGraph<Graph>, Box<[Return]>) {
        (self.block_graph, self.returns)
    }
}

impl<Return, Graph> Explain for ProfiledConstantProgram<Return, Graph>
where
    Return: LocalLabel,
    Graph: ExecutionGraphProfile,
    Graph::ExternalFunctionId: FunctionLabelSource,
    Graph::ExternalListFunctionId: FunctionLabelSource,
    Graph::ExternalFunctionFunctionId: FunctionLabelSource,
    Graph::ExternalListFunctionFunctionId: FunctionLabelSource,
    Graph::ExternalInstruction: Explain,
    Graph::ExternalListInstruction: Explain,
    Graph::ExternalFunctionInstruction: Explain,
{
    fn write_explanation(&self, context: &mut ExplainContext<'_, '_>) {
        self.block_graph()
            .write_explanation(context, &[], &[], self);
    }
}

impl<Return, Graph> BlockGraphExitExplanation for ProfiledConstantProgram<Return, Graph>
where
    Return: LocalLabel,
    Graph: ExecutionGraphProfile,
{
    fn write_exit(&self, context: &mut ExplainContext<'_, '_>, exit: BlockGraphExitId) {
        context.push_str("return ");
        context.write(self.return_(exit));
    }
}

#[cfg(test)]
mod explain_tests {
    use crate::plan::execution::constant::ConstantId;
    use crate::plan::execution::explain;
    use crate::plan::execution::graph::{BlockGraphExitExplanation, BlockGraphExitId, IntLocalId};

    #[test]
    fn writes_reusable_constant_graph_program() {
        let source = r#"
const one = 1
pub fn main() { one }
"#;
        let expected = concat!(
            "  entry b0 params=[] captures=[]\n",
            "  block b0 params=[]\n",
            "    %int#0:shape#0(Int) = int.value 1\n",
            "    return %int#0\n",
        );

        assert_explanation(source, expected);
    }

    #[test]
    fn writes_constant_block_graph_exit() {
        let source = r#"
const one = 1
pub fn main() { one }
"#;
        let expected = "return %int#0";

        explain::assert_rendered(source, expected, |plan, output| {
            let program = plan.constant(ConstantId::<IntLocalId>::new(0));
            let mut context = explain::ExplainContext::new(plan, output);
            program.write_exit(&mut context, BlockGraphExitId::new(0));
        });
    }

    fn assert_explanation(source: &str, expected: &str) {
        explain::assert_rendered(source, expected, |plan, output| {
            let program = plan.constant(ConstantId::<IntLocalId>::new(0));
            let mut context = explain::ExplainContext::new(plan, output);
            context.write(program);
        });
    }
}