use ecow::EcoString; use itertools::Itertools; use rpds::HashTrieMap; use crate::bytecode::OpCodes; use crate::error::Result; use crate::ty::internal::{Thunk, Value}; use crate::vm::{CapturedEnv, VM}; #[derive(Debug, Clone)] pub enum Param { Ident(EcoString), Formals { formals: Vec<(EcoString, Option)>, ellipsis: bool, alias: Option, }, } #[derive(Debug, Clone)] pub struct Func { pub env: CapturedEnv, pub param: Option, pub opcodes: OpCodes, } impl Func { pub fn new(env: CapturedEnv, opcodes: OpCodes) -> Func { Func { env, opcodes, param: None, } } pub fn call(self, vm: &VM, arg: Value) -> Result { use Param::*; let env = self.env.released(); match self.param.unwrap() { Ident(ident) => env.enter(HashTrieMap::new_sync().insert(ident.into(), arg)), Formals { formals, ellipsis, alias, } => { let arg = arg.unwrap_attr_set(); let mut new = HashTrieMap::new_sync(); if !ellipsis && arg .as_inner() .iter() .map(|(k, _)| k.as_inner()) .sorted() .ne(formals.iter().map(|(k, _)| k).sorted()) { todo!() } for (formal, default) in formals { let arg = arg .select(formal.clone().into()) .or_else(|| default.map(|thunk| Value::Thunk(thunk))) .unwrap(); new.insert_mut(formal.into(), arg); } if let Some(alias) = alias { new.insert_mut(alias.into(), Value::AttrSet(arg)); } env.enter(new); } } vm.eval(self.opcodes, env) } pub fn push_ident_param(&mut self, param: EcoString) { self.param = Some(Param::Ident(param)) } pub fn push_formal_param(&mut self, param: EcoString) { let Param::Formals { formals, .. } = self.param.get_or_insert_with(|| Param::Formals { formals: Vec::with_capacity(1), ellipsis: false, alias: None, }) else { panic!() }; formals.push((param, None)); } pub fn push_default_param(&mut self, default: Thunk) { let Param::Formals { formals, .. } = self.param.as_mut().unwrap() else { panic!() }; formals.last_mut().unwrap().1 = Some(default) } pub fn set_ellipsis(&mut self) { let Param::Formals { ellipsis, .. } = self.param.get_or_insert_with(|| Param::Formals { formals: Vec::new(), ellipsis: false, alias: None, }) else { panic!() }; *ellipsis = true; } pub fn set_alias(&mut self, sym: EcoString) { let Param::Formals { alias, .. } = self.param.get_or_insert_with(|| Param::Formals { formals: Vec::new(), ellipsis: false, alias: None, }) else { panic!() }; *alias = Some(sym); } } impl PartialEq for Func { fn eq(&self, _: &Self) -> bool { false } }