use fnv::{FnvHashMap};
use owning_ref::{OwningHandle};
use self::stock_syms::*;
use smallvec::{SmallVec};
use std::{fmt, fs, mem, str, u32};
use std::any::{Any, TypeId, type_name};
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::cmp::{Ordering};
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, Gc, GcVisitor, Heap, Raw, Header, Slot, Root, Visitor};
use super::iter::{RawCallable, 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::{
Callable, CallableOps, FromVal, IntoVal, IntoCallArgs, wrap,
WrappedCall, Wrapper
};
#[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);
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().expect(
"no glsp runtime is active; consider calling Runtime::run()"
);
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!("no glsp runtime is active; consider calling Runtime::run()")
}
})
}
#[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!("no glsp runtime is active; consider calling Runtime::run()")
}
})
}
#[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!("no glsp runtime is active; consider calling Runtime::run()")
}
})
}
#[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();
}
);
}
#[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| {
loop {
let mut any_freed = false;
for rdata in engine.heap.all_unfreed_rdata() {
if !rdata.is_freed() {
rdata.free().unwrap();
any_freed = true;
}
}
if !any_freed {
break
}
}
while let Some(type_id) = engine.rglobals_ordering.borrow_mut().pop() {
let rglobal = engine.rglobals.borrow_mut().remove(&type_id).unwrap();
drop(rglobal);
}
engine.lazy_storage.borrow_mut().clear();
engine.syms.borrow_mut().clear();
engine.rclasses.borrow_mut().clear();
engine.vm.clear();
engine.heap.clear();
});
Ok(())
});
free_engine_id(self.0.heap.engine_id);
assert!(Rc::strong_count(&self.0) == 1);
}
}
struct EngineStorage {
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>>,
rclasses: RefCell<HashMap<TypeId, Rc<RClass>>>,
rclass_names: RefCell<HashSet<Sym>>,
in_expander: RefCell<Option<(Option<Sym>, Span, Rc<Env>)>>,
errors_verbose: Cell<bool>,
rglobals: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
rglobals_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
}
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, PhantomData))
}));
let spans = vec![SpanStorage::Generated];
let mut spans_map = HashMap::new();
spans_map.insert(SpanStorage::Generated, Span(0));
let filenames = vec!["".into()];
let engine = Engine(Rc::new(EngineStorage {
heap: Heap::new(alloc_engine_id().expect("more than 256 simultaneous Runtimes")),
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()),
rclasses: RefCell::new(HashMap::new()),
rclass_names: RefCell::new(HashSet::new()),
in_expander: RefCell::new(None),
errors_verbose: Cell::new(true),
rglobals: RefCell::new(HashMap::new()),
rglobals_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>
{
let old_active_engine = ACTIVE_ENGINE.with(|ref_cell| {
ref_cell.replace(Some(Rc::clone(&self.0)))
});
let _guard = Guard::new(|| {
ACTIVE_ENGINE.with(|ref_cell| {
ref_cell.replace(old_active_engine);
});
});
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, pub(crate) PhantomData<*mut ()>);
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, PhantomData)
}
#[doc(hidden)]
pub fn to_u32(&self) -> u32 {
self.0
}
}
impl PartialOrd<Sym> for Sym {
fn partial_cmp(&self, other: &Sym) -> Option<Ordering> {
if self.0 == other.0 {
Some(Ordering::Equal)
} else {
(*self.name()).partial_cmp(&*other.name())
}
}
}
impl Ord for Sym {
fn cmp(&self, other: &Sym) -> Ordering {
if self.0 == other.0 {
Ordering::Equal
} else {
(*self.name()).cmp(&*other.name())
}
}
}
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
}
pub struct RFn {
header: Header,
pub(crate) name: Cell<Option<Sym>>,
wrapped_fn: Box<dyn WrappedCall>
}
impl Allocate for RFn {
fn header(&self) -> &Header {
&self.header
}
fn visit_raws<V: Visitor>(&self, _visitor: &mut V) {
}
fn clear_raws(&self) {
}
fn owned_memory_usage(&self) -> usize {
mem::size_of_val::<dyn WrappedCall>(&*self.wrapped_fn)
}
}
impl RFn {
pub(crate) fn set_name(&self, new_name: Option<Sym>) {
self.name.set(new_name)
}
}
impl CallableOps for Root<RFn> {
#[inline(always)]
fn receive_call(&self, arg_count: usize) -> GResult<Val> {
Ok(glsp::call_rfn(self, arg_count)?.root())
}
fn name(&self) -> Option<Sym> {
self.name.get()
}
fn arg_limits(&self) -> (usize, Option<usize>) {
let (min, max) = self.wrapped_fn.arg_limits();
(min, if max == usize::MAX { None } else { Some(max) })
}
}
impl CallableOps for Raw<RFn> {
#[inline(always)]
fn receive_call(&self, arg_count: usize) -> GResult<Val> {
Ok(glsp::call_rfn(&self.root(), arg_count)?.root())
}
fn name(&self) -> Option<Sym> {
self.name.get()
}
fn arg_limits(&self) -> (usize, Option<usize>) {
let (min, max) = self.wrapped_fn.arg_limits();
(min, if max == usize::MAX { None } else { Some(max) })
}
}
#[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: $crate::Sym,)*
}
impl $struct_name {
#[allow(unused_variables)]
$struct_vis fn new() -> Self {
$struct_name {
$($name: $crate::sym($contents).unwrap(),)*
}
}
}
);
}
#[macro_export]
macro_rules! sym {
($arg:expr,) => (sym!($arg));
($arg:expr) => (
$crate::sym($arg).unwrap()
)
}
pub trait RGlobal: 'static + Sized {
fn borrow() -> RGlobalRef<Self> {
glsp::rglobal::<Self>()
}
fn borrow_mut() -> RGlobalRefMut<Self> {
glsp::rglobal_mut::<Self>()
}
fn try_borrow() -> GResult<RGlobalRef<Self>> {
glsp::try_rglobal::<Self>()
}
fn try_borrow_mut() -> GResult<RGlobalRefMut<Self>> {
glsp::try_rglobal_mut::<Self>()
}
}
pub struct RGlobalRef<T: 'static> {
handle: OwningHandle<Rc<RefCell<T>>, Ref<'static, T>>
}
impl<T> Deref for RGlobalRef<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.handle
}
}
pub struct RGlobalRefMut<T: 'static> {
handle: OwningHandle<Rc<RefCell<T>>, RefMut<'static, T>>
}
impl<T> Deref for RGlobalRefMut<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.handle
}
}
impl<T> DerefMut for RGlobalRefMut<T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.handle
}
}
pub struct RClass {
name: Sym,
bindings: FnvHashMap<Sym, RBinding>,
trace: Option<Box<dyn Fn(&dyn Any, &mut GcVisitor)>>
}
enum RBinding {
Met(Root<RFn>),
Prop(Option<Root<RFn>>, Option<Root<RFn>>)
}
impl RClass {
pub fn name(&self) -> Sym {
self.name
}
}
#[must_use]
pub struct RClassBuilder<T> {
name: Option<Sym>,
bindings: FnvHashMap<Sym, RBinding>,
trace: Option<Box<dyn Fn(&dyn Any, &mut GcVisitor)>>,
phantom: PhantomData<T>
}
impl<T: 'static> RClassBuilder<T> {
pub fn new() -> RClassBuilder<T> {
let raw_name = type_name::<T>();
let mut name = None;
if !raw_name.contains(&['<', '>', '[', ']', ';', '\''][..]) {
let filtered: String = raw_name
.chars()
.filter(|ch| !char::is_whitespace(*ch))
.collect();
let start = filtered.rfind(':').map(|i| i + 1).unwrap_or(0);
let suffix = &filtered[start..];
if glsp::is_valid_sym(&suffix) {
name = Some(glsp::sym(&suffix).unwrap());
}
}
RClassBuilder {
name,
bindings: FnvHashMap::default(),
trace: None,
phantom: PhantomData
}
}
pub fn name<S: ToSym>(self, name: S) -> RClassBuilder<T> {
RClassBuilder {
name: Some(name.to_sym().unwrap()),
..self
}
}
pub fn met<S, ArgsWithTag, Ret, F>(mut self, name: S, f: F) -> RClassBuilder<T>
where
S: ToSym,
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let name = name.to_sym().unwrap();
let rfn = glsp::named_rfn(name, f);
match self.bindings.entry(name) {
Vacant(entry) => {
entry.insert(RBinding::Met(rfn));
}
Occupied(_) => panic!("duplicate method {}", name)
}
self
}
pub fn prop_get<S, ArgsWithTag, Ret, F>(mut self, name: S, f: F) -> RClassBuilder<T>
where
S: ToSym,
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let name = name.to_sym().unwrap();
let rfn = glsp::named_rfn(name, f);
match self.bindings.entry(name) {
Vacant(entry) => {
entry.insert(RBinding::Prop(Some(rfn), None));
}
Occupied(mut entry) => {
match entry.get_mut() {
RBinding::Met(_) => {
panic!("{} is bound to both a method and a property", name)
}
RBinding::Prop(Some(_), _) => {
panic!("duplicate property getter {}", name)
}
RBinding::Prop(ref mut none, _) => *none = Some(rfn)
}
}
}
self
}
pub fn prop_set<S, ArgsWithTag, Ret, F>(mut self, name: S, f: F) -> RClassBuilder<T>
where
S: ToSym,
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let name = name.to_sym().unwrap();
let rfn = glsp::named_rfn(name, f);
match self.bindings.entry(name) {
Vacant(entry) => {
entry.insert(RBinding::Prop(None, Some(rfn)));
}
Occupied(mut entry) => {
match entry.get_mut() {
RBinding::Met(_) => {
panic!("{} is bound to both a method and a property", name)
}
RBinding::Prop(_, Some(_)) => {
panic!("duplicate property setter {}", name)
}
RBinding::Prop(_, ref mut none) => *none = Some(rfn)
}
}
}
self
}
pub fn trace(mut self, f: fn(&T, &mut GcVisitor)) -> RClassBuilder<T> {
self.trace = Some(Box::new(move |any: &dyn Any, visitor: &mut GcVisitor| {
f(&any.downcast_ref::<RefCell<T>>().unwrap().borrow(), visitor)
}));
self
}
pub fn build(self) {
glsp::add_rclass::<T>(RClass {
name: self.name.expect("RClass has no name"),
bindings: self.bindings,
trace: self.trace
});
}
}
pub struct RData {
header: Header,
storage: RefCell<Option<Rc<dyn Any>>>,
pub(crate) rclass: Option<Rc<RClass>>,
raw_self: Cell<Option<Raw<RData>>>
}
impl Allocate for RData {
fn header(&self) -> &Header {
&self.header
}
fn visit_raws<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_raw(&self.raw_self());
let storage = self.storage.borrow();
if let Some(rc_any) = storage.as_ref() {
let rc_any = Rc::clone(rc_any);
drop(storage);
let mut gc_visitor = GcVisitor(visitor);
if let Some(rclass) = self.rclass.as_ref() {
if let Some(trace) = rclass.trace.as_ref() {
with_heap(|heap| {
let _root_storage = heap.root_storage.borrow();
ACTIVE_ENGINE.with(|active_engine_ref_cell| {
let _active_engine = active_engine_ref_cell.borrow();
panic::catch_unwind(AssertUnwindSafe(|| {
trace(&*rc_any, &mut gc_visitor);
})).ok();
});
});
}
}
}
}
fn clear_raws(&self) {
self.raw_self.set(None);
}
fn owned_memory_usage(&self) -> usize {
match &*self.storage.borrow() {
Some(ref rc) => mem::size_of_val::<dyn Any>(&**rc),
None => 0
}
}
}
pub struct RRef<T: 'static>(OwningHandle<Rc<RefCell<T>>, Ref<'static, T>>);
impl<T> Deref for RRef<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
pub struct RRefMut<T: 'static>(OwningHandle<Rc<RefCell<T>>, RefMut<'static, T>>);
impl<T> Deref for RRefMut<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
impl<T> DerefMut for RRefMut<T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.0
}
}
impl RData {
pub(crate) fn new<T: 'static>(rdata: T, rclass: Option<Rc<RClass>>) -> RData {
RData {
header: Header::new(),
storage: RefCell::new(Some(Rc::new(RefCell::new(rdata)))),
rclass,
raw_self: Cell::new(None)
}
}
pub fn rclass(&self) -> Option<Rc<RClass>> {
self.rclass.clone()
}
pub fn is<T: 'static>(&self) -> bool {
match self.storage.borrow().as_ref() {
Some(rc_any) => rc_any.is::<RefCell<T>>(),
None => false
}
}
pub fn borrow<T>(&self) -> RRef<T> {
self.try_borrow::<T>().unwrap()
}
pub fn try_borrow<T>(&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()) {
Ok(rc_ref_cell) => {
ensure!(rc_ref_cell.try_borrow().is_ok(),
"try_borrow<{}> failed: value is mutably borrowed", type_name::<T>());
Ok(RRef(OwningHandle::new(rc_ref_cell)))
}
Err(_) => bail!("type mismatch in try_borrow<{}>()", type_name::<T>())
}
} else {
bail!("try_borrow<{}> failed: attempted to access a freed RData", type_name::<T>())
}
}
pub fn borrow_mut<T>(&self) -> RRefMut<T> {
self.try_borrow_mut::<T>().unwrap()
}
pub fn try_borrow_mut<T>(&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()) {
Ok(rc_ref_cell) => {
ensure!(rc_ref_cell.try_borrow_mut().is_ok(),
"try_borrow_mut<{}> failed: value is currently borrowed",
type_name::<T>());
Ok(RRefMut(OwningHandle::new_mut(rc_ref_cell)))
}
Err(_) => bail!("type mismatch in try_borrow<{}>()", type_name::<T>())
}
} else {
bail!("try_borrow_mut<{}> failed: attempted to access a freed RData", type_name::<T>())
}
}
pub fn take<T: 'static>(&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();
drop(borrow_mut);
let rc = match Rc::downcast::<RefCell<T>>(rc_any) {
Ok(rc) => rc,
Err(_) => {
bail!("type mismatch when calling take::<{}>()", type_name::<T>())
}
};
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 raw_self(&self) -> Raw<RData> {
let raw_self = self.raw_self.take();
self.raw_self.set(raw_self.clone());
raw_self.unwrap()
}
pub fn access_giter(rdata: &Root<RData>, giter: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::AccessRData(rdata.to_raw(), giter.to_raw()))
}
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.rclass.as_ref().and_then(|rclass| rclass.bindings.get(&sym)) {
Some(RBinding::Prop(Some(ref rfn), _)) => {
with_vm(|vm| {
vm.stacks.borrow_mut().regs.push(Slot::RData(self.raw_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: IntoVal
{
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: IntoVal
{
let sym = key.to_sym()?;
match self.rclass.as_ref().and_then(|rclass| rclass.bindings.get(&sym)) {
Some(RBinding::Prop(_, Some(ref rfn))) => {
with_vm(|vm| {
let mut stacks = vm.stacks.borrow_mut();
stacks.regs.push(Slot::RData(self.raw_self()));
stacks.regs.push(val.into_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: IntoCallArgs,
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: IntoCallArgs,
R: FromVal
{
let sym = key.to_sym()?;
match self.rclass.as_ref().and_then(|rclass| rclass.bindings.get(&sym)) {
Some(RBinding::Met(ref rfn)) => {
with_vm(|vm| {
let mut stacks = vm.stacks.borrow_mut();
let starting_len = stacks.regs.len();
stacks.regs.push(Slot::RData(self.raw_self()));
args.into_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_met<S: ToSym>(&self, key: S) -> GResult<bool> {
let sym = key.to_sym()?;
match self.rclass.as_ref().and_then(|rclass| rclass.bindings.get(&sym)) {
Some(RBinding::Met(_)) => Ok(true),
_ => Ok(false)
}
}
pub(crate) fn get_method(&self, key: Sym) -> Option<(Slot, bool, bool, Slot)> {
match self.rclass.as_ref().and_then(|rclass| rclass.bindings.get(&key)) {
Some(&RBinding::Met(ref rfn)) => {
Some((Slot::RFn(rfn.to_raw()), true, false, Slot::Nil))
}
_ => None
}
}
pub fn try_eq(&self, other: &Root<RData>) -> GResult<bool> {
match (&self.rclass, &other.rclass) {
(Some(ref self_rclass), Some(ref other_rclass)) => {
if !Rc::ptr_eq(self_rclass, other_rclass) {
return Ok(false)
}
}
_ => 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>(Root<RData>, PhantomData<Rc<RefCell<T>>>);
impl<T> Clone for RRoot<T> {
fn clone(&self) -> RRoot<T> {
RRoot(self.0.clone(), PhantomData)
}
}
impl<T> Debug for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl<T> Display for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<T> Pointer for RRoot<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Pointer::fmt(&self.0, f)
}
}
impl<T> RRoot<T> {
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 fn downgrade(&self) -> RGc<T> {
RGc(self.0.downgrade(), PhantomData)
}
pub(crate) fn to_raw(&self) -> Raw<RData> {
self.0.to_raw()
}
pub(crate) fn into_raw(self) -> Raw<RData> {
self.0.into_raw()
}
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 free(&self) -> GResult<()> {
self.0.free()
}
pub fn is_freed(&self) -> bool {
self.0.is_freed()
}
}
impl<T: 'static> RRoot<T> {
pub fn new(root: Root<RData>) -> RRoot<T> {
assert!(root.is::<T>(), "type mismatch when constructing an RRoot<{}>", type_name::<T>());
RRoot(root, PhantomData)
}
pub fn take(&self) -> GResult<T> {
self.0.take()
}
}
pub struct RGc<T>(pub(crate) Gc<RData>, PhantomData<Rc<RefCell<T>>>);
impl<T> Clone for RGc<T> {
fn clone(&self) -> RGc<T> {
RGc(self.0.clone(), PhantomData)
}
}
impl<T> RGc<T> {
pub fn upgrade(&self) -> Option<RRoot<T>> {
self.0.upgrade().map(|root| RRoot(root, PhantomData))
}
}
#[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(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, PhantomData);
syms_map.insert(name, sym);
Ok(sym)
}
})
}
pub fn is_valid_sym(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)
})
}
}
}
#[inline]
pub fn is_valid_sym_char(ch: char) -> bool {
lex::is_valid_sym_char(ch)
}
pub fn is_representable_sym(st: &str) -> bool {
if !is_valid_sym(st) {
false
} else {
sym(st).unwrap().is_representable()
}
}
pub fn gensym() -> Sym {
glsp::gensym_impl(None)
}
pub fn gensym_with_tag(tag: &str) -> GResult<Sym> {
ensure!(glsp::is_valid_sym(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: IntoVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
let val = val.into_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: IntoVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
let val = t.into_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: IntoVal
{
with_engine(|engine| {
let sym = s.to_sym()?;
ensure!(sym.is_bindable(), "unable to bind name '{}' to global", sym);
let val = t.into_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: 'static>(rdata: T) -> Root<RData> {
with_engine(|engine| {
let rclass = engine.rclasses.borrow().get(&rdata.type_id()).cloned();
let root = glsp::alloc(RData::new(rdata, rclass));
root.raw_self.set(Some(root.to_raw()));
root
})
}
pub fn rroot<T: 'static>(rdata: T) -> RRoot<T> {
RRoot::new(glsp::rdata(rdata))
}
pub fn add_rglobal<T: RGlobal>(rglobal: T) {
with_engine(|engine| {
let rc = Rc::new(RefCell::new(rglobal)) as Rc<dyn Any>;
if engine.rglobals.borrow_mut().insert(TypeId::of::<T>(), rc).is_some() {
panic!("glsp.add_rglobal() called twice for the same type");
}
engine.rglobals_ordering.borrow_mut().push(TypeId::of::<T>());
})
}
pub fn take_rglobal<T: RGlobal>() -> GResult<T> {
with_engine(|engine| {
let rc = match engine.rglobals.borrow_mut().remove(&TypeId::of::<T>()) {
Some(rc) => rc.downcast::<RefCell<T>>().unwrap(),
None => bail!("attempted to take nonexistent global {}", type_name::<T>())
};
let rglobal = match Rc::try_unwrap(rc) {
Ok(ref_cell) => ref_cell.into_inner(),
Err(_) => bail!("called take_rglobal for {}, which is currently borrowed",
type_name::<T>())
};
engine.rglobals_ordering.borrow_mut().retain(|&type_id| type_id != TypeId::of::<T>());
Ok(rglobal)
})
}
pub(crate) fn rglobal<T: RGlobal>() -> RGlobalRef<T> {
match glsp::try_rglobal::<T>() {
Ok(rglobal_ref) => rglobal_ref,
Err(err) => panic!("{}", err.val())
}
}
pub(crate) fn rglobal_mut<T: RGlobal>() -> RGlobalRefMut<T> {
match glsp::try_rglobal_mut::<T>() {
Ok(rglobal_ref_mut) => rglobal_ref_mut,
Err(err) => panic!("{}", err.val())
}
}
pub(crate) fn try_rglobal<T: RGlobal>() -> GResult<RGlobalRef<T>> {
with_engine(|engine| {
let rglobals = engine.rglobals.borrow();
let rc = match rglobals.get(&TypeId::of::<T>()) {
Some(rc) => rc.clone(),
None => bail!("global type {} was never registered, or it 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 global {} during a mutable borrow", type_name::<T>())
}
Ok(RGlobalRef {
handle: OwningHandle::new(rc_ref_cell)
})
})
}
pub(crate) fn try_rglobal_mut<T: RGlobal>() -> GResult<RGlobalRefMut<T>> {
with_engine(|engine| {
let rglobals = engine.rglobals.borrow();
let rc = match rglobals.get(&TypeId::of::<T>()) {
Some(rc) => rc.clone(),
None => bail!("global type {} was never registered, or it 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(RGlobalRefMut {
handle: OwningHandle::new_mut(rc_ref_cell)
})
})
}
pub(crate) fn add_rclass<T: 'static>(rclass: RClass) {
with_engine(|engine| {
let mut rclasses = engine.rclasses.borrow_mut();
let mut rclass_names = engine.rclass_names.borrow_mut();
assert!(
!rclasses.contains_key(&TypeId::of::<T>()),
"duplicate RClass for type {}",
type_name::<T>()
);
assert!(
!rclass_names.contains(&rclass.name),
"duplicate RClass name {}",
rclass.name
);
rclass_names.insert(rclass.name);
rclasses.insert(TypeId::of::<T>(), Rc::new(rclass));
})
}
pub fn rfn<ArgsWithTag, Ret, F>(f: F) -> Root<RFn>
where
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
glsp::alloc(RFn {
header: Header::new(),
name: Cell::new(None),
wrapped_fn: wrap(f)
})
}
pub fn named_rfn<ArgsWithTag, Ret, F>(name: Sym, f: F) -> Root<RFn>
where
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let rfn = glsp::rfn(f);
rfn.set_name(Some(name));
rfn
}
pub fn bind_rfn<S: ToSym, ArgsWithTag, Ret, F>(name: S, f: F) -> GResult<()>
where
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let sym = name.to_sym()?;
let rfn = glsp::named_rfn(sym, f);
glsp::bind_global(sym, rfn)
}
pub fn bind_rfn_macro<S: ToSym, ArgsWithTag, Ret, F>(name: S, f: F) -> GResult<()>
where
Wrapper<ArgsWithTag, Ret, F>: WrappedCall + 'static
{
let sym = name.to_sym()?;
let rfn = glsp::named_rfn(sym, f);
glsp::bind_macro(sym, Expander::RFn(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 regs = Ref::map(stacks, |stacks| &stacks.regs[base_reg..]);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
rfn.wrapped_fn.wrapped_call(regs)
}));
let mut stacks = engine.vm.stacks.borrow_mut();
stacks.regs.truncate(base_reg);
drop(stacks);
match result {
Ok(glsp_result) => glsp_result,
Err(payload) => {
#[cold]
fn handle_error(rfn: &RFn, payload: Box<dyn Any + Send>) -> GResult<Slot> {
let rfn_description = match rfn.name.get() {
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)
}
}
handle_error(rfn, payload)
}
}
})
}
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;
})
}
#[inline]
pub(crate) fn push_frame(frame: Frame) {
with_engine(|engine| {
engine.vm.push_frame(frame);
})
}
#[inline]
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
}
}
}
})
}
#[inline]
pub(crate) fn generated_span() -> Span {
Span(0)
}
#[inline]
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: IntoCallArgs
{
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()
})
}
#[inline]
pub(crate) fn alloc<T: Allocate>(t: T) -> Root<T> {
with_engine(|engine| {
engine.heap.alloc(t)
})
}
#[doc(hidden)]
#[inline]
pub fn alloc_raw<T: Allocate>(t: T) -> Raw<T> {
with_engine(|engine| {
engine.heap.alloc_raw(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>(elem: T, reps: usize) -> GResult<Root<Arr>>
where
T: Clone + IntoVal
{
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: IntoVal
{
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: IntoVal,
V: IntoVal
{
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_raw()))
}
}
}
pub fn once_with(callable: Callable) -> Root<GIter> {
glsp::giter(GIterState::OnceWith(RawCallable::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_raw(), 0, (arr.len() - 1) as u32)))
}
}
}
pub fn repeat_with(callable: Callable) -> Root<GIter> {
glsp::giter(GIterState::RepeatWith(RawCallable::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_raw())))
}
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_raw())))
}
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_raw())))
}
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_raw())))
}
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_raw())))
}
pub fn lines(st: &Root<Str>) -> Root<GIter> {
glsp::giter(GIterState::Lines(st.shallow_clone().to_raw()))
}
pub fn split(src: &Root<Str>, split_at: &Root<Str>) -> Root<GIter> {
glsp::giter(GIterState::Split(src.shallow_clone().to_raw(),
split_at.shallow_clone().to_raw()))
}
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_raw())))
}
pub fn enumerate(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Enumerate(base.to_raw(), 0))
}
pub fn cloned(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Cloned(base.to_raw()))
}
pub fn deep_cloned(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::DeepCloned(base.to_raw()))
}
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_raw())))
}
pub fn map(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Map(RawCallable::from_callable(callable), base.to_raw()))
}
pub fn filter(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Filter(RawCallable::from_callable(callable), base.to_raw()))
}
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_raw()))
}
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_raw()))
}
pub fn flatten(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Flatten(base.to_raw(), None))
}
pub fn cycle(base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Cycle(Some(base.to_raw()), glsp::arr().to_raw(), 0))
}
pub fn take(n: usize, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Take(n as u32, base.to_raw()))
}
pub fn take_while(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::TakeWhile(RawCallable::from_callable(callable), base.to_raw()))
}
pub fn skip(n: usize, base: &Root<GIter>) -> Root<GIter> {
glsp::giter(GIterState::Skip(n as u32, base.to_raw()))
}
pub fn skip_while(callable: &Callable, base: &Root<GIter>) -> Root<GIter> {
let raw_callable = RawCallable::from_callable(callable);
glsp::giter(GIterState::SkipWhile(Some(raw_callable), base.to_raw()))
}
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 write_barrier(rdata: &Root<RData>) {
with_engine(|engine| {
engine.heap.write_barrier_rdata(rdata);
})
}
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)
}
}
pub fn load_str(text: &str) -> GResult<Val> {
glsp::push_frame(Frame::GlspApi(GlspApiName::LoadStr, None));
let _guard = Guard::new(|| glsp::pop_frame());
let vals = glsp::parse_all(&text, None)?;
eval::eval(&vals, None, false)
}
#[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: IntoCallArgs,
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.into_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_raw()));
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
}
const STOCK_SYMS: [(&str, SymKind); StockSym::STOCK_SYM_COUNT as usize] = [
$($(($sym_str, SymKind::$kind)),+),+
];
const STOCK_SYMS_BY_KIND: [&[Sym]; 3] = [
$(&[$(Sym(StockSym::$sym_name as u32, PhantomData)),+]),+
];
#[doc(hidden)]
pub mod stock_syms {
use std::marker::{PhantomData};
use super::{StockSym, Sym};
$($(
pub const $sym_name: Sym = Sym(StockSym::$sym_name as u32, PhantomData);
)+)+
}
);
);
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),
("met-name", MET_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),
("met", MET_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-met", CALL_MET_SYM),
("call-met-opt", CALL_MET_OPT_SYM),
("call-base-raw", CALL_BASE_RAW_SYM),
("global", GLOBAL_SYM),
("global=", SET_GLOBAL_SYM)
);