DEV: Removing gratuitous use of Rc.

It was unnecessary once I better understood the ownership
model and it had a significant performance cost. Removing
it brought the runtime for the integration tests down from
an average of 6 seconds to less than a second on my laptop.
This commit is contained in:
Jeremy Wall 2020-01-04 09:10:25 -06:00
parent 9f787972ac
commit c1414bdde4
8 changed files with 160 additions and 114 deletions

View File

@ -12,16 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cell::RefCell;
use regex::Regex;
//TODO(jwall): use super::assets::MemoryCache;
use super::FileBuilder;
use crate::build::opcode::Environment;
fn assert_build(input: &str) {
let i_paths = Vec::new();
let out_buffer: Vec<u8> = Vec::new();
let err_buffer: Vec<u8> = Vec::new();
let mut b = FileBuilder::new("<Eval>", &i_paths, out_buffer, err_buffer);
let env = RefCell::new(Environment::new(out_buffer, err_buffer));
let mut b = FileBuilder::new("<Eval>", &i_paths, &env);
b.enable_validate_mode();
b.eval_string(input).unwrap();
let env = b.environment.borrow();
@ -34,7 +38,8 @@ fn assert_build_failure(input: &str, expect: Vec<Regex>) {
let i_paths = Vec::new();
let out_buffer: Vec<u8> = Vec::new();
let err_buffer: Vec<u8> = Vec::new();
let mut b = FileBuilder::new("<Eval>", &i_paths, out_buffer, err_buffer);
let env = RefCell::new(Environment::new(out_buffer, err_buffer));
let mut b = FileBuilder::new("<Eval>", &i_paths, &env);
b.enable_validate_mode();
let err = b.eval_string(input);
match err {

View File

@ -16,7 +16,6 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryInto;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Read;
@ -91,7 +90,7 @@ where
Stdout: std::io::Write + Clone,
Stderr: std::io::Write + Clone,
{
pub environment: Rc<RefCell<Environment<Stdout, Stderr>>>,
pub environment: &'a RefCell<Environment<Stdout, Stderr>>,
working_dir: PathBuf,
strict: bool,
// FIXME(jwall): These need to be compiled and added to the op cache.
@ -112,13 +111,10 @@ where
pub fn new<P: Into<PathBuf>>(
working_dir: P,
import_paths: &'a Vec<PathBuf>,
stdout: Stdout,
stderr: Stderr,
environment: &'a RefCell<Environment<Stdout, Stderr>>,
) -> Self {
let mut environment = Environment::new_with_vars(stdout, stderr, env::vars().collect());
environment.populate_stdlib();
FileBuilder {
environment: Rc::new(RefCell::new(environment)),
environment: environment,
strict: false,
// Our import stack is initialized with ourself.
working_dir: working_dir.into(),
@ -201,7 +197,6 @@ where
let mut vm = VM::new(
self.strict,
Rc::new(ops),
self.environment.clone(),
&self.working_dir,
);
if path.is_some() {
@ -210,7 +205,7 @@ where
if self.validate_mode {
vm.enable_validate_mode();
}
vm.run()?;
vm.run(self.environment)?;
self.out = Some(Rc::new(vm.symbols_to_tuple(false).into()));
Ok(())
}
@ -227,7 +222,6 @@ where
let mut vm = VM::new(
self.strict,
Rc::new(PositionMap::new()),
self.environment.clone(),
&self.working_dir,
);
loop {
@ -286,7 +280,7 @@ where
let stmts = parse(OffsetStrIter::new(&stmt), None)?;
let ops = translate::AST::translate(stmts, &self.working_dir);
vm = vm.to_new_pointer(OpPointer::new(Rc::new(ops)));
match vm.run() {
match vm.run(self.environment) {
// print the result
Err(e) => eprintln!("{}", e),
Ok(_) => {
@ -346,13 +340,12 @@ where
let mut vm = VM::new(
self.strict,
Rc::new(ops_map),
self.environment.clone(),
&self.working_dir,
);
if self.validate_mode {
vm.enable_validate_mode();
}
vm.run()?;
vm.run(self.environment)?;
if let Some((val, _)) = vm.last.clone() {
return Ok(Rc::new(val.try_into()?));
}

View File

@ -51,7 +51,7 @@ impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
}
pub fn new_with_vars(out: Stdout, err: Stderr, vars: BTreeMap<String, String>) -> Self {
Self {
let mut me = Self {
val_cache: BTreeMap::new(),
env_vars: vars,
op_cache: cache::Ops::new(),
@ -61,7 +61,9 @@ impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
stdout: out,
stderr: err,
out_lock: BTreeSet::new(),
}
};
me.populate_stdlib();
return me
}
pub fn get_cached_path_val(&self, path: &String) -> Option<Rc<Value>> {
@ -119,7 +121,7 @@ impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
Ok(())
}
pub fn populate_stdlib(&mut self) {
fn populate_stdlib(&mut self) {
for (p, s) in stdlib::get_libs().drain() {
// We unwrap the error here since we expect stdlibs to
// always compile.

View File

@ -57,12 +57,12 @@ impl Builtins {
self.validate_mode = true;
}
pub fn handle<P, WP, O, E>(
pub fn handle<'a, P, WP, O, E>(
&mut self,
path: Option<P>,
h: Hook,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &mut Vec<String>,
working_dir: WP,
pos: Position,
@ -178,11 +178,11 @@ impl Builtins {
Ok(contents)
}
fn import<P, O, E>(
fn import<'a, P, O, E>(
&mut self,
base_path: P,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &mut Vec<String>,
pos: Position,
) -> Result<(), Error>
@ -220,11 +220,10 @@ impl Builtins {
let mut vm = VM::with_pointer(
self.strict,
op_pointer,
env.clone(),
normalized.parent().unwrap(),
)
.with_import_stack(import_stack.clone());
vm.run()?;
vm.run(env)?;
let result = Rc::new(vm.symbols_to_tuple(true));
env.borrow_mut().update_path_val(&path, result.clone());
stack.push((result, pos));
@ -238,11 +237,11 @@ impl Builtins {
unreachable!();
}
fn include<P, O, E>(
fn include<'a, P, O, E>(
&self,
base_path: P,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
pos: Position,
) -> Result<(), Error>
where
@ -307,10 +306,10 @@ impl Builtins {
Ok(())
}
fn assert<O, E>(
fn assert<'a, O, E>(
&mut self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
) -> Result<(), Error>
where
O: std::io::Write + Clone,
@ -365,11 +364,11 @@ impl Builtins {
return Ok(());
}
fn out<P, O, E>(
fn out<'a, P, O, E>(
&self,
path: Option<P>,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
pos: Position,
) -> Result<(), Error>
where
@ -432,10 +431,10 @@ impl Builtins {
unreachable!();
}
fn convert<O, E>(
fn convert<'a, O, E>(
&self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
pos: Position,
) -> Result<(), Error>
where
@ -477,10 +476,10 @@ impl Builtins {
unreachable!()
}
fn map<O, E>(
fn map<'a, O, E>(
&self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &Vec<String>,
pos: Position,
) -> Result<(), Error>
@ -589,10 +588,10 @@ impl Builtins {
Ok(())
}
fn filter<O, E>(
fn filter<'a, O, E>(
&self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &Vec<String>,
pos: Position,
) -> Result<(), Error>
@ -735,10 +734,10 @@ impl Builtins {
Ok(())
}
fn reduce<O, E>(
fn reduce<'a, O, E>(
&self,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &Vec<String>,
pos: Position,
) -> Result<(), Error>
@ -875,11 +874,11 @@ impl Builtins {
Ok(())
}
fn trace<O, E>(
fn trace<'a, O, E>(
&mut self,
stack: &mut Vec<(Rc<Value>, Position)>,
pos: Position,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
) -> Result<(), Error>
where
O: std::io::Write + Clone,

View File

@ -42,41 +42,30 @@ fn construct_reserved_word_set() -> BTreeSet<&'static str> {
words
}
pub struct VM<O, E>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
pub struct VM {
working_dir: PathBuf,
stack: Vec<(Rc<Value>, Position)>,
symbols: Stack,
import_stack: Vec<String>,
runtime: runtime::Builtins,
ops: OpPointer,
pub env: Rc<RefCell<Environment<O, E>>>,
pub last: Option<(Rc<Value>, Position)>,
self_stack: Vec<(Rc<Value>, Position)>,
reserved_words: BTreeSet<&'static str>,
}
impl<'a, O, E> VM<O, E>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
impl VM {
pub fn new<P: Into<PathBuf>>(
strict: bool,
ops: Rc<PositionMap>,
env: Rc<RefCell<Environment<O, E>>>,
working_dir: P,
) -> Self {
Self::with_pointer(strict, OpPointer::new(ops), env, working_dir)
Self::with_pointer(strict, OpPointer::new(ops), working_dir)
}
pub fn with_pointer<P: Into<PathBuf>>(
strict: bool,
ops: OpPointer,
env: Rc<RefCell<Environment<O, E>>>,
working_dir: P,
) -> Self {
Self {
@ -86,7 +75,6 @@ where
import_stack: Vec::new(),
runtime: runtime::Builtins::new(strict),
ops: ops,
env: env,
last: None,
self_stack: Vec::new(),
reserved_words: construct_reserved_word_set(),
@ -119,7 +107,6 @@ where
import_stack: Vec::new(),
runtime: self.runtime.clone(),
ops: self.ops.clone(),
env: self.env.clone(),
last: None,
self_stack: self.self_stack.clone(),
reserved_words: self.reserved_words.clone(),
@ -148,7 +135,11 @@ where
self.symbols.remove_symbol(sym)
}
pub fn run(&mut self) -> Result<(), Error> {
pub fn run<'a, O, E>(&mut self, env: &'a RefCell<Environment<O, E>>) -> Result<(), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
loop {
let op = if let Some(op) = self.ops.next() {
op.clone()
@ -184,7 +175,7 @@ where
Op::Index => self.op_index(!self.runtime.strict, pos)?,
Op::SafeIndex => self.op_index(true, pos)?,
Op::Exist => self.op_exist(pos)?,
Op::Cp => self.op_copy(pos)?,
Op::Cp => self.op_copy(pos, env)?,
//FIXME(jwall): Should this take a user provided message?
Op::Bang => self.op_bang()?,
Op::InitThunk(jp) => self.op_thunk(idx, jp, pos)?,
@ -199,8 +190,8 @@ where
Op::Or(jp) => self.op_or(jp, pos)?,
Op::Module(mptr) => self.op_module(idx, mptr, pos)?,
Op::Func(jptr) => self.op_func(idx, jptr, pos)?,
Op::FCall => self.op_fcall(pos)?,
Op::NewScope(jp) => self.op_new_scope(jp, self.ops.clone())?,
Op::FCall => self.op_fcall(pos, env)?,
Op::NewScope(jp) => self.op_new_scope(jp, self.ops.clone(), env)?,
Op::Return => {
&self.stack;
return Ok(());
@ -209,7 +200,7 @@ where
self.pop()?;
}
Op::Typ => self.op_typ()?,
Op::Runtime(h) => self.op_runtime(h, pos)?,
Op::Runtime(h) => self.op_runtime(h, pos, env)?,
Op::Render => self.op_render()?,
Op::PushSelf => self.op_push_self()?,
Op::PopSelf => self.op_pop_self()?,
@ -345,7 +336,7 @@ where
Ok(())
}
fn op_select_jump(&'a mut self, jp: i32) -> Result<(), Error> {
fn op_select_jump(&mut self, jp: i32) -> Result<(), Error> {
// pop field value off
let (field_name, _) = self.pop()?;
// pop search value off
@ -372,7 +363,7 @@ where
Ok(())
}
fn op_module(&'a mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> {
fn op_module(&mut self, idx: usize, jptr: i32, pos: Position) -> Result<(), Error> {
let (mod_val, mod_val_pos) = self.pop()?;
let (result_ptr, flds, pos_list) = match mod_val.as_ref() {
&C(Tuple(ref flds, ref pos_list)) => (None, flds.clone(), pos_list.clone()),
@ -467,20 +458,24 @@ where
self.op_jump(jptr)
}
pub fn fcall_impl(
pub fn fcall_impl<'a, O, E>(
f: &Func,
strict: bool,
stack: &mut Vec<(Rc<Value>, Position)>,
env: Rc<RefCell<Environment<O, E>>>,
env: &'a RefCell<Environment<O, E>>,
import_stack: &Vec<String>,
) -> Result<(Rc<Value>, Position), Error> {
) -> Result<(Rc<Value>, Position), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
let Func {
ref ptr,
ref bindings,
ref snapshot,
} = f;
// use the captured scope snapshot for the function.
let mut vm = Self::with_pointer(strict, ptr.clone(), env, std::env::current_dir()?)
let mut vm = Self::with_pointer(strict, ptr.clone(), std::env::current_dir()?)
.to_scoped(snapshot.clone())
.with_import_stack(import_stack.clone());
for nm in bindings.iter() {
@ -491,25 +486,33 @@ where
vm.binding_push(nm.clone(), val, false, &pos, &pos)?;
}
// proceed to the function body
vm.run()?;
vm.run(env)?;
return vm.pop();
}
fn op_new_scope(&mut self, jp: i32, ptr: OpPointer) -> Result<(), Error> {
fn op_new_scope<O, E>(&mut self, jp: i32, ptr: OpPointer, env: &RefCell<Environment<O, E>>) -> Result<(), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
let scope_snapshot = self.symbols.snapshot();
let mut vm = self
.clean_copy()
.to_new_pointer(ptr)
.to_scoped(scope_snapshot)
.with_import_stack(self.import_stack.clone());
vm.run()?;
vm.run(env)?;
let result = vm.pop()?;
self.push(result.0, result.1)?;
self.op_jump(jp)?;
Ok(())
}
fn op_fcall(&mut self, pos: Position) -> Result<(), Error> {
fn op_fcall<O, E>(&mut self, pos: Position, env: &RefCell<Environment<O, E>>) -> Result<(), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
let (f, f_pos) = self.pop()?;
let (arg_length, _) = self.pop()?;
if let &F(ref f) = f.as_ref() {
@ -535,7 +538,7 @@ where
}
}
let (val, _) = decorate_call!(f_pos =>
Self::fcall_impl(f, self.runtime.strict, &mut self.stack, self.env.clone(), &self.import_stack))?;
Self::fcall_impl(f, self.runtime.strict, &mut self.stack, env.clone(), &self.import_stack))?;
self.push(val, pos.clone())?;
} else {
return Err(Error::new(format!("Not a function! {:?}", f,), pos));
@ -888,7 +891,11 @@ where
Ok(())
}
fn op_copy(&mut self, pos: Position) -> Result<(), Error> {
fn op_copy<O, E>(&mut self, pos: Position, env: &RefCell<Environment<O, E>>) -> Result<(), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
// This value should always be a tuple
let (override_val, val_pos) = self.pop()?;
// get target value. It should be a Module or Tuple.
@ -965,7 +972,7 @@ where
.clean_copy()
.to_new_pointer(ptr.clone())
.with_import_stack(self.import_stack.clone());
pkg_vm.run()?;
pkg_vm.run(env)?;
let (pkg_func, val_pos) = pkg_vm.pop()?;
self.merge_field_into_tuple(
&mut flds,
@ -983,10 +990,10 @@ where
.with_import_stack(self.import_stack.clone());
vm.push(Rc::new(S("mod".to_owned())), pos.clone())?;
vm.push(Rc::new(C(Tuple(flds, flds_pos_list))), pos.clone())?;
decorate_call!(pos => vm.run())?;
decorate_call!(pos => vm.run(env))?;
if let Some(ptr) = result_ptr {
vm.ops.jump(ptr.clone())?;
vm.run()?;
vm.run(env)?;
let (result_val, result_pos) = vm.pop()?;
self.push(result_val, result_pos)?;
} else {
@ -1005,8 +1012,8 @@ where
fn merge_field_into_tuple(
&self,
src_fields: &'a mut Vec<(String, Rc<Value>)>,
pos_fields: &'a mut Vec<(Position, Position)>,
src_fields: &mut Vec<(String, Rc<Value>)>,
pos_fields: &mut Vec<(Position, Position)>,
name: String,
name_pos: &Position,
value: Rc<Value>,
@ -1069,7 +1076,7 @@ where
}
pub fn get_binding(
&'a self,
&self,
name: &str,
pos: &Position,
) -> Result<(Rc<Value>, Position), Error> {
@ -1189,12 +1196,16 @@ where
})
}
fn op_runtime(&mut self, h: Hook, pos: Position) -> Result<(), Error> {
fn op_runtime<O, E>(&mut self, h: Hook, pos: Position, env: &RefCell<Environment<O, E>>) -> Result<(), Error>
where
O: std::io::Write + Clone,
E: std::io::Write + Clone,
{
self.runtime.handle(
self.ops.path.as_ref(),
h,
&mut self.stack,
self.env.clone(),
env,
&mut self.import_stack,
&self.working_dir,
pos,

View File

@ -11,11 +11,13 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{FileBuilder, Val};
use crate::ast::*;
use std;
use std::rc::Rc;
use std::cell::RefCell;
use super::{FileBuilder, Val};
use crate::ast::*;
use crate::build::Environment;
fn test_expr_to_val<'a, O, E>(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder<'a, O, E>)
where
@ -33,7 +35,8 @@ fn test_eval_div_expr_fail() {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
test_expr_to_val(
vec![(
Expression::Binary(BinaryOpDef {
@ -60,7 +63,8 @@ fn test_eval_mul_expr_fail() {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
test_expr_to_val(
vec![(
Expression::Binary(BinaryOpDef {
@ -87,7 +91,8 @@ fn test_eval_subtract_expr_fail() {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
test_expr_to_val(
vec![(
Expression::Binary(BinaryOpDef {
@ -113,7 +118,8 @@ fn test_eval_add_expr_fail() {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
test_expr_to_val(
vec![(
Expression::Binary(BinaryOpDef {
@ -141,7 +147,8 @@ fn test_expr_copy_no_such_tuple() {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
test_expr_to_val(
vec![(
Expression::Copy(CopyDef {

View File

@ -193,9 +193,12 @@ impl Converter for ExecConverter {
#[cfg(test)]
mod exec_test {
use std::cell::RefCell;
use super::*;
use crate::build::FileBuilder;
use crate::convert::traits::Converter;
use crate::build::opcode::Environment;
use std;
use std::io::Cursor;
@ -205,7 +208,8 @@ mod exec_test {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
let conv = ExecConverter::new();
b.eval_string(
"let script = {
@ -228,7 +232,8 @@ mod exec_test {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
let conv = ExecConverter::new();
b.eval_string(
"let script = {
@ -258,7 +263,8 @@ mod exec_test {
let i_paths = Vec::new();
let out: Vec<u8> = Vec::new();
let err: Vec<u8> = Vec::new();
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, out, err);
let env = RefCell::new(Environment::new(out, err));
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, &env);
let conv = ExecConverter::new();
b.eval_string(
"let script = {

View File

@ -17,6 +17,7 @@ extern crate dirs;
extern crate rustyline;
extern crate ucglib;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::error::Error;
use std::fs::File;
@ -26,6 +27,7 @@ use std::path::{Path, PathBuf};
use std::process;
use ucglib::build;
use ucglib::build::opcode::Environment;
use ucglib::convert::{ConverterRegistry, ImporterRegistry};
use ucglib::iter::OffsetStrIter;
use ucglib::parse::parse;
@ -124,14 +126,13 @@ fn build_file<'a>(
validate: bool,
strict: bool,
import_paths: &'a Vec<PathBuf>,
env: &'a RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) -> Result<build::FileBuilder<'a, StdoutWrapper, StderrWrapper>, Box<dyn Error>> {
let mut file_path_buf = PathBuf::from(file);
if file_path_buf.is_relative() {
file_path_buf = std::env::current_dir()?.join(file_path_buf);
}
let out = StdoutWrapper::new();
let err = StderrWrapper::new();
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, out, err);
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, env);
builder.set_strict(strict);
if validate {
builder.enable_validate_mode();
@ -143,9 +144,14 @@ fn build_file<'a>(
Ok(builder)
}
fn do_validate(file: &str, strict: bool, import_paths: &Vec<PathBuf>) -> bool {
fn do_validate<'a>(
file: &'a str,
strict: bool,
import_paths: &'a Vec<PathBuf>,
env: &'a RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) -> bool {
println!("Validating {}", file);
match build_file(file, true, strict, import_paths) {
match build_file(file, true, strict, import_paths, env) {
Ok(b) => {
if b.assert_results() {
println!("File {} Pass\n", file);
@ -162,9 +168,14 @@ fn do_validate(file: &str, strict: bool, import_paths: &Vec<PathBuf>) -> bool {
return true;
}
fn do_compile(file: &str, strict: bool, import_paths: &Vec<PathBuf>) -> bool {
fn do_compile<'a>(
file: &'a str,
strict: bool,
import_paths: &'a Vec<PathBuf>,
env: &'a RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) -> bool {
println!("Building {}", file);
let builder = match build_file(file, false, strict, import_paths) {
let builder = match build_file(file, false, strict, import_paths, env) {
Ok(builder) => builder,
Err(err) => {
eprintln!("{}", err);
@ -183,6 +194,7 @@ fn visit_ucg_files(
validate: bool,
strict: bool,
import_paths: &Vec<PathBuf>,
env: &RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) -> Result<bool, Box<dyn Error>> {
let our_path = String::from(path.to_string_lossy());
let mut result = true;
@ -200,35 +212,35 @@ fn visit_ucg_files(
let next_path = next_item.path();
let path_as_string = String::from(next_path.to_string_lossy());
if next_path.is_dir() && recurse {
if let Err(e) = visit_ucg_files(&next_path, recurse, validate, strict, import_paths)
if let Err(e) = visit_ucg_files(&next_path, recurse, validate, strict, import_paths, env)
{
eprintln!("{}", e);
result = false;
}
} else {
if validate && path_as_string.ends_with("_test.ucg") {
if !do_validate(&path_as_string, strict, import_paths) {
if !do_validate(&path_as_string, strict, import_paths, env) {
result = false;
summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
} else {
summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
}
} else if !validate && path_as_string.ends_with(".ucg") {
if !do_compile(&path_as_string, strict, import_paths) {
if !do_compile(&path_as_string, strict, import_paths, env) {
result = false;
}
}
}
}
} else if validate && our_path.ends_with("_test.ucg") {
if !do_validate(&our_path, strict, import_paths) {
if !do_validate(&our_path, strict, import_paths, env) {
result = false;
summary.push_str(format!("{} - FAIL\n", our_path).as_str());
} else {
summary.push_str(format!("{} - PASS\n", &our_path).as_str());
}
} else if !validate {
if !do_compile(&our_path, strict, import_paths) {
if !do_compile(&our_path, strict, import_paths, env) {
result = false;
}
}
@ -239,13 +251,18 @@ fn visit_ucg_files(
Ok(result)
}
fn build_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict: bool) {
fn build_command(
matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
strict: bool,
env: &RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) {
let files = matches.values_of("INPUT");
let recurse = matches.is_present("recurse");
let mut ok = true;
if files.is_none() {
let curr_dir = std::env::current_dir().unwrap();
let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, strict, import_paths);
let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, strict, import_paths, env);
if let Ok(false) = ok {
process::exit(1)
}
@ -253,7 +270,7 @@ fn build_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict
}
for file in files.unwrap() {
let pb = PathBuf::from(file);
if let Ok(false) = visit_ucg_files(&pb, recurse, false, strict, import_paths) {
if let Ok(false) = visit_ucg_files(&pb, recurse, false, strict, import_paths, env) {
ok = false;
}
}
@ -325,12 +342,17 @@ fn fmt_command(matches: &clap::ArgMatches) -> std::result::Result<(), Box<dyn Er
Ok(())
}
fn test_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict: bool) {
fn test_command(
matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
strict: bool,
env: &RefCell<Environment<StdoutWrapper, StderrWrapper>>,
) {
let files = matches.values_of("INPUT");
let recurse = matches.is_present("recurse");
if files.is_none() {
let curr_dir = std::env::current_dir().unwrap();
let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, strict, import_paths);
let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, strict, import_paths, env);
if let Ok(false) = ok {
process::exit(1)
}
@ -339,7 +361,7 @@ fn test_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict:
for file in files.unwrap() {
let pb = PathBuf::from(file);
//if pb.is_dir() {
if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, strict, import_paths) {
if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, strict, import_paths, env) {
ok = false;
}
}
@ -426,11 +448,11 @@ fn do_repl(import_paths: &Vec<PathBuf>, strict: bool) -> std::result::Result<(),
}
}
}
let env = std::cell::RefCell::new(build::opcode::Environment::new(StdoutWrapper::new(), StderrWrapper::new()));
let mut builder = build::FileBuilder::new(
std::env::current_dir()?,
import_paths,
StdoutWrapper::new(),
StderrWrapper::new(),
&env,
);
builder.set_strict(strict);
@ -451,6 +473,7 @@ fn main() {
// FIXME(jwall): Do we want these to be shared or not?
let registry = ConverterRegistry::make_registry();
let mut import_paths = Vec::new();
let env = RefCell::new(Environment::new(StdoutWrapper::new(), StderrWrapper::new()));
if let Some(mut p) = dirs::home_dir() {
p.push(".ucg");
// Attempt to create directory if it doesn't exist.
@ -470,9 +493,9 @@ fn main() {
true
};
if let Some(matches) = app_matches.subcommand_matches("build") {
build_command(matches, &import_paths, strict);
build_command(matches, &import_paths, strict, &env);
} else if let Some(matches) = app_matches.subcommand_matches("test") {
test_command(matches, &import_paths, strict);
test_command(matches, &import_paths, strict, &env);
} else if let Some(matches) = app_matches.subcommand_matches("converters") {
converters_command(matches, &registry)
} else if let Some(_) = app_matches.subcommand_matches("importers") {