monty 0.0.21

A sandboxed, snapshotable Python interpreter written in Rust.
Documentation
//! Python builtin functions, types, and exception constructors.
//!
//! This module provides the interpreter-native implementation of Python builtins.
//! Each builtin function has its own submodule for organization.

mod abs;
mod all;
mod any;
mod bin;
mod chr;
mod divmod;
mod enumerate;
mod filter;
mod getattr;
mod hasattr;
mod hash;
mod hex;
mod id;
mod isinstance;
mod len;
mod map;
mod min_max; // min and max share implementation
mod next;
mod oct;
pub(crate) mod open;
mod ord;
mod pow;
mod print;
mod repr;
mod reversed;
mod round;
mod setattr;
mod sorted;
mod sum;
mod type_;
mod zip;

use std::{fmt, fmt::Write, str::FromStr};

use crate::{
    args::ArgValues,
    bytecode::{CallResult, VM},
    exception_private::{ExcType, ExcTypeExt, RunResult},
    types::Type,
    value::Value,
};

/// Enumerates every interpreter-native Python builtins
///
/// Uses strum derives for automatic `Display`, `FromStr`, and `AsRef<str>` implementations.
/// All variants serialize to lowercase (e.g., `Print` -> "print").
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub(crate) enum Builtins {
    /// A builtin function like `print`, `len`, `type`, etc.
    Function(BuiltinsFunctions),
    /// An exception type constructor like `ValueError`, `TypeError`, etc.
    ExcType(ExcType),
    /// A type constructor like `list`, `dict`, `int`, etc.
    Type(Type),
}

impl Builtins {
    /// Resolves builtin names that evaluate directly to singleton values.
    #[must_use]
    pub fn value_from_name(name: &str) -> Option<Value> {
        match name {
            "Ellipsis" => Some(Value::Ellipsis),
            "NotImplemented" => Some(Value::NotImplemented),
            _ => name.parse::<Self>().ok().map(Value::Builtin),
        }
    }

    /// Calls this builtin, allowing builtins that need host involvement to yield.
    ///
    /// Most builtins complete synchronously and produce a [`CallResult::Value`].
    /// `open()` is the exception: it must touch the host filesystem at call
    /// time to perform the open-time effect, so it returns a
    /// [`CallResult::OsCall`] for [`OsFunctionCall::Open`](monty_types::OsFunctionCall) (see
    /// [`crate::builtins::open`]).
    pub fn call(self, vm: &mut VM<'_>, args: ArgValues) -> RunResult<CallResult> {
        match self {
            Self::Function(b) => b.call(vm, args),
            Self::ExcType(exc) => exc.call(vm, args).map(CallResult::Value),
            Self::Type(t) => t.call(vm, args).map(CallResult::Value),
        }
    }

    /// Writes the Python repr() string for this callable to a formatter.
    pub fn py_repr_fmt<W: Write>(self, f: &mut W) -> fmt::Result {
        match self {
            Self::Function(b) => write!(f, "<built-in function {b}>"),
            Self::ExcType(e) => write!(f, "<class '{e}'>"),
            Self::Type(t) => write!(f, "<class '{t}'>"),
        }
    }

    /// Returns the type of this builtin.
    pub fn py_type(self) -> Type {
        match self {
            Self::Function(_) => Type::BuiltinFunction,
            Self::ExcType(_) => Type::Type,
            Self::Type(_) => Type::Type,
        }
    }
}

impl FromStr for Builtins {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // Priority: BuiltinsFunctions > ExcType > Type
        // Only matches names that are true Python builtins (accessible without imports).
        if let Ok(b) = BuiltinsFunctions::from_str(s) {
            Ok(Self::Function(b))
        } else if let Ok(exc) = ExcType::from_str(s) {
            Ok(Self::ExcType(exc))
        } else if let Some(t) = Type::from_builtin_name(s) {
            Ok(Self::Type(t))
        } else {
            Err(())
        }
    }
}

pub use monty_types::BuiltinsFunctions;

pub(crate) trait BuiltinsFunctionsExt: Sized {
    fn call(self, vm: &mut VM<'_>, args: ArgValues) -> RunResult<CallResult>;
}

impl BuiltinsFunctionsExt for BuiltinsFunctions {
    fn call(self, vm: &mut VM<'_>, args: ArgValues) -> RunResult<CallResult> {
        let r = match self {
            Self::Abs => abs::builtin_abs(vm, args),
            Self::All => all::builtin_all(vm, args),
            Self::Any => any::builtin_any(vm, args),
            Self::Bin => bin::builtin_bin(vm, args),
            Self::Chr => chr::builtin_chr(vm, args),
            Self::Divmod => divmod::builtin_divmod(vm, args),
            Self::Enumerate => enumerate::builtin_enumerate(vm, args),
            Self::Filter => filter::builtin_filter(vm, args),
            Self::Getattr => getattr::builtin_getattr(vm, args),
            Self::Hasattr => hasattr::builtin_hasattr(vm, args),
            Self::Hash => hash::builtin_hash(vm, args),
            Self::Hex => hex::builtin_hex(vm, args),
            Self::Id => id::builtin_id(vm, args),
            Self::Isinstance => isinstance::builtin_isinstance(vm, args),
            Self::Len => len::builtin_len(vm, args),
            Self::Map => map::builtin_map(vm, args),
            Self::Max => min_max::builtin_max(vm, args),
            Self::Min => min_max::builtin_min(vm, args),
            Self::Next => next::builtin_next(vm, args),
            Self::Oct => oct::builtin_oct(vm, args),
            // `open()` yields an OS call rather than a plain value.
            Self::Open => return open::builtin_open(vm, args),
            Self::Ord => ord::builtin_ord(vm, args),
            Self::Pow => pow::builtin_pow(vm, args),
            Self::Print => print::builtin_print(vm, args),
            Self::Repr => repr::builtin_repr(vm, args),
            Self::Reversed => reversed::builtin_reversed(vm, args),
            Self::Round => round::builtin_round(vm, args),
            Self::Setattr => setattr::builtin_setattr(vm, args),
            Self::Sorted => sorted::builtin_sorted(vm, args),
            Self::Sum => sum::builtin_sum(vm, args),
            Self::Type => type_::builtin_type(vm, args),
            Self::Zip => zip::builtin_zip(vm, args),
        };
        r.map(CallResult::Value)
    }
}