Skip to main content

ucglib/build/opcode/
environment.rs

1// Copyright 2019 Jeremy Wall
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use 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
34// Shared Environmental between VM's for runtime usage.
35pub 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>>, // Environment Variables
49    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        // TODO(jwall): populate this with environment variables?
55        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                // FIXME(jwall): We need to do proper error handling here.
102                let p = path.into();
103                let root = p.parent().unwrap();
104                // first we read in the file
105                let mut f = File::open(&p)?;
106                // then we parse it
107                let mut contents = String::new();
108                f.read_to_string(&mut contents)?;
109                let iter = OffsetStrIter::new(&contents).with_src_file(&p);
110                // FIXME(jwall): Unify BuildError and our other Error
111                let mut stmts = parse(iter, None).unwrap();
112                // type check the parsed AST
113                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                // then we create an ops from it
127                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                // FIXME(jwall): Unify BuildError and our other Error
145                let stmts = parse(iter, None).unwrap();
146                // then we create an ops from it
147                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            // We unwrap the error here since we expect stdlibs to
158            // always compile.
159            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}