runmat-vm 0.5.0

RunMat virtual machine and bytecode interpreter
Documentation
use crate::accel::auto_promote::{
    accel_promote_binary, accel_promote_unary, AutoBinaryOp, AutoUnaryOp,
};
use crate::call::descriptor::{execute_callable_descriptor, CallableCallKind, CallableDescriptor};
use crate::call::shared::external_qualified_identity;
use crate::interpreter::dispatch::logical_truth_from_value;
use crate::ops::arithmetic as arithmetic_ops;
use crate::ops::comparison as comparison_ops;
use runmat_builtins::Value;
use runmat_hir::CallableFallbackPolicy;
use runmat_runtime::RuntimeError;

async fn call_operator_method(obj: Value, method: &str, arg: Value) -> Result<Value, RuntimeError> {
    crate::call::shared::call_object_operator_method(obj, method, arg).await
}

pub async fn dispatch_arithmetic(
    instr: &crate::bytecode::Instr,
    stack: &mut Vec<Value>,
) -> Result<bool, RuntimeError> {
    match instr {
        crate::bytecode::Instr::Add => {
            arithmetic_ops::add(stack, call_operator_method, |a, b| async move {
                let (a_acc, b_acc) =
                    accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                runmat_runtime::call_builtin_async("plus", &[a_acc, b_acc]).await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Sub => {
            arithmetic_ops::sub(
                stack,
                call_operator_method,
                |obj, lhs| async move {
                    let class_name = match &obj {
                        Value::Object(o) => o.class_name.clone(),
                        _ => String::new(),
                    };
                    let identity = external_qualified_identity(&class_name, "minus");
                    let descriptor = CallableDescriptor::resolved(
                        identity,
                        vec![lhs, obj],
                        1,
                        CallableFallbackPolicy::RuntimeNameResolution,
                        CallableCallKind::Direct,
                    );
                    execute_callable_descriptor(descriptor).await
                },
                |a, b| async move {
                    let (a_acc, b_acc) =
                        accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                    runmat_runtime::call_builtin_async("minus", &[a_acc, b_acc]).await
                },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Mul => {
            arithmetic_ops::mul(stack, call_operator_method, |a, b| async move {
                let (a_acc, b_acc) = accel_promote_binary(AutoBinaryOp::MatMul, &a, &b).await?;
                runmat_runtime::value_matmul(&a_acc, &b_acc).await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::ElemMul => {
            arithmetic_ops::binary_method(
                stack,
                "times",
                call_operator_method,
                |a, b| async move {
                    let (a_acc, b_acc) =
                        accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                    runmat_runtime::call_builtin_async("times", &[a_acc, b_acc]).await
                },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::ElemDiv => {
            arithmetic_ops::binary_method(
                stack,
                "rdivide",
                call_operator_method,
                |a, b| async move {
                    let (a_acc, b_acc) =
                        accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                    runmat_runtime::call_builtin_async("rdivide", &[a_acc, b_acc]).await
                },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::ElemPow => {
            arithmetic_ops::power(
                stack,
                |obj, _method, arg| async move {
                    runmat_runtime::call_builtin_async("power", &[obj, arg]).await
                },
                |a, b| async move {
                    let (a_acc, b_acc) =
                        accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                    runmat_runtime::call_builtin_async("power", &[a_acc, b_acc]).await
                },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::ElemLeftDiv => {
            arithmetic_ops::binary_method(
                stack,
                "ldivide",
                call_operator_method,
                |a, b| async move {
                    let (b_acc, a_acc) =
                        accel_promote_binary(AutoBinaryOp::Elementwise, &b, &a).await?;
                    runmat_runtime::call_builtin_async("rdivide", &[b_acc, a_acc]).await
                },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::RightDiv => {
            arithmetic_ops::binary_fallback(stack, |a, b| async move {
                arithmetic_ops::execute_right_division(
                    &a,
                    &b,
                    call_operator_method,
                    |lhs, rhs| async move {
                        let (lhs_acc, rhs_acc) =
                            accel_promote_binary(AutoBinaryOp::Elementwise, &lhs, &rhs).await?;
                        runmat_runtime::call_builtin_async("rdivide", &[lhs_acc, rhs_acc]).await
                    },
                    |lhs, rhs| async move {
                        runmat_runtime::call_builtin_async("mrdivide", &[lhs, rhs]).await
                    },
                )
                .await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::LeftDiv => {
            arithmetic_ops::binary_fallback(stack, |a, b| async move {
                arithmetic_ops::execute_left_division(
                    &a,
                    &b,
                    call_operator_method,
                    |lhs, rhs| async move {
                        let (rhs_acc, lhs_acc) =
                            accel_promote_binary(AutoBinaryOp::Elementwise, &rhs, &lhs).await?;
                        runmat_runtime::call_builtin_async("rdivide", &[rhs_acc, lhs_acc]).await
                    },
                    |lhs, rhs| async move {
                        runmat_runtime::call_builtin_async("mldivide", &[lhs, rhs]).await
                    },
                )
                .await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Neg => {
            arithmetic_ops::unary(stack, |value| async move {
                match &value {
                    Value::Object(obj) => {
                        let args = vec![Value::Object(obj.clone())];
                        match runmat_runtime::call_builtin_async("uminus", &args).await {
                            Ok(v) => Ok(v),
                            Err(_) => {
                                runmat_runtime::call_builtin_async(
                                    "times",
                                    &[value.clone(), Value::Num(-1.0)],
                                )
                                .await
                            }
                        }
                    }
                    _ => {
                        runmat_runtime::call_builtin_async(
                            "times",
                            &[value.clone(), Value::Num(-1.0)],
                        )
                        .await
                    }
                }
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Pow => {
            arithmetic_ops::power(stack, call_operator_method, |a, b| async move {
                let (a_acc, b_acc) =
                    accel_promote_binary(AutoBinaryOp::Elementwise, &a, &b).await?;
                runmat_runtime::power(&a_acc, &b_acc).map_err(RuntimeError::from)
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::UPlus => {
            arithmetic_ops::unary(stack, |value| async move {
                match &value {
                    Value::Object(obj) => {
                        let args = vec![Value::Object(obj.clone())];
                        match runmat_runtime::call_builtin_async("uplus", &args).await {
                            Ok(v) => Ok(v),
                            Err(_) => Ok(value),
                        }
                    }
                    _ => Ok(value),
                }
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Transpose => {
            arithmetic_ops::unary(stack, |value| async move {
                let promoted = accel_promote_unary(AutoUnaryOp::Transpose, &value).await?;
                runmat_runtime::call_builtin_async("transpose", &[promoted]).await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::ConjugateTranspose => {
            arithmetic_ops::unary(stack, |value| async move {
                let promoted = accel_promote_unary(AutoUnaryOp::Transpose, &value).await?;
                runmat_runtime::call_builtin_async("ctranspose", &[promoted]).await
            })
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::LessEqual => {
            comparison_ops::relation_inverted(
                stack,
                comparison_ops::RelationInvertedSpec {
                    name: "le",
                    inverse_name: "gt",
                    right_name: "ge",
                    right_inverse_name: "lt",
                    predicate: |aa, bb| aa <= bb,
                },
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
                |v, label| async move { logical_truth_from_value(&v, &label).await },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Less => {
            comparison_ops::relation(
                stack,
                "lt",
                "gt",
                |aa, bb| aa < bb,
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Greater => {
            comparison_ops::relation(
                stack,
                "gt",
                "lt",
                |aa, bb| aa > bb,
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::GreaterEqual => {
            comparison_ops::relation_inverted(
                stack,
                comparison_ops::RelationInvertedSpec {
                    name: "ge",
                    inverse_name: "lt",
                    right_name: "le",
                    right_inverse_name: "gt",
                    predicate: |aa, bb| aa >= bb,
                },
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
                |v, label| async move { logical_truth_from_value(&v, &label).await },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::Equal => {
            comparison_ops::equal(
                stack,
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
                |_v, _label| async move { Ok(false) },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::NotEqual => {
            comparison_ops::not_equal(
                stack,
                call_operator_method,
                |name, a, b| async move { runmat_runtime::call_builtin_async(name, &[a, b]).await },
                |v, label| async move { logical_truth_from_value(&v, &label).await },
            )
            .await?;
            Ok(true)
        }
        crate::bytecode::Instr::LogicalNot => {
            let value = stack.pop().ok_or_else(|| {
                crate::interpreter::errors::mex("StackUnderflow", "stack underflow")
            })?;
            stack.push(runmat_runtime::call_builtin_async("not", &[value]).await?);
            Ok(true)
        }
        crate::bytecode::Instr::LogicalAnd => {
            let rhs = stack.pop().ok_or_else(|| {
                crate::interpreter::errors::mex("StackUnderflow", "stack underflow")
            })?;
            let lhs = stack.pop().ok_or_else(|| {
                crate::interpreter::errors::mex("StackUnderflow", "stack underflow")
            })?;
            stack.push(runmat_runtime::call_builtin_async("and", &[lhs, rhs]).await?);
            Ok(true)
        }
        crate::bytecode::Instr::LogicalOr => {
            let rhs = stack.pop().ok_or_else(|| {
                crate::interpreter::errors::mex("StackUnderflow", "stack underflow")
            })?;
            let lhs = stack.pop().ok_or_else(|| {
                crate::interpreter::errors::mex("StackUnderflow", "stack underflow")
            })?;
            stack.push(runmat_runtime::call_builtin_async("or", &[lhs, rhs]).await?);
            Ok(true)
        }
        _ => Ok(false),
    }
}