geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
mod instruction;
mod terminator;

pub(crate) use instruction::{
    BitArrayBitsSize, BitArrayEvaluatedSize, BitArrayInstruction, BitArraySegment, BoolInstruction,
    CustomInstruction, ExternalFunctionCallTarget, ExternalFunctionInstruction,
    ExternalFunctionInstructionKind, ExternalFunctionInstructionView, ExternalFunctionTarget,
    ExternalInstruction, ExternalInstructionRef, ExternalInstructionView, ExternalListInstruction,
    ExternalListInstructionView, FloatInstruction, FunctionCapture, FunctionInstruction,
    FunctionInstructionKind, FunctionTarget, Instruction, InstructionKind, IntInstruction,
    ListInstruction, NilInstruction, ParameterListInstruction, ProfiledInstruction,
    ProfiledInstructionKind, StringInstruction, TupleInstruction, TypedListInstruction,
    UtfCodepointInstruction,
};
pub(crate) use terminator::{
    BitArrayBindingPattern, BitArrayPattern, BitArrayPatternSegment, BitArrayPatternSize,
    BitArrayPatternSizeExpr, BitArrayPatternValue, BitArrayStringPattern, BoolBranch, Echo, Edge,
    FloatSwitch, IntSwitch, Jump, LetAssertPanic, Match, MatchEdge, MatchEdgeArgument,
    MatchIntBindingId, MatchPattern, MatchPatternBinding, MatchPatternList, MatchPatternListTail,
    NeverCall, NeverCallTarget, Signedness, SourceStop, SourceStopKind, StringSwitch, Terminator,
};

use crate::plan::execution::explain::Explain;
use crate::plan::execution::function::{
    ExecutionGraphProfile, FunctionLabelSource, HostedExecutionGraph,
};
use crate::plan::execution::graph::{BlockGraphExplainContext, ParamSlot};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct BlockId(usize);

pub(crate) struct ProfiledBlock<Graph: ExecutionGraphProfile> {
    params: Box<[ParamSlot]>,
    instructions: Box<[ProfiledInstruction<Graph>]>,
    terminator: Terminator,
}

pub(crate) type Block = ProfiledBlock<HostedExecutionGraph>;
pub(in crate::plan::execution) type ProfiledBlockParts<Graph> = (
    Box<[ParamSlot]>,
    Box<[ProfiledInstruction<Graph>]>,
    Terminator,
);

impl BlockId {
    pub(in crate::plan::execution) fn new(index: usize) -> Self {
        Self(index)
    }

    pub(crate) fn index(self) -> usize {
        self.0
    }
}

impl<Graph: ExecutionGraphProfile> ProfiledBlock<Graph> {
    pub(in crate::plan::execution) fn new(
        params: Vec<ParamSlot>,
        instructions: Vec<ProfiledInstruction<Graph>>,
        terminator: Terminator,
    ) -> Self {
        Self {
            params: params.into_boxed_slice(),
            instructions: instructions.into_boxed_slice(),
            terminator,
        }
    }

    pub(crate) fn params(&self) -> &[ParamSlot] {
        &self.params
    }

    pub(crate) fn instructions(&self) -> &[ProfiledInstruction<Graph>] {
        &self.instructions
    }

    pub(crate) fn terminator(&self) -> &Terminator {
        &self.terminator
    }

    pub(in crate::plan::execution) fn into_parts(self) -> ProfiledBlockParts<Graph> {
        (self.params, self.instructions, self.terminator)
    }

    pub(in crate::plan::execution::graph) fn write_explanation(
        &self,
        context: &mut BlockGraphExplainContext<'_, '_, '_>,
        index: usize,
    ) where
        Graph::ExternalFunctionId: FunctionLabelSource,
        Graph::ExternalListFunctionId: FunctionLabelSource,
        Graph::ExternalFunctionFunctionId: FunctionLabelSource,
        Graph::ExternalListFunctionFunctionId: FunctionLabelSource,
        Graph::ExternalInstruction: Explain,
        Graph::ExternalListInstruction: Explain,
        Graph::ExternalFunctionInstruction: Explain,
    {
        context.push_str("  block b");
        context.push_str(&index.to_string());
        context.push_str(" params=");
        context.write_list(self.params(), |context, slot| context.write(slot));
        context.push('\n');
        for instruction in self.instructions() {
            context.write(instruction);
        }
        context.push_str("    ");
        self.terminator().write_explanation(context);
        context.push('\n');
    }
}

#[cfg(test)]
mod explain_tests {
    use crate::plan::execution::explain;
    use crate::plan::execution::function::IntFunctionId;

    #[test]
    fn writes_block_parameters_instructions_and_terminator() {
        let source = "pub fn main() { 1 }";
        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);
    }

    fn assert_explanation(source: &str, expected: &str) {
        explain::assert_rendered(source, expected, |plan, output| {
            let function = plan.int_function(IntFunctionId(0));
            let body = function.body();
            let mut context = explain::ExplainContext::new(plan, output);
            body.write_explanation(
                &mut context,
                "int",
                function.entry().params(body),
                function.entry().captures(body),
            );
        });
    }
}