From 8ff022fa2cec34bc1d46ac3655d0c3d228ef893c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 30 Jan 2020 17:16:25 +0000 Subject: Move parse and resolve up a level --- dhall/src/error/mod.rs | 2 +- dhall/src/lib.rs | 6 +- dhall/src/semantics/mod.rs | 3 +- dhall/src/semantics/parse.rs | 37 +++++++ dhall/src/semantics/phase/mod.rs | 2 - dhall/src/semantics/phase/parse.rs | 37 ------- dhall/src/semantics/phase/resolve.rs | 181 ----------------------------------- dhall/src/semantics/resolve.rs | 181 +++++++++++++++++++++++++++++++++++ 8 files changed, 224 insertions(+), 225 deletions(-) create mode 100644 dhall/src/semantics/parse.rs delete mode 100644 dhall/src/semantics/phase/mod.rs delete mode 100644 dhall/src/semantics/phase/parse.rs delete mode 100644 dhall/src/semantics/phase/resolve.rs create mode 100644 dhall/src/semantics/resolve.rs diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 8991758..4df018d 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -1,6 +1,6 @@ use std::io::Error as IOError; -use crate::semantics::phase::resolve::ImportStack; +use crate::semantics::resolve::ImportStack; use crate::semantics::Value; use crate::syntax::{Import, ParseError}; use crate::NormalizedExpr; diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index cbf3ceb..e9f8aa3 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -21,9 +21,9 @@ use std::fmt::Display; use std::path::Path; use crate::error::{EncodeError, Error, ImportError, TypeError}; -use crate::semantics::phase::parse; -use crate::semantics::phase::resolve; -use crate::semantics::phase::resolve::ImportRoot; +use crate::semantics::parse; +use crate::semantics::resolve; +use crate::semantics::resolve::ImportRoot; use crate::semantics::{typecheck, typecheck_with, TyExpr, Value, ValueKind}; use crate::syntax::binary; use crate::syntax::{Builtin, Const, Expr}; diff --git a/dhall/src/semantics/mod.rs b/dhall/src/semantics/mod.rs index 92e5912..98fdf5a 100644 --- a/dhall/src/semantics/mod.rs +++ b/dhall/src/semantics/mod.rs @@ -1,6 +1,7 @@ pub mod builtins; pub mod nze; -pub mod phase; +pub mod parse; +pub mod resolve; pub mod tck; pub(crate) use self::builtins::*; pub(crate) use self::nze::*; diff --git a/dhall/src/semantics/parse.rs b/dhall/src/semantics/parse.rs new file mode 100644 index 0000000..ee35536 --- /dev/null +++ b/dhall/src/semantics/parse.rs @@ -0,0 +1,37 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; + +use crate::error::Error; +use crate::semantics::resolve::ImportRoot; +use crate::syntax::binary; +use crate::syntax::parse_expr; +use crate::Parsed; + +pub(crate) fn parse_file(f: &Path) -> Result { + let mut buffer = String::new(); + File::open(f)?.read_to_string(&mut buffer)?; + let expr = parse_expr(&*buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr, root)) +} + +pub(crate) fn parse_str(s: &str) -> Result { + let expr = parse_expr(s)?; + let root = ImportRoot::LocalDir(std::env::current_dir()?); + Ok(Parsed(expr, root)) +} + +pub(crate) fn parse_binary(data: &[u8]) -> Result { + let expr = binary::decode(data)?; + let root = ImportRoot::LocalDir(std::env::current_dir()?); + Ok(Parsed(expr, root)) +} + +pub(crate) fn parse_binary_file(f: &Path) -> Result { + let mut buffer = Vec::new(); + File::open(f)?.read_to_end(&mut buffer)?; + let expr = binary::decode(&buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr, root)) +} diff --git a/dhall/src/semantics/phase/mod.rs b/dhall/src/semantics/phase/mod.rs deleted file mode 100644 index 02e2b18..0000000 --- a/dhall/src/semantics/phase/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod parse; -pub(crate) mod resolve; diff --git a/dhall/src/semantics/phase/parse.rs b/dhall/src/semantics/phase/parse.rs deleted file mode 100644 index b72fe7f..0000000 --- a/dhall/src/semantics/phase/parse.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::fs::File; -use std::io::Read; -use std::path::Path; - -use crate::error::Error; -use crate::semantics::phase::resolve::ImportRoot; -use crate::syntax::binary; -use crate::syntax::parse_expr; -use crate::Parsed; - -pub(crate) fn parse_file(f: &Path) -> Result { - let mut buffer = String::new(); - File::open(f)?.read_to_string(&mut buffer)?; - let expr = parse_expr(&*buffer)?; - let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); - Ok(Parsed(expr, root)) -} - -pub(crate) fn parse_str(s: &str) -> Result { - let expr = parse_expr(s)?; - let root = ImportRoot::LocalDir(std::env::current_dir()?); - Ok(Parsed(expr, root)) -} - -pub(crate) fn parse_binary(data: &[u8]) -> Result { - let expr = binary::decode(data)?; - let root = ImportRoot::LocalDir(std::env::current_dir()?); - Ok(Parsed(expr, root)) -} - -pub(crate) fn parse_binary_file(f: &Path) -> Result { - let mut buffer = Vec::new(); - File::open(f)?.read_to_end(&mut buffer)?; - let expr = binary::decode(&buffer)?; - let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); - Ok(Parsed(expr, root)) -} diff --git a/dhall/src/semantics/phase/resolve.rs b/dhall/src/semantics/phase/resolve.rs deleted file mode 100644 index 3acf114..0000000 --- a/dhall/src/semantics/phase/resolve.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::collections::HashMap; -use std::path::{Path, PathBuf}; - -use crate::error::{Error, ImportError}; -use crate::syntax; -use crate::syntax::{FilePath, ImportLocation, URL}; -use crate::{Normalized, NormalizedExpr, Parsed, Resolved}; - -type Import = syntax::Import; - -/// A root from which to resolve relative imports. -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) enum ImportRoot { - LocalDir(PathBuf), -} - -type ImportCache = HashMap; - -pub(crate) type ImportStack = Vec; - -fn resolve_import( - import: &Import, - root: &ImportRoot, - import_cache: &mut ImportCache, - import_stack: &ImportStack, -) -> Result { - use self::ImportRoot::*; - use syntax::FilePrefix::*; - use syntax::ImportLocation::*; - let cwd = match root { - LocalDir(cwd) => cwd, - }; - match &import.location { - Local(prefix, path) => { - let path_buf: PathBuf = path.file_path.iter().collect(); - let path_buf = match prefix { - // TODO: fail gracefully - Parent => cwd.parent().unwrap().join(path_buf), - Here => cwd.join(path_buf), - _ => unimplemented!("{:?}", import), - }; - Ok(load_import(&path_buf, import_cache, import_stack).map_err( - |e| ImportError::Recursive(import.clone(), Box::new(e)), - )?) - } - _ => unimplemented!("{:?}", import), - } -} - -fn load_import( - f: &Path, - import_cache: &mut ImportCache, - import_stack: &ImportStack, -) -> Result { - Ok( - do_resolve_expr(Parsed::parse_file(f)?, import_cache, import_stack)? - .typecheck()? - .normalize(), - ) -} - -fn do_resolve_expr( - parsed: Parsed, - import_cache: &mut ImportCache, - import_stack: &ImportStack, -) -> Result { - let Parsed(mut expr, root) = parsed; - let mut resolve = |import: Import| -> Result { - if import_stack.contains(&import) { - return Err(ImportError::ImportCycle(import_stack.clone(), import)); - } - match import_cache.get(&import) { - Some(expr) => Ok(expr.clone()), - None => { - // Copy the import stack and push the current import - let mut import_stack = import_stack.clone(); - import_stack.push(import.clone()); - - // Resolve the import recursively - let expr = resolve_import( - &import, - &root, - import_cache, - &import_stack, - )?; - - // Add the import to the cache - import_cache.insert(import, expr.clone()); - Ok(expr) - } - } - }; - expr.traverse_resolve_mut(&mut resolve)?; - Ok(Resolved(expr)) -} - -pub(crate) fn resolve(e: Parsed) -> Result { - do_resolve_expr(e, &mut HashMap::new(), &Vec::new()) -} - -pub(crate) fn skip_resolve_expr( - parsed: Parsed, -) -> Result { - let mut expr = parsed.0; - let mut resolve = |import: Import| -> Result { - Err(ImportError::UnexpectedImport(import)) - }; - expr.traverse_resolve_mut(&mut resolve)?; - Ok(Resolved(expr)) -} - -pub trait Canonicalize { - fn canonicalize(&self) -> Self; -} - -impl Canonicalize for FilePath { - fn canonicalize(&self) -> FilePath { - let mut file_path = Vec::new(); - let mut file_path_components = self.file_path.clone().into_iter(); - - loop { - let component = file_path_components.next(); - match component.as_ref() { - // ─────────────────── - // canonicalize(ε) = ε - None => break, - - // canonicalize(directory₀) = directory₁ - // ─────────────────────────────────────── - // canonicalize(directory₀/.) = directory₁ - Some(c) if c == "." => continue, - - Some(c) if c == ".." => match file_path_components.next() { - // canonicalize(directory₀) = ε - // ──────────────────────────── - // canonicalize(directory₀/..) = /.. - None => file_path.push("..".to_string()), - - // canonicalize(directory₀) = directory₁/.. - // ────────────────────────────────────────────── - // canonicalize(directory₀/..) = directory₁/../.. - Some(ref c) if c == ".." => { - file_path.push("..".to_string()); - file_path.push("..".to_string()); - } - - // canonicalize(directory₀) = directory₁/component - // ─────────────────────────────────────────────── ; If "component" is not - // canonicalize(directory₀/..) = directory₁ ; ".." - Some(_) => continue, - }, - - // canonicalize(directory₀) = directory₁ - // ───────────────────────────────────────────────────────── ; If no other - // canonicalize(directory₀/component) = directory₁/component ; rule matches - Some(c) => file_path.push(c.clone()), - } - } - - FilePath { file_path } - } -} - -impl Canonicalize for ImportLocation { - fn canonicalize(&self) -> ImportLocation { - match self { - ImportLocation::Local(prefix, file) => { - ImportLocation::Local(*prefix, file.canonicalize()) - } - ImportLocation::Remote(url) => ImportLocation::Remote(URL { - scheme: url.scheme, - authority: url.authority.clone(), - path: url.path.canonicalize(), - query: url.query.clone(), - headers: url.headers.clone(), - }), - ImportLocation::Env(name) => ImportLocation::Env(name.to_string()), - ImportLocation::Missing => ImportLocation::Missing, - } - } -} diff --git a/dhall/src/semantics/resolve.rs b/dhall/src/semantics/resolve.rs new file mode 100644 index 0000000..3acf114 --- /dev/null +++ b/dhall/src/semantics/resolve.rs @@ -0,0 +1,181 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +use crate::error::{Error, ImportError}; +use crate::syntax; +use crate::syntax::{FilePath, ImportLocation, URL}; +use crate::{Normalized, NormalizedExpr, Parsed, Resolved}; + +type Import = syntax::Import; + +/// A root from which to resolve relative imports. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum ImportRoot { + LocalDir(PathBuf), +} + +type ImportCache = HashMap; + +pub(crate) type ImportStack = Vec; + +fn resolve_import( + import: &Import, + root: &ImportRoot, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result { + use self::ImportRoot::*; + use syntax::FilePrefix::*; + use syntax::ImportLocation::*; + let cwd = match root { + LocalDir(cwd) => cwd, + }; + match &import.location { + Local(prefix, path) => { + let path_buf: PathBuf = path.file_path.iter().collect(); + let path_buf = match prefix { + // TODO: fail gracefully + Parent => cwd.parent().unwrap().join(path_buf), + Here => cwd.join(path_buf), + _ => unimplemented!("{:?}", import), + }; + Ok(load_import(&path_buf, import_cache, import_stack).map_err( + |e| ImportError::Recursive(import.clone(), Box::new(e)), + )?) + } + _ => unimplemented!("{:?}", import), + } +} + +fn load_import( + f: &Path, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result { + Ok( + do_resolve_expr(Parsed::parse_file(f)?, import_cache, import_stack)? + .typecheck()? + .normalize(), + ) +} + +fn do_resolve_expr( + parsed: Parsed, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result { + let Parsed(mut expr, root) = parsed; + let mut resolve = |import: Import| -> Result { + if import_stack.contains(&import) { + return Err(ImportError::ImportCycle(import_stack.clone(), import)); + } + match import_cache.get(&import) { + Some(expr) => Ok(expr.clone()), + None => { + // Copy the import stack and push the current import + let mut import_stack = import_stack.clone(); + import_stack.push(import.clone()); + + // Resolve the import recursively + let expr = resolve_import( + &import, + &root, + import_cache, + &import_stack, + )?; + + // Add the import to the cache + import_cache.insert(import, expr.clone()); + Ok(expr) + } + } + }; + expr.traverse_resolve_mut(&mut resolve)?; + Ok(Resolved(expr)) +} + +pub(crate) fn resolve(e: Parsed) -> Result { + do_resolve_expr(e, &mut HashMap::new(), &Vec::new()) +} + +pub(crate) fn skip_resolve_expr( + parsed: Parsed, +) -> Result { + let mut expr = parsed.0; + let mut resolve = |import: Import| -> Result { + Err(ImportError::UnexpectedImport(import)) + }; + expr.traverse_resolve_mut(&mut resolve)?; + Ok(Resolved(expr)) +} + +pub trait Canonicalize { + fn canonicalize(&self) -> Self; +} + +impl Canonicalize for FilePath { + fn canonicalize(&self) -> FilePath { + let mut file_path = Vec::new(); + let mut file_path_components = self.file_path.clone().into_iter(); + + loop { + let component = file_path_components.next(); + match component.as_ref() { + // ─────────────────── + // canonicalize(ε) = ε + None => break, + + // canonicalize(directory₀) = directory₁ + // ─────────────────────────────────────── + // canonicalize(directory₀/.) = directory₁ + Some(c) if c == "." => continue, + + Some(c) if c == ".." => match file_path_components.next() { + // canonicalize(directory₀) = ε + // ──────────────────────────── + // canonicalize(directory₀/..) = /.. + None => file_path.push("..".to_string()), + + // canonicalize(directory₀) = directory₁/.. + // ────────────────────────────────────────────── + // canonicalize(directory₀/..) = directory₁/../.. + Some(ref c) if c == ".." => { + file_path.push("..".to_string()); + file_path.push("..".to_string()); + } + + // canonicalize(directory₀) = directory₁/component + // ─────────────────────────────────────────────── ; If "component" is not + // canonicalize(directory₀/..) = directory₁ ; ".." + Some(_) => continue, + }, + + // canonicalize(directory₀) = directory₁ + // ───────────────────────────────────────────────────────── ; If no other + // canonicalize(directory₀/component) = directory₁/component ; rule matches + Some(c) => file_path.push(c.clone()), + } + } + + FilePath { file_path } + } +} + +impl Canonicalize for ImportLocation { + fn canonicalize(&self) -> ImportLocation { + match self { + ImportLocation::Local(prefix, file) => { + ImportLocation::Local(*prefix, file.canonicalize()) + } + ImportLocation::Remote(url) => ImportLocation::Remote(URL { + scheme: url.scheme, + authority: url.authority.clone(), + path: url.path.canonicalize(), + query: url.query.clone(), + headers: url.headers.clone(), + }), + ImportLocation::Env(name) => ImportLocation::Env(name.to_string()), + ImportLocation::Missing => ImportLocation::Missing, + } + } +} -- cgit v1.2.3