From 81ce30dde067ca0067fda32b1e0ade1dbdfbdf58 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 29 Feb 2020 23:21:18 +0000 Subject: Implement `as Location` imports --- dhall/build.rs | 1 + dhall/src/semantics/resolve/hir.rs | 3 + dhall/src/semantics/resolve/resolve.rs | 129 ++++++++++++++++----- dhall/src/syntax/ast/text.rs | 29 ----- .../import/success/unit/asLocation/Chain3A.dhall | 2 +- 5 files changed, 104 insertions(+), 60 deletions(-) (limited to 'dhall') diff --git a/dhall/build.rs b/dhall/build.rs index 137ef02..2c70b89 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -258,6 +258,7 @@ fn generate_tests() -> std::io::Result<()> { || path == "hashFromCache" || path == "headerForwarding" || path == "noHeaderForwarding" + || path == "unit/asLocation/Remote" }), input_type: FileType::Text, output_type: Some(FileType::Text), diff --git a/dhall/src/semantics/resolve/hir.rs b/dhall/src/semantics/resolve/hir.rs index 2f3464a..317708a 100644 --- a/dhall/src/semantics/resolve/hir.rs +++ b/dhall/src/semantics/resolve/hir.rs @@ -72,6 +72,9 @@ impl Hir { ) -> Result, TypeError> { type_with(env, self, None) } + pub fn typecheck_noenv<'hir>(&'hir self) -> Result, TypeError> { + self.typecheck(&TyEnv::new()) + } /// Eval the Hir. It will actually get evaluated only as needed on demand. pub fn eval(&self, env: impl Into) -> Nir { diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 82800ec..b27dd2c 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use std::borrow::Cow; use std::path::{Path, PathBuf}; @@ -5,7 +6,11 @@ use crate::error::ErrorBuilder; use crate::error::{Error, ImportError}; use crate::semantics::{mkerr, Hir, HirKind, ImportEnv, NameEnv, Type}; use crate::syntax; -use crate::syntax::{BinOp, Expr, ExprKind, FilePath, ImportLocation, URL}; +use crate::syntax::map::DupTreeMap; +use crate::syntax::{ + BinOp, Builtin, Expr, ExprKind, FilePath, FilePrefix, ImportLocation, + ImportMode, Span, URL, +}; use crate::{Parsed, ParsedExpr, Resolved}; // TODO: evaluate import headers @@ -25,24 +30,95 @@ fn resolve_one_import( import: &Import, root: &ImportRoot, ) -> Result { - use self::ImportRoot::*; - use syntax::FilePrefix::*; - use syntax::ImportLocation::*; let cwd = match root { - LocalDir(cwd) => cwd, + ImportRoot::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), + + match import.mode { + ImportMode::Code => { + match &import.location { + ImportLocation::Local(prefix, path) => { + let path_buf: PathBuf = path.file_path.iter().collect(); + let path_buf = match prefix { + // TODO: fail gracefully + FilePrefix::Parent => { + cwd.parent().unwrap().join(path_buf) + } + FilePrefix::Here => cwd.join(path_buf), + _ => unimplemented!("{:?}", import), + }; + Ok(load_import(env, &path_buf)?) + } + _ => unimplemented!("{:?}", import), + } + } + ImportMode::RawText => unimplemented!("{:?}", import), + ImportMode::Location => { + let mkexpr = |kind| Expr::new(kind, Span::Artificial); + let text_type = mkexpr(ExprKind::Builtin(Builtin::Text)); + let mut location_union = DupTreeMap::default(); + location_union.insert("Local".into(), Some(text_type.clone())); + location_union.insert("Remote".into(), Some(text_type.clone())); + location_union + .insert("Environment".into(), Some(text_type.clone())); + location_union.insert("Missing".into(), None); + let location_union = mkexpr(ExprKind::UnionType(location_union)); + + let expr = match &import.location { + ImportLocation::Local(prefix, path) => { + let mut cwd: Vec = cwd + .components() + .map(|component| { + component.as_os_str().to_string_lossy().into_owned() + }) + .collect(); + let root = match prefix { + FilePrefix::Here => cwd, + FilePrefix::Parent => { + cwd.push("..".to_string()); + cwd + } + FilePrefix::Absolute => vec![], + FilePrefix::Home => vec![], + }; + let path: Vec<_> = root + .into_iter() + .chain(path.file_path.iter().cloned()) + .collect(); + let path = + (FilePath { file_path: path }).canonicalize().file_path; + let prefix = match prefix { + FilePrefix::Here | FilePrefix::Parent => ".", + FilePrefix::Absolute => "", + FilePrefix::Home => "~", + }; + let path = Some(prefix.to_string()) + .into_iter() + .chain(path) + .join("/"); + + mkexpr(ExprKind::App( + mkexpr(ExprKind::Field(location_union, "Local".into())), + mkexpr(ExprKind::TextLit(path.into())), + )) + } + ImportLocation::Env(name) => mkexpr(ExprKind::App( + mkexpr(ExprKind::Field( + location_union, + "Environment".into(), + )), + mkexpr(ExprKind::TextLit(name.clone().into())), + )), + ImportLocation::Missing => { + mkexpr(ExprKind::Field(location_union, "Missing".into())) + } _ => unimplemented!("{:?}", import), }; - Ok(load_import(env, &path_buf)?) + + let hir = skip_resolve(&expr)?; + let ty = hir.typecheck_noenv()?.ty().clone(); + Ok((hir, ty)) } - _ => unimplemented!("{:?}", import), } } @@ -166,21 +242,15 @@ pub trait Canonicalize { 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, + for c in &self.file_path { + match c.as_ref() { // canonicalize(directory₀) = directory₁ // ─────────────────────────────────────── // canonicalize(directory₀/.) = directory₁ - Some(c) if c == "." => continue, + "." => continue, - Some(c) if c == ".." => match file_path_components.next() { + ".." => match file_path.last() { // canonicalize(directory₀) = ε // ──────────────────────────── // canonicalize(directory₀/..) = /.. @@ -189,21 +259,20 @@ impl Canonicalize for FilePath { // canonicalize(directory₀) = directory₁/.. // ────────────────────────────────────────────── // canonicalize(directory₀/..) = directory₁/../.. - Some(ref c) if c == ".." => { - file_path.push("..".to_string()); - file_path.push("..".to_string()); - } + Some(c) if c == ".." => file_path.push("..".to_string()), // canonicalize(directory₀) = directory₁/component // ─────────────────────────────────────────────── ; If "component" is not // canonicalize(directory₀/..) = directory₁ ; ".." - Some(_) => continue, + Some(_) => { + file_path.pop(); + } }, // canonicalize(directory₀) = directory₁ // ───────────────────────────────────────────────────────── ; If no other // canonicalize(directory₀/component) = directory₁/component ; rule matches - Some(c) => file_path.push(c.clone()), + _ => file_path.push(c.clone()), } } diff --git a/dhall/src/syntax/ast/text.rs b/dhall/src/syntax/ast/text.rs index 83aaf9a..c40f4a1 100644 --- a/dhall/src/syntax/ast/text.rs +++ b/dhall/src/syntax/ast/text.rs @@ -54,16 +54,6 @@ impl InterpolatedTextContents { Text(s) => Text(s.clone()), }) } - pub fn traverse_mut<'a, E, F>(&'a mut self, mut f: F) -> Result<(), E> - where - F: FnMut(&'a mut SubExpr) -> Result<(), E>, - { - use InterpolatedTextContents::Expr; - if let Expr(e) = self { - f(e)?; - } - Ok(()) - } pub fn map_ref<'a, SubExpr2, F>( &'a self, mut f: F, @@ -77,15 +67,6 @@ impl InterpolatedTextContents { Text(s) => Text(s.clone()), } } - pub fn map_mut<'a, F>(&'a mut self, mut f: F) - where - F: FnMut(&'a mut SubExpr), - { - use InterpolatedTextContents::Expr; - if let Expr(e) = self { - f(e); - } - } } impl InterpolatedText { @@ -126,16 +107,6 @@ impl InterpolatedText { }) } - pub fn traverse_mut<'a, E, F>(&'a mut self, mut f: F) -> Result<(), E> - where - F: FnMut(&'a mut SubExpr) -> Result<(), E>, - { - for (e, _) in &mut self.tail { - f(e)? - } - Ok(()) - } - pub fn iter<'a>( &'a self, ) -> impl Iterator> + 'a { diff --git a/dhall/tests/import/success/unit/asLocation/Chain3A.dhall b/dhall/tests/import/success/unit/asLocation/Chain3A.dhall index b44f0d4..57751f6 100644 --- a/dhall/tests/import/success/unit/asLocation/Chain3A.dhall +++ b/dhall/tests/import/success/unit/asLocation/Chain3A.dhall @@ -1 +1 @@ -./../asLocation/Canonicalize4A.dhall +../asLocation/Canonicalize4A.dhall -- cgit v1.2.3