use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use super::pointer::OpPointer;
use super::Error;
use super::{cache, Primitive};
use super::{Composite, Value};
use crate::ast::walk::Walker;
use crate::ast::Shape;
use crate::build::AssertCollector;
use crate::build::Val;
use crate::convert::{ConverterRegistry, ImporterRegistry};
use crate::iter::OffsetStrIter;
use crate::parse::parse;
use crate::{ast::Position, build::stdlib};
pub struct Environment<Stdout, Stderr>
where
Stdout: Write + Clone,
Stderr: Write + Clone,
{
pub val_cache: BTreeMap<Rc<str>, Rc<Value>>,
pub shape_cache: Rc<RefCell<BTreeMap<PathBuf, Shape>>>,
pub op_cache: cache::Ops,
pub converter_registry: ConverterRegistry,
pub importer_registry: ImporterRegistry,
pub assert_results: AssertCollector,
pub stdout: Stdout,
pub stderr: Stderr,
pub env_vars: BTreeMap<Rc<str>, Rc<str>>, pub out_lock: BTreeSet<PathBuf>,
}
impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
pub fn new(out: Stdout, err: Stderr) -> Self {
Self::new_with_vars(out, err, BTreeMap::new())
}
pub fn new_with_vars(out: Stdout, err: Stderr, vars: BTreeMap<Rc<str>, Rc<str>>) -> Self {
let mut me = Self {
val_cache: BTreeMap::new(),
shape_cache: Rc::new(RefCell::new(BTreeMap::new())),
env_vars: vars,
op_cache: cache::Ops::new(),
assert_results: AssertCollector::new(),
converter_registry: ConverterRegistry::make_registry(),
importer_registry: ImporterRegistry::make_registry(),
stdout: out,
stderr: err,
out_lock: BTreeSet::new(),
};
me.populate_stdlib();
return me;
}
pub fn get_env_vars_tuple(&self) -> Value {
let mut fields = Vec::new();
let mut positions = Vec::new();
for (key, val) in self.env_vars.iter() {
fields.push((key.clone(), Rc::new(Value::P(Primitive::Str(val.clone())))));
positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0)));
}
Value::C(Composite::Tuple(fields, positions))
}
pub fn get_cached_path_val(&self, path: Rc<str>) -> Option<Rc<Value>> {
self.val_cache.get(&path).cloned()
}
pub fn update_path_val(&mut self, path: Rc<str>, val: Rc<Value>) {
self.val_cache.insert(path.clone(), val);
}
pub fn get_ops_for_path<P>(&mut self, path: P) -> Result<OpPointer, Error>
where
P: Into<PathBuf> + Clone,
{
let path_copy = path.clone();
let shape_cache = self.shape_cache.clone();
self.op_cache.entry(path.clone()).get_pointer_or_else(
|| {
let p = path.into();
let root = p.parent().unwrap();
let mut f = File::open(&p)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
let iter = OffsetStrIter::new(&contents).with_src_file(&p);
let mut stmts = parse(iter, None).unwrap();
let mut checker = crate::ast::typecheck::Checker::new()
.with_working_dir(root)
.with_shape_cache(shape_cache);
checker.walk_statement_list(stmts.iter_mut().collect());
if let Err(type_err) = checker.result() {
let pos = type_err
.pos
.unwrap_or_else(|| crate::ast::Position::new(0, 0, 0));
return Err(Error::new(
format!("Type error: {}", type_err.msg).into(),
pos,
));
}
let ops = super::translate::AST::translate(stmts, &root);
Ok(ops)
},
path_copy,
)
}
fn add_ops_for_path_and_content<P>(&mut self, path: P, contents: &str) -> Result<(), Error>
where
P: Into<PathBuf> + Clone,
{
let path_copy = path.clone();
self.op_cache.entry(path.clone()).get_pointer_or_else(
|| {
let p = path.into();
let root = p.parent().unwrap();
let iter = OffsetStrIter::new(contents).with_src_file(&p);
let stmts = parse(iter, None).unwrap();
let ops = super::translate::AST::translate(stmts, &root);
Ok(ops)
},
path_copy,
)?;
Ok(())
}
fn populate_stdlib(&mut self) {
for (p, s) in stdlib::get_libs().drain() {
self.add_ops_for_path_and_content(p, s).unwrap();
}
}
pub fn record_assert_result(&mut self, desc: &str, ok: bool) {
self.assert_results.record_assert_result(desc, ok);
}
pub fn get_out_lock_for_path<P: AsRef<Path>>(&self, path: P) -> bool {
self.out_lock.contains(path.as_ref())
}
pub fn set_out_lock_for_path<P: Into<PathBuf>>(&mut self, path: P) {
self.out_lock.insert(path.into());
}
pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) {
self.out_lock.remove(path.as_ref());
}
pub fn stdout(&self) -> Stdout {
self.stdout.clone()
}
pub fn stderr(&self) -> Stderr {
self.stderr.clone()
}
pub fn convert_val(&mut self, typ: &str, writer: &mut dyn Write, val: Rc<Val>) -> bool {
match self.converter_registry.get_converter(typ) {
Some(c) => {
if let Err(e) = c.convert(val, writer) {
writeln!(&mut self.stderr, "{}", e).unwrap();
return false;
}
}
None => {
writeln!(
&mut self.stderr,
"No such format {}\nrun `ucg converters` to see available formats.",
typ
)
.unwrap();
return false;
}
}
return true;
}
}