use fnv::{FnvHashMap};
use owning_ref::{OwningHandle};
use self::stock_syms::*;
use smallvec::{SmallVec};
use std::{fmt, fs, str, u32};
use std::any::{Any, TypeId, type_name};
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::{HashMap, hash_map::Entry::{Occupied, Vacant}, HashSet};
use std::convert::{TryFrom};
use std::fmt::{Debug, Display, Formatter, Pointer};
use std::io::{self, stderr, stdout, Write};
use std::iter::{FromIterator};
use std::marker::{PhantomData};
use std::num::{NonZeroU32};
use std::ops::{Deref, DerefMut};
use std::panic::{self, AssertUnwindSafe};
use std::path::{PathBuf};
use std::rc::{Rc};
use std::time::{SystemTime, UNIX_EPOCH};
use super::{eval, lex};
use super::class::{Class, Obj};
use super::code::{Coro, GFn};
use super::collections::{Arr, DequeAccess, DequeOps, IntoElement, Str, Tab};
use super::error::{GResult};
use super::eval::{Env, EnvMode, Expander, Expansion};
use super::gc::{Allocate, Heap, Gc, GcHeader, Slot, Root, Visitor};
use super::iter::{GcCallable, GIter, GIterState, Iterable, IterableOps};
use super::parse::{Parser};
use super::transform::{KnownOp, known_ops};
use super::val::{Num, Val};
use super::vm::{Frame, GlspApiName, Vm};
use super::wrap::{FromVal, ToCallArgs, Callable, CallableOps, ToVal, WrappedFn};
#[cfg(feature = "compiler")]
use std::mem::forget;
#[cfg(feature = "compiler")]
use super::{code::Stay, compile::{Action, Recording}};
thread_local! {
static ACTIVE_ENGINE: RefCell<Option<Rc<EngineStorage>>> = RefCell::new(None);
pub(crate) static ACTIVE_ENGINE_ID: Cell<Option<u8>> = Cell::new(None);
static ID_BITSET: [Cell<u32>; 8] = [
Cell::new(0), Cell::new(0), Cell::new(0), Cell::new(0),
Cell::new(0), Cell::new(0), Cell::new(0), Cell::new(0)
];
}
fn alloc_engine_id() -> Option<u8> {
ID_BITSET.with(|bitset| {
for i in 0 .. 8 {
let mut bits = bitset[i].get();
let mut j = 0;
if bits != 0xffffffff {
while bits & 0x1 == 0x1 {
bits >>= 1;
j += 1;
}
assert!(j <= 31 && i*32 + j <= 255);
bitset[i].set(bitset[i].get() | (1 << j));
return Some((i*32 + j) as u8)
}
}
None
})
}
fn free_engine_id(id: u8) {
ID_BITSET.with(|bitset| {
let i = id as usize / 32;
let j = id as u32 % 32;
assert!(bitset[i].get() & (0x1 << j) != 0);
bitset[i].set(bitset[i].get() & !(0x1 << j));
})
}
#[inline(always)]
fn with_engine<R, F: FnOnce(&EngineStorage) -> R>(f: F) -> R {
ACTIVE_ENGINE.with(|ref_cell| {
let borrow = ref_cell.borrow();
let rc = borrow.as_ref().unwrap();
f(&rc)
})
}
#[inline(always)]
pub(crate) fn with_heap<R, F: FnOnce(&Heap) -> R>(f: F) -> R {
ACTIVE_ENGINE.with(|ref_cell| {
let opt_rc = ref_cell.borrow();
match &*opt_rc {
Some(rc) => f(&rc.heap),
None => panic!("called with_heap when no glsp engine is active")
}
})
}
#[inline(always)]
pub(crate) fn with_vm<R, F: FnOnce(&Vm) -> R>(f: F) -> R {
ACTIVE_ENGINE.with(|ref_cell| {
let opt_rc = ref_cell.borrow();
match &*opt_rc {
Some(rc) => f(&rc.vm),
None => panic!("called with_vm when no glsp engine is active")
}
})
}
#[inline(always)]
pub(crate) fn with_known_ops<R, F: FnOnce(&HashMap<Sym, KnownOp>) -> R>(f: F) -> R {
ACTIVE_ENGINE.with(|ref_cell| {
let opt_rc = ref_cell.borrow();
match &*opt_rc {
Some(rc) => f(&rc.known_ops),
None => panic!("called with_known_ops when no glsp engine is active")
}
})
}
#[doc(hidden)]
pub fn with_lazy_val<R, FInit, FUse>(key: &str, f_init: FInit, f_use: FUse) -> R
where
FInit: FnOnce() -> Val,
FUse: FnOnce(&Val) -> R
{
let val = with_engine(|engine| {
engine.lazy_storage.borrow().get(key).cloned()
});
if let Some(val) = val {
f_use(&val)
} else {
let new_val = f_init();
with_engine(|engine| {
engine.lazy_storage.borrow_mut().insert(key.to_string(), new_val.clone());
});
f_use(&new_val)
}
}
#[doc(hidden)]
pub struct PrWriter;
impl Write for PrWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
with_engine(|engine| engine.pr_writer.borrow_mut().write(buf))
}
fn flush(&mut self) -> io::Result<()> {
with_engine(|engine| engine.pr_writer.borrow_mut().flush())
}
}
#[doc(hidden)]
pub struct EprWriter;
impl Write for EprWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
with_engine(|engine| engine.epr_writer.borrow_mut().write(buf))
}
fn flush(&mut self) -> io::Result<()> {
with_engine(|engine| engine.epr_writer.borrow_mut().flush())
}
}
#[macro_export]
macro_rules! pr {
($($arg:tt)*) => (
{
use std::io::Write;
write!($crate::PrWriter, $($arg)*).ok();
$crate::PrWriter.flush().ok();
}
);
}
#[macro_export]
macro_rules! prn {
($($arg:tt)*) => (
{
use std::io::Write;
writeln!($crate::PrWriter, $($arg)*).ok();
}
);
}
#[macro_export]
macro_rules! epr {
($($arg:tt)*) => (
{
use std::io::Write;
write!($crate::EprWriter, $($arg)*).ok();
$crate::EprWriter.flush().ok();
}
);
}
#[macro_export]
macro_rules! eprn {
($($arg:tt)*) => (
{
use std::io::Write;
writeln!($crate::EprWriter, $($arg)*).ok();
}
);
}
pub auto trait GSend { }
impl !GSend for Sym { }
impl !GSend for RFn { }
impl<T> !GSend for Gc<T> { }
impl<T> !GSend for Root<T> { }
impl<T> !GSend for LibRef<T> { }
impl<T> !GSend for LibRefMut<T> { }
impl<T> !GSend for RRef<T> { }
impl<T> !GSend for RRefMut<T> { }
pub auto trait GStore { }
impl GStore for RData { }
impl<T> !GStore for Root<T> { }
impl<T> !GStore for LibRef<T> { }
impl<T> !GStore for LibRefMut<T> { }
impl<T> !GStore for RRef<T> { }
impl<T> !GStore for RRefMut<T> { }
impl<T> !Send for Gc<T> { }
impl<T> !Send for Root<T> { }
impl !Send for Sym { }
impl !Send for RFn { }
#[doc(hidden)]
pub struct EngineBuilder;
impl EngineBuilder {
pub fn new() -> EngineBuilder {
EngineBuilder
}
pub fn build(self) -> Engine {
Engine::new()
}
}
#[doc(hidden)]
pub struct Engine(Rc<EngineStorage>);
impl Drop for Engine {
fn drop(&mut self) {
self.run(|| {
with_engine(|engine| {
while let Some(type_id) = engine.libs_ordering.borrow_mut().pop() {
let lib = engine.libs.borrow_mut().remove(&type_id).unwrap();
drop(lib);
}
engine.lazy_storage.borrow_mut().clear();
engine.syms.borrow_mut().clear();
engine.rfns.borrow_mut().clear();
engine.vm.clear();
engine.heap.clear();
});
Ok(())
});
free_engine_id(self.0.id);
debug_assert!(Rc::strong_count(&self.0) == 1);
}
}
struct EngineStorage {
id: u8,
heap: Heap,
vm: Vm,
pr_writer: RefCell<Box<dyn Write>>,
epr_writer: RefCell<Box<dyn Write>>,
syms: RefCell<Vec<SymEntry>>,
syms_map: RefCell<HashMap<Rc<str>, Sym>>,
gensym_counter: Cell<u32>,
gensym_seed: RefCell<Option<String>>,
spans: RefCell<Vec<SpanStorage>>,
spans_map: RefCell<HashMap<SpanStorage, Span>>,
filenames: RefCell<Vec<Rc<str>>>,
filenames_map: RefCell<HashMap<Rc<str>, Filename>>,
required: RefCell<HashSet<PathBuf>>,
rfns: RefCell<Vec<RFnEntry>>,
rfns_map: RefCell<HashMap<usize, RFn>>,
rclasses: RefCell<HashMap<TypeId, Rc<RClass>>>,
rclass_names: RefCell<HashSet<&'static str>>,
in_expander: RefCell<Option<(Option<Sym>, Span, Rc<Env>)>>,
errors_verbose: Cell<bool>,
libs: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
libs_ordering: RefCell<Vec<TypeId>>,
#[cfg(feature = "compiler")] recording: RefCell<Option<Recording>>,
#[cfg(feature = "compiler")] playing_back: RefCell<Option<Recording>>,
lazy_storage: RefCell<HashMap<String, Val>>,
known_ops: HashMap<Sym, KnownOp>
}
struct SymEntry {
name: Rc<str>,
kind: SymKind,
bound_global: Option<GlobalEntry>,
bound_macro: Option<Expander>
}
struct GlobalEntry {
val: Val,
frozen: bool
}
struct RFnEntry {
name: Option<Sym>,
wrapped_fn: WrappedFn
}
impl Engine {
pub fn new() -> Engine {
let syms = Vec::from_iter(STOCK_SYMS.iter().map(|&(name, kind)| {
SymEntry {
name: name.into(),
kind,
bound_global: None,
bound_macro: None
}
}));
let syms_map = HashMap::from_iter(syms.iter().enumerate().map(|(i, sym_entry)| {
(Rc::clone(&sym_entry.name), Sym(i as u32))
}));
let spans = vec![SpanStorage::Generated];
let mut spans_map = HashMap::new();
spans_map.insert(SpanStorage::Generated, Span(0));
let rfns = vec![RFnEntry {
name: None,
wrapped_fn: rfn!(|| panic!())
}];
let filenames = vec!["".into()];
let engine = Engine(Rc::new(EngineStorage {
id: alloc_engine_id().expect("more than 256 simultaneous Runtimes"),
heap: Heap::new(),
vm: Vm::new(),
pr_writer: RefCell::new(Box::new(stdout())),
epr_writer: RefCell::new(Box::new(stderr())),
syms: RefCell::new(syms),
syms_map: RefCell::new(syms_map),
gensym_counter: Cell::new(0),
gensym_seed: RefCell::new(None),
spans: RefCell::new(spans),
spans_map: RefCell::new(spans_map),
filenames: RefCell::new(filenames),
filenames_map: RefCell::new(HashMap::new()),
required: RefCell::new(HashSet::new()),
rfns: RefCell::new(rfns),
rfns_map: RefCell::new(HashMap::new()),
rclasses: RefCell::new(HashMap::new()),
rclass_names: RefCell::new(HashSet::new()),
in_expander: RefCell::new(None),
errors_verbose: Cell::new(true),
libs: RefCell::new(HashMap::new()),
libs_ordering: RefCell::new(Vec::new()),
#[cfg(feature = "compiler")] recording: RefCell::new(None),
#[cfg(feature = "compiler")] playing_back: RefCell::new(None),
lazy_storage: RefCell::new(HashMap::new()),
known_ops: known_ops()
}));
engine
}
pub fn run<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce() -> GResult<R>,
F: GSend,
R: GSend
{
let old_active_engine = ACTIVE_ENGINE.with(|ref_cell| {
ref_cell.replace(Some(self.0.clone()))
});
let old_engine_id = ACTIVE_ENGINE_ID.with(|cell| {
cell.replace(Some(self.0.id))
});
let _guard = Guard::new(|| {
ACTIVE_ENGINE.with(|ref_cell| {
ref_cell.replace(old_active_engine);
});
ACTIVE_ENGINE_ID.with(|cell| {
cell.set(old_engine_id);
});
});
let result = f();
if let Err(ref error) = result {
if error.stack_trace().is_some() {
eprn!("\nunhandled error in run() call:\n\n{}", error);
} else {
eprn!("\nunhandled error in run() call: {}", error);
}
}
result.ok()
}
}
pub(crate) struct Guard<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Guard<F> {
pub(crate) fn new(f: F) -> Guard<F> {
Guard(Some(f))
}
}
impl<F: FnOnce()> Drop for Guard<F> {
fn drop(&mut self) {
(self.0.take().unwrap())()
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct Sym(pub(crate) u32);
const MAX_SYM: u32 = 0xffffff;
impl Sym {
pub fn name(&self) -> Rc<str> {
with_engine(|engine| {
Rc::clone(&engine.syms.borrow()[self.0 as usize].name)
})
}
pub fn is_gensym(&self) -> bool {
self.kind() == SymKind::Gensym
}
#[doc(hidden)]
pub fn kind(&self) -> SymKind {
with_engine(|engine| {
engine.syms.borrow()[self.0 as usize].kind
})
}
pub(crate) fn is_bindable(&self) -> bool {
match self.kind() {
SymKind::StockSpecial => false,
SymKind::Normal | SymKind::StockKeyword |
SymKind::StockTransform | SymKind::Gensym => true
}
}
#[doc(hidden)]
pub fn from_u32(id: u32) -> Sym {
assert!(id <= MAX_SYM);
Sym(id)
}
#[doc(hidden)]
pub fn to_u32(&self) -> u32 {
self.0
}
}
pub trait ToSym {
fn to_sym(&self) -> GResult<Sym>;
}
impl<'a, T> ToSym for T
where
T: Deref,
T::Target: ToSym
{
fn to_sym(&self) -> GResult<Sym> {
(**self).to_sym()
}
}
impl ToSym for Sym {
fn to_sym(&self) -> GResult<Sym> {
Ok(*self)
}
}
impl<'a> ToSym for str {
fn to_sym(&self) -> GResult<Sym> {
glsp::sym(self)
}
}
impl<'a> ToSym for Str {
fn to_sym(&self) -> GResult<Sym> {
glsp::sym(&self.to_string())
}
}
#[doc(hidden)]
#[derive(Clone, Copy, PartialEq)]
pub enum SymKind {
Normal,
StockSpecial,
StockKeyword,
StockTransform,
Gensym
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct RFn(NonZeroU32);
impl RFn {
pub(crate) fn set_name(&self, new_name: Option<Sym>) {
with_engine(|engine| {
engine.rfns.borrow_mut()[self.0.get() as usize].name = new_name;
})
}
}
impl CallableOps for RFn {
fn receive_call(&self, arg_count: usize) -> GResult<Val> {
glsp::call_rfn(*self, arg_count).map(|slot| slot.root())
}
fn name(&self) -> Option<Sym> {
with_engine(|engine| {
engine.rfns.borrow()[self.0.get() as usize].name
})
}
fn arg_limits(&self) -> (usize, Option<usize>) {
with_engine(|engine| {
engine.rfns.borrow()[self.0.get() as usize].wrapped_fn.arg_limits
})
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub struct Filename(NonZeroU32);
#[macro_export]
macro_rules! syms {
(
$(#[$struct_attr:meta])*
$struct_vis:vis struct $struct_name:ident {
$($vis:vis $name:ident: $contents:literal),+
}
) => (
$crate::syms! {
$(#[$struct_attr])*
$struct_vis struct $struct_name {
$($vis $name: $contents,)+
}
}
);
(
$(#[$struct_attr:meta])*
$struct_vis:vis struct $struct_name:ident {
$($vis:vis $name:ident: $contents:literal,)*
}
) => (
$struct_vis struct $struct_name {
$($vis $name: Sym,)*
}
impl $struct_name {
#[allow(unused_variables)]
$struct_vis fn new() -> GResult<Syms> {
Ok($struct_name {
$($name: glsp::sym($contents)?,)*
})
}
}
);
}
pub trait Lib: 'static + Sized {
fn type_name() -> &'static str;
fn borrow() -> LibRef<Self> {
glsp::lib::<Self>()
}
fn borrow_mut() -> LibRefMut<Self> {
glsp::lib_mut::<Self>()
}
fn try_borrow() -> GResult<LibRef<Self>> {
glsp::try_lib::<Self>()
}
fn try_borrow_mut() -> GResult<LibRefMut<Self>> {
glsp::try_lib_mut::<Self>()
}
}
pub struct LibRef<T: Lib> {
handle: OwningHandle<Rc<RefCell<T>>, Ref<'static, T>>
}
impl<T: Lib> Deref for LibRef<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.handle
}
}
pub struct LibRefMut<T: Lib> {
handle: OwningHandle<Rc<RefCell<T>>, RefMut<'static, T>>
}
impl<T: Lib> Deref for LibRefMut<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.handle
}
}
impl<T: Lib> DerefMut for LibRefMut<T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.handle
}
}
trait RAllocate: GStore + 'static {
fn type_name(&self) -> &'static str;
fn size_of(&self) -> usize;
fn as_any(&self) -> &dyn Any;
fn as_rc_any(self: Rc<Self>) -> Rc<dyn Any>;
}
impl<T: RStore> RAllocate for RefCell<T> {
fn type_name(&self) -> &'static str {
T::type_name()
}
fn size_of(&self) -> usize {
T::size_of()
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
pub trait RStore: GStore + 'static {
fn type_name() -> &'static str;
fn size_of() -> usize;
fn rclass() -> GResult<RClass>;
}
pub struct RClass {
name: Sym,
bindings: FnvHashMap<Sym, RBinding>
}
enum RBinding {
Meth(RFn),
Prop(Option<RFn>, Option<RFn>)
}
impl RClass {
pub fn from_vec(
class_name: &'static str,
raw_bindings: Vec<(&'static str, &'static str, WrappedFn)>
) -> GResult<RClass> {
let class_name = glsp::sym(class_name)?;
let mut bindings = FnvHashMap::with_capacity_and_hasher(
raw_bindings.len(),
Default::default()
);
for (kind, key, wrapped_fn) in raw_bindings {
let name = glsp::sym(key)?;
let rfn = glsp::rfn(wrapped_fn);
if rfn.name().is_none() {
rfn.set_name(Some(name));
}
match (kind, bindings.entry(name)) {
("", Vacant(entry)) => { entry.insert(RBinding::Meth(rfn)); }
("get", Vacant(entry)) => { entry.insert(RBinding::Prop(Some(rfn), None)); }
("set", Vacant(entry)) => { entry.insert(RBinding::Prop(None, Some(rfn))); }
("", Occupied(_)) => bail!("duplicate meth name {}", name),
("get", Occupied(mut entry)) => {
match entry.get_mut() {
RBinding::Meth(_) => bail!("{} is bound to both a meth and a prop", name),
RBinding::Prop(Some(_), _) => bail!("duplicate getter {}", name),
RBinding::Prop(ref mut none, _) => *none = Some(rfn)
}
}
("set", Occupied(mut entry)) => {
match entry.get_mut() {
RBinding::Meth(_) => bail!("{} is bound to both a meth and a prop", name),
RBinding::Prop(_, Some(_)) => bail!("duplicate setter {}", name),
RBinding::Prop(_, ref mut none) => *none = Some(rfn)
}
}
(kind, _) => bail!("{} is not a valid tag for an RData meth", kind)
}
}
Ok(RClass {
name: class_name,
bindings
})
}
}
pub struct RData {
header: GcHeader,
storage: RefCell<Option<Rc<dyn RAllocate>>>,
pub(crate) class: Rc<RClass>,
gc_self: Cell<Option<Gc<RData>>>
}
impl Allocate for RData {
fn header(&self) -> &GcHeader {
&self.header
}
fn visit_gcs<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_gc(&self.gc_self());
}
fn clear_gcs(&self) {
self.gc_self.set(None);
}
fn owned_memory_usage(&self) -> usize {
match self.storage.borrow().as_ref() {
Some(rc_ref) => rc_ref.size_of(),
None => 0
}
}
}
pub struct RRef<T: RStore>(OwningHandle<Rc<RefCell<T>>, Ref<'static, T>>);
impl<T: RStore> Deref for RRef<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
pub struct RRefMut<T: RStore>(OwningHandle<Rc<RefCell<T>>, RefMut<'static, T>>);
impl<T: RStore> Deref for RRefMut<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
impl<T: RStore> DerefMut for RRefMut<T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.0
}
}
impl RData {
pub(crate) fn new<T: RStore>(rdata: T, class: Rc<RClass>) -> RData {
RData {
header: GcHeader::new(),
storage: RefCell::new(Some(Rc::new(RefCell::new(rdata)))),
class,
gc_self: Cell::new(None)
}
}
pub fn type_name(&self) -> &'static str {
match self.storage.borrow().as_ref() {
Some(rc_ref) => rc_ref.type_name(),
None => ""
}
}
pub fn class_name(&self) -> Sym {
self.class.name
}
pub fn is<T: RStore>(&self) -> bool {
self.storage.borrow().as_ref().unwrap().as_any().is::<RefCell<T>>()
}
pub fn borrow<T: RStore>(&self) -> RRef<T> {
self.try_borrow::<T>().unwrap()
}
pub fn try_borrow<T: RStore>(&self) -> GResult<RRef<T>> {
let borrow = match self.storage.try_borrow() {
Ok(borrow) => borrow,
Err(_) => {
unreachable!()
}
};
if let Some(ref rc) = *borrow {
match Rc::downcast::<RefCell<T>>(rc.clone().as_rc_any()) {
Ok(rc_ref_cell) => {
ensure!(rc_ref_cell.try_borrow().is_ok(),
"try_borrow<{}> failed: value is mutably borrowed", T::type_name());
Ok(RRef(OwningHandle::new(rc_ref_cell)))
}
Err(_) => bail!("type mismatch in try_borrow<{}>()", T::type_name())
}
} else {
bail!("try_borrow<{}> failed: attempted to access a freed RData", T::type_name())
}
}
pub fn borrow_mut<T: RStore>(&self) -> RRefMut<T> {
self.try_borrow_mut::<T>().unwrap()
}
pub fn try_borrow_mut<T: RStore>(&self) -> GResult<RRefMut<T>> {
let borrow = match self.storage.try_borrow() {
Ok(borrow) => borrow,
Err(_) => {
unreachable!()
}
};
if let Some(ref rc) = *borrow {
match Rc::downcast::<RefCell<T>>(rc.clone().as_rc_any()) {
Ok(rc_ref_cell) => {
ensure!(rc_ref_cell.try_borrow_mut().is_ok(),
"try_borrow_mut<{}> failed: value is currently borrowed",
T::type_name());
Ok(RRefMut(OwningHandle::new_mut(rc_ref_cell)))
}
Err(_) => bail!("type mismatch in try_borrow<{}>()", T::type_name())
}
} else {
bail!("try_borrow_mut<{}> failed: attempted to access a freed RData", T::type_name())
}
}
pub fn take<T: RStore>(&self) -> GResult<T> {
let prev_usage = self.owned_memory_usage();
match self.storage.try_borrow_mut() {
Ok(mut borrow_mut) => {
match *borrow_mut {
Some(ref rc) if Rc::strong_count(rc) == 1 => {
drop(rc);
let rc_any = (*borrow_mut).take().unwrap().as_rc_any();
drop(borrow_mut);
let rc = match Rc::downcast::<RefCell<T>>(rc_any) {
Ok(rc) => rc,
Err(_) => {
bail!("type mismatch when calling take::<{}>()", T::type_name())
}
};
let cur_usage = self.owned_memory_usage();
with_heap(|heap| heap.memory_usage_barrier(self, prev_usage, cur_usage));
let ref_cell = Rc::try_unwrap(rc).ok().unwrap();
Ok(ref_cell.into_inner())
}
Some(_) => bail!("called take() on an RData which is currently borrowed"),
None => bail!("called take() on an RData which has already been freed")
}
}
Err(_) => bail!("called take() on an RData which is currently borrowed")
}
}
pub fn free(&self) -> GResult<()> {
let prev_usage = self.owned_memory_usage();
match self.storage.try_borrow_mut() {
Ok(mut borrow_mut) => {
match *borrow_mut {
Some(ref rc) if Rc::strong_count(rc) == 1 => {
drop(rc);
*borrow_mut = None;
drop(borrow_mut);
let cur_usage = self.owned_memory_usage();
with_heap(|heap| heap.memory_usage_barrier(self, prev_usage, cur_usage));
Ok(())
}
Some(_) => bail!("called free() on an RData which is currently borrowed"),
None => bail!("called free() on an RData which has already been freed")
}
}
Err(_) => bail!("called take() on an RData which is currently borrowed")
}
}
pub fn is_freed(&self) -> bool {
match self.storage.try_borrow() {
Ok(borrow) => borrow.is_none(),
Err(_) => unreachable!()
}
}
fn gc_self(&self) -> Gc<RData> {
let gc_self = self.gc_self.take();
self.gc_self.set(gc_self.clone());
gc_self.unwrap()
}
pub fn access_giter(rdata: &Root<RData>, giter: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::AccessRData(rdata.to_gc(), giter.to_gc()))
}
pub fn get<S, R>(&self, key: S) -> GResult<R>
where
S: ToSym,
R: FromVal
{
let sym = key.to_sym()?;
match self.get_if_present(sym)? {
Some(r) => Ok(r),
None => bail!("attempted to access nonexistent prop getter '{}'", sym)
}
}
pub fn get_if_present<S, R>(&self, key: S) -> GResult<Option<R>>
where
S: ToSym,
R: FromVal
{
let sym = key.to_sym()?;
match self.class.bindings.get(&sym) {
Some(RBinding::Prop(Some(rfn), _)) => {
with_vm(|vm| {
vm.stacks.borrow_mut().regs.push(Slot::RData(self.gc_self()));
Ok(Some(R::from_val(&rfn.receive_call(1)?)?))
})
}
_ => Ok(None)
}
}
pub fn set<S, V>(&self, key: S, val: V) -> GResult<()>
where
S: ToSym,
V: ToVal
{
let sym = key.to_sym()?;
ensure!(self.set_if_present(sym, val)?, "attempted to assign to nonexistent or \
readonly prop '{}'", sym);
Ok(())
}
pub fn set_if_present<S, V>(&self, key: S, val: V) -> GResult<bool>
where
S: ToSym,
V: ToVal
{
let sym = key.to_sym()?;
match self.class.bindings.get(&sym) {
Some(RBinding::Prop(_, Some(rfn))) => {
with_vm(|vm| {
let mut stacks = vm.stacks.borrow_mut();
stacks.regs.push(Slot::RData(self.gc_self()));
stacks.regs.push(val.to_slot()?);
drop(stacks);
rfn.receive_call(2)?;
Ok(true)
})
}
_ => Ok(false)
}
}
pub fn call<S, A, R>(&self, key: S, args: &A) -> GResult<R>
where
S: ToSym,
A: ToCallArgs + ?Sized,
R: FromVal
{
let sym = key.to_sym()?;
match self.call_if_present(sym, args)? {
Some(r) => Ok(r),
None => bail!("attempted to call nonexistent method '{}'", sym)
}
}
pub fn call_if_present<S, A, R>(&self, key: S, args: &A) -> GResult<Option<R>>
where
S: ToSym,
A: ToCallArgs + ?Sized,
R: FromVal
{
let sym = key.to_sym()?;
match self.class.bindings.get(&sym) {
Some(RBinding::Meth(rfn)) => {
with_vm(|vm| {
let mut stacks = vm.stacks.borrow_mut();
let starting_len = stacks.regs.len();
stacks.regs.push(Slot::RData(self.gc_self()));
args.to_call_args(&mut stacks.regs)?;
let arg_count = stacks.regs.len() - starting_len;
drop(stacks);
let val = rfn.receive_call(arg_count)?;
Ok(Some(R::from_val(&val)?))
})
}
_ => Ok(None)
}
}
pub fn has_meth<S: ToSym>(&self, key: S) -> GResult<bool> {
let sym = key.to_sym()?;
match self.class.bindings.get(&sym) {
Some(RBinding::Meth(_)) => Ok(true),
_ => Ok(false)
}
}
pub(crate) fn get_method(&self, key: Sym) -> Option<(Slot, bool, bool, Slot)> {
match self.class.bindings.get(&key) {
Some(&RBinding::Meth(rfn)) => Some((Slot::RFn(rfn), true, false, Slot::Nil)),
_ => None
}
}
pub fn try_eq(&self, other: &Root<RData>) -> GResult<bool> {
if !Rc::ptr_eq(&self.class, &other.class) {
return Ok(false)
}
let val: Option<Val> = self.call_if_present(OP_EQP_SYM, &[other])?;
match val {
Some(val) => Ok(val.is_truthy()),
None => Ok(false)
}
}
}
impl PartialEq<Root<RData>> for RData {
fn eq(&self, other: &Root<RData>) -> bool {
self.try_eq(other).unwrap()
}
}
pub struct RRoot<T: RStore>(Root<RData>, PhantomData<Rc<RefCell<T>>>);
impl<T: RStore> Clone for RRoot<T> {
fn clone(&self) -> RRoot<T> {
RRoot(self.0.clone(), PhantomData)
}
}
impl<T: RStore> Debug for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl<T: RStore> Display for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<T: RStore> Pointer for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Pointer::fmt(&self.0, f)
}
}
impl<T: RStore> RRoot<T> {
pub fn new(root: Root<RData>) -> RRoot<T> {
assert!(root.is::<T>(), "type mismatch when constructing an RRoot<{}>", T::type_name());
RRoot(root, PhantomData)
}
pub fn to_root(&self) -> Root<RData> {
self.0.clone()
}
pub fn into_root(self) -> Root<RData> {
self.0
}
pub fn ptr_eq(rr0: &RRoot<T>, rr1: &RRoot<T>) -> bool {
Root::ptr_eq(&rr0.0, &rr1.0)
}
pub(crate) fn to_gc(&self) -> Gc<RData> {
self.0.to_gc()
}
#[allow(dead_code)]
pub(crate) fn into_gc(self) -> Gc<RData> {
self.0.into_gc()
}
pub fn borrow(&self) -> RRef<T> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> RRefMut<T> {
self.0.borrow_mut()
}
pub fn try_borrow(&self) -> GResult<RRef<T>> {
self.0.try_borrow()
}
pub fn try_borrow_mut(&self) -> GResult<RRefMut<T>> {
self.0.try_borrow_mut()
}
pub fn take(&self) -> GResult<T> {
self.0.take()
}
pub fn free(&self) -> GResult<()> {
self.0.free()
}
pub fn is_freed(&self) -> bool {
self.0.is_freed()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum SpanStorage {
Loaded(Filename, usize),
Expanded(Option<Sym>, Span, Span),
Generated
}
#[doc(hidden)]
#[derive(Default, PartialEq, Eq, Hash, Copy, Clone)]
pub struct Span(u32);
pub mod glsp {
use super::*;
pub fn sym(name: &str) -> GResult<Sym> {
ensure!(glsp::is_valid_sym_str(name), "invalid sym '{}'", name);
glsp::sym_impl(name, SymKind::Normal)
}
fn sym_impl(name: &str, kind: SymKind) -> GResult<Sym> {
with_engine(|engine| {
let mut syms_map = engine.syms_map.borrow_mut();
if let Some(sym) = syms_map.get(name) {
Ok(*sym)
} else {
let name = Rc::<str>::from(name);
let mut syms = engine.syms.borrow_mut();
syms.push(SymEntry {
name: name.clone(),
kind,
bound_global: None,
bound_macro: None
});
assert!(syms.len() - 1 <= MAX_SYM as usize,
"program requires more than {} unique symbols", MAX_SYM + 1);
let sym = Sym((syms.len() - 1) as u32);
syms_map.insert(name, sym);
Ok(sym)
}
})
}
pub fn is_valid_sym_str(st: &str) -> bool {
let mut rev_iter = st.chars().rev();
match rev_iter.next() {
None => false,
Some('#') => st.len() > 1 && rev_iter.all(|ch| lex::is_valid_sym_char(ch)),
Some(last_char) => {
lex::is_valid_sym_char(last_char) && rev_iter.all(|ch| {
lex::is_valid_sym_char(ch)
})
}
}
}
pub fn is_valid_sym_char(ch: char) -> bool {
lex::is_valid_sym_char(ch)
}
pub fn gensym() -> Sym {
glsp::gensym_impl(None)
}
pub fn gensym_with_tag(tag: &str) -> GResult<Sym> {
ensure!(glsp::is_valid_sym_str(tag) && !tag.ends_with("#"),
"invalid gensym tag '{}': tags should be a valid sym without a trailing '#'",
tag);
Ok(glsp::gensym_impl(Some(tag)))
}
fn gensym_impl(tag: Option<&str>) -> Sym {
with_engine(|engine| {
let counter = engine.gensym_counter.get();
let seed = engine.gensym_seed.borrow();
let mut bytes = SmallVec::<[u8; 64]>::new();
write!(&mut bytes, "#<gs").unwrap();
if let Some(tag) = tag {
write!(&mut bytes, ":{}", tag).unwrap();
}
write!(&mut bytes, ":{}", counter).unwrap();
if let Some(ref seed) = *seed {
write!(&mut bytes, ":{}", seed).unwrap();
}
write!(&mut bytes, ">").unwrap();
engine.gensym_counter.set(counter + 1);
drop(seed);
let name = str::from_utf8(&bytes[..]).unwrap();
glsp::sym_impl(name, SymKind::Gensym).unwrap()
})
}
pub fn seed_gensym() {
with_engine(|engine| {
let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
static CHARS: [char; 64] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '_'
];
let mut seed = String::new();
let mut bits = nanos;
while bits > 0 {
seed.push(CHARS[(bits & 0x3f) as usize]);
bits >>= 6;
}
*engine.gensym_seed.borrow_mut() = Some(seed);
})
}
pub fn global<S, T>(s: S) -> GResult<T>
where
S: ToSym,
T: FromVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
let syms = engine.syms.borrow();
let entry = &syms[sym.0 as usize];
match entry.bound_global {
Some(ref global) => T::from_val(&global.val),
None => bail!("symbol {} is not bound to a global", entry.name)
}
})
}
pub(crate) fn try_global<S, T>(s: S) -> GResult<Option<T>>
where
S: ToSym,
T: FromVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
match engine.syms.borrow()[sym.0 as usize].bound_global {
Some(ref global) => T::from_val(&global.val).map(|t| Some(t)),
None => Ok(None)
}
})
}
pub fn set_global<S, T>(s: S, val: T) -> GResult<()>
where
S: ToSym,
T: ToVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
let val = val.to_val()?;
let mut syms = engine.syms.borrow_mut();
let entry = &mut syms[sym.0 as usize];
match entry.bound_global {
Some(ref mut global) => {
if global.frozen {
let name = entry.name.clone();
drop(global);
drop(entry);
drop(syms);
bail!("attempted to mutate frozen global {}", name);
}
global.val = val;
Ok(())
}
None => {
let name = entry.name.clone();
drop(entry);
drop(syms);
bail!("symbol {} is not bound to a global", name)
}
}
})
}
pub(crate) enum TrySetGlobalOutcome {
Success,
NotBound,
Frozen
}
pub(crate) fn try_set_global<S, T>(s: S, t: T) -> GResult<TrySetGlobalOutcome>
where
S: ToSym,
T: ToVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
let val = t.to_val()?;
let mut syms = engine.syms.borrow_mut();
let entry = &mut syms[sym.0 as usize];
match entry.bound_global {
Some(ref mut global) => {
if global.frozen {
return Ok(TrySetGlobalOutcome::Frozen)
}
global.val = val;
Ok(TrySetGlobalOutcome::Success)
}
None => Ok(TrySetGlobalOutcome::NotBound)
}
})
}
pub fn freeze_global<S>(s: S) -> GResult<()>
where
S: ToSym
{
with_engine(|engine| {
let sym = s.to_sym()?;
let mut syms = engine.syms.borrow_mut();
let entry = &mut syms[sym.0 as usize];
match entry.bound_global {
Some(ref mut global) => {
global.frozen = true;
Ok(())
}
None => {
let name = entry.name.clone();
drop(entry);
drop(syms);
bail!("attempted to freeze the unbound global {}", name)
}
}
})
}
#[doc(hidden)]
pub fn freeze_transform_fns() {
with_engine(|engine| {
let mut syms = engine.syms.borrow_mut();
for sym in STOCK_SYMS_BY_KIND[2] {
let entry = &mut syms[sym.0 as usize];
if entry.kind != SymKind::StockTransform {
panic!()
}
if let Some(ref mut global) = entry.bound_global {
global.frozen = true;
} else {
panic!("transform fn {} is not bound", entry.name)
}
}
})
}
pub fn has_global<S>(s: S) -> GResult<bool>
where
S: ToSym
{
with_engine(|engine| {
let sym = s.to_sym()?;
Ok(engine.syms.borrow()[sym.0 as usize].bound_global.is_some())
})
}
pub fn bind_global<S, T>(s: S, t: T) -> GResult<()>
where
S: ToSym,
T: ToVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
ensure!(sym.is_bindable(), "unable to bind name '{}' to global", sym);
let val = t.to_val()?;
let mut syms = engine.syms.borrow_mut();
let entry = &mut syms[sym.0 as usize];
if entry.bound_global.is_none() {
entry.bound_global = Some(GlobalEntry {
val,
frozen: false
});
Ok(())
} else {
let name = entry.name.clone();
drop(entry);
drop(syms);
bail!("attempted to bind the global '{}', which is already bound", name)
}
})
}
pub fn del_global<S>(s: S) -> GResult<()>
where
S: ToSym
{
with_engine(|engine| {
let sym = s.to_sym()?;
let mut syms = engine.syms.borrow_mut();
let entry = &mut syms[sym.0 as usize];
if let Some(ref mut global) = entry.bound_global {
ensure!(!global.frozen, "attempted to unbind '{}', which is frozen", entry.name);
entry.bound_global = None;
Ok(())
} else {
let name = entry.name.clone();
drop(entry);
drop(syms);
bail!("attempted to unbind '{}', which is not bound to a global", name)
}
})
}
pub fn get_macro<S: ToSym>(s: S) -> GResult<Expander> {
with_engine(|engine| {
let sym = s.to_sym()?;
match engine.syms.borrow()[sym.0 as usize].bound_macro {
Some(ref expander) => Ok(expander.clone()),
None => bail!("symbol {} is not bound to a macro", sym)
}
})
}
pub fn set_macro<S: ToSym>(s: S, expander: Expander) -> GResult<()> {
with_engine(|engine| {
let sym = s.to_sym()?;
let mut syms = engine.syms.borrow_mut();
match syms[sym.0 as usize].bound_macro {
Some(ref mut storage) => {
*storage = expander;
Ok(())
}
None => {
drop(syms);
bail!("symbol {} is not bound to a macro", sym)
}
}
})
}
pub fn has_macro<S: ToSym>(s: S) -> GResult<bool> {
with_engine(|engine| {
let sym = s.to_sym()?;
Ok(engine.syms.borrow()[sym.0 as usize].bound_macro.is_some())
})
}
pub fn bind_macro<S: ToSym>(s: S, expander: Expander) -> GResult<()> {
with_engine(|engine| {
let sym = s.to_sym()?;
let mut syms = engine.syms.borrow_mut();
match syms[sym.0 as usize].bound_macro {
Some(_) => {
drop(syms);
bail!("attempted to bind the macro {}, which is already bound", sym)
}
ref mut storage @ None => {
*storage = Some(expander);
Ok(())
}
}
})
}
pub fn del_macro<S: ToSym>(s: S) -> GResult<()> {
with_engine(|engine| {
let sym = s.to_sym()?;
let mut syms = engine.syms.borrow_mut();
match syms[sym.0 as usize].bound_macro {
ref mut storage @ Some(_) => {
*storage = None;
Ok(())
}
None => {
drop(syms);
bail!("attempted to unbind the macro {}, which is not bound", sym)
}
}
})
}
#[doc(hidden)]
pub fn filename(st: &str) -> Filename {
with_engine(|engine| {
let mut filenames_map = engine.filenames_map.borrow_mut();
if let Some(filename) = filenames_map.get(st) {
*filename
} else {
let mut filenames = engine.filenames.borrow_mut();
assert!(filenames.len() <= u32::MAX as usize,
"cannot load more than {} unique files", (u32::MAX as u64) + 1);
let rc = Rc::<str>::from(st);
filenames.push(Rc::clone(&rc));
let id = u32::try_from(filenames.len() - 1).unwrap();
let filename = Filename(NonZeroU32::new(id).unwrap());
filenames_map.insert(rc, filename);
filename
}
})
}
pub(crate) fn filename_str(filename: Filename) -> Rc<str> {
with_engine(|engine| {
Rc::clone(&engine.filenames.borrow()[filename.0.get() as usize])
})
}
pub fn rdata<T: RStore>(rdata: T) -> GResult<Root<RData>> {
with_engine(|engine| {
let class_rc = match engine.rclasses.borrow_mut().entry(rdata.type_id()) {
Vacant(entry) => {
let type_name = T::type_name();
ensure!(engine.rclass_names.borrow_mut().insert(&type_name),
"multiple distinct RData types share the same name: {}", type_name);
let class = Rc::new(T::rclass()?);
entry.insert(Rc::clone(&class));
class
}
Occupied(entry) => {
Rc::clone(&*entry.get())
}
};
let root = glsp::alloc(RData::new(rdata, class_rc));
root.gc_self.set(Some(root.to_gc()));
Ok(root)
})
}
pub fn rroot<T: RStore>(rdata: T) -> GResult<RRoot<T>> {
Ok(RRoot::new(glsp::rdata(rdata)?))
}
pub fn add_lib<T: Lib>(lib: T) {
with_engine(|engine| {
let rc = Rc::new(RefCell::new(lib)) as Rc<dyn Any>;
if engine.libs.borrow_mut().insert(TypeId::of::<T>(), rc).is_some() {
panic!("glsp.add_lib() called twice for the same type");
}
engine.libs_ordering.borrow_mut().push(TypeId::of::<T>());
})
}
pub fn take_lib<T: Lib>() -> GResult<T> {
with_engine(|engine| {
let rc = match engine.libs.borrow_mut().remove(&TypeId::of::<T>()) {
Some(rc) => rc.downcast::<RefCell<T>>().unwrap(),
None => bail!("attempted to take nonexistent lib {}", type_name::<T>())
};
match Rc::try_unwrap(rc) {
Ok(ref_cell) => Ok(ref_cell.into_inner()),
Err(_) => bail!("called take_lib for {}, which is currently borrowed",
type_name::<T>())
}
})
}
pub fn lib<T: Lib>() -> LibRef<T> {
match glsp::try_lib::<T>() {
Ok(lib_ref) => lib_ref,
Err(_) => panic!("attempted to access nonexistent lib {}", type_name::<T>())
}
}
pub fn lib_mut<T: Lib>() -> LibRefMut<T> {
match glsp::try_lib_mut::<T>() {
Ok(lib_ref_mut) => lib_ref_mut,
Err(_) => panic!("attempted to access nonexistent lib {}", type_name::<T>())
}
}
pub fn try_lib<T: Lib>() -> GResult<LibRef<T>> {
with_engine(|engine| {
let libs = engine.libs.borrow();
let rc = match libs.get(&TypeId::of::<T>()) {
Some(rc) => rc.clone(),
None => bail!("lib type {} was never registered, or has been \
dropped", type_name::<T>())
};
let rc_ref_cell = rc.downcast::<RefCell<T>>().unwrap();
if rc_ref_cell.try_borrow().is_err() {
bail!("attempted to borrow lib {} during a mutable borrow", type_name::<T>())
}
Ok(LibRef {
handle: OwningHandle::new(rc_ref_cell)
})
})
}
pub fn try_lib_mut<T: Lib>() -> GResult<LibRefMut<T>> {
with_engine(|engine| {
let libs = engine.libs.borrow();
let rc = match libs.get(&TypeId::of::<T>()) {
Some(rc) => rc.clone(),
None => bail!("lib type {} was never registered, or has been \
dropped", type_name::<T>())
};
let rc_ref_cell = rc.downcast::<RefCell<T>>().unwrap();
if rc_ref_cell.try_borrow_mut().is_err() {
bail!("attempted to mutably borrow {}, which is already borrowed", type_name::<T>())
}
Ok(LibRefMut {
handle: OwningHandle::new_mut(rc_ref_cell)
})
})
}
pub fn rfn(wrapped_fn: WrappedFn) -> RFn {
with_engine(|engine| {
let address = wrapped_fn.as_usize();
let mut rfns_map = engine.rfns_map.borrow_mut();
if let Some(rfn) = rfns_map.get(&address) {
*rfn
} else {
let mut rfns = engine.rfns.borrow_mut();
rfns.push(RFnEntry {
name: None,
wrapped_fn
});
let id = u32::try_from(rfns.len() - 1).unwrap();
let rfn = RFn(NonZeroU32::new(id).unwrap());
rfns_map.insert(address, rfn);
rfn
}
})
}
pub fn named_rfn(name: Sym, wrapped_fn: WrappedFn) -> RFn {
let rfn = glsp::rfn(wrapped_fn);
rfn.set_name(Some(name));
rfn
}
pub fn bind_rfn<S: ToSym>(name: S, wrapped_fn: WrappedFn) -> GResult<RFn> {
let sym = name.to_sym()?;
let rfn = glsp::named_rfn(sym, wrapped_fn);
glsp::bind_global(sym, rfn)?;
Ok(rfn)
}
pub fn bind_rfn_macro<S: ToSym>(name: S, wrapped_fn: WrappedFn) -> GResult<RFn> {
let sym = name.to_sym()?;
let rfn = glsp::named_rfn(sym, wrapped_fn);
glsp::bind_macro(sym, Expander::RFn(rfn))?;
Ok(rfn)
}
pub(crate) fn call_rfn(rfn: RFn, arg_count: usize) -> GResult<Slot> {
with_engine(|engine| {
let stacks = engine.vm.stacks.borrow();
let base_reg = stacks.regs.len() - arg_count;
let _guard = Guard::new(|| {
let mut stacks = engine.vm.stacks.borrow_mut();
stacks.regs.truncate(base_reg);
});
let regs = Ref::map(stacks, |stacks| &stacks.regs[base_reg..]);
let wrapped_fn = engine.rfns.borrow()[rfn.0.get() as usize].wrapped_fn;
let result = panic::catch_unwind(AssertUnwindSafe(|| {
wrapped_fn.call(regs)
}));
match result {
Ok(glsp_result) => glsp_result,
Err(payload) => {
let rfn_description = match rfn.name() {
Some(sym) => format!("rfn ({})", sym),
None => format!("anonymous rfn")
};
if let Some(msg) = payload.downcast_ref::<&str>() {
bail!("{} panicked, '{}'", rfn_description, msg)
} else if let Some(msg) = payload.downcast_ref::<String>() {
bail!("{} panicked, '{}'", rfn_description, msg)
} else {
bail!("{} panicked", rfn_description)
}
}
}
})
}
pub fn parse(text: &mut &str, filename: Option<&str>) -> GResult<Option<Val>> {
let file_id = filename.map(|path| glsp::filename(path));
let mut parser = Parser::new(file_id);
glsp::push_frame(Frame::GlspApi(GlspApiName::Parse, file_id));
let _guard = Guard::new(|| glsp::pop_frame());
while text.len() > 0 {
if let Some(form) = parser.parse(text)? {
return Ok(Some(form))
}
}
parser.ensure_finished()?;
Ok(None)
}
pub fn parse_all(mut text: &str, filename: Option<&str>) -> GResult<Vec<Val>> {
let file_id = filename.map(|path| glsp::filename(path));
glsp::push_frame(Frame::GlspApi(GlspApiName::ParseAll, file_id));
let _guard = Guard::new(|| glsp::pop_frame());
let mut parser = Parser::new(file_id);
let mut results = Vec::new();
while text.len() > 0 {
if let Some(form) = parser.parse(&mut text)? {
results.push(form);
}
}
parser.ensure_finished()?;
Ok(results)
}
pub fn parse_1(mut text: &str, filename: Option<&str>) -> GResult<Val> {
let file_id = filename.map(|path| glsp::filename(path));
glsp::push_frame(Frame::GlspApi(GlspApiName::Parse1, file_id));
let _guard = Guard::new(|| glsp::pop_frame());
let mut parser = Parser::new(file_id);
while text.len() > 0 {
if let Some(form) = parser.parse(&mut text)? {
while text.len() > 0 {
if let Some(_) = parser.parse(&mut text)? {
bail!("parse-1 produced multiple forms")
}
}
return Ok(form)
}
}
bail!("parse-1 did not produce a form")
}
pub fn set_pr_writer(pr_writer: Box<dyn Write>) {
with_engine(|engine| {
*engine.pr_writer.borrow_mut() = pr_writer;
})
}
pub fn set_epr_writer(epr_writer: Box<dyn Write>) {
with_engine(|engine| {
*engine.epr_writer.borrow_mut() = epr_writer;
})
}
pub(crate) fn push_frame(frame: Frame) {
with_engine(|engine| {
engine.vm.push_frame(frame);
})
}
pub(crate) fn pop_frame() {
with_engine(|engine| {
engine.vm.pop_frame();
})
}
pub(crate) fn enter_expander(arr: &Arr, env: Rc<Env>) -> Option<(Option<Sym>, Span, Rc<Env>)> {
with_engine(|engine| {
let name = if arr.len() >= 1 && arr.get::<Val>(0).unwrap().is_sym() {
Some(arr.get::<Sym>(0).unwrap())
} else {
None
};
let callsite = arr.span();
let prev_in_expander = engine.in_expander.borrow().clone();
*engine.in_expander.borrow_mut() = Some((name, callsite, env));
prev_in_expander
})
}
pub(crate) fn env() -> Option<Rc<Env>> {
with_engine(|engine| {
match *engine.in_expander.borrow() {
Some((_, _, ref env)) => Some(Rc::clone(env)),
None => None
}
})
}
pub(crate) fn leave_expander(prev: Option<(Option<Sym>, Span, Rc<Env>)>) {
with_engine(|engine| {
*engine.in_expander.borrow_mut() = prev;
})
}
pub(crate) fn span(storage: SpanStorage) -> Span {
with_engine(|engine| {
if storage == SpanStorage::Generated {
Span(0)
} else {
let mut spans_map = engine.spans_map.borrow_mut();
match spans_map.entry(storage) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let mut spans = engine.spans.borrow_mut();
spans.push(storage);
let span = Span((spans.len() - 1) as u32);
entry.insert(span);
span
}
}
}
})
}
pub(crate) fn generated_span() -> Span {
Span(0)
}
pub(crate) fn span_storage(span: Span) -> SpanStorage {
with_engine(|engine| {
engine.spans.borrow()[span.0 as usize]
})
}
pub(crate) fn span_file_location<F>(f: &mut F, mut span: Span) -> Result<bool, fmt::Error>
where
F: fmt::Write
{
loop {
match glsp::span_storage(span) {
SpanStorage::Expanded(_, macro_invocation_span, _) => {
span = macro_invocation_span;
}
SpanStorage::Loaded(file_id, line) => {
write!(f, "{}:{}", &glsp::filename_str(file_id), line)?;
return Ok(true)
}
SpanStorage::Generated => {
return Ok(false)
}
}
}
}
pub(crate) fn span_context<F, C>(
f: &mut F,
span: Span,
callback: &C
) -> fmt::Result
where
F: fmt::Write,
C: ?Sized + Fn(&mut F) -> fmt::Result
{
match glsp::span_storage(span) {
SpanStorage::Generated => {
callback(f)?;
}
SpanStorage::Loaded(file_id, line_number) => {
callback(f)?;
write!(f, " at {}:{}", &glsp::filename_str(file_id), line_number)?;
}
SpanStorage::Expanded(macro_name, expander_callsite, mut constructor_callsite) => {
let print_macro_name: &dyn Fn(&mut F) -> fmt::Result = &|f| {
match macro_name {
Some(macro_name) => write!(f, "({})", macro_name),
None => write!(f, "an anonymous macro")
}
};
span_context(f, expander_callsite, print_macro_name)?;
write!(f, "\n expanded to ")?;
callback(f)?;
while let SpanStorage::Expanded(_, _, meta) =
glsp::span_storage(constructor_callsite) {
constructor_callsite = meta;
}
match glsp::span_storage(constructor_callsite) {
SpanStorage::Generated => (),
SpanStorage::Loaded(file_id, line_number) => {
write!(f, " at {}:{}", &glsp::filename_str(file_id), line_number)?;
}
SpanStorage::Expanded(..) => unreachable!()
}
}
}
Ok(())
}
pub fn file_location() -> Option<String> {
with_engine(|engine| {
let mut builder = String::new();
if engine.vm.file_location(&mut builder).unwrap() {
Some(builder)
} else {
None
}
})
}
pub fn stack_trace() -> String {
with_engine(|engine| {
let mut builder = String::new();
engine.vm.stack_trace(&mut builder).unwrap();
builder
})
}
pub fn try_call<R, A>(is_verbose: bool, receiver: &R, args: &A) -> GResult<Val>
where
R: CallableOps,
A: ToCallArgs + ?Sized
{
with_engine(|engine| {
let prev = engine.errors_verbose.get();
engine.errors_verbose.set(is_verbose);
let _guard = Guard::new(|| engine.errors_verbose.set(prev));
glsp::call(receiver, args)
})
}
pub(crate) fn errors_verbose() -> bool {
with_engine(|engine| {
engine.errors_verbose.get()
})
}
pub(crate) fn alloc<T: Allocate>(t: T) -> Root<T> {
with_engine(|engine| {
engine.heap.alloc(t)
})
}
#[doc(hidden)]
pub fn alloc_gc<T: Allocate>(t: T) -> Gc<T> {
with_engine(|engine| {
engine.heap.alloc_gc(t)
})
}
pub(crate) fn new_arr_span(callsite: Option<Span>) -> Span {
with_engine(|engine| {
if let Some(callsite) = callsite {
if let Some((expander_name, expander_callsite, _)) = *engine.in_expander.borrow() {
glsp::span(SpanStorage::Expanded(expander_name, expander_callsite, callsite))
} else {
Span::default()
}
} else {
if engine.vm.in_expander() {
engine.vm.expander_cur_span()
} else {
Span::default()
}
}
})
}
pub fn arr() -> Root<Arr> {
let arr = with_heap(|heap| heap.recycler.arr());
arr.set_span(glsp::new_arr_span(None));
arr
}
pub fn arr_with_capacity(capacity: usize) -> Root<Arr> {
let arr = with_heap(|heap| heap.recycler.arr_with_capacity(capacity));
arr.set_span(glsp::new_arr_span(None));
arr
}
pub fn arr_from_elem<T: ToVal>(elem: T, reps: usize) -> GResult<Root<Arr>> {
let arr = with_heap(|heap| heap.recycler.arr_from_elem(elem, reps))?;
arr.set_span(glsp::new_arr_span(None));
Ok(arr)
}
pub fn arr_from_iter<T>(iter: T) -> GResult<Root<Arr>>
where
T: IntoIterator,
T::Item: ToVal
{
let arr = with_heap(|heap| heap.recycler.arr_from_iter(iter))?;
arr.set_span(glsp::new_arr_span(None));
Ok(arr)
}
pub fn str() -> Root<Str> {
glsp::alloc(Str::new())
}
pub fn str_from_rust_str(src: &str) -> Root<Str> {
glsp::alloc(Str::from_rust_str(src))
}
pub fn str_from_iter<T>(iter: T) -> GResult<Root<Str>>
where
T: IntoIterator,
T::Item: IntoElement<char>
{
Ok(glsp::alloc(Str::from_iter(iter)?))
}
pub fn str_with_capacity(capacity: usize) -> Root<Str> {
glsp::alloc(Str::with_capacity(capacity))
}
pub fn tab() -> Root<Tab> {
glsp::alloc(Tab::new())
}
pub fn tab_from_iter<T, K, V>(iter: T) -> GResult<Root<Tab>>
where
T: IntoIterator<Item = (K, V)>,
K: ToVal,
V: ToVal
{
Ok(glsp::alloc(Tab::from_iter(iter)?))
}
pub fn tab_with_capacity(capacity: usize) -> Root<Tab> {
glsp::alloc(Tab::with_capacity(capacity))
}
#[doc(hidden)]
pub fn class(raw_class: &Tab) -> GResult<Root<Class>> {
Ok(glsp::alloc(Class::new(raw_class)?))
}
pub(crate) fn call_class(class: &Root<Class>, arg_count: usize) -> GResult<Root<Obj>> {
with_engine(|engine| {
let stacks = engine.vm.stacks.borrow();
let base_reg = stacks.regs.len() - arg_count;
let _guard = Guard::new(|| {
let mut stacks = engine.vm.stacks.borrow_mut();
stacks.regs.truncate(base_reg);
});
let mut args = SmallVec::<[Slot; 16]>::new();
for i in base_reg .. stacks.regs.len() {
args.push(stacks.regs[i].clone());
}
drop(stacks);
Obj::new(class, &args[..])
})
}
pub(crate) fn giter(state: GIterState) -> Root<GIter> {
with_heap(|heap| heap.recycler.giter(state))
}
pub fn rn(start: Num, end: Option<Num>, step_by: Num) -> GResult<Root<GIter>> {
ensure!(step_by != Num::Int(0), "a range can't be stepped by 0");
match (start, end, step_by) {
(Num::Int(start), Some(Num::Int(end)), Num::Int(step_by)) => {
Ok(glsp::giter(GIterState::RnExclusive(start, end, step_by)))
}
(Num::Int(start), None, Num::Int(step_by)) => {
Ok(glsp::giter(GIterState::RnOpen(start, step_by)))
}
(start, Some(end), step_by) => {
Ok(glsp::giter(GIterState::FRnExclusive(
start.into_f32(),
end.into_f32(),
step_by.into_f32())))
}
(start, None, step_by) => {
Ok(glsp::giter(GIterState::FRnOpen(start.into_f32(), step_by.into_f32())))
}
}
}
pub fn rni(start: Num, end: Option<Num>, step_by: Num) -> GResult<Root<GIter>> {
ensure!(step_by != Num::Int(0), "a range can't be stepped by 0");
match (start, end, step_by) {
(Num::Int(start), Some(Num::Int(end)), Num::Int(step_by)) => {
Ok(glsp::giter(GIterState::RnInclusive(start, end, step_by)))
}
(Num::Int(start), None, Num::Int(step_by)) => {
Ok(glsp::giter(GIterState::RnOpen(start, step_by)))
}
(start, Some(end), step_by) => {
Ok(glsp::giter(GIterState::FRnInclusive(
start.into_f32(),
end.into_f32(),
step_by.into_f32())))
}
(start, None, step_by) => {
Ok(glsp::giter(GIterState::FRnOpen(start.into_f32(), step_by.into_f32())))
}
}
}
pub fn once(args: &[Val]) -> Root<GIter> {
match args.len() {
0 => glsp::giter(GIterState::Empty),
1 => glsp::giter(GIterState::Once1(Slot::from_val(&args[0]))),
_ => {
let arr = glsp::arr_from_iter(args.iter()).unwrap();
glsp::giter(GIterState::OnceN(arr.to_gc()))
}
}
}
pub fn once_with(callable: Callable) -> Root<GIter> {
glsp::giter(GIterState::OnceWith(GcCallable::from_callable(&callable)))
}
pub fn repeat(args: &[Val]) -> GResult<Root<GIter>> {
match args.len() {
0 => bail!("the repeat iterator requires at least one argument"),
1 => Ok(glsp::giter(GIterState::Repeat1(Slot::from_val(&args[0])))),
_ => {
let arr = glsp::arr_from_iter(args.iter()).unwrap();
Ok(glsp::giter(GIterState::RepeatN(arr.to_gc(), 0, (arr.len() - 1) as u32)))
}
}
}
pub fn repeat_with(callable: Callable) -> Root<GIter> {
glsp::giter(GIterState::RepeatWith(GcCallable::from_callable(&callable)))
}
pub fn chunks(chunk_len: usize, src_arr: &Root<Arr>) -> GResult<Root<GIter>> {
ensure!(chunk_len > 0, "cannot split an array into chunks of length 0");
Ok(glsp::giter(GIterState::Chunks(chunk_len as u32, src_arr.shallow_clone().to_gc())))
}
pub fn chunks_exact(chunk_len: usize, src_arr: &Root<Arr>) -> GResult<Root<GIter>> {
ensure!(chunk_len > 0, "cannot split an array into chunks of length 0");
let len = src_arr.len() - (src_arr.len() % chunk_len);
let arr = glsp::arr_from_iter(src_arr.iter().take(len)).unwrap();
Ok(glsp::giter(GIterState::Chunks(chunk_len as u32, arr.to_gc())))
}
pub fn rchunks(chunk_len: usize, src_arr: &Root<Arr>) -> GResult<Root<GIter>> {
ensure!(chunk_len > 0, "cannot split an array into chunks of length 0");
Ok(glsp::giter(GIterState::RChunks(chunk_len as u32, src_arr.shallow_clone().to_gc())))
}
pub fn rchunks_exact(chunk_len: usize, src_arr: &Root<Arr>) -> GResult<Root<GIter>> {
ensure!(chunk_len > 0, "cannot split an array into chunks of length 0");
let to_skip = src_arr.len() % chunk_len;
let arr = glsp::arr_from_iter(src_arr.iter().skip(to_skip)).unwrap();
Ok(glsp::giter(GIterState::RChunks(chunk_len as u32, arr.to_gc())))
}
pub fn windows(window_len: usize, src_arr: &Root<Arr>) -> GResult<Root<GIter>> {
ensure!(window_len > 0, "cannot split an array into windows of length 0");
Ok(glsp::giter(GIterState::Windows(window_len as u32, src_arr.shallow_clone().to_gc())))
}
pub fn lines(st: &Root<Str>) -> Root<GIter> {
glsp::giter(GIterState::Lines(st.shallow_clone().to_gc()))
}
pub fn split(src: &Root<Str>, split_at: &Root<Str>) -> Root<GIter> {
glsp::giter(GIterState::Split(src.shallow_clone().to_gc(),
split_at.shallow_clone().to_gc()))
}
pub fn rev(base: &Root<GIter>) -> GResult<Root<GIter>> {
ensure!(base.is_double_ended(), "{} iterators are not double-ended", base.state_name());
Ok(glsp::giter(GIterState::Rev(base.to_gc())))
}
pub fn enumerate(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Enumerate(base.to_gc(), 0))
}
pub fn cloned(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Cloned(base.to_gc()))
}
pub fn deep_cloned(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::DeepCloned(base.to_gc()))
}
pub fn step_by(step_by: usize, base: &Root<GIter>) -> GResult<Root<GIter>> {
ensure!(step_by > 0, "cannot step an iterator by 0");
Ok(glsp::giter(GIterState::StepBy(step_by as u32, base.to_gc())))
}
pub fn map(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Map(GcCallable::from_callable(callable), base.to_gc()))
}
pub fn filter(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Filter(GcCallable::from_callable(callable), base.to_gc()))
}
pub fn zip(iterables: &[Iterable]) -> Root<GIter> {
let arr = glsp::arr_from_iter(iterables.iter().map(|iterable| iterable.giter())).unwrap();
glsp::giter(GIterState::Zip(arr.to_gc()))
}
pub fn chain(iterables: &[Iterable]) -> Root<GIter> {
let arr = glsp::arr_from_iter(iterables.iter().map(|iterable| iterable.giter())).unwrap();
glsp::giter(GIterState::Chain(arr.to_gc()))
}
pub fn flatten(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Flatten(base.to_gc(), None))
}
pub fn cycle(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Cycle(Some(base.to_gc()), glsp::arr().to_gc(), 0))
}
pub fn take(n: usize, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Take(n as u32, base.to_gc()))
}
pub fn take_while(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::TakeWhile(GcCallable::from_callable(callable), base.to_gc()))
}
pub fn skip(n: usize, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Skip(n as u32, base.to_gc()))
}
pub fn skip_while(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
let gc_callable = GcCallable::from_callable(callable);
glsp::giter(GIterState::SkipWhile(Some(gc_callable), base.to_gc()))
}
pub fn gc() {
with_engine(|engine| {
engine.vm.traverse_stacks();
engine.heap.step()
})
}
pub fn gc_ratio() -> f32 {
with_engine(|engine| {
engine.heap.ratio()
})
}
pub fn gc_set_ratio(ratio: f32) {
with_engine(|engine| {
engine.heap.set_ratio(ratio)
})
}
pub fn gc_young_bytes() -> usize {
with_engine(|engine| {
engine.heap.young_memory_usage()
})
}
pub fn gc_old_bytes() -> usize {
with_engine(|engine| {
engine.heap.old_memory_usage()
})
}
pub fn gc_ghost_bytes() -> usize {
with_engine(|engine| {
engine.heap.ghost_memory_usage()
})
}
pub fn eval(val: &Val, env_mode: Option<EnvMode>) -> GResult<Val> {
glsp::push_frame(Frame::GlspApi(GlspApiName::Eval, None));
let _guard = Guard::new(|| glsp::pop_frame());
eval::eval(&[val.clone()], env_mode, false)
}
pub fn eval_multi(vals: &[Val], env_mode: Option<EnvMode>) -> GResult<Val> {
glsp::push_frame(Frame::GlspApi(GlspApiName::EvalMulti, None));
let _guard = Guard::new(|| glsp::pop_frame());
eval::eval(vals, env_mode, false)
}
pub fn load(filename: &str) -> GResult<Val> {
let file_id = glsp::filename(filename);
glsp::push_frame(Frame::GlspApi(GlspApiName::Load, Some(file_id)));
let _guard = Guard::new(|| glsp::pop_frame());
#[cfg(feature = "compiler")] {
if is_playing_back() {
return glsp::load_playback(filename)
} else {
glsp::record_action(Action::StartLoad(file_id));
}
}
#[cfg(feature = "compiler")]
let _guard = Guard::new(|| glsp::record_action(Action::EndLoad));
let text = match fs::read_to_string(&filename) {
Ok(text) => text,
Err(err) => return Err(error!("unable to load file '{}'", filename).with_source(err))
};
let vals = glsp::parse_all(&text, Some(filename))?;
eval::eval(&vals, None, true)
}
pub fn require(filename: &str) -> GResult<Val> {
let file_id = glsp::filename(filename);
glsp::push_frame(Frame::GlspApi(GlspApiName::Require, Some(file_id)));
let _guard = Guard::new(|| glsp::pop_frame());
let path = match PathBuf::from(filename).canonicalize() {
Ok(path) => path,
Err(err) => {
let msg = error!("invalid filename '{}' passed to glsp::require", filename);
return Err(msg.with_source(err))
}
};
let already_seen = with_engine(|engine| {
let mut required = engine.required.borrow_mut();
if required.contains(&path) {
return true
} else {
required.insert(path);
return false
}
});
if already_seen {
Ok(Val::Nil)
} else {
glsp::load(filename)
}
}
#[cfg(feature = "compiler")]
pub fn load_and_compile(filename: &str) -> GResult<(Val, Vec<u8>)> {
let file_id = glsp::filename(filename);
glsp::push_frame(Frame::GlspApi(GlspApiName::LoadAndCompile, Some(file_id)));
let _guard = Guard::new(|| glsp::pop_frame());
let text = match fs::read_to_string(&filename) {
Ok(text) => text,
Err(err) => return Err(error!("unable to load file '{}'", filename).with_source(err))
};
glsp::load_and_compile_str(&text, filename)
}
#[doc(hidden)]
#[cfg(feature = "compiler")]
pub fn load_and_compile_str(
content: &str,
filename: &str
) -> GResult<(Val, Vec<u8>)> {
let file_id = glsp::filename(filename);
glsp::push_frame(Frame::GlspApi(GlspApiName::LoadAndCompileStr, Some(file_id)));
let _guard = Guard::new(|| glsp::pop_frame());
let vals = glsp::parse_all(content, Some(filename))?;
glsp::load_and_compile_vals(&vals[..], filename)
}
#[doc(hidden)]
#[cfg(feature = "compiler")]
pub fn load_and_compile_vals(
vals: &[Val],
filename: &str
) -> GResult<(Val, Vec<u8>)> {
let file_id = glsp::filename(filename);
glsp::push_frame(Frame::GlspApi(GlspApiName::LoadAndCompileVals, Some(file_id)));
let _guard = Guard::new(|| glsp::pop_frame());
with_engine(|engine| {
ensure!(engine.playing_back.borrow().is_none(),
"cannot load_and_compile and load_compiled simultaneously");
let mut recording = engine.recording.borrow_mut();
ensure!(recording.is_none(), "multiple simultaneous calls to load_and_compile");
*recording = Some(Recording::new());
Ok(())
})?;
let recording_guard = Guard::new(|| {
with_engine(|engine| {
*engine.recording.borrow_mut() = None;
})
});
glsp::seed_gensym();
glsp::record_action(Action::StartLoad(file_id));
let end_load_guard = Guard::new(|| glsp::record_action(Action::EndLoad));
let result = eval::eval(vals, None, true)?;
forget(recording_guard);
drop(end_load_guard);
let bytes = with_engine(|engine| {
engine.recording.borrow_mut().take().unwrap().into_bytes()
});
Ok((result, bytes))
}
#[cfg(feature = "compiler")]
pub(crate) fn record_action(action: Action) {
with_engine(|engine| {
let mut recording = engine.recording.borrow_mut();
if let Some(ref mut recording) = *recording {
recording.add_action(action);
}
});
}
#[cfg(feature = "compiler")]
pub(crate) fn is_playing_back() -> bool {
with_engine(|engine| {
engine.playing_back.borrow().is_some()
})
}
#[cfg(feature = "compiler")]
pub(crate) fn pop_action() -> GResult<Action> {
with_engine(|engine| {
engine.playing_back.borrow_mut().as_mut().unwrap().pop()
})
}
#[cfg(feature = "compiler")]
pub fn load_compiled(bytes: &[u8]) -> GResult<Val> {
glsp::push_frame(Frame::GlspApi(GlspApiName::LoadCompiled, None));
let _guard = Guard::new(|| glsp::pop_frame());
let recording = Recording::from_bytes(bytes)?;
let root_filename = match recording.peek()? {
&Action::StartLoad(filename) => filename,
_ => bail!("invalid Recording: first Action must be StartLoad")
};
with_engine(|engine| {
ensure!(engine.recording.borrow().is_none(),
"cannot load_and_compile and load_compiled simultaneously");
let mut playing_back = engine.playing_back.borrow_mut();
ensure!(playing_back.is_none(), "multiple simultaneous calls to load_compiled");
*playing_back = Some(recording);
Ok(())
})?;
let playing_back_guard = Guard::new(|| {
with_engine(|engine| {
*engine.playing_back.borrow_mut() = None;
});
});
let result = glsp::load_playback(&glsp::filename_str(root_filename))?;
forget(playing_back_guard);
let recording = with_engine(|engine| {
engine.playing_back.borrow_mut().take().unwrap()
});
ensure!(recording.is_empty(), "invalid Recording: some Actions are unused");
Ok(result)
}
#[cfg(feature = "compiler")]
pub(crate) fn load_playback(expected_filename: &str) -> GResult<Val> {
assert!(glsp::is_playing_back());
match glsp::pop_action()? {
Action::StartLoad(recorded_filename)
if expected_filename == &*glsp::filename_str(recorded_filename) => (),
_ => bail!("invalid Recording: unexpected call to (load {:?})", expected_filename)
}
let mut result = Val::Nil;
let mut toplevel_let: Option<Root<Stay>> = None;
loop {
match glsp::pop_action()? {
Action::Execute(bytecode) => {
with_vm(|vm| {
result = vm.exec_bytecode(&bytecode)?;
Ok(())
})?;
if let Some(stay) = toplevel_let.take() {
stay.set(Slot::from_val(&result));
result = Val::Nil;
}
}
Action::ToplevelLet(stay) => {
ensure!(toplevel_let.is_none(), "invalid Recording: unexpected ToplevelLet");
toplevel_let = Some(stay);
}
Action::StartLoad(filename) => {
bail!("invalid Recording: unexpected StartLoad({})",
glsp::filename_str(filename))
}
Action::EndLoad => {
ensure!(toplevel_let.is_none(), "invalid Recording: unexpected ToplevelLet");
return Ok(result)
}
}
}
}
pub fn call<C, A, R>(receiver: &C, args: &A) -> GResult<R>
where
C: CallableOps,
A: ToCallArgs + ?Sized,
R: FromVal
{
glsp::push_frame(Frame::GlspCall(receiver.name()));
let _guard = Guard::new(|| glsp::pop_frame());
with_engine(|engine| {
let mut stacks = engine.vm.stacks.borrow_mut();
let starting_len = stacks.regs.len();
args.to_call_args(&mut stacks.regs)?;
let arg_count = stacks.regs.len() - starting_len;
drop(stacks);
R::from_val(&receiver.receive_call(arg_count)?)
})
}
pub(crate) fn call_gfn(gfn: &Root<GFn>, arg_count: usize) -> GResult<Val> {
with_engine(|engine| {
Ok(engine.vm.exec_gfn(gfn, arg_count)?)
})
}
pub fn coro_run(coro: &Root<Coro>, resume_arg: Option<Val>) -> GResult<Val> {
glsp::push_frame(Frame::GlspCoroRun(coro.to_gc()));
let _guard = Guard::new(|| glsp::pop_frame());
with_engine(|engine| {
Ok(engine.vm.coro_run(coro, resume_arg)?)
})
}
pub fn coro_finish(coro: &Root<Coro>) -> GResult<()> {
glsp::push_frame(Frame::GlspApi(GlspApiName::CoroFinish, None));
let _guard = Guard::new(|| glsp::pop_frame());
with_engine(|engine| {
Ok(engine.vm.coro_finish(coro)?)
})
}
pub fn expand(val: &Val, env_mode: Option<EnvMode>) -> GResult<Val> {
glsp::push_frame(Frame::GlspApi(GlspApiName::Expand, None));
let _guard = Guard::new(|| glsp::pop_frame());
let mut expanded = eval::expand(&[val.clone()], env_mode, true)?;
assert!(expanded.len() == 1);
Ok(expanded.pop().unwrap())
}
pub fn expand_multi(vals: &[Val], env_mode: Option<EnvMode>) -> GResult<Vec<Val>> {
glsp::push_frame(Frame::GlspApi(GlspApiName::ExpandMulti, None));
let _guard = Guard::new(|| glsp::pop_frame());
eval::expand(vals, env_mode, false)
}
pub fn expand_1(
form: &Val,
expander: Option<Expander>,
env_mode: Option<EnvMode>
) -> GResult<Expansion> {
glsp::push_frame(Frame::GlspApi(GlspApiName::Expand1, None));
let _guard = Guard::new(|| glsp::pop_frame());
eval::expand_1(form, expander, env_mode)
}
}
macro_rules! define_stock_syms(
($($kind:ident : $(($sym_str:literal, $sym_name:ident)),+),+) => (
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub(crate) enum StockSym {
$($($sym_name),+),+,
STOCK_SYM_COUNT
}
static STOCK_SYMS: [(&str, SymKind); StockSym::STOCK_SYM_COUNT as usize] = [
$($(($sym_str, SymKind::$kind)),+),+
];
static STOCK_SYMS_BY_KIND: [&[Sym]; 3] = [
$(&[$(Sym(StockSym::$sym_name as u32)),+]),+
];
#[doc(hidden)]
pub mod stock_syms {
use super::{StockSym, Sym};
$($(
pub const $sym_name: Sym = Sym(StockSym::$sym_name as u32);
)+)+
}
);
);
define_stock_syms!(
StockSpecial:
("do", DO_SYM),
("quote", QUOTE_SYM),
("if", IF_SYM),
("let", LET_SYM),
("fn", FN_SYM),
("return", RETURN_SYM),
("yield", YIELD_SYM),
("defer", DEFER_SYM),
("defer-yield", DEFER_YIELD_SYM),
("block", BLOCK_SYM),
("finish-block", FINISH_BLOCK_SYM),
("restart-block", RESTART_BLOCK_SYM),
("=", ASSIGNMENT_SYM),
StockKeyword:
("&name", FLAG_NAME_SYM),
("&arg-limits", FLAG_ARG_LIMITS_SYM),
("?", QUESTION_MARK_SYM),
(":", COLON_SYM),
("_", UNDERSCORE_SYM),
("at", AT_SYM),
("and", AND_SYM),
("or", OR_SYM),
("def", DEF_SYM),
("with-global", WITH_GLOBAL_SYM),
("template-str", TEMPLATE_STR_SYM),
("newborn", NEWBORN_SYM),
("running", RUNNING_SYM),
("paused", PAUSED_SYM),
("finished", FINISHED_SYM),
("poisoned", POISONED_SYM),
("splice", SPLICE_SYM),
("let-macro", LET_MACRO_SYM),
("expanded-to", EXPANDED_TO_SYM),
("macro-no-op", MACRO_NO_OP_SYM),
("not-a-macro", NOT_A_MACRO_SYM),
("fresh", FRESH_SYM),
("copied", COPIED_SYM),
("ok", OK_SYM),
("err", ERR_SYM),
("brief", BRIEF_SYM),
("verbose", VERBOSE_SYM),
("end-of-input", END_OF_INPUT_SYM),
("else", ELSE_SYM),
("same?", SAMEP_SYM),
("eq?", EQP_SYM),
("cond", COND_SYM),
("any-of", ANY_OF_SYM),
("in", IN_SYM),
("backquote", BACKQUOTE_SYM),
("unquote", UNQUOTE_SYM),
("splay", SPLAY_SYM),
("meth-name", METH_NAME_SYM),
("atsign", ATSIGN_SYM),
("atsign=", SET_ATSIGN_SYM),
("atsign-opt", ATSIGN_OPT_SYM),
("atsign-opt=", SET_ATSIGN_OPT_SYM),
("access-opt=", SET_ACCESS_OPT_SYM),
("nil", NIL_SYM),
("char", CHAR_SYM),
("sym", SYM_SYM),
("rfn", RFN_SYM),
("str", STR_SYM),
("tab", TAB_SYM),
("obj", OBJ_SYM),
("class", CLASS_SYM),
("coro", CORO_SYM),
("rdata", RDATA_SYM),
("infinite", INFINITE_SYM),
("unknown", UNKNOWN_SYM),
("field", FIELD_SYM),
("const", CONST_SYM),
("meth", METH_SYM),
("wrap", WRAP_SYM),
("wildcard-wrap", WILDCARD_WRAP_SYM),
("prop", PROP_SYM),
("wrap-prop", WRAP_PROP_SYM),
("wildcard-wrap-prop", WILDCARD_WRAP_PROP_SYM),
("get", GET_SYM),
("set", SET_SYM),
("init", INIT_SYM),
("init-mixin", INIT_MIXIN_SYM),
("init-state", INIT_STATE_SYM),
("inits", INITS_SYM),
("fini", FINI_SYM),
("fini-mixin", FINI_MIXIN_SYM),
("fini-state", FINI_STATE_SYM),
("finis", FINIS_SYM),
("mixin", MIXIN_SYM),
("mixin?", MIXINP_SYM),
("name", NAME_SYM),
("class-name", CLASS_NAME_SYM),
("state-name", STATE_NAME_SYM),
("self", SELF_SYM),
("base", BASE_SYM),
("Main", MAIN_SYM),
("states", STATES_SYM),
("state", STATE_SYM),
("state*", STATEX_SYM),
("enab!", ENAB_SYM),
("enab?", ENABP_SYM),
("disab!", DISAB_SYM),
("fsm", FSM_SYM),
("fsm-siblings", FSM_SIBLINGS_SYM),
("parent", PARENT_SYM),
("children", CHILDREN_SYM),
("enabled-by-default?", ENABLED_BY_DEFAULTP_SYM),
("bindings", BINDINGS_SYM),
("op-clone", OP_CLONE_SYM),
("op-deep-clone", OP_DEEP_CLONE_SYM),
("op-eq?", OP_EQP_SYM),
("ratio", RATIO_SYM),
("min-ratio", MIN_RATIO_SYM),
("default-ratio", DEFAULT_RATIO_SYM),
("young-bytes", YOUNG_BYTES_SYM),
("old-bytes", OLD_BYTES_SYM),
("ghost-bytes", GHOST_BYTES_SYM),
StockTransform:
("+", ADD_SYM),
("-", SUB_SYM),
("*", MUL_SYM),
("/", DIV_SYM),
("%", REM_SYM),
("abs", ABS_SYM),
("bitand", BITAND_SYM),
("bitor", BITOR_SYM),
("bitxor", BITXOR_SYM),
("sign", SIGN_SYM),
("min", MIN_SYM),
("max", MAX_SYM),
("nil?", NILP_SYM),
("num?", NUMP_SYM),
("int?", INTP_SYM),
("flo?", FLOP_SYM),
("nan?", NANP_SYM),
("inf?", INFP_SYM),
("bool?", BOOLP_SYM),
("sym?", SYMP_SYM),
("deque?", DEQUEP_SYM),
("arr?", ARRP_SYM),
("str?", STRP_SYM),
("tab?", TABP_SYM),
("iter?", ITERP_SYM),
("iterable?", ITERABLEP_SYM),
("obj?", OBJP_SYM),
("class?", CLASSP_SYM),
("fn?", FNP_SYM),
("rfn?", RFNP_SYM),
("coro?", COROP_SYM),
("rdata?", RDATAP_SYM),
("callable?", CALLABLEP_SYM),
("expander?", EXPANDERP_SYM),
("int", INT_SYM),
("flo", FLO_SYM),
("bool", BOOL_SYM),
("==", NUM_EQ_SYM),
("<", LT_SYM),
("<=", LTE_SYM),
(">", GT_SYM),
(">=", GTE_SYM),
("not", NOT_SYM),
("iter", ITER_SYM),
("iter-next!", ITER_NEXT_SYM),
("iter-next-back!", ITER_NEXT_BACK_SYM),
("iter-finished?", ITER_FINISHEDP_SYM),
("len", LEN_SYM),
("has?", HASP_SYM),
("access", ACCESS_SYM),
("access=", SET_ACCESS_SYM),
("arr", ARR_SYM),
("call-meth", CALL_METH_SYM),
("call-meth-opt", CALL_METH_OPT_SYM),
("call-base-raw", CALL_BASE_RAW_SYM),
("global", GLOBAL_SYM),
("global=", SET_GLOBAL_SYM)
);