mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: A while raft of unit test fixes for error reporting mostly.
This commit is contained in:
parent
e256abfee6
commit
3017ced8d9
@ -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(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -17,6 +17,7 @@ use std::rc::Rc;
|
||||
mod cache;
|
||||
mod debug;
|
||||
pub mod environment;
|
||||
#[macro_use]
|
||||
mod error;
|
||||
pub mod pointer;
|
||||
mod runtime;
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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> {
|
||||
|
238
src/main.rs
238
src/main.rs
@ -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(),
|
||||
®istry,
|
||||
);
|
||||
if let Ok(false) = ok {
|
||||
process::exit(1)
|
||||
@ -376,8 +362,6 @@ fn build_command<C: Cache>(
|
||||
false,
|
||||
strict,
|
||||
import_paths,
|
||||
cache.clone(),
|
||||
®istry,
|
||||
) {
|
||||
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(),
|
||||
®istry,
|
||||
);
|
||||
if let Ok(false) = ok {
|
||||
process::exit(1)
|
||||
@ -484,8 +464,6 @@ fn test_command<C: Cache>(
|
||||
true,
|
||||
strict,
|
||||
import_paths,
|
||||
cache.clone(),
|
||||
®istry,
|
||||
) {
|
||||
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, ®istry, strict);
|
||||
inspect_command(matches, &import_paths, strict);
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("build") {
|
||||
build_command(matches, &import_paths, cache, ®istry, strict);
|
||||
build_command(matches, &import_paths, strict);
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("test") {
|
||||
test_command(matches, &import_paths, cache, ®istry, strict);
|
||||
test_command(matches, &import_paths, strict);
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("converters") {
|
||||
converters_command(matches, ®istry)
|
||||
} 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, ®istry)
|
||||
repl(&import_paths)
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("fmt") {
|
||||
if let Err(e) = fmt_command(matches) {
|
||||
eprintln!("{}", e);
|
||||
|
Loading…
x
Reference in New Issue
Block a user