mirror of
https://github.com/Dummi26/mers.git
synced 2025-05-17 00:36:15 +02:00
337 lines
11 KiB
Rust
Executable File
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(¬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,
|
|
}
|
|
}
|
|
}
|