diff --git a/mers/Cargo.toml b/mers/Cargo.toml index ca8311f..259e476 100644 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers" -version = "0.8.12" +version = "0.8.13" edition = "2021" license = "MIT OR Apache-2.0" description = "dynamically typed but type-checked programming language" @@ -11,7 +11,7 @@ repository = "https://github.com/Dummi26/mers" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mers_lib = "0.8.12" -# mers_lib = { path = "../mers_lib" } +# mers_lib = "0.8.13" +mers_lib = { path = "../mers_lib" } clap = { version = "4.3.19", features = ["derive"] } colored = "2.1.0" diff --git a/mers/src/main.rs b/mers/src/main.rs index a0e4bda..82f8706 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -52,6 +52,7 @@ enum From { enum Configs { None, Base, + Pure, Std, } @@ -60,6 +61,7 @@ fn main() { let config = cfg_globals::add_general(match args.config { Configs::None => Config::new(), Configs::Base => Config::new().bundle_base(), + Configs::Pure => Config::new().bundle_pure(), Configs::Std => Config::new().bundle_std(), }); fn get_source(source: From) -> Source { diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index fad9db3..6f00ddb 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers_lib" -version = "0.8.12" +version = "0.8.13" edition = "2021" license = "MIT OR Apache-2.0" description = "library to use the mers language in other projects" diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index ffaa53c..8407552 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -90,6 +90,8 @@ pub mod error_colors { pub const StacktraceDescend: Color = Color::Yellow; pub const StacktraceDescendHashInclude: Color = Color::Red; + + pub const MaximumRuntimeExceeded: Color = Color::BrightYellow; } #[derive(Clone)] pub enum CheckErrorComponent { diff --git a/mers_lib/src/program/configs/mod.rs b/mers_lib/src/program/configs/mod.rs index 506eb57..9b9f56a 100755 --- a/mers_lib/src/program/configs/mod.rs +++ b/mers_lib/src/program/configs/mod.rs @@ -22,12 +22,6 @@ pub mod with_string; /// bundle_* for bundles (combines multiple groups or even bundles) /// with_* for usage-oriented groups /// add_* to add custom things -/// -/// For doc-comments: -/// Description -/// `bundle_std()` -/// `type` - description -/// `var: type` - description pub struct Config { globals: usize, info_parsed: super::parsed::Info, @@ -37,25 +31,32 @@ pub struct Config { impl Config { /// standard utilitis used in many programs - /// `bundle_base()` - /// `with_stdio()` - /// `with_list()` - /// `with_string()` - /// `with_command_running()` - /// `with_multithreading()` + /// + /// - `bundle_pure()` + /// - `with_stdio()` + /// - `with_command_running()` + /// - `with_multithreading()` pub fn bundle_std(self) -> Self { self.with_multithreading() .with_command_running() - .with_string() - .with_list() .with_stdio() - .bundle_base() + .bundle_pure() + } + /// standard utilities, but don't allow code to do any I/O. + /// (multithreading can be added using `.with_multithreading()`) + /// + /// - `bundle_base()` + /// - `with_list()` + /// - `with_string()` + pub fn bundle_pure(self) -> Self { + self.with_string().with_list().bundle_base() } /// base utilities used in most programs - /// `with_base()` - /// `with_math()` - /// `with_get()` - /// `with_iters()` + /// + /// - `with_base()` + /// - `with_math()` + /// - `with_get()` + /// - `with_iters()` pub fn bundle_base(self) -> Self { self.with_iters().with_get().with_math().with_base() } @@ -86,6 +87,7 @@ impl Config { } } + /// Add a variable. Its type will be that of the value stored in `val`. pub fn add_var(self, name: String, val: Data) -> Self { let t = val.get().as_type(); self.add_var_arc(name, Arc::new(RwLock::new(val)), t) diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 588b411..233860d 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -1,6 +1,6 @@ use std::{ sync::{Arc, Mutex, RwLock}, - time::Duration, + time::{Duration, Instant}, }; use crate::{ @@ -86,15 +86,20 @@ impl Config { } else { Err(format!("cannot call sleep with non-int or non-float argument.").into()) }), - run: Arc::new(|a, _i| { + run: Arc::new(|a, i| { let a = a.get(); - std::thread::sleep(if let Some(data::int::Int(n)) = a.as_any().downcast_ref() { + let mut sleep_dur = if let Some(data::int::Int(n)) = a.as_any().downcast_ref() { Duration::from_secs(*n as _) } else if let Some(data::float::Float(n)) = a.as_any().downcast_ref() { Duration::from_secs_f64(*n) } else { return Err("sleep called on non-int/non-float".into()); - }); + }; + // limit how long sleep can take + if let Some(cutoff) = i.global.limit_runtime { + sleep_dur = sleep_dur.min(cutoff.saturating_duration_since(Instant::now())); + } + std::thread::sleep(sleep_dur); Ok(Data::empty_tuple()) }), inner_statements: None, diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index c0f368b..4cf1f80 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -464,7 +464,7 @@ fn genfunc_iter_in_val_out( name: String, iter_type: impl MersType + 'static, out_type: Type, - run: impl Fn(Data, &mut crate::info::Info) -> Result + run: impl Fn(Data, &mut crate::info::Info) -> Result + Send + Sync + 'static, diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 9a8acef..72c0439 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -2,11 +2,12 @@ use std::{ collections::HashMap, fmt::Debug, sync::{Arc, Mutex, RwLock}, + time::Instant, }; use crate::{ data::{self, Data, Type}, - errors::{CheckError, SourceRange}, + errors::{error_colors, CheckError, SourceRange}, info, }; @@ -94,6 +95,16 @@ pub trait MersStatement: Debug + Send + Sync { o } fn run(&self, info: &mut Info) -> Result { + if let Some(cutoff) = info.global.limit_runtime { + if Instant::now() >= cutoff { + return Err(CheckError::new() + .msg("maximum runtime exceeded".to_owned()) + .src(vec![( + self.source_range(), + Some(error_colors::MaximumRuntimeExceeded), + )])); + } + } if self.has_scope() { info.create_scope(); } @@ -108,13 +119,18 @@ pub trait MersStatement: Debug + Send + Sync { fn as_any(&self) -> &dyn std::any::Any; } -pub type Info = info::Info; +pub type Info = info::Info; pub type CheckInfo = info::Info; #[derive(Default, Clone, Debug)] -pub struct Local { +pub struct RunLocal { pub vars: Vec>>, } +#[derive(Default, Clone, Debug)] +pub struct RunLocalGlobalInfo { + /// if set, if `Instant::now()` is equal to or after the set `Instant`, stop the program with an error. + pub limit_runtime: Option, +} #[derive(Default, Clone)] pub struct CheckLocal { pub vars: Vec, @@ -149,10 +165,10 @@ impl Debug for CheckLocal { write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys()) } } -impl info::Local for Local { +impl info::Local for RunLocal { type VariableIdentifier = usize; type VariableData = Arc>; - type Global = (); + type Global = RunLocalGlobalInfo; fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); while self.vars.len() <= id {