Files
endorphin-aster-vm/crates/core/src/builtins.rs
T
codex-bot 6fa95b64b0
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 14m27s
Implement validated wordcode execution with shared code objects and frames
2026-10-18 20:41:56 +08:00

348 lines
9.0 KiB
Rust

use std::any::Any;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::{CompareOp, ObjectPayload, ObjectRef, Value, VmError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EndorStringObject {
text: String,
}
impl EndorStringObject {
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into() }
}
pub fn as_str(&self) -> &str {
&self.text
}
pub fn char_count(&self) -> usize {
self.text.chars().count()
}
pub fn char_at(&self, index: usize) -> Option<char> {
self.text.chars().nth(index)
}
}
impl ObjectPayload for EndorStringObject {
fn as_any(&self) -> &dyn Any {
self
}
fn kind_name(&self) -> &'static str {
"string"
}
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("{:?}", self.text))
}
fn str(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(self.text.clone())
}
fn is_true(&self) -> Result<bool, VmError> {
Ok(!self.text.is_empty())
}
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
let mut hasher = DefaultHasher::new();
self.text.hash(&mut hasher);
Ok(hasher.finish())
}
fn compare(&self, other: &dyn ObjectPayload, op: CompareOp) -> Result<Option<bool>, VmError> {
let Some(other) = other.as_any().downcast_ref::<Self>() else {
return Ok(None);
};
Ok(Some(match op {
CompareOp::Eq => self.text == other.text,
CompareOp::Ne => self.text != other.text,
CompareOp::Lt => self.text < other.text,
CompareOp::Le => self.text <= other.text,
CompareOp::Gt => self.text > other.text,
CompareOp::Ge => self.text >= other.text,
}))
}
}
/// Insertion-ordered entries. Mutation is only exposed through Vm so every
/// key/value reference is validated, retained, traced, and released correctly.
/// Linear lookup is intentional for the initial representation layer.
#[derive(Debug, Clone, Default)]
pub struct EndorDictObject {
pub(crate) entries: Vec<(Value, Value)>,
}
impl EndorDictObject {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn entries(&self) -> &[(Value, Value)] {
&self.entries
}
}
impl ObjectPayload for EndorDictObject {
fn as_any(&self) -> &dyn Any {
self
}
fn kind_name(&self) -> &'static str {
"dict"
}
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
for (key, value) in &self.entries {
trace_value(key, visit);
trace_value(value, visit);
}
}
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("<dict len={}>", self.len()))
}
fn is_true(&self) -> Result<bool, VmError> {
Ok(!self.is_empty())
}
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
Err(VmError::UnsupportedOperation("dict hash"))
}
}
/// An empty cell differs from a cell containing Value::Nil.
#[derive(Debug, Clone, Default)]
pub struct EndorCellObject {
pub(crate) value: Option<Value>,
}
impl EndorCellObject {
pub fn new(value: Option<Value>) -> Self {
Self { value }
}
pub fn value(&self) -> Option<&Value> {
self.value.as_ref()
}
}
impl ObjectPayload for EndorCellObject {
fn as_any(&self) -> &dyn Any {
self
}
fn kind_name(&self) -> &'static str {
"cell"
}
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
if let Some(value) = &self.value {
trace_value(value, visit);
}
}
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(if self.value.is_some() {
"<cell occupied>"
} else {
"<cell empty>"
}
.into())
}
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
Err(VmError::UnsupportedOperation("cell hash"))
}
}
#[derive(Debug, Clone)]
pub struct EndorFunctionObject {
name: String,
code: ObjectRef,
builtins: Option<ObjectRef>,
globals: ObjectRef,
defaults: Vec<Value>,
closure: Vec<ObjectRef>,
}
impl EndorFunctionObject {
pub(crate) fn new(
name: String,
code: ObjectRef,
globals: ObjectRef,
builtins: Option<ObjectRef>,
defaults: Vec<Value>,
closure: Vec<ObjectRef>,
) -> Self {
Self {
name,
code,
globals,
builtins,
defaults,
closure,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn code(&self) -> ObjectRef {
self.code
}
pub fn builtins(&self) -> Option<ObjectRef> {
self.builtins
}
pub fn globals(&self) -> ObjectRef {
self.globals
}
pub fn defaults(&self) -> &[Value] {
&self.defaults
}
pub fn closure(&self) -> &[ObjectRef] {
&self.closure
}
}
impl ObjectPayload for EndorFunctionObject {
fn as_any(&self) -> &dyn Any {
self
}
fn kind_name(&self) -> &'static str {
"function"
}
fn allows_attributes(&self) -> bool {
true
}
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
visit(self.code);
visit(self.globals);
if let Some(builtins) = self.builtins {
visit(builtins);
}
for reference in &self.closure {
visit(*reference);
}
for value in &self.defaults {
trace_value(value, visit);
}
}
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("<function {}>", self.name))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EndorTracebackEntry {
pub function: String,
pub file: String,
pub line: usize,
}
/// Common exception instance data, not an exception class or inheritance tree.
#[derive(Debug, Clone)]
pub struct EndorBaseExceptionObject {
name: String,
args: Vec<Value>,
cause: Option<ObjectRef>,
context: Option<ObjectRef>,
suppress_context: bool,
notes: Vec<String>,
traceback: Vec<EndorTracebackEntry>,
}
impl EndorBaseExceptionObject {
pub fn new(name: impl Into<String>, args: Vec<Value>) -> Self {
Self {
name: name.into(),
args,
cause: None,
context: None,
suppress_context: false,
notes: Vec::new(),
traceback: Vec::new(),
}
}
pub fn with_cause(mut self, cause: ObjectRef) -> Self {
self.cause = Some(cause);
self.suppress_context = true;
self
}
pub fn with_context(mut self, context: ObjectRef) -> Self {
self.context = Some(context);
self
}
pub fn suppress_context(mut self, suppress: bool) -> Self {
self.suppress_context = suppress;
self
}
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
pub fn with_traceback(mut self, entry: EndorTracebackEntry) -> Self {
self.traceback.push(entry);
self
}
pub fn name(&self) -> &str {
&self.name
}
pub fn args(&self) -> &[Value] {
&self.args
}
pub fn cause(&self) -> Option<ObjectRef> {
self.cause
}
pub fn context(&self) -> Option<ObjectRef> {
self.context
}
pub fn context_suppressed(&self) -> bool {
self.suppress_context
}
pub fn notes(&self) -> &[String] {
&self.notes
}
pub fn traceback(&self) -> &[EndorTracebackEntry] {
&self.traceback
}
}
impl ObjectPayload for EndorBaseExceptionObject {
fn as_any(&self) -> &dyn Any {
self
}
fn kind_name(&self) -> &'static str {
"exception"
}
fn allows_attributes(&self) -> bool {
true
}
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
for value in &self.args {
trace_value(value, visit);
}
for reference in [self.cause, self.context].into_iter().flatten() {
visit(reference);
}
}
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!(
"{}({})",
self.name,
self.args
.iter()
.map(|arg| format!("{arg:?}"))
.collect::<Vec<_>>()
.join(", ")
))
}
fn str(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(match self.args.as_slice() {
[] => String::new(),
[Value::Text(text)] => text.clone(),
[Value::Number(number)] => number.to_string(),
args => format!("{args:?}"),
})
}
}
pub(crate) fn trace_value(value: &Value, visit: &mut dyn FnMut(ObjectRef)) {
if let Value::Object(reference) = value {
visit(*reference);
}
}