FEATURE: Support search paths for import statements.

A step toward #9

Adds builder support. We do not yet wire in the import paths.
This commit is contained in:
Jeremy Wall 2018-12-13 18:29:41 -06:00
parent a2267a2b3d
commit f26e26d2ad

View File

@ -43,8 +43,7 @@ impl MacroDef {
pub fn eval( pub fn eval(
&self, &self,
root: PathBuf, root: PathBuf,
cache: Rc<RefCell<assets::Cache>>, parent_builder: &Builder,
env: Rc<Val>,
mut args: Vec<Rc<Val>>, mut args: Vec<Rc<Val>>,
) -> Result<Vec<(PositionedItem<String>, Rc<Val>)>, Box<Error>> { ) -> Result<Vec<(PositionedItem<String>, Rc<Val>)>, Box<Error>> {
// Error conditions. If the args don't match the length and types of the argdefs then this is // Error conditions. If the args don't match the length and types of the argdefs then this is
@ -67,7 +66,8 @@ impl MacroDef {
for (i, arg) in args.drain(0..).enumerate() { for (i, arg) in args.drain(0..).enumerate() {
scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); scope.entry(self.argdefs[i].clone()).or_insert(arg.clone());
} }
let mut b = Builder::new_with_env_and_scope(root, cache, scope, env); let mut b = parent_builder.clone_builder(root);
b.set_scope(scope);
let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new(); let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new();
for &(ref key, ref expr) in self.fields.iter() { for &(ref key, ref expr) in self.fields.iter() {
// We clone the expressions here because this macro may be consumed // We clone the expressions here because this macro may be consumed
@ -95,6 +95,7 @@ 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 {
file: PathBuf, file: PathBuf,
import_path: Vec<PathBuf>,
validate_mode: bool, validate_mode: bool,
pub assert_collector: AssertCollector, pub assert_collector: AssertCollector,
strict: bool, strict: bool,
@ -165,6 +166,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(),
validate_mode: false, validate_mode: false,
assert_collector: AssertCollector { assert_collector: AssertCollector {
success: true, success: true,
@ -182,10 +184,41 @@ impl Builder {
} }
} }
pub fn clone_builder<P: Into<PathBuf>>(&self, file: P) -> Self {
Builder {
// Our import stack is initialized with ourself.
import_stack: self.import_stack.clone(),
file: file.into(),
import_path: self.import_path.clone(),
validate_mode: false,
assert_collector: AssertCollector {
success: true,
summary: String::new(),
failures: String::new(),
},
env: self.env.clone(),
strict: true,
assets: self.assets.clone(),
build_output: HashMap::new(),
out_lock: None,
stack: None,
is_module: false,
last: None,
}
}
pub fn set_scope(&mut self, scope: ValueMap) {
self.build_output = scope;
}
pub fn set_strict(&mut self, to: bool) { pub fn set_strict(&mut self, to: bool) {
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());
@ -333,10 +366,22 @@ impl Builder {
sym.pos.clone(), sym.pos.clone(),
))); )));
} }
// Try a relative path first.
let mut normalized = self.file.parent().unwrap().to_path_buf(); let mut normalized = self.file.parent().unwrap().to_path_buf();
let import_path = PathBuf::from(&def.path.fragment); let import_path = PathBuf::from(&def.path.fragment);
if import_path.is_relative() { if import_path.is_relative() {
normalized.push(&import_path); normalized.push(&import_path);
// First see if the normalized file exists or not.
if !normalized.exists() {
// If it does not then look for it in the list of import_paths
for mut p in self.import_path.iter().cloned() {
p.push(&import_path);
if p.exists() {
normalized = p;
break;
}
}
}
} else { } else {
normalized = import_path; normalized = import_path;
} }
@ -359,8 +404,7 @@ impl Builder {
let result = match maybe_asset { let result = match maybe_asset {
Some(v) => v.clone(), Some(v) => v.clone(),
None => { None => {
let mut b = Self::new(normalized.clone(), self.assets.clone()); let mut b = self.clone_builder(normalized.clone());
b.prepend_import_stack(&self.import_stack);
b.build()?; b.build()?;
b.get_outputs_as_val() b.get_outputs_as_val()
} }
@ -989,7 +1033,7 @@ impl Builder {
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone(); let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() { if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
// 1. First we create a builder. // 1. First we create a builder.
let mut b = Self::new(self.file.clone(), self.assets.clone()); let mut b = self.clone_builder(self.file.clone());
b.is_module = true; b.is_module = true;
// 2. We construct an argument tuple by copying from the defs // 2. We construct an argument tuple by copying from the defs
// argset. // argset.
@ -1063,12 +1107,7 @@ impl Builder {
for arg in args.iter() { for arg in args.iter() {
argvals.push(self.eval_expr(arg)?); argvals.push(self.eval_expr(arg)?);
} }
let fields = m.eval( let fields = m.eval(self.file.clone(), self, argvals)?;
self.file.clone(),
self.assets.clone(),
self.env.clone(),
argvals,
)?;
return Ok(Rc::new(Val::Tuple(fields))); return Ok(Rc::new(Val::Tuple(fields)));
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
@ -1174,12 +1213,7 @@ impl Builder {
let mut out = Vec::new(); let mut out = Vec::new();
for item in l.iter() { for item in l.iter() {
let argvals = vec![item.clone()]; let argvals = vec![item.clone()];
let fields = macdef.eval( let fields = macdef.eval(self.file.clone(), self, argvals)?;
self.file.clone(),
self.assets.clone(),
self.env.clone(),
argvals,
)?;
if let Some(v) = Self::find_in_fieldlist(&def.field, &fields) { if let Some(v) = Self::find_in_fieldlist(&def.field, &fields) {
match def.typ { match def.typ {
ListOpType::Map => { ListOpType::Map => {