use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use libloading::Library;
use crate::parser::types::Value;
pub type DynamicFn = Arc<
Mutex<
dyn for<'a> Fn(&'a mut Interpreter, Vec<Value>)
-> Result<Value, String>
+ Send
+ Sync
+ 'static
>
>;
pub struct DynamicFnInfo {
pub func: DynamicFn,
pub is_pure: bool,
}
impl DynamicFnInfo {
pub fn new(func: DynamicFn, is_pure: bool) -> Self {
Self { func, is_pure }
}
}
pub type PollerFn = Arc<Mutex<dyn Fn(&mut Interpreter) -> usize + Send + Sync + 'static>>;
pub struct Interpreter {
pub scopes: Vec<HashMap<String, Arc<Mutex<Value>>>>,
pub prints: Vec<String>,
pub verbose: bool,
pub allow_extend: bool,
pub dynamic_funcs: HashMap<String, DynamicFnInfo>,
pub libraries: Vec<Library>,
pub pollers: Vec<PollerFn>,
pub is_repl_mode: bool, }
impl Interpreter {
pub fn new() -> Self {
Self {
scopes: vec![HashMap::new()],
prints: Vec::new(),
verbose: false,
allow_extend: true,
dynamic_funcs: HashMap::new(),
libraries: Vec::new(),
pollers: Vec::new(),
is_repl_mode: false,
}
}
pub fn push_scope(&mut self) {
self.scopes.push(HashMap::new());
}
pub fn pop_scope(&mut self) {
if self.scopes.len() > 1 {
self.scopes.pop();
}
}
pub fn add_library_handle(&mut self, lib: Library) {
self.libraries.push(lib);
}
pub fn register_dynamic_function_ex(&mut self, name: &str, info: DynamicFnInfo) {
self.dynamic_funcs.insert(name.to_string(), info);
}
pub fn register_dynamic_function(&mut self, name: &str, func: DynamicFn) {
let info = DynamicFnInfo::new(func, false);
self.register_dynamic_function_ex(name, info);
}
pub fn get_dynamic_function(&self, name: &str) -> Option<DynamicFn> {
self.dynamic_funcs.get(name).map(|inf| inf.func.clone())
}
pub fn is_function_pure(&self, name: &str) -> bool {
self.dynamic_funcs.get(name).map(|inf| inf.is_pure).unwrap_or(false)
}
pub fn get_dynamic_functions_for_clone(&self) -> &std::collections::HashMap<String, DynamicFnInfo> {
&self.dynamic_funcs
}
pub fn set_verbose(&mut self, yes: bool) { self.verbose = yes; }
pub fn is_verbose(&self) -> bool { self.verbose }
pub fn disallow_extend(&mut self) { self.allow_extend = false; }
pub fn can_extend(&self) -> bool { self.allow_extend }
pub fn set_repl_mode(&mut self, flag: bool) {
self.is_repl_mode = flag;
}
pub fn is_repl_mode(&self) -> bool {
self.is_repl_mode
}
pub fn get_variables(&self) -> HashMap<String, Value> {
let mut map = HashMap::new();
for scope in &self.scopes {
for (k, v) in scope.iter() {
map.insert(k.clone(), v.lock().unwrap().clone());
}
}
map
}
pub fn get_variables_mut(&mut self) -> &mut HashMap<String, Arc<Mutex<Value>>> {
self.scopes.last_mut().unwrap()
}
pub fn set_variable(&mut self, name: &str, val: Value) {
self.scopes.last_mut().unwrap().insert(name.to_string(), Arc::new(Mutex::new(val)));
}
pub fn set_global_variable(&mut self, name: &str, val: Value) {
self.scopes[0].insert(name.to_string(), Arc::new(Mutex::new(val)));
}
pub fn lookup_variable(&self, name: &str) -> Option<Value> {
for scope in self.scopes.iter().rev() {
if let Some(v) = scope.get(name) {
return Some(Value::Ref(v.clone()));
}
}
None
}
pub fn lookup_variable_storage(&self, name: &str) -> Option<Arc<Mutex<Value>>> {
for scope in self.scopes.iter().rev() {
if let Some(v) = scope.get(name) {
return Some(v.clone());
}
}
None
}
pub fn get_prints(&self) -> &Vec<String> { &self.prints }
pub fn push_print(&mut self, text: &str) { self.prints.push(text.to_string()); }
pub fn print_value(&mut self, val: &Value) {
let text = self.value_to_bracketed_string(val);
println!("{}", text);
self.push_print(&text);
}
pub fn value_to_bracketed_string(&self, val: &Value) -> String {
use Value::*;
match val {
Int(n) => format!("{}", n),
IntArray(xs) => {
if xs.is_empty() {
"[]".to_string()
} else {
let joined = xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
format!("[{}]", joined)
}
}
Int2DArray(rows) => {
let mut outer = String::new();
outer.push('[');
for (i, row) in rows.iter().enumerate() {
if i > 0 { outer.push_str(", "); }
let joined = row.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
outer.push('['); outer.push_str(&joined); outer.push(']');
}
outer.push(']');
outer
}
StrArray(ss) => {
if ss.is_empty() { "[]".to_string() }
else { let joined = ss.join("\", \""); format!("[\"{}\"]", joined) }
}
KeyedArray(map) => {
if map.is_empty() { "{}".to_string() }
else {
let mut parts = Vec::new();
for (k, v) in map.iter() {
parts.push(format!("{}: {}", k, self.value_to_bracketed_string(v)));
}
format!("{{ {} }}", parts.join(", "))
}
}
Function(_) => "[Function]".to_string(),
Bool(b) => b.to_string(),
BoolArray(bb) => {
if bb.is_empty() { "[]".to_string() }
else {
let joined = bb.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
format!("[{}]", joined)
}
}
Placeholder => "_".to_string(),
SingleString(s) => s.clone(),
Long(l) => format!("{}", l),
Float(ff) => ff.to_string(),
FloatArray(ffs) => {
if ffs.is_empty() { "[]".to_string() }
else {
let joined = ffs.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(", ");
format!("[{}]", joined)
}
}
Stream(sh) => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
Float2DArray(rows) => {
let mut outer = String::new();
outer.push('[');
for (i, row) in rows.iter().enumerate() {
if i > 0 { outer.push_str(", "); }
let joined = row.iter().map(|f| f.to_string()).collect::<Vec<_>>().join(", ");
outer.push('['); outer.push_str(&joined); outer.push(']');
}
outer.push(']');
outer
}
InkIterator(_) => "[InkIterator]".to_string(),
InkTransform(_) => "[InkTransform]".to_string(),
Tensor(_) => "[Tensor]".to_string(),
MixedArray(items) => {
if items.is_empty() { "[]".to_string() }
else {
let joined = items.iter().map(|x| self.value_to_bracketed_string(x)).collect::<Vec<_>>().join(", ");
format!("[{}]", joined)
}
}
Ref(arc_mutex) => self.value_to_bracketed_string(&arc_mutex.lock().unwrap()),
}
}
pub fn poll_all(&mut self) -> usize {
let pollers_clone = self.pollers.clone();
let mut total = 0;
for p in pollers_clone {
total += p.lock().unwrap()(self);
}
total
}
pub fn remove_all_pollers(&mut self) { self.pollers.clear(); }
pub fn add_poller(&mut self, poller: PollerFn) { self.pollers.push(poller); }
}