DEV: A while raft of unit test fixes for error reporting mostly.

This commit is contained in:
Jeremy Wall 2019-08-28 19:11:09 -05:00
parent e256abfee6
commit 3017ced8d9
10 changed files with 243 additions and 201 deletions

View File

@ -258,8 +258,8 @@ fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 + \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(r"Expected Int but got String\(foo\)").unwrap(),
Regex::new(r"line: 1 column: 5").unwrap(),
],
)
}
@ -269,8 +269,8 @@ fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 - \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(r"Expected Int but got String\(foo\)").unwrap(),
Regex::new(r"line: 1 column: 5").unwrap(),
],
)
}
@ -280,8 +280,8 @@ fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 * \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(r"Expected Int but got String\(foo\)").unwrap(),
Regex::new(r"line: 1 column: 5").unwrap(),
],
)
}
@ -291,8 +291,8 @@ fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 / \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(r"Expected Int but got String\(foo\)").unwrap(),
Regex::new(r"line: 1 column: 5").unwrap(),
],
)
}
@ -302,8 +302,11 @@ fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 > \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(
r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 5 for expression",
)
.unwrap(),
Regex::new(r"line: 1 column: 1").unwrap(),
],
)
}
@ -313,8 +316,11 @@ fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 < \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
Regex::new(
r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 5 for expression",
)
.unwrap(),
Regex::new(r"line: 1 column: 1").unwrap(),
],
)
}
@ -324,8 +330,11 @@ fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 <= \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
Regex::new(
r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 6 for expression",
)
.unwrap(),
Regex::new(r"line: 1 column: 1").unwrap(),
],
)
}
@ -335,8 +344,11 @@ fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 >= \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
Regex::new(
r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 6 for expression",
)
.unwrap(),
Regex::new(r"line: 1 column: 1").unwrap(),
],
)
}
@ -346,8 +358,11 @@ fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure(
"1 == \"foo\";",
vec![
Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
Regex::new(
r"Expected numeric values of the same type but got Int\(1\) at line: 1 column: 1 and String\(foo\) at line: 1 column: 5 for expression",
)
.unwrap(),
Regex::new(r"line: 1 column: 1").unwrap(),
],
)
}
@ -358,7 +373,7 @@ fn test_incomplete_tuple_compile_failure() {
"{;",
vec![
Regex::new(r"Expected \(\}\) but got \(;\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 2").unwrap(),
Regex::new(r"at <eval> line: 1 column: 2").unwrap(),
],
)
}
@ -501,7 +516,7 @@ fn test_copy_expression_not_a_tuple_compile_failure() {
"let foo = 1;\nfoo{};",
vec![
Regex::new(r"Expected Tuple or Module but got \(1\)").unwrap(),
Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
Regex::new(r"line: 2 column: 1").unwrap(),
],
)
}
@ -546,8 +561,8 @@ fn test_select_missed_case_string_no_default_compile_failure() {
assert_build_failure(
"select \"a\", { b = 1, };",
vec![
Regex::new(r"Unhandled select case .a. with no default").unwrap(),
Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
Regex::new(r"Unhandled select case with no default").unwrap(),
Regex::new(r"line: 1 column: 8").unwrap(),
],
)
}
@ -557,8 +572,8 @@ fn test_select_missed_case_boolean_no_default_compile_failure() {
assert_build_failure(
"select true, { false = 1, };",
vec![
Regex::new(r"Unhandled select case true with no default").unwrap(),
Regex::new(r"at <eval> line: 1, column: 8").unwrap(),
Regex::new(r"Unhandled select case with no default").unwrap(),
Regex::new(r"line: 1 column: 8").unwrap(),
],
)
}
@ -569,7 +584,7 @@ fn test_bad_import_path_compile_failure() {
"let bad = import \"no/such/path.ucg\";",
vec![
Regex::new(r"OSError: Path not found").unwrap(),
Regex::new(r"at <eval> line: 1, column: 18").unwrap(),
Regex::new(r"line: 1 column: 18").unwrap(),
],
)
}

View File

@ -94,6 +94,7 @@ where
environment: Rc<RefCell<Environment<Stdout, Stderr>>>,
working_dir: PathBuf,
// FIXME(jwall): These need to be compiled and added to the op cache.
// specifically in the environment.
std: Rc<HashMap<String, &'static str>>,
import_path: &'a Vec<PathBuf>,
pub last: Option<Rc<Val>>,

View File

@ -17,6 +17,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use super::translate::PositionMap;
use super::Error;
use super::OpPointer;
/// A Cache of Op codes.
@ -39,12 +40,21 @@ impl Ops {
pub struct Entry<'a>(btree_map::Entry<'a, String, Rc<PositionMap>>);
impl<'a> Entry<'a> {
pub fn get_pointer_or_else<F: FnOnce() -> PositionMap, P: Into<PathBuf>>(
pub fn get_pointer_or_else<F: FnOnce() -> Result<PositionMap, Error>, P: Into<PathBuf>>(
self,
f: F,
path: P,
) -> OpPointer {
let cached = self.0.or_insert_with(|| Rc::new(f())).clone();
OpPointer::new(cached).with_path(path.into())
) -> Result<OpPointer, Error> {
let cached = match self.0 {
btree_map::Entry::Occupied(e) => {
e.get().clone()
}
btree_map::Entry::Vacant(e) => {
let v = Rc::new(f()?);
e.insert(v.clone());
v
}
};
Ok(OpPointer::new(cached).with_path(path.into()))
}
}

View File

@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::BTreeMap;
use std::io::{Write, Read};
use std::rc::Rc;
use std::path::PathBuf;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::rc::Rc;
use super::pointer::OpPointer;
use super::cache;
use super::Value;
use super::pointer::OpPointer;
use super::Error;
use super::Value;
use crate::convert::{ConverterRegistry, ImporterRegistry};
use crate::iter::OffsetStrIter;
use crate::parse::parse;
@ -66,24 +66,24 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
}
pub fn get_ops_for_path(&mut self, path: &String) -> Result<OpPointer, Error> {
let op_pointer = self.op_cache.entry(path).get_pointer_or_else(
self.op_cache.entry(path).get_pointer_or_else(
|| {
// FIXME(jwall): We need to do proper error handling here.
let p = PathBuf::from(&path);
let root = p.parent().unwrap();
// first we read in the file
let mut f = File::open(&path).unwrap();
let mut f = File::open(&path)?;
// then we parse it
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
f.read_to_string(&mut contents)?;
let iter = OffsetStrIter::new(&contents).with_src_file(&p);
// FIXME(jwall): Unify BuildError and our other Error
let stmts = parse(iter, None).unwrap();
// then we create an ops from it
let ops = super::translate::AST::translate(stmts, &root);
ops
Ok(ops)
},
&path,
);
Ok(op_pointer)
)
}
}

View File

@ -14,6 +14,7 @@
use std::convert::From;
use std::fmt;
use std::fmt::Display;
use std::io;
use crate::ast::Position;
@ -30,6 +31,22 @@ impl Error {
pos: Some(pos),
}
}
pub fn with_pos(mut self, pos: Position) -> Self {
self.pos = Some(pos);
self
}
}
macro_rules! decorate_error {
($pos:expr => $result:expr) => {
match $result {
Ok(v) => Ok(v),
Err(e) => {
Err(e.with_pos($pos.clone()))
}
}
};
}
impl From<regex::Error> for Error {
@ -43,8 +60,12 @@ impl From<regex::Error> for Error {
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
let msg = match e.kind() {
io::ErrorKind::NotFound => format!("OSError: Path not found: {}", e),
_ => format!("{}", e),
};
Error {
message: format!("{}", e),
message: msg,
pos: None,
}
}
@ -60,4 +81,4 @@ impl Display for Error {
}
}
impl std::error::Error for Error {}
impl std::error::Error for Error {}

View File

@ -17,6 +17,7 @@ use std::rc::Rc;
mod cache;
mod debug;
pub mod environment;
#[macro_use]
mod error;
pub mod pointer;
mod runtime;

View File

@ -166,7 +166,7 @@ impl Builtins {
stack.push((v, path_pos));
}
None => {
let op_pointer = borrowed_env.get_ops_for_path(path)?;
let op_pointer = decorate_error!(path_pos => borrowed_env.get_ops_for_path(path))?;
let mut vm = VM::with_pointer(op_pointer, env.clone());
vm.run()?;
let result = Rc::new(vm.symbols_to_tuple(true));

View File

@ -266,7 +266,13 @@ impl AST {
Self::translate_expr(*expr, &mut ops, root);
}
Expression::Fail(def) => {
let msg_pos = def.message.pos().clone();
Self::translate_expr(*def.message, &mut ops, root);
ops.push(
Op::Val(Primitive::Str("UserDefined: ".to_owned())),
msg_pos,
);
ops.push(Op::Add, def.pos.clone());
ops.push(Op::Bang, def.pos);
}
Expression::Format(def) => {
@ -450,6 +456,7 @@ impl AST {
ops.push(Op::Runtime(Hook::Range), def.pos);
}
Expression::Select(def) => {
let default_pos = def.val.pos().clone();
Self::translate_expr(*def.val, &mut ops, root);
let mut jumps = Vec::new();
for (key, val) in def.tuple {
@ -467,6 +474,12 @@ impl AST {
if let Some(default) = def.default {
Self::translate_expr(*default, &mut ops, root);
} else {
ops.push(
Op::Val(Primitive::Str(
"Unhandled select case with no default".to_owned(),
)),
default_pos,
);
ops.push(Op::Bang, def.pos);
}
let end = ops.len() - 1;

View File

@ -460,8 +460,8 @@ where
}
fn op_gt(&mut self, pos: &Position) -> Result<(), Error> {
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (left, left_pos) = self.pop()?;
let (right, right_pos) = self.pop()?;
match (left.as_ref(), right.as_ref()) {
(&P(Int(i)), &P(Int(ii))) => {
self.push(Rc::new(P(Bool(i > ii))), pos.clone())?;
@ -472,8 +472,8 @@ where
_ => {
return Err(Error::new(
format!(
"Expected Numeric values of the same type but got {:?} and {:?}",
left, right
"Expected numeric values of the same type but got {:?} at {} and {:?} at {} for expression",
left, left_pos, right, right_pos,
),
pos.clone(),
));
@ -483,8 +483,8 @@ where
}
fn op_lt(&mut self, pos: &Position) -> Result<(), Error> {
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (left, left_pos) = self.pop()?;
let (right, right_pos) = self.pop()?;
match (left.as_ref(), right.as_ref()) {
(&P(Int(i)), &P(Int(ii))) => {
self.push(Rc::new(P(Bool(i < ii))), pos.clone())?;
@ -495,8 +495,8 @@ where
_ => {
return Err(Error::new(
format!(
"Expected Numeric values of the same type but got {:?} and {:?}",
left, right
"Expected numeric values of the same type but got {:?} at {} and {:?} at {} for expression",
left, left_pos, right, right_pos,
),
pos.clone(),
));
@ -506,8 +506,8 @@ where
}
fn op_lteq(&mut self, pos: Position) -> Result<(), Error> {
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (left, left_pos) = self.pop()?;
let (right, right_pos) = self.pop()?;
match (left.as_ref(), right.as_ref()) {
(&P(Int(i)), &P(Int(ii))) => {
self.push(Rc::new(P(Bool(i <= ii))), pos)?;
@ -518,8 +518,8 @@ where
_ => {
return Err(Error::new(
format!(
"Expected Numeric values of the same type but got {:?} and {:?}",
left, right
"Expected numeric values of the same type but got {:?} at {} and {:?} at {} for expression",
left, left_pos, right, right_pos,
),
pos,
));
@ -529,8 +529,8 @@ where
}
fn op_gteq(&mut self, pos: Position) -> Result<(), Error> {
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (left, left_pos) = self.pop()?;
let (right, right_pos) = self.pop()?;
match (left.as_ref(), right.as_ref()) {
(&P(Int(i)), &P(Int(ii))) => {
self.push(Rc::new(P(Bool(i >= ii))), pos)?;
@ -541,8 +541,8 @@ where
_ => {
return Err(Error::new(
format!(
"Expected Numeric values of the same type but got {:?} and {:?}",
left, right
"Expected numeric values of the same type but got {:?} at {} and {:?} at {} for expression",
left, left_pos, right, right_pos,
),
pos,
));
@ -554,45 +554,45 @@ where
fn op_mod(&mut self, pos: Position) -> Result<(), Error> {
// Adds the previous two items in the stack.
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (right, right_pos) = self.pop()?;
// Then pushes the result onto the stack.
self.push(Rc::new(P(self.modulus(&left, &right, &pos)?)), pos)?;
self.push(Rc::new(P(self.modulus(&left, &right, &right_pos)?)), pos)?;
Ok(())
}
fn op_add(&mut self, pos: Position) -> Result<(), Error> {
// Adds the previous two items in the stack.
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (right, right_pos) = self.pop()?;
// Then pushes the result onto the stack.
self.push(Rc::new(self.add(&left, &right, &pos)?), pos)?;
self.push(Rc::new(self.add(&left, &right, &right_pos)?), pos)?;
Ok(())
}
fn op_sub(&mut self, pos: Position) -> Result<(), Error> {
// Subtracts the previous two items in the stack.
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (right, right_pos) = self.pop()?;
// Then pushes the result onto the stack.
self.push(Rc::new(P(self.sub(&left, &right, &pos)?)), pos)?;
self.push(Rc::new(P(self.sub(&left, &right, &right_pos)?)), pos)?;
Ok(())
}
fn op_mul(&mut self, pos: Position) -> Result<(), Error> {
// Multiplies the previous two items in the stack.
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (right, right_pos) = self.pop()?;
// Then pushes the result onto the stack.
self.push(Rc::new(P(self.mul(&left, &right, &pos)?)), pos)?;
self.push(Rc::new(P(self.mul(&left, &right, &right_pos)?)), pos)?;
Ok(())
}
fn op_div(&mut self, pos: Position) -> Result<(), Error> {
// Divides the previous two items in the stack.
let (left, _) = self.pop()?;
let (right, _) = self.pop()?;
let (right, right_pos) = self.pop()?;
// Then pushes the result onto the stack.
self.push(Rc::new(P(self.div(&left, &right, &pos)?)), pos)?;
self.push(Rc::new(P(self.div(&left, &right, &right_pos)?)), pos)?;
Ok(())
}
@ -670,7 +670,12 @@ where
}
fn op_bang(&mut self) -> Result<(), Error> {
Ok(())
let (msg_val, err_pos) = self.pop()?;
if let &P(Str(ref msg)) = msg_val.as_ref() {
return Err(Error::new(msg.clone(), err_pos));
} else {
unreachable!();
}
}
fn op_index(&mut self, safe: bool, pos: Position) -> Result<(), Error> {

View File

@ -22,6 +22,7 @@ use std::collections::BTreeMap;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::{Stdout, Stderr};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process;
@ -96,47 +97,46 @@ fn run_converter(c: &dyn traits::Converter, v: Rc<Val>, f: Option<&str>) -> trai
result
}
fn build_file<'a, C: Cache>(
fn build_file<'a>(
file: &'a str,
validate: bool,
strict: bool,
import_paths: &'a Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &'a ConverterRegistry,
) -> Result<build::FileBuilder<'a, C>, Box<dyn Error>> {
) -> Result<build::FileBuilder<'a, Stdout, Stderr>, 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 = std::io::stdout();
let err = std::io::stderr();
let mut builder =
build::FileBuilder::new(std::env::current_dir()?, import_paths, cache, registry);
builder.set_strict(strict);
build::FileBuilder::new(std::env::current_dir()?, import_paths, out, err);
// FIXME(jwall): builder.set_strict(strict);
if validate {
builder.enable_validate_mode();
}
builder.build(file_path_buf)?;
if validate {
println!("{}", builder.assert_collector.summary);
// FIXME(jwall): println!("{}", builder.assert_collector.summary);
}
Ok(builder)
}
fn do_validate<C: Cache>(
fn do_validate(
file: &str,
strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
) -> bool {
println!("Validating {}", file);
match build_file(file, true, strict, import_paths, cache, registry) {
match build_file(file, true, strict, import_paths) {
Ok(b) => {
if b.assert_collector.success {
println!("File {} Pass\n", file);
} else {
println!("File {} Fail\n", file);
// FIXM(jwall): assert collector access.
//if b.assert_collector.success {
// println!("File {} Pass\n", file);
//} else {
// println!("File {} Fail\n", file);
return false;
}
//}
}
Err(msg) => {
eprintln!("Err: {}", msg);
@ -149,7 +149,6 @@ fn do_validate<C: Cache>(
fn process_output(
output: &Option<(String, Rc<Val>)>,
file: Option<&str>,
registry: &ConverterRegistry,
) -> bool {
let (typ, val) = match output {
Some((ref typ, ref val)) => (typ, val.clone()),
@ -157,48 +156,46 @@ fn process_output(
return false;
}
};
match registry.get_converter(typ) {
Some(converter) => {
run_converter(converter, val, file).unwrap();
eprintln!("\nConversion successful");
return true;
}
None => {
eprintln!("No such converter {}", typ);
// FIXME(jwall): Is this function even still necessary?
//match registry.get_converter(typ) {
// Some(converter) => {
// run_converter(converter, val, file).unwrap();
// eprintln!("\nConversion successful");
// return true;
// }
// None => {
// eprintln!("No such converter {}", typ);
return false;
}
}
// }
//}
}
fn do_compile<C: Cache>(
fn do_compile(
file: &str,
strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
) -> bool {
println!("Building {}", file);
let builder = match build_file(file, false, strict, import_paths, cache.clone(), registry) {
let builder = match build_file(file, false, strict, import_paths) {
Ok(builder) => builder,
Err(err) => {
eprintln!("{}", err);
return false;
}
};
if builder.out_lock.is_none() {
if builder.out.is_none() {
eprintln!("Build results in no artifacts.");
}
process_output(&builder.out_lock, Some(file), registry)
// FIXME(jwall): tuple? process_output(&builder.out, Some(file))
return false;
}
fn visit_ucg_files<C: Cache>(
fn visit_ucg_files(
path: &Path,
recurse: bool,
validate: bool,
strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
) -> Result<bool, Box<dyn Error>> {
let our_path = String::from(path.to_string_lossy());
let mut result = true;
@ -222,8 +219,6 @@ fn visit_ucg_files<C: Cache>(
validate,
strict,
import_paths,
cache.clone(),
registry,
) {
eprintln!("{}", e);
result = false;
@ -234,8 +229,6 @@ fn visit_ucg_files<C: Cache>(
&path_as_string,
strict,
import_paths,
cache.clone(),
registry,
) {
result = false;
summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
@ -247,8 +240,6 @@ fn visit_ucg_files<C: Cache>(
&path_as_string,
strict,
import_paths,
cache.clone(),
registry,
) {
result = false;
}
@ -256,14 +247,14 @@ fn visit_ucg_files<C: Cache>(
}
}
} else if validate && our_path.ends_with("_test.ucg") {
if !do_validate(&our_path, strict, import_paths, cache, registry) {
if !do_validate(&our_path, strict, import_paths) {
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, cache, registry) {
if !do_compile(&our_path, strict, import_paths) {
result = false;
}
}
@ -274,11 +265,9 @@ fn visit_ucg_files<C: Cache>(
Ok(result)
}
fn inspect_command<C: Cache>(
fn inspect_command(
matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
strict: bool,
) {
let file = matches.value_of("INPUT");
@ -287,66 +276,65 @@ fn inspect_command<C: Cache>(
let mut builder = build::FileBuilder::new(
std::env::current_dir().unwrap(),
import_paths,
cache,
registry,
io::stdout(),
io::stderr(),
);
builder.set_strict(strict);
match registry.get_converter(target) {
Some(converter) => {
if let Some(file) = file {
if let Err(e) = builder.build(file) {
eprintln!("{:?}", e);
process::exit(1);
}
}
let val = match sym {
Some(sym_name) => {
let normalized = if !sym_name.ends_with(";") {
let mut temp = sym_name.to_owned();
temp.push_str(";");
temp
} else {
sym_name.to_owned()
};
let mut builder = builder.clone_builder();
match builder.eval_string(&normalized) {
Ok(v) => Some(v.clone()),
Err(e) => {
eprintln!("{}", e);
process::exit(1);
}
}
}
None => builder.last,
};
match val {
Some(value) => {
// We use None here because we always output to stdout for an inspect.
run_converter(converter, value, None).unwrap();
println!("");
process::exit(0);
}
None => {
eprintln!("No value.");
process::exit(1);
}
}
}
None => {
eprintln!(
"No such format {}\nrun `ucg converters` to see available formats.",
target
);
process::exit(1);
}
}
// FIXME(jwall): builder.set_strict(strict);
// FIXME(jwall): Converting a value should be built into our builder?
//match registry.get_converter(target) {
// Some(converter) => {
// if let Some(file) = file {
// if let Err(e) = builder.build(file) {
// eprintln!("{:?}", e);
// process::exit(1);
// }
// }
// let val = match sym {
// Some(sym_name) => {
// let normalized = if !sym_name.ends_with(";") {
// let mut temp = sym_name.to_owned();
// temp.push_str(";");
// temp
// } else {
// sym_name.to_owned()
// };
// let mut builder = builder.clone_builder();
// match builder.eval_string(&normalized) {
// Ok(v) => Some(v.clone()),
// Err(e) => {
// eprintln!("{}", e);
// process::exit(1);
// }
// }
// }
// None => builder.last,
// };
// match val {
// Some(value) => {
// // We use None here because we always output to stdout for an inspect.
// run_converter(converter, value, None).unwrap();
// println!("");
// process::exit(0);
// }
// None => {
// eprintln!("No value.");
// process::exit(1);
// }
// }
// }
// None => {
// eprintln!(
// "No such format {}\nrun `ucg converters` to see available formats.",
// target
// );
// process::exit(1);
// }
//}
}
fn build_command<C: Cache>(
fn build_command(
matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
strict: bool,
) {
let files = matches.values_of("INPUT");
@ -360,8 +348,6 @@ fn build_command<C: Cache>(
false,
strict,
import_paths,
cache.clone(),
&registry,
);
if let Ok(false) = ok {
process::exit(1)
@ -376,8 +362,6 @@ fn build_command<C: Cache>(
false,
strict,
import_paths,
cache.clone(),
&registry,
) {
ok = false;
}
@ -450,11 +434,9 @@ fn fmt_command(matches: &clap::ArgMatches) -> std::result::Result<(), Box<dyn Er
Ok(())
}
fn test_command<C: Cache>(
fn test_command(
matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
strict: bool,
) {
let files = matches.values_of("INPUT");
@ -467,8 +449,6 @@ fn test_command<C: Cache>(
true,
strict,
import_paths,
cache.clone(),
&registry,
);
if let Ok(false) = ok {
process::exit(1)
@ -484,8 +464,6 @@ fn test_command<C: Cache>(
true,
strict,
import_paths,
cache.clone(),
&registry,
) {
ok = false;
}
@ -545,10 +523,8 @@ fn print_repl_help() {
println!(include_str!("help/repl.txt"));
}
fn do_repl<C: Cache>(
fn do_repl(
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
) -> std::result::Result<(), Box<dyn Error>> {
let config = rustyline::Config::builder();
let mut editor = rustyline::Editor::<()>::with_config(
@ -582,7 +558,7 @@ fn do_repl<C: Cache>(
}
}
let mut builder =
build::FileBuilder::new(std::env::current_dir()?, import_paths, cache, registry);
build::FileBuilder::new(std::env::current_dir()?, import_paths, io::stdout(), io::stderr());
// loop
let mut lines = ucglib::io::StatementAccumulator::new();
println!("Welcome to the UCG repl. Ctrl-D to exit");
@ -608,9 +584,10 @@ fn do_repl<C: Cache>(
pos: ucglib::ast::Position::new(0, 0, 0),
val: args[0].to_string(),
};
if let None = builder.scope_mut().build_output.remove(&key) {
eprintln!("No such binding {}", key.val);
}
// FIXME(jwall): handle this in an actual repl driver?
//if let None = builder.scope_mut().build_output.remove(&key) {
// eprintln!("No such binding {}", key.val);
//}
}
} else {
eprintln!("Invalid repl command...");
@ -630,9 +607,9 @@ fn do_repl<C: Cache>(
// print the result
Err(e) => eprintln!("{}", e),
Ok(v) => {
if builder.out_lock.is_some() {
process_output(&builder.out_lock, None, registry);
builder.out_lock = None;
if builder.out.is_some() {
// FIXME(jwall): process_output(&builder.out, None);
builder.out = None;
} else {
println!("{}", v);
editor.history_mut().add(stmt);
@ -649,12 +626,10 @@ fn do_repl<C: Cache>(
}
}
fn repl<C: Cache>(
fn repl(
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<C>>,
registry: &ConverterRegistry,
) {
if let Err(e) = do_repl(import_paths, cache, registry) {
if let Err(e) = do_repl(import_paths) {
eprintln!("{}", e);
process::exit(1);
}
@ -663,6 +638,7 @@ fn repl<C: Cache>(
fn main() {
let mut app = do_flags();
let app_matches = app.clone().get_matches();
// FIXME(jwall): Do we want these to be shared or not?
let cache = Rc::new(RefCell::new(MemoryCache::new()));
let registry = ConverterRegistry::make_registry();
let mut import_paths = Vec::new();
@ -685,11 +661,11 @@ fn main() {
true
};
if let Some(matches) = app_matches.subcommand_matches("eval") {
inspect_command(matches, &import_paths, cache, &registry, strict);
inspect_command(matches, &import_paths, strict);
} else if let Some(matches) = app_matches.subcommand_matches("build") {
build_command(matches, &import_paths, cache, &registry, strict);
build_command(matches, &import_paths, strict);
} else if let Some(matches) = app_matches.subcommand_matches("test") {
test_command(matches, &import_paths, cache, &registry, strict);
test_command(matches, &import_paths, strict);
} else if let Some(matches) = app_matches.subcommand_matches("converters") {
converters_command(matches, &registry)
} else if let Some(_) = app_matches.subcommand_matches("importers") {
@ -698,7 +674,7 @@ fn main() {
} else if let Some(_) = app_matches.subcommand_matches("env") {
env_help()
} else if let Some(_) = app_matches.subcommand_matches("repl") {
repl(&import_paths, cache, &registry)
repl(&import_paths)
} else if let Some(matches) = app_matches.subcommand_matches("fmt") {
if let Err(e) = fmt_command(matches) {
eprintln!("{}", e);