1use std::cell::RefCell;
15use std::collections::{BTreeMap, BTreeSet};
16use std::fs::File;
17use std::io::{Read, Write};
18use std::path::{Path, PathBuf};
19use std::rc::Rc;
20
21use super::pointer::OpPointer;
22use super::Error;
23use super::{cache, Primitive};
24use super::{Composite, Value};
25use crate::ast::walk::Walker;
26use crate::ast::Shape;
27use crate::build::AssertCollector;
28use crate::build::Val;
29use crate::convert::{ConverterRegistry, ImporterRegistry};
30use crate::iter::OffsetStrIter;
31use crate::parse::parse;
32use crate::{ast::Position, build::stdlib};
33
34pub struct Environment<Stdout, Stderr>
36where
37 Stdout: Write + Clone,
38 Stderr: Write + Clone,
39{
40 pub val_cache: BTreeMap<Rc<str>, Rc<Value>>,
41 pub shape_cache: Rc<RefCell<BTreeMap<PathBuf, Shape>>>,
42 pub op_cache: cache::Ops,
43 pub converter_registry: ConverterRegistry,
44 pub importer_registry: ImporterRegistry,
45 pub assert_results: AssertCollector,
46 pub stdout: Stdout,
47 pub stderr: Stderr,
48 pub env_vars: BTreeMap<Rc<str>, Rc<str>>, pub out_lock: BTreeSet<PathBuf>,
50}
51
52impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
53 pub fn new(out: Stdout, err: Stderr) -> Self {
54 Self::new_with_vars(out, err, BTreeMap::new())
56 }
57
58 pub fn new_with_vars(out: Stdout, err: Stderr, vars: BTreeMap<Rc<str>, Rc<str>>) -> Self {
59 let mut me = Self {
60 val_cache: BTreeMap::new(),
61 shape_cache: Rc::new(RefCell::new(BTreeMap::new())),
62 env_vars: vars,
63 op_cache: cache::Ops::new(),
64 assert_results: AssertCollector::new(),
65 converter_registry: ConverterRegistry::make_registry(),
66 importer_registry: ImporterRegistry::make_registry(),
67 stdout: out,
68 stderr: err,
69 out_lock: BTreeSet::new(),
70 };
71 me.populate_stdlib();
72 return me;
73 }
74
75 pub fn get_env_vars_tuple(&self) -> Value {
76 let mut fields = Vec::new();
77 let mut positions = Vec::new();
78 for (key, val) in self.env_vars.iter() {
79 fields.push((key.clone(), Rc::new(Value::P(Primitive::Str(val.clone())))));
80 positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0)));
81 }
82 Value::C(Composite::Tuple(fields, positions))
83 }
84
85 pub fn get_cached_path_val(&self, path: Rc<str>) -> Option<Rc<Value>> {
86 self.val_cache.get(&path).cloned()
87 }
88
89 pub fn update_path_val(&mut self, path: Rc<str>, val: Rc<Value>) {
90 self.val_cache.insert(path.clone(), val);
91 }
92
93 pub fn get_ops_for_path<P>(&mut self, path: P) -> Result<OpPointer, Error>
94 where
95 P: Into<PathBuf> + Clone,
96 {
97 let path_copy = path.clone();
98 let shape_cache = self.shape_cache.clone();
99 self.op_cache.entry(path.clone()).get_pointer_or_else(
100 || {
101 let p = path.into();
103 let root = p.parent().unwrap();
104 let mut f = File::open(&p)?;
106 let mut contents = String::new();
108 f.read_to_string(&mut contents)?;
109 let iter = OffsetStrIter::new(&contents).with_src_file(&p);
110 let mut stmts = parse(iter, None).unwrap();
112 let mut checker = crate::ast::typecheck::Checker::new()
114 .with_working_dir(root)
115 .with_shape_cache(shape_cache);
116 checker.walk_statement_list(stmts.iter_mut().collect());
117 if let Err(type_err) = checker.result() {
118 let pos = type_err
119 .pos
120 .unwrap_or_else(|| crate::ast::Position::new(0, 0, 0));
121 return Err(Error::new(
122 format!("Type error: {}", type_err.msg).into(),
123 pos,
124 ));
125 }
126 let ops = super::translate::AST::translate(stmts, &root);
128 Ok(ops)
129 },
130 path_copy,
131 )
132 }
133
134 fn add_ops_for_path_and_content<P>(&mut self, path: P, contents: &str) -> Result<(), Error>
135 where
136 P: Into<PathBuf> + Clone,
137 {
138 let path_copy = path.clone();
139 self.op_cache.entry(path.clone()).get_pointer_or_else(
140 || {
141 let p = path.into();
142 let root = p.parent().unwrap();
143 let iter = OffsetStrIter::new(contents).with_src_file(&p);
144 let stmts = parse(iter, None).unwrap();
146 let ops = super::translate::AST::translate(stmts, &root);
148 Ok(ops)
149 },
150 path_copy,
151 )?;
152 Ok(())
153 }
154
155 fn populate_stdlib(&mut self) {
156 for (p, s) in stdlib::get_libs().drain() {
157 self.add_ops_for_path_and_content(p, s).unwrap();
160 }
161 }
162
163 pub fn record_assert_result(&mut self, desc: &str, ok: bool) {
164 self.assert_results.record_assert_result(desc, ok);
165 }
166
167 pub fn get_out_lock_for_path<P: AsRef<Path>>(&self, path: P) -> bool {
168 self.out_lock.contains(path.as_ref())
169 }
170
171 pub fn set_out_lock_for_path<P: Into<PathBuf>>(&mut self, path: P) {
172 self.out_lock.insert(path.into());
173 }
174
175 pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) {
176 self.out_lock.remove(path.as_ref());
177 }
178
179 pub fn stdout(&self) -> Stdout {
180 self.stdout.clone()
181 }
182 pub fn stderr(&self) -> Stderr {
183 self.stderr.clone()
184 }
185
186 pub fn convert_val(&mut self, typ: &str, writer: &mut dyn Write, val: Rc<Val>) -> bool {
187 match self.converter_registry.get_converter(typ) {
188 Some(c) => {
189 if let Err(e) = c.convert(val, writer) {
190 writeln!(&mut self.stderr, "{}", e).unwrap();
191 return false;
192 }
193 }
194 None => {
195 writeln!(
196 &mut self.stderr,
197 "No such format {}\nrun `ucg converters` to see available formats.",
198 typ
199 )
200 .unwrap();
201 return false;
202 }
203 }
204 return true;
205 }
206}