DEV: Unify more path handling logic

This commit is contained in:
Jeremy Wall 2020-05-25 14:22:45 -04:00
parent 7a8b8e46eb
commit 2b835e5886
3 changed files with 15 additions and 112 deletions

View File

@ -27,7 +27,6 @@ use rustyline::error::ReadlineError;
use simple_error; use simple_error;
use crate::ast::*; use crate::ast::*;
#[macro_use]
use crate::build::opcode::pointer::OpPointer; use crate::build::opcode::pointer::OpPointer;
use crate::build::opcode::translate; use crate::build::opcode::translate;
use crate::build::opcode::translate::OpsMap; use crate::build::opcode::translate::OpsMap;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::{Debug, Display}; use std::fmt::Debug;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -57,25 +57,23 @@ impl Builtins {
self.validate_mode = true; self.validate_mode = true;
} }
pub fn handle<'a, P, WP, O, E>( pub fn handle<'a, P, O, E>(
&mut self, &mut self,
path: Option<P>, path: Option<P>,
h: Hook, h: Hook,
stack: &mut Vec<(Rc<Value>, Position)>, stack: &mut Vec<(Rc<Value>, Position)>,
env: &'a RefCell<Environment<O, E>>, env: &'a RefCell<Environment<O, E>>,
import_stack: &mut Vec<String>, import_stack: &mut Vec<String>,
working_dir: WP,
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
P: AsRef<Path> + Debug, P: AsRef<Path> + Debug,
WP: Into<PathBuf> + Clone + Debug,
O: std::io::Write + Clone, O: std::io::Write + Clone,
E: std::io::Write + Clone, E: std::io::Write + Clone,
{ {
match h { match h {
Hook::Import => self.import(working_dir, stack, env, import_stack, pos), Hook::Import => self.import(stack, env, import_stack, pos),
Hook::Include => self.include(working_dir, stack, env, pos), Hook::Include => self.include(stack, env, pos),
Hook::Assert => self.assert(stack, env), Hook::Assert => self.assert(stack, env),
Hook::Convert => self.convert(stack, env, pos), Hook::Convert => self.convert(stack, env, pos),
Hook::Out => self.out(path, stack, env, pos), Hook::Out => self.out(path, stack, env, pos),
@ -88,99 +86,16 @@ impl Builtins {
} }
} }
fn normalize_path<P, BP>( fn get_file_as_string(&self, path: &str) -> Result<String, Error> {
&self, let mut f = File::open(path)?;
base_path: BP,
use_import_path: bool,
path: P,
) -> Result<PathBuf, Error>
where
BP: Into<PathBuf>,
P: Into<PathBuf>,
{
// Try a relative path first.
let path = path.into();
// stdlib paths are special
if path.starts_with(format!("std{}", std::path::MAIN_SEPARATOR)) {
return Ok(path);
}
let mut normalized = base_path.into();
if path.is_relative() {
normalized.push(&path);
// First see if the normalized file exists or not.
if !normalized.exists() && use_import_path {
// TODO(jwall): Support importing from a zip file in this
// import_path?
// 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(&path);
if p.exists() {
normalized = p;
break;
}
}
}
} else {
normalized = path;
}
// The canonicalize method on windows is not what we want so we'll
// do something a little different on windows that we would do on
// other Operating Systems.
#[cfg(target_os = "windows")]
{
Ok(normalized)
}
#[cfg(not(target_os = "windows"))]
{
Ok(normalized.canonicalize()?)
}
}
fn find_file<P, BP>(
&self,
base_path: BP,
path: P,
use_import_path: bool,
pos: Position,
) -> Result<PathBuf, Error>
where
P: Into<PathBuf> + Display + Clone,
BP: Into<PathBuf>,
{
// FIXME(jwall): Use import paths if desired.
match self.normalize_path(base_path, use_import_path, path.clone()) {
Ok(p) => Ok(p),
Err(e) => {
//panic!(format!("Error finding path {}: {}", path, e));
Err(Error::new(
format!("Error finding path {}: {}", path, e),
pos,
))
}
}
}
fn get_file_as_string<P: Into<PathBuf>>(
&self,
base_path: P,
path: &str,
pos: Position,
) -> Result<String, Error> {
let sep = format!("{}", std::path::MAIN_SEPARATOR);
let raw_path = path.replace("/", &sep);
// FIXME(jwall): import paths?
let normalized = self.find_file(base_path, raw_path, false, pos)?;
// TODO(jwall): Proper error here
let mut f = File::open(normalized)?;
let mut contents = String::new(); let mut contents = String::new();
// TODO(jwall): Proper error here // TODO(jwall): Proper error here
f.read_to_string(&mut contents)?; f.read_to_string(&mut contents)?;
Ok(contents) Ok(contents)
} }
fn import<'a, P, O, E>( fn import<'a, O, E>(
&mut self, &mut self,
base_path: P,
stack: &mut Vec<(Rc<Value>, Position)>, stack: &mut Vec<(Rc<Value>, Position)>,
env: &'a RefCell<Environment<O, E>>, env: &'a RefCell<Environment<O, E>>,
import_stack: &mut Vec<String>, import_stack: &mut Vec<String>,
@ -189,23 +104,17 @@ impl Builtins {
where where
O: std::io::Write + Clone, O: std::io::Write + Clone,
E: std::io::Write + Clone, E: std::io::Write + Clone,
P: Into<PathBuf> + Clone + Debug,
{ {
let path = stack.pop(); let path = stack.pop();
if let Some((val, path_pos)) = path { if let Some((val, path_pos)) = path {
if let &Value::P(Str(ref path)) = val.as_ref() { if let &Value::P(Str(ref path)) = val.as_ref() {
// FIXME(jwall): Most of this is no longer necessary since
// we do it before hand at the linker step.
// TODO(jwall): A bit hacky we should probably change import stacks to be pathbufs. // TODO(jwall): A bit hacky we should probably change import stacks to be pathbufs.
let normalized =
decorate_error!(path_pos => self.normalize_path(base_path, false, path))?;
// first we chack the cache // first we chack the cache
let path = normalized.to_string_lossy().to_string();
if let Some(val) = env.borrow().get_cached_path_val(&path) { if let Some(val) = env.borrow().get_cached_path_val(&path) {
stack.push((val, path_pos)); stack.push((val, path_pos));
return Ok(()); return Ok(());
} }
if import_stack.iter().find(|p| *p == &path).is_some() { if import_stack.iter().find(|p| *p == path).is_some() {
return Err(Error::new( return Err(Error::new(
format!("Import cycle detected: {} in {:?}", path, import_stack), format!("Import cycle detected: {} in {:?}", path, import_stack),
pos, pos,
@ -217,10 +126,12 @@ impl Builtins {
stack.push((v, path_pos)); stack.push((v, path_pos));
} }
None => { None => {
let op_pointer = decorate_error!(path_pos => env.borrow_mut().get_ops_for_path(&normalized))?; let path_buf = PathBuf::from(path);
let op_pointer =
decorate_error!(path_pos => env.borrow_mut().get_ops_for_path(&path))?;
// TODO(jwall): What if we don't have a base path? // TODO(jwall): What if we don't have a base path?
let mut vm = let mut vm =
VM::with_pointer(self.strict, op_pointer, normalized.parent().unwrap()) VM::with_pointer(self.strict, op_pointer, path_buf.parent().unwrap())
.with_import_stack(import_stack.clone()); .with_import_stack(import_stack.clone());
vm.run(env)?; vm.run(env)?;
let result = Rc::new(vm.symbols_to_tuple(true)); let result = Rc::new(vm.symbols_to_tuple(true));
@ -236,9 +147,8 @@ impl Builtins {
unreachable!(); unreachable!();
} }
fn include<'a, P, O, E>( fn include<'a, O, E>(
&self, &self,
base_path: P,
stack: &mut Vec<(Rc<Value>, Position)>, stack: &mut Vec<(Rc<Value>, Position)>,
env: &'a RefCell<Environment<O, E>>, env: &'a RefCell<Environment<O, E>>,
pos: Position, pos: Position,
@ -246,7 +156,6 @@ impl Builtins {
where where
O: std::io::Write + Clone, O: std::io::Write + Clone,
E: std::io::Write + Clone, E: std::io::Write + Clone,
P: Into<PathBuf> + Clone + Debug,
{ {
let path = stack.pop(); let path = stack.pop();
let typ = stack.pop(); let typ = stack.pop();
@ -273,18 +182,14 @@ impl Builtins {
}; };
if typ == "str" { if typ == "str" {
stack.push(( stack.push((
Rc::new(P(Str(self.get_file_as_string( Rc::new(P(Str(self.get_file_as_string(&path)?))),
base_path,
&path,
pos.clone(),
)?))),
pos.clone(), pos.clone(),
)); ));
} else { } else {
stack.push(( stack.push((
Rc::new(match env.borrow().importer_registry.get_importer(&typ) { Rc::new(match env.borrow().importer_registry.get_importer(&typ) {
Some(importer) => { Some(importer) => {
let contents = self.get_file_as_string(base_path, &path, pos.clone())?; let contents = self.get_file_as_string(&path)?;
if contents.len() == 0 { if contents.len() == 0 {
eprintln!("including an empty file. Use NULL as the result"); eprintln!("including an empty file. Use NULL as the result");
P(Empty) P(Empty)

View File

@ -1210,7 +1210,6 @@ impl VM {
&mut self.stack, &mut self.stack,
env, env,
&mut self.import_stack, &mut self.import_stack,
&self.working_dir,
pos, pos,
) )
} }