diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs index bb79d335..af679d4b 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -407,6 +407,7 @@ impl ExecEnv { self.stack.pop(); res?; } + Functio::Builtin(b) => b.call(args).map_err(|e| Error::new(e, span.clone()))?, Functio::Chain { functios, kind } => { let (left_functio, right_functio) = *functios; match kind { diff --git a/ablescript/src/variables.rs b/ablescript/src/variables.rs index 545f2d45..805c54ef 100644 --- a/ablescript/src/variables.rs +++ b/ablescript/src/variables.rs @@ -44,6 +44,7 @@ pub enum Functio { params: Vec, body: Vec, }, + Builtin(BuiltinFunctio), Chain { functios: Box<(Functio, Functio)>, kind: FunctioChainKind, @@ -59,12 +60,61 @@ impl Functio { tape_len: _, } => 0, Functio::Able { params, body: _ } => params.len(), + Functio::Builtin(b) => b.arity, Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(), Functio::Eval(_) => 0, } } } +#[derive(Clone)] +pub struct BuiltinFunctio { + function: Rc>]) -> Result<(), crate::error::ErrorKind>>, + arity: usize, +} + +impl BuiltinFunctio { + pub fn new(f: F, arity: usize) -> Self + where + F: Fn(&[Rc>]) -> Result<(), crate::error::ErrorKind> + 'static, + { + Self { + function: Rc::new(f), + arity, + } + } + + pub fn call(&self, args: &[Rc>]) -> Result<(), crate::error::ErrorKind> { + (self.function)(args) + } + + pub fn fn_addr(&self) -> usize { + Rc::as_ptr(&self.function) as *const () as _ + } +} + +impl std::fmt::Debug for BuiltinFunctio { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BuiltinFunctio") + .field("function", &"built-in") + .field("arity", &self.arity) + .finish() + } +} + +impl PartialEq for BuiltinFunctio { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.function, &other.function) && self.arity == other.arity + } +} + +impl Hash for BuiltinFunctio { + fn hash(&self, state: &mut H) { + self.fn_addr().hash(state); + self.arity.hash(state); + } +} + #[derive(Debug, PartialEq, Copy, Clone, Hash)] pub enum FunctioChainKind { Equal, @@ -134,6 +184,7 @@ impl Value { .sum::() + body.len() as i32 } + Functio::Builtin(b) => (b.fn_addr() + b.arity) as _, Functio::Chain { functios, kind } => { let (lf, rf) = *functios; Value::Functio(lf).into_i32() @@ -222,6 +273,7 @@ impl Value { Value::Int((params + body) % 3 - 1).into_abool() } + Functio::Builtin(b) => Value::Bool(b.fn_addr() % b.arity == 0).into_abool(), Functio::Chain { functios, kind } => { let (lhs, rhs) = *functios; match kind { @@ -391,6 +443,22 @@ impl Value { ); cart } + Functio::Builtin(b) => { + let mut cart = HashMap::new(); + cart.insert( + Value::Str("addr".to_owned()), + Rc::new(RefCell::new(Value::Cart( + Value::Int(b.fn_addr() as _).into_cart(), + ))), + ); + + cart.insert( + Value::Str("arity".to_owned()), + Rc::new(RefCell::new(Value::Int(b.arity as _))), + ); + + cart + } Functio::Chain { functios, kind } => { let (lhs, rhs) = *functios; match kind { @@ -440,6 +508,9 @@ impl Value { tape_len, } => (instructions.len() + tape_len) as _, Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _, + Functio::Builtin(b) => { + (std::mem::size_of_val(b.function.as_ref()) + b.arity) as i32 + } Functio::Chain { functios, kind } => { let (lhs, rhs) = *functios.clone(); match kind { @@ -564,6 +635,27 @@ impl ops::Sub for Value { ) .into_functio(), }, + Functio::Builtin(b) => { + let arity = b.arity; + let resulting_arity = arity.saturating_sub(rhs.into_i32() as usize); + + Functio::Builtin(BuiltinFunctio::new( + move |args| { + b.call( + &args + .iter() + .cloned() + .take(resulting_arity) + .chain(std::iter::repeat_with(|| { + Rc::new(RefCell::new(Value::Nul)) + })) + .take(arity) + .collect::>(), + ) + }, + resulting_arity, + )) + } Functio::Chain { functios, .. } => { let rhs = rhs.into_functio(); let (a, b) = *functios; @@ -692,6 +784,10 @@ impl ops::Div for Value { .collect(), } } + Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio { + arity: b.arity + rhs.into_i32() as usize, + ..b + }), Functio::Chain { functios, kind } => { let functios = *functios; Functio::Chain { @@ -765,6 +861,13 @@ impl ops::Not for Value { Functio::Able { params, body } } + Functio::Builtin(b) => { + let arity = b.arity; + Functio::Builtin(BuiltinFunctio::new( + move |args| b.call(&args.iter().cloned().rev().collect::>()), + arity, + )) + } Functio::Chain { functios, kind } => { let (a, b) = *functios; Functio::Chain { @@ -858,6 +961,7 @@ impl Display for Value { body, ) } + Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()), Functio::Chain { functios, kind } => { let (a, b) = *functios.clone(); write!(