#![allow(dead_code)]
#![allow(unused_macros)]
pub mod compiler;
pub mod runtime;
mod collections;
mod convert;
mod format;
mod lang;
mod rng;
mod stdlib;
mod string;
mod util;
mod value;
mod func;
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::*;
use crate::compiler::*;
use crate::lang::Sequence;
use crate::rng::RantRng;
use crate::runtime::{RuntimeResult, IntoRuntimeResult, RuntimeError, RuntimeErrorType, VM};
use std::{path::Path, rc::Rc, cell::RefCell, fmt::Display, path::PathBuf, io::ErrorKind, collections::HashMap};
use std::env;
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 ENV_MODULES_PATH_KEY: &str = "RANT_MODULES_PATH";
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";
pub(crate) type ModuleLoadResult = Result<RantProgram, ModuleLoadError>;
#[derive(Debug)]
pub struct Rant {
rng: Rc<RantRng>,
debug_mode: bool,
globals: HashMap<InternalString, RantVar, FnvBuildHasher>,
options: RantOptions,
}
impl Rant {
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()
})
}
pub fn with_options(options: RantOptions) -> Self {
let mut rant = Self {
debug_mode: options.debug_mode,
globals: Default::default(),
rng: Rc::new(RantRng::new(options.seed)),
options,
};
if rant.options.use_stdlib {
stdlib::load_stdlib(&mut rant);
}
rant
}
}
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, CompilerErrorKind> {
compiler::compile_string(source, reporter, self.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, CompilerErrorKind> {
compiler::compile_string(source, reporter, self.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, CompilerErrorKind> {
compiler::compile_string(source, &mut (), self.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, CompilerErrorKind> {
compiler::compile_string(source, &mut (), self.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, CompilerErrorKind> {
compiler::compile_file(path, reporter, self.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, CompilerErrorKind> {
compiler::compile_file(path, &mut (), self.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 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 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 run_into_string(&mut self, program: &RantProgram) -> RuntimeResult<String> {
let mut vm = VM::new(self.rng.clone(), self, program);
Ok(vm.run()?.to_string())
}
pub fn run_into_string_with<A>(&mut self, program: &RantProgram, args: A) -> RuntimeResult<String>
where A: Into<Option<HashMap<String, RantValue>>>
{
let mut vm = VM::new(self.rng.clone(), self, program);
Ok(vm.run_with(args)?.to_string())
}
pub(crate) fn try_load_module(&mut self, module_path: &str, caller_origin: Rc<RantProgramInfo>) -> ModuleLoadResult {
if !self.options.enable_require {
return Err(ModuleLoadError {
name: module_path.to_owned(),
reason: ModuleLoadErrorReason::NotAllowed,
})
}
if let Some(full_module_path) = self.find_module_path(module_path, caller_origin.path.as_deref()) {
let mut errors = vec![];
let compile_result = self.compile_file(full_module_path, &mut errors);
match compile_result {
Ok(module) => Ok(module),
Err(err) => {
Err(ModuleLoadError {
name: module_path.to_owned(),
reason: match err{
CompilerErrorKind::SyntaxError => {
ModuleLoadErrorReason::CompileFailed(errors)
},
CompilerErrorKind::IOError(ioerr) => {
match ioerr {
IOErrorKind::NotFound => {
ModuleLoadErrorReason::NotFound
},
IOErrorKind::PermissionDenied => {
ModuleLoadErrorReason::NotAllowed
},
_ => ModuleLoadErrorReason::FileIOError(ioerr)
}
}
}
})
}
}
} else {
Err(ModuleLoadError {
name: module_path.to_owned(),
reason: ModuleLoadErrorReason::NotFound,
})
}
}
#[inline]
fn find_module_path(&self, module_path: &str, dependant_path: Option<&str>) -> Option<PathBuf> {
let module_path = PathBuf::from(
module_path.replace("/", &String::from(std::path::MAIN_SEPARATOR))
)
.with_extension(RANT_FILE_EXTENSION);
macro_rules! search_for_module {
($path:expr) => {
let path = $path;
if let Ok(full_module_path) = path
.join(&module_path)
.canonicalize()
{
if full_module_path.starts_with(path)
&& full_module_path.exists()
{
return Some(full_module_path)
}
}
}
}
if let Some(program_path) =
dependant_path
.map(PathBuf::from)
.as_deref()
.and_then(|p| p.parent())
{
search_for_module!(program_path);
}
if let Some(local_modules_path) =
self.options.local_modules_path
.as_ref()
.map(PathBuf::from)
.or_else(||
env::current_dir()
.ok()
)
.and_then(|p| p.canonicalize().ok())
{
search_for_module!(local_modules_path);
}
if self.options.enable_global_modules {
if let Some(global_modules_path) =
env::var_os(ENV_MODULES_PATH_KEY)
.map(PathBuf::from)
.and_then(|p| p.canonicalize().ok())
{
search_for_module!(global_modules_path);
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct RantOptions {
pub use_stdlib: bool,
pub debug_mode: bool,
pub seed: u64,
pub enable_require: bool,
pub enable_global_modules: bool,
pub local_modules_path: Option<String>,
}
impl Default for RantOptions {
fn default() -> Self {
Self {
use_stdlib: true,
debug_mode: false,
seed: 0,
enable_require: true,
enable_global_modules: true,
local_modules_path: None,
}
}
}
#[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 struct ModuleLoadError {
name: String,
reason: ModuleLoadErrorReason,
}
impl ModuleLoadError {
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn reason(&self) -> &ModuleLoadErrorReason {
&self.reason
}
}
#[derive(Debug)]
pub enum ModuleLoadErrorReason {
NotAllowed,
NotFound,
CompileFailed(Vec<CompilerMessage>),
FileIOError(ErrorKind),
}
impl Display for ModuleLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.reason() {
ModuleLoadErrorReason::NotAllowed => write!(f, "module loading is disabled"),
ModuleLoadErrorReason::NotFound => write!(f, "module '{}' not found", self.name()),
ModuleLoadErrorReason::CompileFailed(msgs) => write!(f, "module '{}' failed to compile: {}",
self.name(),
msgs.iter().fold(String::new(), |mut acc, msg| {
acc.push_str(&format!("[{}] {}\n", msg.severity(), msg.message()));
acc
})),
ModuleLoadErrorReason::FileIOError(ioerr) => write!(f, "file I/O error ({:?})", ioerr),
}
}
}
impl IntoRuntimeResult<RantProgram> for ModuleLoadResult {
fn into_runtime_result(self) -> RuntimeResult<RantProgram> {
self.map_err(|err| RuntimeError {
description: err.to_string(),
error_type: RuntimeErrorType::ModuleLoadError(err),
stack_trace: None,
})
}
}