FEATURE: Use UCG_IMPORT_PATH to identify the import paths.

fixes #9
This commit is contained in:
Jeremy Wall 2018-12-13 19:03:22 -06:00
parent f26e26d2ad
commit 2b47ca8617
5 changed files with 99 additions and 42 deletions

View File

@ -19,8 +19,9 @@ use super::assets::MemoryCache;
use super::Builder; use super::Builder;
fn assert_build(input: &str) { fn assert_build(input: &str) {
let i_paths = Vec::new();
let cache = MemoryCache::new(); let cache = MemoryCache::new();
let mut b = Builder::new("<Eval>", Rc::new(RefCell::new(cache))); let mut b = Builder::new("<Eval>", &i_paths, Rc::new(RefCell::new(cache)));
b.enable_validate_mode(); b.enable_validate_mode();
b.eval_string(input).unwrap(); b.eval_string(input).unwrap();
if !b.assert_collector.success { if !b.assert_collector.success {

View File

@ -93,9 +93,9 @@ pub struct AssertCollector {
} }
/// Builder handles building ucg code for a single file. /// Builder handles building ucg code for a single file.
pub struct Builder { pub struct Builder<'a> {
file: PathBuf, file: PathBuf,
import_path: Vec<PathBuf>, import_path: &'a Vec<PathBuf>,
validate_mode: bool, validate_mode: bool,
pub assert_collector: AssertCollector, pub assert_collector: AssertCollector,
strict: bool, strict: bool,
@ -139,24 +139,36 @@ macro_rules! eval_binary_expr {
}; };
} }
impl Builder { impl<'a> Builder<'a> {
/// Constructs a new Builder. /// Constructs a new Builder.
pub fn new<P: Into<PathBuf>>(file: P, cache: Rc<RefCell<assets::Cache>>) -> Self { pub fn new<P: Into<PathBuf>>(
Self::new_with_scope(file, cache, HashMap::new()) file: P,
import_paths: &'a Vec<PathBuf>,
cache: Rc<RefCell<assets::Cache>>,
) -> Self {
Self::new_with_scope(file, import_paths, cache, HashMap::new())
} }
/// Constructs a new Builder with a provided scope. /// Constructs a new Builder with a provided scope.
pub fn new_with_scope<P: Into<PathBuf>>( pub fn new_with_scope<P: Into<PathBuf>>(
root: P, root: P,
import_paths: &'a Vec<PathBuf>,
cache: Rc<RefCell<assets::Cache>>, cache: Rc<RefCell<assets::Cache>>,
scope: ValueMap, scope: ValueMap,
) -> Self { ) -> Self {
let env_vars: Vec<(String, String)> = env::vars().collect(); let env_vars: Vec<(String, String)> = env::vars().collect();
Self::new_with_env_and_scope(root, cache, scope, Rc::new(Val::Env(env_vars))) Self::new_with_env_and_scope(
root,
import_paths,
cache,
scope,
Rc::new(Val::Env(env_vars)),
)
} }
pub fn new_with_env_and_scope<P: Into<PathBuf>>( pub fn new_with_env_and_scope<P: Into<PathBuf>>(
file: P, file: P,
import_paths: &'a Vec<PathBuf>,
cache: Rc<RefCell<assets::Cache>>, cache: Rc<RefCell<assets::Cache>>,
scope: ValueMap, scope: ValueMap,
env: Rc<Val>, env: Rc<Val>,
@ -166,7 +178,7 @@ impl Builder {
// Our import stack is initialized with ourself. // Our import stack is initialized with ourself.
import_stack: vec![file.to_string_lossy().to_string()], import_stack: vec![file.to_string_lossy().to_string()],
file: file, file: file,
import_path: Vec::new(), import_path: import_paths,
validate_mode: false, validate_mode: false,
assert_collector: AssertCollector { assert_collector: AssertCollector {
success: true, success: true,
@ -189,7 +201,7 @@ impl Builder {
// Our import stack is initialized with ourself. // Our import stack is initialized with ourself.
import_stack: self.import_stack.clone(), import_stack: self.import_stack.clone(),
file: file.into(), file: file.into(),
import_path: self.import_path.clone(), import_path: self.import_path,
validate_mode: false, validate_mode: false,
assert_collector: AssertCollector { assert_collector: AssertCollector {
success: true, success: true,
@ -215,10 +227,6 @@ impl Builder {
self.strict = to; self.strict = to;
} }
pub fn set_import_paths(&mut self, paths: Vec<PathBuf>) {
self.import_path = paths;
}
pub fn prepend_import_stack(&mut self, imports: &Vec<String>) { pub fn prepend_import_stack(&mut self, imports: &Vec<String>) {
let mut new_stack = self.import_stack.clone(); let mut new_stack = self.import_stack.clone();
new_stack.append(imports.clone().as_mut()); new_stack.append(imports.clone().as_mut());

View File

@ -28,8 +28,9 @@ fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: Builder) {
#[test] #[test]
#[should_panic(expected = "Expected Float")] #[should_panic(expected = "Expected Float")]
fn test_eval_div_expr_fail() { fn test_eval_div_expr_fail() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let b = Builder::new(std::env::current_dir().unwrap(), cache); let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
test_expr_to_val( test_expr_to_val(
vec![( vec![(
Expression::Binary(BinaryOpDef { Expression::Binary(BinaryOpDef {
@ -53,8 +54,9 @@ fn test_eval_div_expr_fail() {
#[test] #[test]
#[should_panic(expected = "Expected Float")] #[should_panic(expected = "Expected Float")]
fn test_eval_mul_expr_fail() { fn test_eval_mul_expr_fail() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let b = Builder::new(std::env::current_dir().unwrap(), cache); let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
test_expr_to_val( test_expr_to_val(
vec![( vec![(
Expression::Binary(BinaryOpDef { Expression::Binary(BinaryOpDef {
@ -78,8 +80,9 @@ fn test_eval_mul_expr_fail() {
#[test] #[test]
#[should_panic(expected = "Expected Float")] #[should_panic(expected = "Expected Float")]
fn test_eval_subtract_expr_fail() { fn test_eval_subtract_expr_fail() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let b = Builder::new(std::env::current_dir().unwrap(), cache); let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
test_expr_to_val( test_expr_to_val(
vec![( vec![(
Expression::Binary(BinaryOpDef { Expression::Binary(BinaryOpDef {
@ -102,8 +105,9 @@ fn test_eval_subtract_expr_fail() {
#[test] #[test]
#[should_panic(expected = "Expected Float")] #[should_panic(expected = "Expected Float")]
fn test_eval_add_expr_fail() { fn test_eval_add_expr_fail() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let b = Builder::new(std::env::current_dir().unwrap(), cache); let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
test_expr_to_val( test_expr_to_val(
vec![( vec![(
Expression::Binary(BinaryOpDef { Expression::Binary(BinaryOpDef {
@ -126,8 +130,9 @@ fn test_eval_add_expr_fail() {
#[test] #[test]
fn test_eval_simple_lookup_error() { fn test_eval_simple_lookup_error() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.build_output b.build_output
.entry(value_node!("var1".to_string(), Position::new(1, 0, 0))) .entry(value_node!("var1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Int(1))); .or_insert(Rc::new(Val::Int(1)));
@ -142,8 +147,9 @@ fn test_eval_simple_lookup_error() {
#[test] #[test]
#[should_panic(expected = "Unable to find binding tpl1")] #[should_panic(expected = "Unable to find binding tpl1")]
fn test_expr_copy_no_such_tuple() { fn test_expr_copy_no_such_tuple() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let b = Builder::new(std::env::current_dir().unwrap(), cache); let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
test_expr_to_val( test_expr_to_val(
vec![( vec![(
Expression::Copy(CopyDef { Expression::Copy(CopyDef {
@ -163,8 +169,9 @@ fn test_expr_copy_no_such_tuple() {
#[test] #[test]
#[should_panic(expected = "Expected Tuple or Module got Int(1)")] #[should_panic(expected = "Expected Tuple or Module got Int(1)")]
fn test_expr_copy_not_a_tuple() { fn test_expr_copy_not_a_tuple() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.build_output b.build_output
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Int(1))); .or_insert(Rc::new(Val::Int(1)));
@ -187,8 +194,9 @@ fn test_expr_copy_not_a_tuple() {
#[test] #[test]
#[should_panic(expected = "Expected type Integer for field fld1 but got String")] #[should_panic(expected = "Expected type Integer for field fld1 but got String")]
fn test_expr_copy_field_type_error() { fn test_expr_copy_field_type_error() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.build_output b.build_output
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Tuple(vec![( .or_insert(Rc::new(Val::Tuple(vec![(
@ -223,8 +231,9 @@ fn test_expr_copy_field_type_error() {
#[test] #[test]
#[should_panic(expected = "Unable to find binding arg1")] #[should_panic(expected = "Unable to find binding arg1")]
fn test_macro_hermetic() { fn test_macro_hermetic() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.build_output b.build_output
.entry(value_node!("arg1".to_string(), Position::new(1, 0, 0))) .entry(value_node!("arg1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Str("bar".to_string()))); .or_insert(Rc::new(Val::Str("bar".to_string())));
@ -266,8 +275,9 @@ fn test_macro_hermetic() {
#[test] #[test]
#[should_panic(expected = "Expected String but got Integer in Select expression")] #[should_panic(expected = "Expected String but got Integer in Select expression")]
fn test_select_expr_not_a_string() { fn test_select_expr_not_a_string() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.build_output b.build_output
.entry(value_node!("foo".to_string(), Position::new(1, 0, 0))) .entry(value_node!("foo".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Int(4))); .or_insert(Rc::new(Val::Int(4)));

View File

@ -205,8 +205,9 @@ mod exec_test {
#[test] #[test]
fn convert_just_command_test() { fn convert_just_command_test() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
let conv = ExecConverter::new(); let conv = ExecConverter::new();
b.eval_string( b.eval_string(
"let script = { "let script = {
@ -226,8 +227,9 @@ mod exec_test {
#[test] #[test]
fn convert_command_with_env_test() { fn convert_command_with_env_test() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
let conv = ExecConverter::new(); let conv = ExecConverter::new();
b.eval_string( b.eval_string(
"let script = { "let script = {
@ -254,8 +256,9 @@ mod exec_test {
#[test] #[test]
fn convert_command_with_arg_test() { fn convert_command_with_arg_test() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache);
let conv = ExecConverter::new(); let conv = ExecConverter::new();
b.eval_string( b.eval_string(
"let script = { "let script = {

View File

@ -72,17 +72,18 @@ fn run_converter(c: &traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::
c.convert(v, file.as_mut()) c.convert(v, file.as_mut())
} }
fn build_file( fn build_file<'a>(
file: &str, file: &'a str,
validate: bool, validate: bool,
strict: bool, strict: bool,
import_paths: &'a Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> { ) -> Result<build::Builder<'a>, Box<Error>> {
let mut file_path_buf = PathBuf::from(file); let mut file_path_buf = PathBuf::from(file);
if file_path_buf.is_relative() { if file_path_buf.is_relative() {
file_path_buf = std::env::current_dir().unwrap().join(file_path_buf); file_path_buf = std::env::current_dir().unwrap().join(file_path_buf);
} }
let mut builder = build::Builder::new(file_path_buf, cache); let mut builder = build::Builder::new(file_path_buf, import_paths, cache);
builder.set_strict(strict); builder.set_strict(strict);
if validate { if validate {
builder.enable_validate_mode(); builder.enable_validate_mode();
@ -94,9 +95,14 @@ fn build_file(
Ok(builder) Ok(builder)
} }
fn do_validate(file: &str, strict: bool, cache: Rc<RefCell<Cache>>) -> bool { fn do_validate(
file: &str,
strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>,
) -> bool {
println!("Validating {}", file); println!("Validating {}", file);
match build_file(file, true, strict, cache) { match build_file(file, true, strict, import_paths, cache) {
Ok(b) => { Ok(b) => {
if b.assert_collector.success { if b.assert_collector.success {
println!("File {} Pass\n", file); println!("File {} Pass\n", file);
@ -116,11 +122,12 @@ fn do_validate(file: &str, strict: bool, cache: Rc<RefCell<Cache>>) -> bool {
fn do_compile( fn do_compile(
file: &str, file: &str,
strict: bool, strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry, registry: &ConverterRegistry,
) -> bool { ) -> bool {
println!("Building {}", file); println!("Building {}", file);
let builder = match build_file(file, false, strict, cache.clone()) { let builder = match build_file(file, false, strict, import_paths, cache.clone()) {
Ok(builder) => builder, Ok(builder) => builder,
Err(err) => { Err(err) => {
eprintln!("{}", err); eprintln!("{}", err);
@ -152,6 +159,7 @@ fn visit_ucg_files(
recurse: bool, recurse: bool,
validate: bool, validate: bool,
strict: bool, strict: bool,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry, registry: &ConverterRegistry,
) -> Result<bool, Box<Error>> { ) -> Result<bool, Box<Error>> {
@ -177,6 +185,7 @@ fn visit_ucg_files(
recurse, recurse,
validate, validate,
strict, strict,
import_paths,
cache.clone(), cache.clone(),
registry, registry,
) { ) {
@ -185,28 +194,34 @@ fn visit_ucg_files(
} }
} else { } else {
if validate && path_as_string.ends_with("_test.ucg") { if validate && path_as_string.ends_with("_test.ucg") {
if !do_validate(&path_as_string, strict, cache.clone()) { if !do_validate(&path_as_string, strict, import_paths, cache.clone()) {
result = false; result = false;
summary.push_str(format!("{} - FAIL\n", path_as_string).as_str()) summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
} else { } else {
summary.push_str(format!("{} - PASS\n", path_as_string).as_str()) summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
} }
} else if !validate && path_as_string.ends_with(".ucg") { } else if !validate && path_as_string.ends_with(".ucg") {
if !do_compile(&path_as_string, strict, cache.clone(), registry) { if !do_compile(
&path_as_string,
strict,
import_paths,
cache.clone(),
registry,
) {
result = false; result = false;
} }
} }
} }
} }
} else if validate && our_path.ends_with("_test.ucg") { } else if validate && our_path.ends_with("_test.ucg") {
if !do_validate(&our_path, strict, cache) { if !do_validate(&our_path, strict, import_paths, cache) {
result = false; result = false;
summary.push_str(format!("{} - FAIL\n", our_path).as_str()); summary.push_str(format!("{} - FAIL\n", our_path).as_str());
} else { } else {
summary.push_str(format!("{} - PASS\n", &our_path).as_str()); summary.push_str(format!("{} - PASS\n", &our_path).as_str());
} }
} else if !validate { } else if !validate {
if !do_compile(&our_path, strict, cache, registry) { if !do_compile(&our_path, strict, import_paths, cache, registry) {
result = false; result = false;
} }
} }
@ -219,6 +234,7 @@ fn visit_ucg_files(
fn inspect_command( fn inspect_command(
matches: &clap::ArgMatches, matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry, registry: &ConverterRegistry,
strict: bool, strict: bool,
@ -226,7 +242,7 @@ fn inspect_command(
let file = matches.value_of("INPUT").unwrap(); let file = matches.value_of("INPUT").unwrap();
let sym = matches.value_of("sym"); let sym = matches.value_of("sym");
let target = matches.value_of("target").unwrap(); let target = matches.value_of("target").unwrap();
let mut builder = build::Builder::new(file, cache); let mut builder = build::Builder::new(file, import_paths, cache);
builder.set_strict(strict); builder.set_strict(strict);
match registry.get_converter(target) { match registry.get_converter(target) {
Some(converter) => { Some(converter) => {
@ -262,6 +278,7 @@ fn inspect_command(
fn build_command( fn build_command(
matches: &clap::ArgMatches, matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry, registry: &ConverterRegistry,
strict: bool, strict: bool,
@ -276,6 +293,7 @@ fn build_command(
recurse, recurse,
false, false,
strict, strict,
import_paths,
cache.clone(), cache.clone(),
&registry, &registry,
); );
@ -286,7 +304,15 @@ fn build_command(
} }
for file in files.unwrap() { for file in files.unwrap() {
let pb = PathBuf::from(file); let pb = PathBuf::from(file);
if let Ok(false) = visit_ucg_files(&pb, recurse, false, strict, cache.clone(), &registry) { if let Ok(false) = visit_ucg_files(
&pb,
recurse,
false,
strict,
import_paths,
cache.clone(),
&registry,
) {
ok = false; ok = false;
} }
} }
@ -297,6 +323,7 @@ fn build_command(
fn test_command( fn test_command(
matches: &clap::ArgMatches, matches: &clap::ArgMatches,
import_paths: &Vec<PathBuf>,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry, registry: &ConverterRegistry,
strict: bool, strict: bool,
@ -310,6 +337,7 @@ fn test_command(
recurse, recurse,
true, true,
strict, strict,
import_paths,
cache.clone(), cache.clone(),
&registry, &registry,
); );
@ -326,6 +354,7 @@ fn test_command(
recurse, recurse,
true, true,
strict, strict,
import_paths,
cache.clone(), cache.clone(),
&registry, &registry,
) { ) {
@ -355,17 +384,23 @@ fn main() {
let app_matches = app.clone().get_matches(); let app_matches = app.clone().get_matches();
let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new())); let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
let registry = ConverterRegistry::make_registry(); let registry = ConverterRegistry::make_registry();
let mut import_paths = Vec::new();
if let Ok(path_list_str) = std::env::var("UCG_IMPORT_PATH") {
for p in std::env::split_paths(&path_list_str) {
import_paths.push(p);
}
}
let strict = if app_matches.is_present("nostrict") { let strict = if app_matches.is_present("nostrict") {
false false
} else { } else {
true true
}; };
if let Some(matches) = app_matches.subcommand_matches("inspect") { if let Some(matches) = app_matches.subcommand_matches("inspect") {
inspect_command(matches, cache, &registry, strict); inspect_command(matches, &import_paths, cache, &registry, strict);
} else if let Some(matches) = app_matches.subcommand_matches("build") { } else if let Some(matches) = app_matches.subcommand_matches("build") {
build_command(matches, cache, &registry, strict); build_command(matches, &import_paths, cache, &registry, strict);
} else if let Some(matches) = app_matches.subcommand_matches("test") { } else if let Some(matches) = app_matches.subcommand_matches("test") {
test_command(matches, cache, &registry, strict); test_command(matches, &import_paths, cache, &registry, strict);
} else if let Some(_) = app_matches.subcommand_matches("converters") { } else if let Some(_) = app_matches.subcommand_matches("converters") {
converters_command(&registry) converters_command(&registry)
} else { } else {