1
0
mirror of https://github.com/Dummi26/mers.git synced 2025-05-17 00:36:15 +02:00
mers/mers_lib/src/program/run/mod.rs

337 lines
11 KiB
Rust
Executable File

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<Type, CheckError>;
fn run_custom(&self, info: &mut Info) -> Result<Data, CheckError>;
/// 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<Type, CheckError> {
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<Data, CheckError> {
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<RunLocal>;
pub type CheckInfo = info::Info<CheckLocal>;
#[derive(Default, Clone, Debug)]
pub struct RunLocal {
pub vars: Vec<Arc<RwLock<Data>>>,
}
#[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<Instant>,
pub object_fields: Arc<Mutex<HashMap<String, usize>>>,
pub object_fields_rev: Arc<Mutex<Vec<String>>>,
pub stdin: Arc<Mutex<Option<Box<dyn Read + Send + Sync>>>>,
pub stdout: Arc<Mutex<Option<(Box<dyn Write + Send + Sync>, Box<dyn Write + Send + Sync>)>>>,
pub allow_process_exit_via_exit: Arc<AtomicBool>,
}
#[derive(Debug)]
#[allow(unused)]
struct RunLocalGlobalInfoDebug<'a> {
pub limit_runtime: &'a Option<Instant>,
pub object_fields: &'a Arc<Mutex<HashMap<String, usize>>>,
pub object_fields_rev: &'a Arc<Mutex<Vec<String>>>,
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<Mutex<HashMap<String, usize>>>) -> 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<Type>,
pub types: HashMap<
String,
Result<
Arc<Type>,
Arc<dyn Fn(&str, &CheckInfo) -> Result<Arc<Type>, CheckError> + Send + Sync>,
>,
>,
}
#[derive(Clone)]
pub struct CheckLocalGlobalInfo {
pub depth: usize,
pub enable_hooks: bool,
pub show_warnings: Option<Arc<dyn Fn(CheckError) + Send + Sync>>,
/// ((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<CheckInfo>, Result<Type, CheckError>)>,
usize,
usize,
)>,
>,
>,
pub unused_try_statements: Arc<Mutex<Vec<(SourceRange, Vec<Option<SourceRange>>)>>>,
pub object_fields: Arc<Mutex<HashMap<String, usize>>>,
pub object_fields_rev: Arc<Mutex<Vec<String>>>,
}
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<Mutex<HashMap<String, usize>>>) -> 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<RwLock<Data>>;
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(&nothing));
}
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,
}
}
}