use crate::parser::{self};
use crate::prelude::*;
use crate::vval::VVal;
use crate::vval::Syntax;
use crate::vval::Env;
use crate::vval::VValFun;
use crate::vval::StackAction;
use crate::vval::CompileError;
use crate::vval::VarPos;
use crate::str_int::*;
use crate::threads::*;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
use std::cell::RefCell;
use std::fmt::{Display, Formatter};
use fnv::FnvHashMap;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
struct CompileLocal {
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum ModuleLoadError<'a> {
NoSuchModule(String),
ModuleEvalError(EvalError),
Other(&'a str),
}
pub trait ModuleResolver {
fn resolve(&self, global: GlobalEnvRef, path: &[String], import_file_path: Option<&str>) -> Result<SymbolTable, ModuleLoadError>;
}
#[derive(Debug, Clone, Default)]
pub struct LocalFileModuleResolver { }
#[allow(dead_code)]
impl LocalFileModuleResolver {
pub fn new() -> LocalFileModuleResolver {
LocalFileModuleResolver { }
}
}
#[derive(Default, Debug, Clone)]
pub struct SymbolTable {
symbols: FnvHashMap<Symbol, VVal>,
}
impl SymbolTable {
pub fn new() -> Self {
SymbolTable {
symbols: FnvHashMap::with_capacity_and_hasher(10, Default::default()),
}
}
#[allow(dead_code)]
pub fn list(&self) -> std::vec::Vec<String> {
let mut v = vec![];
for (s, _) in self.symbols.iter() {
v.push(s.to_string());
}
v
}
#[allow(dead_code)]
pub fn set(&mut self, name: &str, value: VVal) {
self.symbols.insert(s2sym(name), value);
}
#[allow(dead_code)]
pub fn get(&mut self, name: &str) -> Option<&VVal> {
self.symbols.get(&s2sym(name))
}
pub fn fun<T>(
&mut self, fnname: &str, fun: T,
min_args: Option<usize>,
max_args: Option<usize>,
err_arg_ok: bool)
where T: 'static + Fn(&mut Env, usize) -> Result<VVal,StackAction> {
self.symbols.insert(
s2sym(fnname),
VValFun::new_fun(fun, min_args, max_args, err_arg_ok));
}
}
impl ModuleResolver for LocalFileModuleResolver {
fn resolve(&self, global: GlobalEnvRef, path: &[String], import_file_path: Option<&str>)
-> Result<SymbolTable, ModuleLoadError>
{
let genv = GlobalEnv::new_empty_default();
genv.borrow_mut().set_thread_creator(
global.borrow().get_thread_creator());
genv.borrow_mut().import_modules_from(&*global.borrow());
let mut ctx = EvalContext::new(genv);
let pth = format!("{}.wl", path.join("/"));
let mut check_paths = vec![pth];
if let Some(ifp) = import_file_path {
let impfp = std::path::Path::new(ifp);
let import_dir_path =
if impfp.is_file() {
impfp.parent()
} else {
Some(impfp)
};
if let Some(idp) = import_dir_path {
let mut pb = idp.to_path_buf();
for p in path { pb.push(p); }
if let Some(p) = pb.as_path().to_str() {
check_paths.push(format!("{}.wl", p));
}
}
}
for pth in check_paths.iter() {
if std::path::Path::new(pth).exists() {
return match ctx.eval_file(pth) {
Err(e) => Err(ModuleLoadError::ModuleEvalError(e)),
Ok(_v) => Ok(ctx.get_exports()),
}
}
}
Err(ModuleLoadError::NoSuchModule(check_paths.join(";")))
}
}
#[derive(Clone)]
pub struct GlobalEnv {
env: std::collections::HashMap<String, VVal>,
mem_modules:
std::rc::Rc<std::cell::RefCell<std::collections::HashMap<String, SymbolTable>>>,
pub resolver: Option<Rc<RefCell<dyn ModuleResolver>>>,
thread_creator: Option<Arc<Mutex<dyn ThreadCreator>>>,
}
impl std::fmt::Debug for GlobalEnv {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "<<GlobalEnv>>")
}
}
pub type GlobalEnvRef = Rc<RefCell<GlobalEnv>>;
impl GlobalEnv {
pub fn add_func<T>(&mut self, fnname: &str, fun: T, min_args: Option<usize>, max_args: Option<usize>)
where T: 'static + Fn(&mut Env, usize) -> Result<VVal,StackAction> {
self.env.insert(
String::from(fnname),
VValFun::new_fun(fun, min_args, max_args, false));
}
#[allow(dead_code)]
pub fn set_var(&mut self, var: &str, val: &VVal) {
match self.env.get(var) {
Some(v) => { v.set_ref(val.clone()); }
None => { self.env.insert(String::from(var), val.to_ref()); }
}
}
#[allow(dead_code)]
pub fn get_var(&mut self, var: &str) -> Option<VVal> {
match self.env.get(var) {
Some(v) => Some(v.deref()),
None => None,
}
}
#[allow(dead_code)]
pub fn get_var_ref(&mut self, var: &str) -> Option<VVal> {
match self.env.get(var) {
Some(v) => Some(v.clone()),
None => None,
}
}
#[allow(dead_code)]
pub fn set_module(&mut self, mod_name: &str, symtbl: SymbolTable) {
self.mem_modules.borrow_mut().insert(mod_name.to_string(), symtbl);
}
pub fn import_module_as(&mut self, mod_name: &str, prefix: &str) -> bool {
let prefix =
if !prefix.is_empty() { prefix.to_string() + ":" }
else { String::from("") };
if let Some(st) = self.mem_modules.borrow_mut().get(mod_name) {
for (k, v) in &st.symbols {
self.env.insert(prefix.clone() + &k, v.clone());
}
true
} else {
false
}
}
pub fn set_resolver(&mut self, res: Rc<RefCell<dyn ModuleResolver>>) {
self.resolver = Some(res.clone());
}
pub fn new() -> GlobalEnvRef {
Rc::new(RefCell::new(GlobalEnv {
env: std::collections::HashMap::new(),
mem_modules:
std::rc::Rc::new(std::cell::RefCell::new(
std::collections::HashMap::new())),
resolver: None,
thread_creator: None,
}))
}
pub fn new_default() -> GlobalEnvRef {
let g = Self::new_empty_default();
g.borrow_mut().import_module_as("wlambda", "");
g.borrow_mut().import_module_as("std", "std");
g
}
pub fn import_modules_from(&mut self, parent_global_env: &GlobalEnv) {
if parent_global_env.resolver.is_some() {
self.set_resolver(
parent_global_env.resolver.as_ref().unwrap().clone());
}
for (mod_name, symtbl) in parent_global_env.mem_modules.borrow().iter() {
self.set_module(mod_name, symtbl.clone());
}
}
pub fn import_from_symtbl(&mut self, prefix: &str, symtbl: SymbolTable) {
for (k, v) in symtbl.symbols {
self.env.insert(prefix.to_string() + &k, v.clone());
}
}
pub fn new_empty_default() -> GlobalEnvRef {
let g = GlobalEnv::new();
g.borrow_mut().set_module("wlambda", core_symbol_table());
g.borrow_mut().set_module("std", std_symbol_table());
g.borrow_mut().set_thread_creator(
Some(Arc::new(Mutex::new(DefaultThreadCreator::new()))));
g.borrow_mut().set_resolver(
Rc::new(RefCell::new(LocalFileModuleResolver::new())));
g.borrow_mut().set_var("\\", &VVal::None);
g
}
pub fn set_thread_creator(&mut self,
tc: Option<Arc<Mutex<dyn ThreadCreator>>>)
{
self.thread_creator = tc.clone();
}
pub fn get_thread_creator(&self)
-> Option<Arc<Mutex<dyn ThreadCreator>>>
{
self.thread_creator.clone()
}
}
#[derive(Debug)]
pub enum EvalError {
IOError(String, std::io::Error),
ParseError(String),
CompileError(CompileError),
ExecError(StackAction),
}
impl Display for EvalError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
EvalError::IOError(file, e) => write!(f, "IO error: file '{}': {} ", file, e),
EvalError::ParseError(e) => write!(f, "Parse error: {}", e),
EvalError::CompileError(e) => write!(f, "Compile error: {}", e),
EvalError::ExecError(s) => write!(f, "Execution error: Jumped out of execution: {:?}", s),
}
}
}
#[derive(Debug, Clone)]
pub struct EvalContext {
pub global: GlobalEnvRef,
local_compile: Rc<RefCell<CompileEnv>>,
pub local: Rc<RefCell<Env>>,
}
impl EvalContext {
pub fn new(global: GlobalEnvRef) -> EvalContext {
(Self::new_with_user_impl(global, Rc::new(RefCell::new(VVal::vec()))))
.register_self_eval()
}
#[allow(dead_code)]
pub fn new_empty_global_env() -> EvalContext {
Self::new_with_user_impl(
GlobalEnv::new(),
Rc::new(RefCell::new(VVal::vec())))
}
#[allow(dead_code)]
pub fn new_default() -> EvalContext {
Self::new(GlobalEnv::new_default())
}
fn register_self_eval(self) -> Self {
let ctx_clone =
Self::new_with_user_impl(
self.global.clone(),
self.local.borrow().get_user());
self.global.borrow_mut().add_func("std:eval", move |env: &mut Env, _argc: usize| {
let code = env.arg(0).s_raw();
let ctx = ctx_clone.clone();
let mut ctx = ctx.register_self_eval();
match ctx.eval(&code) {
Ok(v) => Ok(v),
Err(e) => Ok(env.new_err(format!("{}", e))),
}
}, Some(1), Some(2));
self
}
pub fn get_exports(&self) -> SymbolTable {
SymbolTable { symbols: self.local.borrow_mut().exports.clone() }
}
#[allow(dead_code)]
fn new_with_user_impl(
global: GlobalEnvRef, user: Rc<RefCell<dyn std::any::Any>>) -> EvalContext {
EvalContext {
global: global.clone(),
local_compile: Rc::new(RefCell::new(CompileEnv {
parent: None,
global: global.clone(),
block_env: BlockEnv::new(),
upvals: Vec::new(),
locals_space: 0,
recent_var: String::new(),
recent_sym: String::new(),
implicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
explicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
quote_func: false,
})),
local: Rc::new(RefCell::new(Env::new_with_user(global, user))),
}
}
#[allow(dead_code)]
pub fn new_with_user(global: GlobalEnvRef, user: Rc<RefCell<dyn std::any::Any>>) -> EvalContext {
(Self::new_with_user_impl(global, user)).register_self_eval()
}
pub fn eval_ast(&mut self, ast: &VVal) -> Result<VVal, EvalError> {
let prog = crate::vm::compile_vm_fun(ast, &mut self.local_compile);
let locals_size = self.local_compile.borrow().get_local_space();
let env = self.local.borrow_mut();
let mut res = Ok(VVal::None);
std::cell::RefMut::map(env, |l_env| {
res = match prog {
Ok(prog_closures) => {
l_env.sp = 0;
l_env.set_bp(locals_size);
match l_env.with_restore_sp(|e: &mut Env| { prog_closures(e) }) {
Ok(v) => Ok(v),
Err(je) => Err(EvalError::ExecError(je)),
}
},
Err(e) => Err(EvalError::CompileError(e)),
};
l_env
});
res
}
#[allow(dead_code)]
pub fn eval_file(&mut self, filename: &str) -> Result<VVal, EvalError> {
let contents = std::fs::read_to_string(filename);
if let Err(err) = contents {
Err(EvalError::IOError(filename.to_string(), err))
} else {
let contents = contents.unwrap();
match parser::parse(&contents, filename) {
Ok(ast) => self.eval_ast(&ast),
Err(e) => Err(EvalError::ParseError(e)),
}
}
}
#[allow(dead_code)]
pub fn eval(&mut self, s: &str) -> Result<VVal, EvalError> {
match parser::parse(s, "<wlambda::eval>") {
Ok(ast) => self.eval_ast(&ast),
Err(e) => Err(EvalError::ParseError(e)),
}
}
#[allow(dead_code)]
pub fn eval_string(&mut self, code: &str, filename: &str)
-> Result<VVal, EvalError>
{
match parser::parse(code, filename) {
Ok(ast) => self.eval_ast(&ast),
Err(e) => Err(EvalError::ParseError(e)),
}
}
#[allow(dead_code)]
pub fn call(&mut self, f: &VVal, args: &[VVal]) -> Result<VVal, StackAction> {
let mut env = self.local.borrow_mut();
f.call(&mut env, args)
}
#[allow(dead_code)]
pub fn set_global_var(&mut self, var: &str, val: &VVal) {
self.global.borrow_mut().set_var(var, val);
}
#[allow(dead_code)]
pub fn get_global_var(&mut self, var: &str) -> Option<VVal> {
self.global.borrow_mut().get_var(var)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ArityParam {
Undefined,
Limit(usize),
Infinite,
}
#[derive(Debug, Clone)]
struct BlockEnv {
local_map_stack: std::vec::Vec<(usize, Box<std::collections::HashMap<String, VarPos>>)>,
locals: std::vec::Vec<(String, CompileLocal)>,
}
#[derive(Debug,Clone,Copy,PartialEq)]
#[repr(u8)]
pub enum ResValue {
None,
OptNone,
Ret,
AccumVal,
AccumFun,
SelfObj,
SelfData,
}
#[derive(Debug,Clone,Copy,PartialEq)]
#[repr(u8)]
pub enum ResPos {
Local(u16),
LocalRef(u16),
Arg(u16),
Up(u16),
UpRef(u16),
Global(u16),
GlobalRef(u16),
Data(u16),
Stack(u16),
Value(ResValue),
}
impl BlockEnv {
fn new() -> Self {
Self {
local_map_stack: vec![(0, Box::new(std::collections::HashMap::new()))],
locals: vec![],
}
}
fn env_size(&self) -> usize {
self.locals.len()
}
fn push_env(&mut self) {
self.local_map_stack.push((0, Box::new(std::collections::HashMap::new())));
}
fn pop_env(&mut self) -> (usize, usize) {
let block_env_vars = self.local_map_stack.pop().unwrap();
let local_count = block_env_vars.0;
for _ in 0..local_count {
self.locals.pop();
}
(self.locals.len(), self.locals.len() + local_count)
}
fn set_upvalue(&mut self, var: &str, idx: usize) -> VarPos {
let last_idx = self.local_map_stack.len() - 1;
self.local_map_stack[last_idx].1
.insert(String::from(var),
VarPos::UpValue(idx));
VarPos::UpValue(idx)
}
fn next_local(&mut self) -> usize {
let next_index = self.locals.len();
self.locals.push((String::from(""), CompileLocal { }));
next_index
}
fn find_local_in_current_block(&self, var: &str) -> Option<usize> {
let last_idx = self.local_map_stack.len() - 1;
let v = self.local_map_stack[last_idx].1.get(var)?;
if let VarPos::Local(idx) = v {
Some(*idx)
} else {
None
}
}
fn def_local(&mut self, var: &str, idx: usize) {
self.locals[idx].0 = String::from(var);
let last_idx = self.local_map_stack.len() - 1;
self.local_map_stack[last_idx].1
.insert(String::from(var),
VarPos::Local(idx));
self.local_map_stack[last_idx].0 += 1;
}
fn get(&self, var: &str) -> VarPos {
for (_locals, map) in self.local_map_stack.iter().rev() {
if let Some(pos) = map.get(var) {
return pos.clone();
}
}
VarPos::NoPos
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CompileEnv {
pub global: GlobalEnvRef,
parent: Option<Rc<RefCell<CompileEnv>>>,
block_env: BlockEnv,
locals_space: usize,
upvals: std::vec::Vec<VarPos>,
pub implicit_arity: (ArityParam, ArityParam),
pub explicit_arity: (ArityParam, ArityParam),
pub recent_var: String,
pub recent_sym: String,
pub quote_func: bool,
}
type CompileEnvRef = Rc<RefCell<CompileEnv>>;
impl CompileEnv {
pub fn new(g: GlobalEnvRef) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(CompileEnv {
parent: None,
global: g,
block_env: BlockEnv::new(),
upvals: Vec::new(),
locals_space: 0,
quote_func: false,
recent_var: String::new(),
recent_sym: String::new(),
implicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
explicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
}))
}
pub fn create_env(parent: Option<CompileEnvRef>) -> Rc<RefCell<CompileEnv>> {
let global = if let Some(p) = &parent {
p.borrow_mut().global.clone()
} else {
GlobalEnv::new()
};
Rc::new(RefCell::new(CompileEnv {
parent,
global,
block_env: BlockEnv::new(),
upvals: Vec::new(),
locals_space: 0,
recent_var: String::new(),
recent_sym: String::new(),
implicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
explicit_arity: (ArityParam::Undefined, ArityParam::Undefined),
quote_func: false,
}))
}
fn def_up(&mut self, s: &str, parent_local_var: VarPos) -> VarPos {
let next_index = self.upvals.len();
self.upvals.push(parent_local_var);
self.block_env.set_upvalue(s, next_index)
}
pub fn def_const(&mut self, s: &str, val: VVal) {
self.global.borrow_mut().env.insert(String::from(s), val);
}
pub fn def_local(&mut self, s: &str, idx: usize) {
self.block_env.def_local(s, idx);
}
pub fn find_or_new_local(&mut self, var: &str) -> usize {
let idx =
if let Some(idx) = self.block_env.find_local_in_current_block(var) {
idx
} else {
self.block_env.next_local()
};
if (idx + 1) > self.locals_space {
self.locals_space = idx + 1;
}
idx
}
pub fn next_local(&mut self) -> usize {
let idx = self.block_env.next_local();
if (idx + 1) > self.locals_space {
self.locals_space = idx + 1;
}
idx
}
pub fn get_local_space(&self) -> usize { self.locals_space }
pub fn def(&mut self, s: &str, is_global: bool) -> VarPos {
if is_global {
let v = VVal::None;
let r = v.to_ref();
self.global.borrow_mut().env.insert(String::from(s), r.clone());
VarPos::Global(r)
} else {
let idx = self.find_or_new_local(s);
self.block_env.def_local(s, idx);
VarPos::Local(idx)
}
}
pub fn get_upval_pos(&self) -> std::vec::Vec<VarPos> {
let mut poses = vec![];
for p in self.upvals.iter() {
match p {
VarPos::UpValue(_) => poses.push(p.clone()),
VarPos::Local(_) => poses.push(p.clone()),
VarPos::Global(_) => {
panic!("Globals can't be captured as upvalues!");
},
VarPos::Const(_) => {
panic!("Consts can't be captured as upvalues!");
},
VarPos::NoPos => poses.push(p.clone()),
}
}
poses
}
pub fn local_env_size(&self) -> usize {
self.block_env.env_size()
}
pub fn push_block_env(&mut self) {
self.block_env.push_env();
}
pub fn pop_block_env(&mut self) -> (usize, usize) {
self.block_env.pop_env()
}
pub fn get(&mut self, s: &str) -> VarPos {
let pos = self.block_env.get(s);
match pos {
VarPos::NoPos => {
let opt_p = self.parent.as_mut();
if opt_p.is_none() {
if let Some(v) = self.global.borrow().env.get(s){
if v.is_ref() {
return VarPos::Global(v.clone());
} else {
return VarPos::Const(v.clone());
}
} else {
return VarPos::NoPos;
}
}
let parent = opt_p.unwrap().clone();
let mut par_mut = parent.borrow_mut();
let par_var_pos = par_mut.block_env.get(s);
let par_var_pos = match par_var_pos {
VarPos::NoPos => par_mut.get(s),
_ => par_var_pos,
};
match par_var_pos {
VarPos::Local(_) => self.def_up(s, par_var_pos),
VarPos::UpValue(_) => self.def_up(s, par_var_pos),
VarPos::Global(g) => VarPos::Global(g),
VarPos::Const(c) => VarPos::Const(c),
VarPos::NoPos => VarPos::NoPos
}
}
_ => pos,
}
}
}
pub fn set_impl_arity(i: usize, ce: &mut Rc<RefCell<CompileEnv>>) {
let min = ce.borrow().implicit_arity.0.clone();
match min {
ArityParam::Undefined => { ce.borrow_mut().implicit_arity.0 = ArityParam::Limit(i); },
ArityParam::Limit(j) => { if j < i { ce.borrow_mut().implicit_arity.0 = ArityParam::Limit(i); }; },
_ => (),
}
let max = ce.borrow_mut().implicit_arity.1.clone();
match max {
ArityParam::Undefined => {
ce.borrow_mut().implicit_arity.1 = ArityParam::Limit(i);
},
ArityParam::Limit(j) => {
if j < i { ce.borrow_mut().implicit_arity.1 = ArityParam::Limit(i); }
},
_ => (),
}
}
pub fn check_for_at_arity(prev_arity: (ArityParam, ArityParam), ast: &VVal, ce: &mut Rc<RefCell<CompileEnv>>, vars: &VVal) {
if ast.at(2).unwrap_or(VVal::None).at(0).unwrap_or(VVal::None).get_syn() == Syntax::Var {
if let VVal::Lst(l) = vars {
let llen = l.borrow().len();
let var = ast.at(2).unwrap().at(1).unwrap();
if var.with_s_ref(|var: &str| var == "@") {
ce.borrow_mut().implicit_arity = prev_arity;
set_impl_arity(llen, ce);
}
}
}
}
pub fn fetch_object_key_access(ast: &VVal) -> Option<(Syntax, VVal, VVal)> {
let syn = ast.v_(0).get_syn();
match syn {
Syntax::GetKey => {
Some((Syntax::GetKey, ast.v_(1), ast.v_(2)))
},
Syntax::GetKey2 => {
let mut get_obj = ast.shallow_clone();
get_obj.set_syn_at(0, Syntax::GetKey);
let key = get_obj.pop();
Some((Syntax::GetKey, get_obj, key))
},
Syntax::GetKey3 => {
let mut get_obj = ast.shallow_clone();
get_obj.set_syn_at(0, Syntax::GetKey2);
let key = get_obj.pop();
Some((Syntax::GetKey, get_obj, key))
},
Syntax::GetSym => {
Some((Syntax::GetSym, ast.v_(1), ast.v_(2)))
},
Syntax::GetSym2 => {
let mut get_obj = ast.shallow_clone();
get_obj.set_syn_at(0, Syntax::GetSym);
let key = get_obj.pop();
Some((Syntax::GetSym, get_obj, key))
},
Syntax::GetSym3 => {
let mut get_obj = ast.shallow_clone();
get_obj.set_syn_at(0, Syntax::GetSym2);
let key = get_obj.pop();
Some((Syntax::GetSym, get_obj, key))
},
_ => None,
}
}
pub fn copy_upvs(upvs: &[VarPos], e: &mut Env, upvalues: &mut std::vec::Vec<VVal>) {
for u in upvs.iter() {
match u {
VarPos::UpValue(i) => upvalues.push(e.get_up_raw(*i)),
VarPos::Local(i) => upvalues.push(e.get_local_up_promotion(*i)),
VarPos::NoPos => upvalues.push(VVal::None.to_ref()),
VarPos::Global(_) => (),
VarPos::Const(_) => (),
}
}
}
#[allow(dead_code)]
pub fn eval(s: &str) -> Result<VVal, EvalError> {
let mut ctx = EvalContext::new_default();
ctx.eval(s)
}