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::collections::{BTreeMap, BTreeSet};
15use std::fs::File;
16use std::io::{Read, Write};
17use std::path::{Path, PathBuf};
18use std::rc::Rc;
19
20use super::pointer::OpPointer;
21use super::Error;
22use super::{cache, Primitive};
23use super::{Composite, Value};
24use crate::build::AssertCollector;
25use crate::build::Val;
26use crate::convert::{ConverterRegistry, ImporterRegistry};
27use crate::iter::OffsetStrIter;
28use crate::parse::parse;
29use crate::{ast::Position, build::stdlib};
30
31// Shared Environmental between VM's for runtime usage.
32pub struct Environment<Stdout, Stderr>
33where
34    Stdout: Write + Clone,
35    Stderr: Write + Clone,
36{
37    pub val_cache: BTreeMap<String, Rc<Value>>,
38    pub op_cache: cache::Ops,
39    pub converter_registry: ConverterRegistry,
40    pub importer_registry: ImporterRegistry,
41    pub assert_results: AssertCollector,
42    pub stdout: Stdout,
43    pub stderr: Stderr,
44    pub env_vars: BTreeMap<String, String>, // Environment Variables
45    pub out_lock: BTreeSet<PathBuf>,
46}
47
48impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
49    pub fn new(out: Stdout, err: Stderr) -> Self {
50        // TODO(jwall): populate this with environment variables?
51        Self::new_with_vars(out, err, BTreeMap::new())
52    }
53
54    pub fn new_with_vars(out: Stdout, err: Stderr, vars: BTreeMap<String, String>) -> Self {
55        let mut me = Self {
56            val_cache: BTreeMap::new(),
57            env_vars: vars,
58            op_cache: cache::Ops::new(),
59            assert_results: AssertCollector::new(),
60            converter_registry: ConverterRegistry::make_registry(),
61            importer_registry: ImporterRegistry::make_registry(),
62            stdout: out,
63            stderr: err,
64            out_lock: BTreeSet::new(),
65        };
66        me.populate_stdlib();
67        return me;
68    }
69
70    pub fn get_env_vars_tuple(&self) -> Value {
71        let mut fields = Vec::new();
72        let mut positions = Vec::new();
73        for (key, val) in self.env_vars.iter() {
74            fields.push((key.clone(), Rc::new(Value::P(Primitive::Str(val.clone())))));
75            positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0)));
76        }
77        Value::C(Composite::Tuple(fields, positions))
78    }
79
80    pub fn get_cached_path_val(&self, path: &String) -> Option<Rc<Value>> {
81        self.val_cache.get(path).cloned()
82    }
83
84    pub fn update_path_val(&mut self, path: &String, val: Rc<Value>) {
85        self.val_cache.insert(path.clone(), val);
86    }
87
88    pub fn get_ops_for_path<P>(&mut self, path: P) -> Result<OpPointer, Error>
89    where
90        P: Into<PathBuf> + Clone,
91    {
92        let path_copy = path.clone();
93        self.op_cache.entry(path.clone()).get_pointer_or_else(
94            || {
95                // FIXME(jwall): We need to do proper error handling here.
96                let p = path.into();
97                let root = p.parent().unwrap();
98                // first we read in the file
99                let mut f = File::open(&p)?;
100                // then we parse it
101                let mut contents = String::new();
102                f.read_to_string(&mut contents)?;
103                let iter = OffsetStrIter::new(&contents).with_src_file(&p);
104                // FIXME(jwall): Unify BuildError and our other Error
105                let stmts = parse(iter, None).unwrap();
106                // then we create an ops from it
107                let ops = super::translate::AST::translate(stmts, &root);
108                Ok(ops)
109            },
110            path_copy,
111        )
112    }
113
114    fn add_ops_for_path_and_content<P>(&mut self, path: P, contents: &str) -> Result<(), Error>
115    where
116        P: Into<PathBuf> + Clone,
117    {
118        let path_copy = path.clone();
119        self.op_cache.entry(path.clone()).get_pointer_or_else(
120            || {
121                let p = path.into();
122                let root = p.parent().unwrap();
123                let iter = OffsetStrIter::new(contents).with_src_file(&p);
124                // FIXME(jwall): Unify BuildError and our other Error
125                let stmts = parse(iter, None).unwrap();
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        Ok(())
133    }
134
135    fn populate_stdlib(&mut self) {
136        for (p, s) in stdlib::get_libs().drain() {
137            // We unwrap the error here since we expect stdlibs to
138            // always compile.
139            self.add_ops_for_path_and_content(p, s).unwrap();
140        }
141    }
142
143    pub fn record_assert_result(&mut self, desc: &str, ok: bool) {
144        self.assert_results.record_assert_result(desc, ok);
145    }
146
147    pub fn get_out_lock_for_path<P: AsRef<Path>>(&self, path: P) -> bool {
148        self.out_lock.contains(path.as_ref())
149    }
150
151    pub fn set_out_lock_for_path<P: Into<PathBuf>>(&mut self, path: P) {
152        self.out_lock.insert(path.into());
153    }
154
155    pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) {
156        self.out_lock.remove(path.as_ref());
157    }
158
159    pub fn stdout(&self) -> Stdout {
160        self.stdout.clone()
161    }
162    pub fn stderr(&self) -> Stderr {
163        self.stderr.clone()
164    }
165
166    pub fn convert_val(&mut self, typ: &str, writer: &mut dyn Write, val: Rc<Val>) -> bool {
167        match self.converter_registry.get_converter(typ) {
168            Some(c) => {
169                if let Err(e) = c.convert(val, writer) {
170                    writeln!(&mut self.stderr, "{}", e).unwrap();
171                    return false;
172                }
173            }
174            None => {
175                writeln!(
176                    &mut self.stderr,
177                    "No such format {}\nrun `ucg converters` to see available formats.",
178                    typ
179                )
180                .unwrap();
181                return false;
182            }
183        }
184        return true;
185    }
186}