use std::{ collections::HashMap, fmt::Debug, io::{Read, Write}, sync::{atomic::AtomicBool, Arc, Mutex, RwLock}, time::Instant, }; use crate::{ data::{self, Data, Type}, errors::{CheckError, EColor, SourceRange}, info::{self, DisplayInfo}, }; #[cfg(feature = "run")] pub mod as_type; #[cfg(feature = "run")] pub mod assign_to; #[cfg(feature = "run")] pub mod block; #[cfg(feature = "run")] pub mod chain; #[cfg(feature = "run")] pub mod custom_type; #[cfg(feature = "run")] pub mod function; #[cfg(feature = "run")] pub mod r#if; #[cfg(feature = "run")] pub mod r#loop; #[cfg(feature = "run")] pub mod object; #[cfg(feature = "run")] pub mod r#try; #[cfg(feature = "run")] pub mod tuple; #[cfg(feature = "run")] pub mod value; #[cfg(feature = "run")] pub mod variable; pub trait MersStatement: Debug + Send + Sync { fn check_custom( &self, info: &mut CheckInfo, init_to: Option<&Type>, ) -> Result; fn run_custom(&self, info: &mut Info) -> Result; /// if true, local variables etc. will be contained inside their own scope. fn has_scope(&self) -> bool; fn check(&self, info: &mut CheckInfo, init_to: Option<&Type>) -> Result { info.global.depth += 1; if self.has_scope() { info.create_scope(); } let o = self.check_custom(info, init_to); if info.global.enable_hooks { // Hooks - keep in sync with run/mod.rs/compile() hooks section 'hook_save_info_at: { // `save_info_at` hook let mut save_info_at = if let Ok(lock) = info.global.save_info_at.try_lock() { lock } else { eprintln!( "[HOOKS/save_info_at] couldn't acquire lock - result may be incomplete" ); break 'hook_save_info_at; }; if !save_info_at.is_empty() { let pos_start = self.source_range().start().pos(); let pos_end = self.source_range().end().pos(); let cloned_info = Arc::new(info.clone()); for (save_to, save_at, deepest_statement) in save_info_at.iter_mut() { if info.global.depth >= *deepest_statement && pos_start <= *save_at && *save_at < pos_end { if info.global.depth > *deepest_statement { *deepest_statement = info.global.depth; save_to.clear(); } save_to.push(( self.source_range(), Arc::clone(&cloned_info), o.clone(), )); } } } } } if self.has_scope() { info.end_scope(); } info.global.depth -= 1; 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_str("maximum runtime exceeded".to_owned()) .src(vec![( self.source_range(), Some(EColor::MaximumRuntimeExceeded), )])); } } if self.has_scope() { info.create_scope(); } let o = self.run_custom(info); if self.has_scope() { info.end_scope(); } o } fn source_range(&self) -> SourceRange; fn inner_statements(&self) -> Vec<&dyn MersStatement>; fn as_any(&self) -> &dyn std::any::Any; } pub type Info = info::Info; pub type CheckInfo = info::Info; #[derive(Default, Clone, Debug)] pub struct RunLocal { pub vars: Vec>>, } #[derive(Clone)] 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, pub object_fields: Arc>>, pub object_fields_rev: Arc>>, pub stdin: Arc>>>, pub stdout: Arc, Box)>>>, pub allow_process_exit_via_exit: Arc, } #[derive(Debug)] #[allow(unused)] struct RunLocalGlobalInfoDebug<'a> { pub limit_runtime: &'a Option, pub object_fields: &'a Arc>>, pub object_fields_rev: &'a Arc>>, pub stdin: bool, pub stdout: bool, pub allow_process_exit_via_exit: bool, } impl Debug for RunLocalGlobalInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{:?}", RunLocalGlobalInfoDebug { limit_runtime: &self.limit_runtime, object_fields: &self.object_fields, object_fields_rev: &self.object_fields_rev, stdin: self.stdin.lock().unwrap().is_some(), stdout: self.stdout.lock().unwrap().is_some(), allow_process_exit_via_exit: self .allow_process_exit_via_exit .load(std::sync::atomic::Ordering::Relaxed), } ) } } impl RunLocalGlobalInfo { pub fn new(object_fields: Arc>>) -> Self { Self { limit_runtime: None, object_fields, object_fields_rev: Default::default(), stdin: Arc::new(Mutex::new(None)), stdout: Arc::new(Mutex::new(None)), allow_process_exit_via_exit: Arc::new(AtomicBool::new(true)), } } } #[derive(Default, Clone)] pub struct CheckLocal { pub vars: Vec, pub types: HashMap< String, Result< Arc, Arc Result, CheckError> + Send + Sync>, >, >, } #[derive(Clone)] pub struct CheckLocalGlobalInfo { pub depth: usize, pub enable_hooks: bool, pub show_warnings: Option>, /// ((results, byte_pos_in_src, deepest_statement)) /// you only have to set `byte_pos_in_src`. `deepest` is used internally. /// These values should be initialized to `(vec![], _, 0)`, but `0` can be replaced by a minimum statement depth, i.e. `2` to exclude the outer scope (which has depth `1`). pub save_info_at: Arc< Mutex< Vec<( Vec<(SourceRange, Arc, Result)>, usize, usize, )>, >, >, pub unused_try_statements: Arc>)>>>, pub object_fields: Arc>>, pub object_fields_rev: Arc>>, } impl CheckLocalGlobalInfo { pub fn show_warnings_to_stderr(&mut self) { self.show_warnings = Some(Arc::new(|e| { #[cfg(feature = "ecolor-term")] let theme = crate::errors::themes::TermDefaultTheme; #[cfg(not(feature = "ecolor-term"))] let theme = crate::errors::themes::NoTheme; eprintln!("{}", e.display(theme)); })); } pub fn new(object_fields: Arc>>) -> Self { Self { depth: 0, enable_hooks: false, show_warnings: None, save_info_at: Default::default(), unused_try_statements: Default::default(), object_fields, object_fields_rev: Default::default(), } } } impl Debug for CheckLocalGlobalInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CheckLocalGlobalInfo {{ depth: {}, enable_hooks: {}, show_warnings: {}, unused_try_statements: {} }}", self.depth, self.enable_hooks, self.show_warnings.is_some(), self.unused_try_statements.lock().unwrap().len()) } } impl Debug for CheckLocal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys()) } } impl info::Local for RunLocal { type VariableIdentifier = usize; type VariableData = Arc>; type Global = RunLocalGlobalInfo; fn neverused_global() -> Self::Global { Self::Global { limit_runtime: None, object_fields: Default::default(), object_fields_rev: Default::default(), stdin: Default::default(), stdout: Default::default(), allow_process_exit_via_exit: Arc::new(AtomicBool::new(false)), } } 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 { self.vars.push(Arc::clone(¬hing)); } self.vars[id] = value; } fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> { match self.vars.get(*id) { Some(v) => Some(v), None => None, } } fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> { match self.vars.get_mut(*id) { Some(v) => Some(v), None => None, } } fn duplicate(&self) -> Self { Self { vars: self .vars .iter() .map(|v| Arc::new(RwLock::new(v.read().unwrap().clone()))) .collect(), } } fn display_info<'a>(global: &'a Self::Global) -> DisplayInfo<'a> { DisplayInfo { object_fields: &global.object_fields, object_fields_rev: &global.object_fields_rev, } } } impl info::Local for CheckLocal { type VariableIdentifier = usize; type VariableData = Type; type Global = CheckLocalGlobalInfo; fn neverused_global() -> Self::Global { Self::Global { depth: 0, enable_hooks: false, show_warnings: None, save_info_at: Default::default(), unused_try_statements: Default::default(), object_fields: Default::default(), object_fields_rev: Default::default(), } } fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { while self.vars.len() <= id { self.vars.push(Type::empty()); } self.vars[id] = value; } fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData> { match self.vars.get(*id) { Some(v) => Some(v), None => None, } } fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData> { match self.vars.get_mut(*id) { Some(v) => Some(v), None => None, } } fn duplicate(&self) -> Self { self.clone() } fn display_info<'a>(global: &'a Self::Global) -> DisplayInfo<'a> { DisplayInfo { object_fields: &global.object_fields, object_fields_rev: &global.object_fields_rev, } } }