#![allow(dead_code)]
#![allow(unused_macros)]
#![allow(clippy::upper_case_acronyms)]
pub mod data;
pub mod compiler;
pub mod runtime;
mod collections;
mod convert;
mod format;
mod func;
mod lang;
mod modres;
mod rng;
mod selector;
mod stdlib;
mod string;
mod util;
mod value;
mod var;
pub use crate::collections::*;
pub use crate::convert::*;
pub use crate::string::*;
pub use crate::value::*;
pub use crate::func::*;
pub use crate::var::*;
pub use crate::modres::*;
pub use crate::selector::*;
use crate::compiler::*;
use crate::lang::Sequence;
use crate::rng::RantRng;
use crate::runtime::{RuntimeResult, IntoRuntimeResult, RuntimeError, RuntimeErrorType, VM};
use std::error::Error;
use std::{path::Path, rc::Rc, fmt::Display, path::PathBuf, collections::HashMap};
use std::env;
use data::DataSource;
use fnv::FnvBuildHasher;
use rand::Rng;
type IOErrorKind = std::io::ErrorKind;
pub(crate) type InternalString = smartstring::alias::CompactString;
pub const BUILD_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RANT_LANG_VERSION: &str = "4.0";
pub const DEFAULT_PROGRAM_NAME: &str = "program";
pub const RANT_FILE_EXTENSION: &str = "rant";
pub(crate) const MODULES_CACHE_KEY: &str = "__MODULES";
#[derive(Debug)]
pub struct Rant {
options: RantOptions,
module_resolver: Rc<dyn ModuleResolver>,
rng: Rc<RantRng>,
data_sources: HashMap<InternalString, Box<dyn DataSource>, FnvBuildHasher>,
globals: HashMap<InternalString, RantVar, FnvBuildHasher>,
}
impl Rant {
#[inline(always)]
pub fn new() -> Self {
Self::with_seed(0)
}
pub fn with_seed(seed: u64) -> Self {
Self::with_options(RantOptions {
seed,
.. Default::default()
})
}
pub fn with_random_seed() -> Self {
Self::with_options(RantOptions {
seed: rand::thread_rng().gen(),
.. Default::default()
})
}
#[inline(always)]
pub fn with_options(options: RantOptions) -> Self {
let mut rant = Self {
module_resolver: Rc::new(DefaultModuleResolver::default()),
globals: Default::default(),
data_sources: Default::default(),
rng: Rc::new(RantRng::new(options.seed)),
options,
};
if rant.options.use_stdlib {
stdlib::load_stdlib(&mut rant);
}
rant
}
#[inline]
pub fn using_module_resolver<R: ModuleResolver + 'static>(self, module_resolver: R) -> Self {
Self {
module_resolver: Rc::new(module_resolver),
.. self
}
}
}
impl Default for Rant {
fn default() -> Self {
Self::new()
}
}
impl Rant {
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile<R: Reporter>(&self, source: &str, reporter: &mut R) -> Result<RantProgram, CompilerError> {
compiler::compile_string(source, reporter, self.options.debug_mode, RantProgramInfo {
name: None,
path: None,
})
}
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile_named<R: Reporter>(&self, source: &str, reporter: &mut R, name: &str) -> Result<RantProgram, CompilerError> {
compiler::compile_string(source, reporter, self.options.debug_mode, RantProgramInfo {
name: Some(name.to_owned()),
path: None,
})
}
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile_quiet(&self, source: &str) -> Result<RantProgram, CompilerError> {
compiler::compile_string(source, &mut (), self.options.debug_mode, RantProgramInfo {
name: None,
path: None,
})
}
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile_quiet_named(&self, source: &str, name: &str) -> Result<RantProgram, CompilerError> {
compiler::compile_string(source, &mut (), self.options.debug_mode, RantProgramInfo {
name: Some(name.to_owned()),
path: None,
})
}
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile_file<P: AsRef<Path>, R: Reporter>(&self, path: P, reporter: &mut R) -> Result<RantProgram, CompilerError> {
compiler::compile_file(path, reporter, self.options.debug_mode)
}
#[must_use = "compiling a program without storing or running it achieves nothing"]
pub fn compile_file_quiet<P: AsRef<Path>>(&self, path: P) -> Result<RantProgram, CompilerError> {
compiler::compile_file(path, &mut (), self.options.debug_mode)
}
#[inline]
pub fn set_global(&mut self, key: &str, value: RantValue) -> bool {
if let Some(global_var) = self.globals.get_mut(key) {
global_var.write(value)
} else {
self.globals.insert(InternalString::from(key), RantVar::ByVal(value));
true
}
}
#[inline]
pub fn set_global_const(&mut self, key: &str, value: RantValue) -> bool {
if let Some(global_var) = self.globals.get(key) {
if global_var.is_const() {
return false
}
}
self.globals.insert(InternalString::from(key), RantVar::ByValConst(value));
true
}
#[inline]
pub fn set_global_force(&mut self, key: &str, value: RantValue, is_const: bool) {
self.globals.insert(InternalString::from(key), if is_const { RantVar::ByValConst(value) } else { RantVar::ByVal(value) });
}
#[inline]
pub fn get_global(&self, key: &str) -> Option<RantValue> {
self.globals.get(key).map(|var| var.value_cloned())
}
#[inline]
pub(crate) fn get_global_var(&self, key: &str) -> Option<&RantVar> {
self.globals.get(key)
}
#[inline]
pub(crate) fn set_global_var(&mut self, key: &str, var: RantVar) {
self.globals.insert(InternalString::from(key), var);
}
#[inline]
pub(crate) fn get_global_var_mut(&mut self, key: &str) -> Option<&mut RantVar> {
self.globals.get_mut(key)
}
#[inline]
pub fn has_global(&self, key: &str) -> bool {
self.globals.contains_key(key)
}
#[inline]
pub fn delete_global(&mut self, key: &str) -> bool {
self.globals.remove(key).is_some()
}
#[inline]
pub fn global_names(&self) -> impl Iterator<Item = &str> {
self.globals.keys().map(|k| k.as_str())
}
pub fn options(&self) -> &RantOptions {
&self.options
}
pub fn seed(&self) -> u64 {
self.rng.seed()
}
pub fn set_seed(&mut self, seed: u64) {
self.rng = Rc::new(RantRng::new(seed));
}
pub fn reset_seed(&mut self) {
let seed = self.rng.seed();
self.rng = Rc::new(RantRng::new(seed));
}
pub fn add_data_source(&mut self, data_source: impl DataSource + 'static) -> Result<(), DataSourceRegisterError> {
let id = data_source.type_id();
if self.has_data_source(id) {
return Err(DataSourceRegisterError::AlreadyExists(id.into()))
}
self.data_sources.insert(id.into(), Box::new(data_source));
Ok(())
}
pub fn remove_data_source(&mut self, name: &str) -> Option<Box<dyn DataSource>> {
self.data_sources.remove(name)
}
pub fn has_data_source(&self, name: &str) -> bool {
self.data_sources.contains_key(name)
}
pub fn clear_data_sources(&mut self) {
self.data_sources.clear();
}
pub fn data_source(&self, name: &str) -> Option<&dyn DataSource> {
self.data_sources.get(name).map(Box::as_ref)
}
pub fn iter_data_sources(&self) -> impl Iterator<Item = (&'_ str, &'_ Box<dyn DataSource + 'static>)> {
self.data_sources.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn run(&mut self, program: &RantProgram) -> RuntimeResult<RantValue> {
VM::new(self.rng.clone(), self, program).run()
}
pub fn run_with<A>(&mut self, program: &RantProgram, args: A) -> RuntimeResult<RantValue>
where A: Into<Option<HashMap<String, RantValue>>>
{
VM::new(self.rng.clone(), self, program).run_with(args)
}
pub fn try_load_global_module(&mut self, module_path: &str) -> Result<(), ModuleLoadError> {
if let Some(module_name) =
PathBuf::from(&module_path)
.with_extension("")
.file_name()
.map(|name| name.to_str())
.flatten()
.map(|name| name.to_owned())
{
if self.get_cached_module(module_name.as_ref()).is_some() {
return Ok(())
}
let module_resolver = Rc::clone(&self.module_resolver);
let module = match module_resolver.try_resolve(self, module_path, None) {
Ok(module_program) => match self.run(&module_program) {
Ok(module) => Ok(module),
Err(err) => Err(ModuleLoadError::RuntimeError(Rc::new(err))),
},
Err(err) => Err(ModuleLoadError::ResolveError(err)),
}?;
if let Some(RantValue::Map(module_cache_ref)) = self.get_global(MODULES_CACHE_KEY) {
module_cache_ref.borrow_mut().raw_set(&module_name, module);
} else {
let mut cache = RantMap::new();
cache.raw_set(&module_name, module);
self.set_global(MODULES_CACHE_KEY, cache.into_rant());
}
Ok(())
} else {
Err(ModuleLoadError::InvalidPath(format!("missing module name from path: '{module_path}'")))
}
}
#[inline]
pub(crate) fn get_cached_module(&self, module_name: &str) -> Option<RantValue> {
if let Some(RantValue::Map(module_cache_ref)) = self.get_global(MODULES_CACHE_KEY) {
if let Some(module @ RantValue::Map(..)) = module_cache_ref.borrow().raw_get(module_name) {
return Some(module.clone())
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct RantOptions {
pub use_stdlib: bool,
pub debug_mode: bool,
pub seed: u64,
}
impl Default for RantOptions {
fn default() -> Self {
Self {
use_stdlib: true,
debug_mode: false,
seed: 0,
}
}
}
#[derive(Debug)]
pub struct RantProgram {
info: Rc<RantProgramInfo>,
root: Rc<Sequence>
}
impl RantProgram {
pub(crate) fn new(root: Rc<Sequence>, info: Rc<RantProgramInfo>) -> Self {
Self {
info,
root,
}
}
#[inline]
pub fn name(&self) -> Option<&str> {
self.info.name.as_deref()
}
#[inline]
pub fn path(&self) -> Option<&str> {
self.info.path.as_deref()
}
#[inline]
pub fn info(&self) -> &RantProgramInfo {
self.info.as_ref()
}
}
#[derive(Debug)]
pub struct RantProgramInfo {
path: Option<String>,
name: Option<String>,
}
impl RantProgramInfo {
#[inline]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
#[inline]
pub fn path(&self) -> Option<&str> {
self.path.as_deref()
}
}
#[derive(Debug)]
pub enum ModuleLoadError {
InvalidPath(String),
RuntimeError(Rc<RuntimeError>),
ResolveError(ModuleResolveError),
}
impl Display for ModuleLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ModuleLoadError::InvalidPath(errmsg) => write!(f, "{}", errmsg),
ModuleLoadError::RuntimeError(err) => write!(f, "runtime error while loading module: {}", err),
ModuleLoadError::ResolveError(err) => write!(f, "unable to resolve module: {}", err),
}
}
}
impl Error for ModuleLoadError {}
#[derive(Debug)]
pub enum DataSourceRegisterError {
InvalidTypeId(String),
AlreadyExists(String),
}
impl Display for DataSourceRegisterError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidTypeId(id) => write!(f, "the type id '{id}' is invalid"),
Self::AlreadyExists(id) => write!(f, "the type id '{id}' was already registered on the context"),
}
}
}