loft 0.0.1-alpha.22

Rusty embedded scripting language
Documentation
#[macro_export]
macro_rules! unbind {
    { $pattern:pat = $expr:expr } => {
        let $pattern = $expr else {
            unreachable!();
        };
    };
}

#[macro_export]
macro_rules! inner_val {
    ($value:ident) => {
        let $value = {
            let borrowed = $value.borrow();
            borrowed.inner()
        };
    };
}

#[macro_export]
macro_rules! val {
    (mut $inner:expr) => {
        Rc::new(RefCell::new(ValueEnum::Mutable($inner)))
    };
    ($inner:expr) => {
        Rc::new(RefCell::new(ValueEnum::Immutable($inner)))
    };
}

#[macro_export]
macro_rules! impl_binary_ops {
    (
        ($left_val:expr, $operator:expr, $right_val:expr),
        [$($num:ident),*],
        [$($int:ident),*],
        [$($else:ident),*]
    ) => {
        {
            let left = unwrap_value(&$left_val);
            let right = unwrap_value(&$right_val);

            let left_borrowed = left.borrow();
            let right_borrowed = right.borrow();

            match (left_borrowed.inner(), $operator, right_borrowed.inner()) {
                (ValueType::Boolean(l), Token::And, ValueType::Boolean(r)) => Ok(val!(ValueType::Boolean(l && r))),
                (ValueType::Boolean(l), Token::Or, ValueType::Boolean(r)) => Ok(val!(ValueType::Boolean(l || r))),

                $(
                    (ValueType::$num(l), Token::Plus, ValueType::$num(r)) => Ok(val!(ValueType::$num(l + r))),
                    (ValueType::$num(l), Token::Minus, ValueType::$num(r)) => Ok(val!(ValueType::$num(l - r))),
                    (ValueType::$num(l), Token::Star, ValueType::$num(r)) => Ok(val!(ValueType::$num(l * r))),
                    (ValueType::$num(l), Token::Slash, ValueType::$num(r)) => Ok(val!(ValueType::$num(l / r))),
                    (ValueType::$num(l), Token::Rem, ValueType::$num(r)) => Ok(val!(ValueType::$num(l % r))),
                )*

                $(
                    (ValueType::$else(l), Token::LeftAngle, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l < r))),
                    (ValueType::$else(l), Token::RightAngle, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l > r))),
                    (ValueType::$else(l), Token::LessEquals, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l <= r))),
                    (ValueType::$else(l), Token::GreaterEquals, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l >= r))),
                    (ValueType::$else(l), Token::Equals, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l == r))),
                    (ValueType::$else(l), Token::NotEquals, ValueType::$else(r)) => Ok(val!(ValueType::Boolean(l != r))),
                )*

                $(
                    (ValueType::$int(l), Token::BitAnd, ValueType::$int(r)) => Ok(val!(ValueType::$int(l & r))),
                    (ValueType::$int(l), Token::BitOr,  ValueType::$int(r)) => Ok(val!(ValueType::$int(l | r))),
                    (ValueType::$int(l), Token::BitXor, ValueType::$int(r)) => Ok(val!(ValueType::$int(l ^ r))),
                    (ValueType::$int(l), Token::Shl, ValueType::$int(r)) => Ok(val!(ValueType::$int(l << r))),
                    (ValueType::$int(l), Token::Shr, ValueType::$int(r)) => Ok(val!(ValueType::$int(l >> r))),
                )*

                (_, Token::Assign, _)
                | (_, Token::PlusEquals, _)
                | (_, Token::MinusEquals, _)
                | (_, Token::StarEquals, _)
                | (_, Token::SlashEquals, _)
                | (_, Token::RemAssign, _)
                | (_, Token::BitAndAssign, _)
                | (_, Token::BitOrAssign, _)
                | (_, Token::BitXorAssign, _)
                | (_, Token::ShlAssign, _)
                | (_, Token::ShrAssign, _)
                    => Err(format!("Operator `{}` is not supported in binary operations", $operator)),

                _ => Err(format!("Invalid binary operation: {left_borrowed} {} {right_borrowed}", $operator)),
            }
        }
    }
}

#[macro_export]
macro_rules! impl_promote_to_type {
    (($value:expr, $target:expr), $(($Value:ident, $type:ident)),*) => {
        {
            let value_ref = $value.borrow();
            let target_ref = $target.borrow();

            match (value_ref.inner(), target_ref.inner()) {
                $(
                    (ValueType::I8(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::U8(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::I16(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::U16(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::I32(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::U32(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::ISize(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                    (ValueType::USize(x), ValueType::$Value(_)) => Ok(val!(ValueType::$Value(x as $type))),
                )*

                (ValueType::I8(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),
                (ValueType::U8(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),
                (ValueType::I16(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),
                (ValueType::I32(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),
                (ValueType::ISize(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),
                (ValueType::USize(x), ValueType::F32(_)) => Ok(val!(ValueType::F32(x as f32))),

                (ValueType::I8(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::U8(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::I16(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::I32(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::F32(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::ISize(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),
                (ValueType::USize(x), ValueType::F64(_)) => Ok(val!(ValueType::F64(x as f64))),

                _ => Err(format!("Cannot promote {:?} to type of {:?}", value_ref.inner(), target_ref.inner())),
            }
        }
    }
}

#[macro_export]
macro_rules! impl_compound_assignment {
    ($env:expr, $left:expr, $right:expr, $op:expr, {
        $(($type:ident, $rust_type:ty, $method:ident)),* $(,)?
    }) => {
        {
            let left_inner = unwrap_assignment(&$env, &$left).borrow().inner();
            let right_inner = unwrap_assignment(&$env, &$right).borrow().inner();

            match (left_inner, right_inner) {
                $(
                    (ValueType::$type(l), ValueType::$type(r)) => {
                        Ok(val!(ValueType::$type(l.$method(r))))
                    },
                )*

                $(
                    (
                        ValueType::Reference { source_name, source_scope, .. },
                        ValueType::$type(r)
                    ) => {
                        if !$left.borrow().is_mutable() {
                            return Err(format!("Cannot modify immutable reference"));
                        }
                        if let Some(scope) = $env.scopes.get(source_scope.expect("HANDLE THIS")) {
                            if let Some(value) = scope.get(&source_name.clone().expect("HANDLE THIS")) {
                                if let ValueType::$type(l) = value.borrow().inner() {
                                    Ok(val!(ValueType::$type(l.$method(r))))
                                } else {
                                    Err(format!(
                                        "Cannot perform {:?} operation between {:?} and {:?}",
                                        $op, $left, $right
                                    ))
                                }
                            } else {
                                Err(format!(
                                    "Cannot perform {:?} operation between {:?} and {:?}",
                                    $op, $left, $right
                                ))
                            }
                        } else {
                            Err(format!("Reference scope not found"))
                        }
                    },
                )*

                $(
                    (
                        ValueType::$type(l),
                        ValueType::Reference { source_name, source_scope, .. }
                    ) => {
                        if let Some(scope) = $env.scopes.get(source_scope.expect("HANDLE THIS")) {
                            if let Some(value) = scope.get(&source_name.clone().expect("HANDLE THIS")) {
                                if let ValueType::$type(r) = value.borrow().inner() {
                                    Ok(val!(ValueType::$type(l.$method(r))))
                                } else {
                                    Err(format!(
                                        "Cannot perform {:?} operation between {:?} and {:?}",
                                        $op, $left, $right
                                    ))
                                }
                            } else {
                                Err(format!(
                                    "Cannot perform {:?} operation between {:?} and {:?}",
                                    $op, $left, $right
                                ))
                            }
                        } else {
                            Err(format!("Reference scope not found"))
                        }
                    },
                )*

                (
                    ValueType::Reference {
                        source_name: left_name,
                        source_scope: left_scope,
                        ..
                    },
                    ValueType::Reference {
                        source_name: right_name,
                        source_scope: right_scope,
                        ..
                    }
                ) => {
                    if !$left.borrow().is_mutable() {
                        return Err(format!("Cannot modify immutable reference"));
                    }
                    if let (Some(left_sc), Some(right_sc)) = (
                        $env.scopes.get(left_scope.expect("HANDLE THIS")),
                        $env.scopes.get(right_scope.expect("HANDLE THIS"))
                    ) {
                        match (
                            left_sc.get(&left_name.clone().expect("HANDLE THIS"))
                                .map(|v| v.borrow().inner()),
                            right_sc.get(&right_name.clone().expect("HANDLE THIS"))
                                .map(|v| v.borrow().inner())
                        ) {
                            $(
                                (Some(ValueType::$type(l)), Some(ValueType::$type(r))) => {
                                    Ok(val!(ValueType::$type(l.$method(r))))
                                },
                            )*
                            _ => Err(format!(
                                "Cannot perform {:?} operation between {:?} and {:?}",
                                $op, $left, $right
                            ))
                        }
                    } else {
                        Err(format!("Reference scope not found"))
                    }
                },
                _ => Err(format!(
                    "Cannot perform {:?} operation between {:?} and {:?}",
                    $op, $left, $right
                ))
            }
        }
    }
}