Most files suffered major changes
- Less ambiguous syntax - Better parser (Chumsky only does tokenization now) - Tidy(|ier) error handling - Facade for simplified embedding - External code grouped in (fairly) self-contained Systems - Dynamic action dispatch - Many STL additions
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// Error produced for the statement `import *`
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ImportAll {
|
||||
/// The file containing the offending import
|
||||
pub offender_file: Vec<String>,
|
||||
/// The module containing the offending import
|
||||
pub offender_mod: Vec<String>,
|
||||
}
|
||||
impl ProjectError for ImportAll {
|
||||
fn description(&self) -> &str {
|
||||
"a top-level glob import was used"
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!("{} imports *", self.offender_mod.join("::"))
|
||||
}
|
||||
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition {
|
||||
location: Location::File(Rc::new(self.offender_file.clone())),
|
||||
message: Some(format!("{} imports *", self.offender_mod.join("::"))),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//! Various errors the pipeline can produce
|
||||
mod import_all;
|
||||
mod not_exported;
|
||||
mod not_found;
|
||||
mod parse_error_with_path;
|
||||
mod project_error;
|
||||
mod too_many_supers;
|
||||
mod unexpected_directory;
|
||||
mod visibility_mismatch;
|
||||
|
||||
pub use import_all::ImportAll;
|
||||
pub use not_exported::NotExported;
|
||||
pub use not_found::NotFound;
|
||||
pub use parse_error_with_path::ParseErrorWithPath;
|
||||
pub use project_error::{ErrorPosition, ProjectError};
|
||||
pub use too_many_supers::TooManySupers;
|
||||
pub use unexpected_directory::UnexpectedDirectory;
|
||||
pub use visibility_mismatch::VisibilityMismatch;
|
||||
@@ -1,41 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// An import refers to a symbol which exists but is not exported.
|
||||
#[derive(Debug)]
|
||||
pub struct NotExported {
|
||||
/// The containing file - files are always exported
|
||||
pub file: Vec<String>,
|
||||
/// The path leading to the unexported module
|
||||
pub subpath: Vec<String>,
|
||||
/// The offending file
|
||||
pub referrer_file: Vec<String>,
|
||||
/// The module containing the offending import
|
||||
pub referrer_subpath: Vec<String>,
|
||||
}
|
||||
impl ProjectError for NotExported {
|
||||
fn description(&self) -> &str {
|
||||
"An import refers to a symbol that exists but isn't exported"
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
Box::new(
|
||||
[
|
||||
ErrorPosition {
|
||||
location: Location::File(Rc::new(self.file.clone())),
|
||||
message: Some(format!("{} isn't exported", self.subpath.join("::"))),
|
||||
},
|
||||
ErrorPosition {
|
||||
location: Location::File(Rc::new(self.referrer_file.clone())),
|
||||
message: Some(format!(
|
||||
"{} cannot see this symbol",
|
||||
self.referrer_subpath.join("::")
|
||||
)),
|
||||
},
|
||||
]
|
||||
.into_iter(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::representations::project::ProjectModule;
|
||||
#[allow(unused)] // For doc
|
||||
use crate::tree::Module;
|
||||
use crate::tree::WalkError;
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::BoxedIter;
|
||||
use crate::{Interner, NameLike, Tok};
|
||||
|
||||
/// Error produced when an import refers to a nonexistent module
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NotFound {
|
||||
/// The file containing the invalid import
|
||||
pub file: Vec<String>,
|
||||
/// The invalid import path
|
||||
pub subpath: Vec<String>,
|
||||
}
|
||||
impl NotFound {
|
||||
/// Produce this error from the parameters of [Module]`::walk_ref` and a
|
||||
/// [WalkError]
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - if `path` is shorter than the `pos` of the error
|
||||
/// - if a walk up to but not including `pos` fails
|
||||
///
|
||||
/// Basically, if `e` was not produced by the `walk*` methods called on
|
||||
/// `path`.
|
||||
pub fn from_walk_error(
|
||||
prefix: &[Tok<String>],
|
||||
path: &[Tok<String>],
|
||||
orig: &ProjectModule<impl NameLike>,
|
||||
e: WalkError,
|
||||
i: &Interner,
|
||||
) -> Self {
|
||||
let last_mod =
|
||||
orig.walk_ref(&path[..e.pos], false).expect("error occured on next step");
|
||||
let mut whole_path =
|
||||
prefix.iter().chain(path.iter()).map(|t| i.r(*t)).cloned();
|
||||
if let Some(file) = &last_mod.extra.file {
|
||||
Self {
|
||||
file: whole_path.by_ref().take(file.len()).collect(),
|
||||
subpath: whole_path.collect(),
|
||||
}
|
||||
} else {
|
||||
Self { file: whole_path.collect(), subpath: Vec::new() }
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ProjectError for NotFound {
|
||||
fn description(&self) -> &str {
|
||||
"an import refers to a nonexistent module"
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"module {} in {} was not found",
|
||||
self.subpath.join("::"),
|
||||
self.file.join("/"),
|
||||
)
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition::just_file(self.file.clone()))
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::parse::ParseError;
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// Produced by stages that parse text when it fails.
|
||||
#[derive(Debug)]
|
||||
pub struct ParseErrorWithPath {
|
||||
/// The complete source of the faulty file
|
||||
pub full_source: String,
|
||||
/// The path to the faulty file
|
||||
pub path: Vec<String>,
|
||||
/// The parse error produced by Chumsky
|
||||
pub error: ParseError,
|
||||
}
|
||||
impl ProjectError for ParseErrorWithPath {
|
||||
fn description(&self) -> &str {
|
||||
"Failed to parse code"
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
match &self.error {
|
||||
ParseError::Lex(lex) => Box::new(lex.iter().map(|s| ErrorPosition {
|
||||
location: Location::Range {
|
||||
file: Rc::new(self.path.clone()),
|
||||
range: s.span(),
|
||||
},
|
||||
message: Some(s.to_string()),
|
||||
})),
|
||||
ParseError::Ast(ast) => Box::new(ast.iter().map(|(_i, s)| {
|
||||
ErrorPosition {
|
||||
location: s
|
||||
.found()
|
||||
.map(|e| Location::Range {
|
||||
file: Rc::new(self.path.clone()),
|
||||
range: e.range.clone(),
|
||||
})
|
||||
.unwrap_or_else(|| Location::File(Rc::new(self.path.clone()))),
|
||||
message: Some(s.label().unwrap_or("Parse error").to_string()),
|
||||
}
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// A point of interest in resolving the error, such as the point where
|
||||
/// processing got stuck, a command that is likely to be incorrect
|
||||
pub struct ErrorPosition {
|
||||
/// The suspected location
|
||||
pub location: Location,
|
||||
/// Any information about the role of this location
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
impl ErrorPosition {
|
||||
/// An error position referring to an entire file with no comment
|
||||
pub fn just_file(file: Vec<String>) -> Self {
|
||||
Self { message: None, location: Location::File(Rc::new(file)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors addressed to the developer which are to be resolved with
|
||||
/// code changes
|
||||
pub trait ProjectError: Debug {
|
||||
/// A general description of this type of error
|
||||
fn description(&self) -> &str;
|
||||
/// A formatted message that includes specific parameters
|
||||
fn message(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
/// Code positions relevant to this error
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition>;
|
||||
/// Convert the error into an `Rc<dyn ProjectError>` to be able to
|
||||
/// handle various errors together
|
||||
fn rc(self) -> Rc<dyn ProjectError>
|
||||
where
|
||||
Self: Sized + 'static,
|
||||
{
|
||||
Rc::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for dyn ProjectError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let description = self.description();
|
||||
let message = self.message();
|
||||
let positions = self.positions();
|
||||
write!(f, "Problem with the project: {description}; {message}")?;
|
||||
for ErrorPosition { location, message } in positions {
|
||||
write!(
|
||||
f,
|
||||
"@{location}: {}",
|
||||
message.unwrap_or("location of interest".to_string())
|
||||
)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// Error produced when an import path starts with more `super` segments
|
||||
/// than the current module's absolute path
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct TooManySupers {
|
||||
/// The offending import path
|
||||
pub path: Vec<String>,
|
||||
/// The file containing the offending import
|
||||
pub offender_file: Vec<String>,
|
||||
/// The module containing the offending import
|
||||
pub offender_mod: Vec<String>,
|
||||
}
|
||||
impl ProjectError for TooManySupers {
|
||||
fn description(&self) -> &str {
|
||||
"an import path starts with more `super` segments than the current \
|
||||
module's absolute path"
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"path {} in {} contains too many `super` steps.",
|
||||
self.path.join("::"),
|
||||
self.offender_mod.join("::")
|
||||
)
|
||||
}
|
||||
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition {
|
||||
location: Location::File(Rc::new(self.offender_file.clone())),
|
||||
message: Some(format!(
|
||||
"path {} in {} contains too many `super` steps.",
|
||||
self.path.join("::"),
|
||||
self.offender_mod.join("::")
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
use super::{ErrorPosition, ProjectError};
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// Produced when a stage that deals specifically with code encounters
|
||||
/// a path that refers to a directory
|
||||
#[derive(Debug)]
|
||||
pub struct UnexpectedDirectory {
|
||||
/// Path to the offending collection
|
||||
pub path: Vec<String>,
|
||||
}
|
||||
impl ProjectError for UnexpectedDirectory {
|
||||
fn description(&self) -> &str {
|
||||
"A stage that deals specifically with code encountered a path that refers \
|
||||
to a directory"
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition::just_file(self.path.clone()))
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"{} was expected to be a file but a directory was found",
|
||||
self.path.join("/")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::project_error::{ErrorPosition, ProjectError};
|
||||
use crate::representations::location::Location;
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::BoxedIter;
|
||||
|
||||
/// Multiple occurences of the same namespace with different visibility
|
||||
#[derive(Debug)]
|
||||
pub struct VisibilityMismatch {
|
||||
/// The namespace with ambiguous visibility
|
||||
pub namespace: Vec<String>,
|
||||
/// The file containing the namespace
|
||||
pub file: Rc<Vec<String>>,
|
||||
}
|
||||
impl ProjectError for VisibilityMismatch {
|
||||
fn description(&self) -> &str {
|
||||
"Some occurences of a namespace are exported but others are not"
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition {
|
||||
location: Location::File(self.file.clone()),
|
||||
message: Some(format!(
|
||||
"{} is opened multiple times with different visibilities",
|
||||
self.namespace.join("::")
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::{fs, io};
|
||||
|
||||
use chumsky::text::Character;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
|
||||
#[allow(unused)] // for doc
|
||||
use crate::facade::System;
|
||||
use crate::interner::Interner;
|
||||
use crate::pipeline::error::{ErrorPosition, ProjectError};
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::{BoxedIter, Cache};
|
||||
use crate::{Stok, VName};
|
||||
@@ -23,10 +25,10 @@ impl ProjectError for FileLoadingError {
|
||||
fn description(&self) -> &str {
|
||||
"Neither a file nor a directory could be read from the requested path"
|
||||
}
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition::just_file(self.path.clone()))
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
fn message(&self, _i: &Interner) -> String {
|
||||
format!("File: {}\nDirectory: {}", self.file, self.dir)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +51,7 @@ impl Loaded {
|
||||
}
|
||||
|
||||
/// Returned by any source loading callback
|
||||
pub type IOResult = Result<Loaded, Rc<dyn ProjectError>>;
|
||||
pub type IOResult = ProjectResult<Loaded>;
|
||||
|
||||
/// Load a file from a path expressed in Rust strings, but relative to
|
||||
/// a root expressed as an OS Path.
|
||||
@@ -103,7 +105,8 @@ pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache<VName, IOResult> {
|
||||
pub fn load_embed<T: 'static + RustEmbed>(path: &str, ext: &str) -> IOResult {
|
||||
let file_path = path.to_string() + ext;
|
||||
if let Some(file) = T::get(&file_path) {
|
||||
let s = file.data.iter().map(|c| c.to_char()).collect::<String>();
|
||||
let s =
|
||||
String::from_utf8(file.data.to_vec()).expect("Embed must be valid UTF-8");
|
||||
Ok(Loaded::Code(Rc::new(s)))
|
||||
} else {
|
||||
let entries = T::iter()
|
||||
@@ -137,3 +140,38 @@ pub fn mk_embed_cache<'a, T: 'static + RustEmbed>(
|
||||
load_embed::<T>(&path, ext)
|
||||
})
|
||||
}
|
||||
|
||||
/// Load all files from an embed and convert them into a map usable in a
|
||||
/// [System]
|
||||
pub fn embed_to_map<T: 'static + RustEmbed>(
|
||||
suffix: &str,
|
||||
i: &Interner,
|
||||
) -> HashMap<Vec<Stok>, Loaded> {
|
||||
let mut files = HashMap::new();
|
||||
let mut dirs = HashMap::new();
|
||||
for path in T::iter() {
|
||||
let vpath = path
|
||||
.strip_suffix(suffix)
|
||||
.expect("the embed must be filtered for suffix")
|
||||
.split('/')
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let tokvpath = vpath.iter().map(|segment| i.i(segment)).collect::<Vec<_>>();
|
||||
let data = T::get(&path).expect("path from iterator").data;
|
||||
let text =
|
||||
String::from_utf8(data.to_vec()).expect("code embeds must be utf-8");
|
||||
files.insert(tokvpath.clone(), text);
|
||||
for (lvl, subname) in vpath.iter().enumerate() {
|
||||
let dirname = tokvpath.split_at(lvl).0;
|
||||
let (_, entries) = (dirs.raw_entry_mut().from_key(dirname))
|
||||
.or_insert_with(|| (dirname.to_vec(), HashSet::new()));
|
||||
entries.get_or_insert_with(subname, Clone::clone);
|
||||
}
|
||||
}
|
||||
(files.into_iter())
|
||||
.map(|(k, s)| (k, Loaded::Code(Rc::new(s))))
|
||||
.chain((dirs.into_iter()).map(|(k, entv)| {
|
||||
(k, Loaded::Collection(Rc::new(entv.into_iter().collect())))
|
||||
}))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::{ProjectError, TooManySupers};
|
||||
use crate::error::{ProjectError, ProjectResult, TooManySupers};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::representations::sourcefile::absolute_path;
|
||||
use crate::utils::Substack;
|
||||
@@ -10,7 +8,7 @@ pub fn import_abs_path(
|
||||
mod_stack: Substack<Tok<String>>,
|
||||
import_path: &[Tok<String>],
|
||||
i: &Interner,
|
||||
) -> Result<Vec<Tok<String>>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<Vec<Tok<String>>> {
|
||||
// path of module within file
|
||||
let mod_pathv = mod_stack.iter().rev_vec_clone();
|
||||
// path of module within compilation
|
||||
@@ -23,9 +21,9 @@ pub fn import_abs_path(
|
||||
// preload-target path within compilation
|
||||
absolute_path(&abs_pathv, import_path, i).map_err(|_| {
|
||||
TooManySupers {
|
||||
path: import_path.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
offender_file: src_path.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
offender_mod: mod_pathv.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
path: import_path.to_vec(),
|
||||
offender_file: src_path.to_vec(),
|
||||
offender_mod: mod_pathv,
|
||||
}
|
||||
.rc()
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::alias_map::AliasMap;
|
||||
use super::decls::UpdatedFn;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{NotExported, NotFound, ProjectError};
|
||||
use crate::error::{NotExported, NotFound, ProjectError, ProjectResult};
|
||||
use crate::interner::Tok;
|
||||
use crate::pipeline::project_tree::split_path;
|
||||
use crate::representations::project::{ProjectModule, ProjectTree};
|
||||
use crate::representations::tree::{ModMember, WalkErrorKind};
|
||||
@@ -15,8 +13,7 @@ fn assert_visible(
|
||||
source: &[Tok<String>], // must point to a file or submodule
|
||||
target: &[Tok<String>], // may point to a symbol or module of any kind
|
||||
project: &ProjectTree<VName>,
|
||||
i: &Interner,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<()> {
|
||||
let (tgt_item, tgt_path) = unwrap_or!(target.split_last(); return Ok(()));
|
||||
let shared_len =
|
||||
source.iter().zip(tgt_path.iter()).take_while(|(a, b)| a == b).count();
|
||||
@@ -27,11 +24,11 @@ fn assert_visible(
|
||||
WalkErrorKind::Private =>
|
||||
unreachable!("visibility is not being checked here"),
|
||||
WalkErrorKind::Missing => NotFound::from_walk_error(
|
||||
source,
|
||||
&[],
|
||||
&tgt_path[..vis_ignored_len],
|
||||
&project.0,
|
||||
e,
|
||||
i,
|
||||
)
|
||||
.rc(),
|
||||
})?;
|
||||
@@ -39,37 +36,51 @@ fn assert_visible(
|
||||
.walk_ref(&tgt_path[vis_ignored_len..], true)
|
||||
.map_err(|e| match e.kind {
|
||||
WalkErrorKind::Missing => NotFound::from_walk_error(
|
||||
source,
|
||||
&tgt_path[..vis_ignored_len],
|
||||
&tgt_path[vis_ignored_len..],
|
||||
&project.0,
|
||||
e,
|
||||
i,
|
||||
)
|
||||
.rc(),
|
||||
WalkErrorKind::Private => {
|
||||
let full_path = &tgt_path[..shared_len + e.pos];
|
||||
let (file, sub) = split_path(full_path, project);
|
||||
let (ref_file, ref_sub) = split_path(source, project);
|
||||
NotExported {
|
||||
file: i.extern_all(file),
|
||||
subpath: i.extern_all(sub),
|
||||
referrer_file: i.extern_all(ref_file),
|
||||
referrer_subpath: i.extern_all(ref_sub),
|
||||
// These errors are encountered during error reporting but they're more
|
||||
// fundamental / higher prio than the error to be raised and would
|
||||
// emerge nonetheless so they take over and the original error is
|
||||
// swallowed
|
||||
match split_path(full_path, project) {
|
||||
Err(e) =>
|
||||
NotFound::from_walk_error(source, &[], full_path, &project.0, e)
|
||||
.rc(),
|
||||
Ok((file, sub)) => {
|
||||
let (ref_file, ref_sub) = split_path(source, project)
|
||||
.expect("Source path assumed to be valid");
|
||||
NotExported {
|
||||
file: file.to_vec(),
|
||||
subpath: sub.to_vec(),
|
||||
referrer_file: ref_file.to_vec(),
|
||||
referrer_subpath: ref_sub.to_vec(),
|
||||
}
|
||||
.rc()
|
||||
},
|
||||
}
|
||||
.rc()
|
||||
},
|
||||
})?;
|
||||
let tgt_item_exported = direct_parent.extra.exports.contains_key(tgt_item);
|
||||
let target_prefixes_source = shared_len == tgt_path.len();
|
||||
if !tgt_item_exported && !target_prefixes_source {
|
||||
let (file, sub) = split_path(target, project);
|
||||
let (ref_file, ref_sub) = split_path(source, project);
|
||||
let (file, sub) = split_path(target, project).map_err(|e| {
|
||||
NotFound::from_walk_error(source, &[], target, &project.0, e).rc()
|
||||
})?;
|
||||
let (ref_file, ref_sub) = split_path(source, project)
|
||||
.expect("The source path is assumed to be valid");
|
||||
Err(
|
||||
NotExported {
|
||||
file: i.extern_all(file),
|
||||
subpath: i.extern_all(sub),
|
||||
referrer_file: i.extern_all(ref_file),
|
||||
referrer_subpath: i.extern_all(ref_sub),
|
||||
file: file.to_vec(),
|
||||
subpath: sub.to_vec(),
|
||||
referrer_file: ref_file.to_vec(),
|
||||
referrer_subpath: ref_sub.to_vec(),
|
||||
}
|
||||
.rc(),
|
||||
)
|
||||
@@ -84,9 +95,8 @@ fn collect_aliases_rec(
|
||||
module: &ProjectModule<VName>,
|
||||
project: &ProjectTree<VName>,
|
||||
alias_map: &mut AliasMap,
|
||||
i: &Interner,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<()> {
|
||||
// Assume injected module has been alias-resolved
|
||||
let mod_path_v = path.iter().rev_vec_clone();
|
||||
if !updated(&mod_path_v) {
|
||||
@@ -94,20 +104,18 @@ fn collect_aliases_rec(
|
||||
};
|
||||
for (&name, target_mod_name) in module.extra.imports_from.iter() {
|
||||
let target_sym_v = pushed(target_mod_name, name);
|
||||
assert_visible(&mod_path_v, &target_sym_v, project, i)?;
|
||||
assert_visible(&mod_path_v, &target_sym_v, project)?;
|
||||
let sym_path_v = pushed(&mod_path_v, name);
|
||||
let target_mod = (project.0.walk_ref(target_mod_name, false))
|
||||
.expect("checked above in assert_visible");
|
||||
let target_sym = target_mod
|
||||
.extra
|
||||
.exports
|
||||
.get(&name)
|
||||
let target_sym = (target_mod.extra.exports.get(&name))
|
||||
.ok_or_else(|| {
|
||||
let file_len =
|
||||
target_mod.extra.file.as_ref().unwrap_or(target_mod_name).len();
|
||||
NotFound {
|
||||
file: i.extern_all(&target_mod_name[..file_len]),
|
||||
subpath: i.extern_all(&target_sym_v[file_len..]),
|
||||
source: Some(mod_path_v.clone()),
|
||||
file: target_mod_name[..file_len].to_vec(),
|
||||
subpath: target_sym_v[file_len..].to_vec(),
|
||||
}
|
||||
.rc()
|
||||
})?
|
||||
@@ -121,7 +129,6 @@ fn collect_aliases_rec(
|
||||
submodule,
|
||||
project,
|
||||
alias_map,
|
||||
i,
|
||||
updated,
|
||||
)?
|
||||
}
|
||||
@@ -133,8 +140,7 @@ pub fn collect_aliases(
|
||||
module: &ProjectModule<VName>,
|
||||
project: &ProjectTree<VName>,
|
||||
alias_map: &mut AliasMap,
|
||||
i: &Interner,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
collect_aliases_rec(Substack::Bottom, module, project, alias_map, i, updated)
|
||||
) -> ProjectResult<()> {
|
||||
collect_aliases_rec(Substack::Bottom, module, project, alias_map, updated)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::alias_map::AliasMap;
|
||||
use super::apply_aliases::apply_aliases;
|
||||
use super::collect_aliases::collect_aliases;
|
||||
use super::decls::{InjectedAsFn, UpdatedFn};
|
||||
use crate::interner::Interner;
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::error::ProjectResult;
|
||||
use crate::representations::project::ProjectTree;
|
||||
use crate::representations::VName;
|
||||
|
||||
@@ -13,12 +10,11 @@ use crate::representations::VName;
|
||||
/// replace these aliases with the original names throughout the tree
|
||||
pub fn resolve_imports(
|
||||
project: ProjectTree<VName>,
|
||||
i: &Interner,
|
||||
injected_as: &impl InjectedAsFn,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> Result<ProjectTree<VName>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<ProjectTree<VName>> {
|
||||
let mut map = AliasMap::new();
|
||||
collect_aliases(&project.0, &project, &mut map, i, updated)?;
|
||||
collect_aliases(&project.0, &project, &mut map, updated)?;
|
||||
let new_mod = apply_aliases(&project.0, &map, injected_as, updated);
|
||||
Ok(ProjectTree(new_mod))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Loading Orchid modules from source
|
||||
pub mod error;
|
||||
pub mod file_loader;
|
||||
mod import_abs_path;
|
||||
mod import_resolution;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::ProjectError;
|
||||
use super::file_loader::IOResult;
|
||||
use super::{import_resolution, project_tree, source_loader};
|
||||
use crate::error::ProjectResult;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::representations::sourcefile::FileEntry;
|
||||
use crate::representations::VName;
|
||||
@@ -22,7 +22,7 @@ pub fn parse_layer<'a>(
|
||||
environment: &'a ProjectTree<VName>,
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
) -> Result<ProjectTree<VName>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<ProjectTree<VName>> {
|
||||
// A path is injected if it is walkable in the injected tree
|
||||
let injected_as = |path: &[Tok<String>]| {
|
||||
let (item, modpath) = path.split_last()?;
|
||||
@@ -40,7 +40,7 @@ pub fn parse_layer<'a>(
|
||||
let tree = project_tree::build_tree(source, i, prelude, &injected_names)?;
|
||||
let sum = ProjectTree(environment.0.clone().overlay(tree.0.clone()));
|
||||
let resolvd =
|
||||
import_resolution::resolve_imports(sum, i, &injected_as, &|path| {
|
||||
import_resolution::resolve_imports(sum, &injected_as, &|path| {
|
||||
tree.0.walk_ref(path, false).is_ok()
|
||||
})?;
|
||||
// Addition among modules favours the left hand side.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::interner::Tok;
|
||||
use crate::representations::sourcefile::{FileEntry, Member, Namespace};
|
||||
use crate::representations::sourcefile::{FileEntry, Member, ModuleBlock};
|
||||
|
||||
fn member_rec(
|
||||
// object
|
||||
@@ -9,9 +9,9 @@ fn member_rec(
|
||||
prelude: &[FileEntry],
|
||||
) -> Member {
|
||||
match member {
|
||||
Member::Namespace(Namespace { name, body }) => {
|
||||
Member::Module(ModuleBlock { name, body }) => {
|
||||
let new_body = entv_rec(body, path, prelude);
|
||||
Member::Namespace(Namespace { name, body: new_body })
|
||||
Member::Module(ModuleBlock { name, body: new_body })
|
||||
},
|
||||
any => any,
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::collect_ops;
|
||||
use super::collect_ops::InjectedOperatorsFn;
|
||||
use super::parse_file::parse_file;
|
||||
use crate::ast::{Constant, Expr};
|
||||
use crate::ast::{Clause, Constant, Expr};
|
||||
use crate::error::{ProjectError, ProjectResult, TooManySupers};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{ProjectError, TooManySupers};
|
||||
use crate::pipeline::source_loader::{LoadedSource, LoadedSourceTable};
|
||||
use crate::representations::project::{ProjectExt, ProjectTree};
|
||||
use crate::representations::sourcefile::{absolute_path, FileEntry, Member};
|
||||
use crate::representations::tree::{ModEntry, ModMember, Module};
|
||||
use crate::representations::{NameLike, VName};
|
||||
use crate::tree::{WalkError, WalkErrorKind};
|
||||
use crate::utils::iter::{box_empty, box_once};
|
||||
use crate::utils::{pushed, unwrap_or, Substack};
|
||||
|
||||
@@ -26,25 +25,27 @@ struct ParsedSource<'a> {
|
||||
|
||||
/// Split a path into file- and subpath in knowledge
|
||||
///
|
||||
/// # Panics
|
||||
/// # Errors
|
||||
///
|
||||
/// if the path is invalid
|
||||
#[allow(clippy::type_complexity)] // bit too sensitive here IMO
|
||||
pub fn split_path<'a>(
|
||||
path: &'a [Tok<String>],
|
||||
proj: &'a ProjectTree<impl NameLike>,
|
||||
) -> (&'a [Tok<String>], &'a [Tok<String>]) {
|
||||
) -> Result<(&'a [Tok<String>], &'a [Tok<String>]), WalkError> {
|
||||
let (end, body) = unwrap_or!(path.split_last(); {
|
||||
return (&[], &[])
|
||||
return Ok((&[], &[]))
|
||||
});
|
||||
let mut module = (proj.0.walk_ref(body, false))
|
||||
.expect("invalid path can't have been split above");
|
||||
if let ModMember::Sub(m) = &module.items[end].member {
|
||||
let mut module = (proj.0.walk_ref(body, false))?;
|
||||
let entry = (module.items.get(end))
|
||||
.ok_or(WalkError { pos: path.len() - 1, kind: WalkErrorKind::Missing })?;
|
||||
if let ModMember::Sub(m) = &entry.member {
|
||||
module = m;
|
||||
}
|
||||
let file =
|
||||
module.extra.file.as_ref().map(|s| &path[..s.len()]).unwrap_or(path);
|
||||
let subpath = &path[file.len()..];
|
||||
(file, subpath)
|
||||
Ok((file, subpath))
|
||||
}
|
||||
|
||||
/// Convert normalized, prefixed source into a module
|
||||
@@ -63,7 +64,7 @@ fn source_to_module(
|
||||
// context
|
||||
i: &Interner,
|
||||
filepath_len: usize,
|
||||
) -> Result<Module<Expr<VName>, ProjectExt<VName>>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<Module<Expr<VName>, ProjectExt<VName>>> {
|
||||
let path_v = path.iter().rev_vec_clone();
|
||||
let imports = (data.iter())
|
||||
.filter_map(|ent| {
|
||||
@@ -73,16 +74,16 @@ fn source_to_module(
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let imports_from = (imports.iter())
|
||||
.map(|imp| -> Result<_, Rc<dyn ProjectError>> {
|
||||
.map(|imp| -> ProjectResult<_> {
|
||||
let mut imp_path_v = i.r(imp.path).clone();
|
||||
imp_path_v.push(imp.name.expect("glob imports had just been resolved"));
|
||||
let mut abs_path = absolute_path(&path_v, &imp_path_v, i)
|
||||
.expect("should have failed in preparsing");
|
||||
let name = abs_path.pop().ok_or_else(|| {
|
||||
TooManySupers {
|
||||
offender_file: i.extern_all(&path_v[..filepath_len]),
|
||||
offender_mod: i.extern_all(&path_v[filepath_len..]),
|
||||
path: i.extern_all(&imp_path_v),
|
||||
offender_file: path_v[..filepath_len].to_vec(),
|
||||
offender_mod: path_v[filepath_len..].to_vec(),
|
||||
path: imp_path_v,
|
||||
}
|
||||
.rc()
|
||||
})?;
|
||||
@@ -96,15 +97,18 @@ fn source_to_module(
|
||||
FileEntry::Export(names) => Box::new(names.iter().copied().map(mk_ent)),
|
||||
FileEntry::Exported(mem) => match mem {
|
||||
Member::Constant(constant) => box_once(mk_ent(constant.name)),
|
||||
Member::Namespace(ns) => box_once(mk_ent(ns.name)),
|
||||
Member::Module(ns) => box_once(mk_ent(ns.name)),
|
||||
Member::Rule(rule) => {
|
||||
let mut names = Vec::new();
|
||||
for e in rule.pattern.iter() {
|
||||
e.visit_names(Substack::Bottom, &mut |n| {
|
||||
if let Some([name]) = n.strip_prefix(&path_v[..]) {
|
||||
names.push((*name, n.clone()))
|
||||
e.search_all(&mut |e| {
|
||||
if let Clause::Name(n) = &e.value {
|
||||
if let Some([name]) = n.strip_prefix(&path_v[..]) {
|
||||
names.push((*name, n.clone()))
|
||||
}
|
||||
}
|
||||
})
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
Box::new(names.into_iter())
|
||||
},
|
||||
@@ -124,7 +128,7 @@ fn source_to_module(
|
||||
let items = (data.into_iter())
|
||||
.filter_map(|ent| {
|
||||
let member_to_item = |exported, member| match member {
|
||||
Member::Namespace(ns) => {
|
||||
Member::Module(ns) => {
|
||||
let new_prep = unwrap_or!(
|
||||
&preparsed.items[&ns.name].member => ModMember::Sub;
|
||||
panic!("Preparsed should include entries for all submodules")
|
||||
@@ -171,7 +175,7 @@ fn files_to_module(
|
||||
path: Substack<Tok<String>>,
|
||||
files: Vec<ParsedSource>,
|
||||
i: &Interner,
|
||||
) -> Result<Module<Expr<VName>, ProjectExt<VName>>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<Module<Expr<VName>, ProjectExt<VName>>> {
|
||||
let lvl = path.len();
|
||||
debug_assert!(
|
||||
files.iter().map(|f| f.path.len()).max().unwrap() >= lvl,
|
||||
@@ -190,7 +194,7 @@ fn files_to_module(
|
||||
let items = (files.into_iter())
|
||||
.group_by(|f| f.path[lvl])
|
||||
.into_iter()
|
||||
.map(|(namespace, files)| -> Result<_, Rc<dyn ProjectError>> {
|
||||
.map(|(namespace, files)| -> ProjectResult<_> {
|
||||
let subpath = path.push(namespace);
|
||||
let files_v = files.collect::<Vec<_>>();
|
||||
let module = files_to_module(subpath, files_v, i)?;
|
||||
@@ -217,7 +221,7 @@ pub fn build_tree(
|
||||
i: &Interner,
|
||||
prelude: &[FileEntry],
|
||||
injected: &impl InjectedOperatorsFn,
|
||||
) -> Result<ProjectTree<VName>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<ProjectTree<VName>> {
|
||||
assert!(!files.is_empty(), "A tree requires at least one module");
|
||||
let ops_cache = collect_ops::mk_cache(&files, i, injected);
|
||||
let mut entries = files
|
||||
@@ -225,7 +229,7 @@ pub fn build_tree(
|
||||
.map(|(path, loaded)| {
|
||||
Ok((path, loaded, parse_file(path, &files, &ops_cache, i, prelude)?))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Rc<dyn ProjectError>>>()?;
|
||||
.collect::<ProjectResult<Vec<_>>>()?;
|
||||
// sort by similarity, then longest-first
|
||||
entries.sort_unstable_by(|a, b| a.0.cmp(b.0).reverse());
|
||||
let files = entries
|
||||
|
||||
@@ -3,14 +3,14 @@ use std::rc::Rc;
|
||||
use hashbrown::HashSet;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::error::{NotFound, ProjectError, ProjectResult};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{NotFound, ProjectError};
|
||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||
use crate::representations::tree::WalkErrorKind;
|
||||
use crate::utils::{split_max_prefix, unwrap_or, Cache};
|
||||
use crate::Sym;
|
||||
|
||||
pub type OpsResult = Result<Rc<HashSet<Tok<String>>>, Rc<dyn ProjectError>>;
|
||||
pub type OpsResult = ProjectResult<Rc<HashSet<Tok<String>>>>;
|
||||
pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>;
|
||||
|
||||
trait_set! {
|
||||
@@ -54,12 +54,9 @@ pub fn collect_exported_ops(
|
||||
unreachable!("visibility is not being checked here")
|
||||
},
|
||||
WalkErrorKind::Missing => NotFound {
|
||||
file: i.extern_all(fpath),
|
||||
subpath: (subpath.iter())
|
||||
.take(walk_err.pos)
|
||||
.map(|t| i.r(*t))
|
||||
.cloned()
|
||||
.collect(),
|
||||
source: None,
|
||||
file: fpath.to_vec(),
|
||||
subpath: subpath[..walk_err.pos].to_vec(),
|
||||
}
|
||||
.rc(),
|
||||
},
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::rc::Rc;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use super::exported_ops::{ExportedOpsCache, OpsResult};
|
||||
use crate::error::ProjectResult;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::parse::is_op;
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::import_abs_path::import_abs_path;
|
||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||
use crate::representations::tree::{ModMember, Module};
|
||||
@@ -39,16 +39,17 @@ pub fn collect_ops_for(
|
||||
let tree = &loaded[file].preparsed.0;
|
||||
let mut ret = HashSet::new();
|
||||
tree_all_ops(tree, &mut ret);
|
||||
tree.visit_all_imports(&mut |modpath, _module, import| {
|
||||
tree.visit_all_imports(&mut |modpath, _m, import| -> ProjectResult<()> {
|
||||
if let Some(n) = import.name {
|
||||
ret.insert(n);
|
||||
} else {
|
||||
let path = import_abs_path(file, modpath, &i.r(import.path)[..], i)
|
||||
.expect("This error should have been caught during loading");
|
||||
let path = i.expect(
|
||||
import_abs_path(file, modpath, &i.r(import.path)[..], i),
|
||||
"This error should have been caught during loading",
|
||||
);
|
||||
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());
|
||||
}
|
||||
Ok::<_, Rc<dyn ProjectError>>(())
|
||||
Ok(())
|
||||
})?;
|
||||
ret.drain_filter(|t| !is_op(i.r(*t)));
|
||||
Ok(Rc::new(ret))
|
||||
Ok(Rc::new(ret.into_iter().filter(|t| is_op(i.r(*t))).collect()))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::collect_ops::ExportedOpsCache;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::import_abs_path::import_abs_path;
|
||||
use crate::representations::sourcefile::{
|
||||
FileEntry, Import, Member, Namespace,
|
||||
FileEntry, Import, Member, ModuleBlock,
|
||||
};
|
||||
use crate::representations::tree::{ModMember, Module};
|
||||
use crate::utils::iter::box_once;
|
||||
@@ -20,14 +20,14 @@ fn member_rec(
|
||||
i: &Interner,
|
||||
) -> Member {
|
||||
match member {
|
||||
Member::Namespace(Namespace { name, body }) => {
|
||||
Member::Module(ModuleBlock { name, body }) => {
|
||||
let subprep = unwrap_or!(
|
||||
&preparsed.items[&name].member => ModMember::Sub;
|
||||
unreachable!("This name must point to a namespace")
|
||||
);
|
||||
let new_body =
|
||||
entv_rec(mod_stack.push(name), subprep, body, path, ops_cache, i);
|
||||
Member::Namespace(Namespace { name, body: new_body })
|
||||
Member::Module(ModuleBlock { name, body: new_body })
|
||||
},
|
||||
any => any,
|
||||
}
|
||||
@@ -58,10 +58,14 @@ fn entv_rec(
|
||||
.into_iter()
|
||||
.flat_map(|import| {
|
||||
if let Import { name: None, path } = import {
|
||||
let p = import_abs_path(mod_path, mod_stack, &i.r(path)[..], i)
|
||||
.expect("Should have emerged in preparsing");
|
||||
let names = (ops_cache.find(&i.i(&p)))
|
||||
.expect("Should have emerged in second parsing");
|
||||
let p = i.expect(
|
||||
import_abs_path(mod_path, mod_stack, &i.r(path)[..], i),
|
||||
"Should have emerged in preparsing",
|
||||
);
|
||||
let names = i.expect(
|
||||
ops_cache.find(&i.i(&p)),
|
||||
"Should have emerged in second parsing",
|
||||
);
|
||||
let imports = (names.iter())
|
||||
.map(move |&n| Import { name: Some(n), path })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -4,9 +4,9 @@ use super::add_prelude::add_prelude;
|
||||
use super::collect_ops::{collect_ops_for, ExportedOpsCache};
|
||||
use super::normalize_imports::normalize_imports;
|
||||
use super::prefix::prefix;
|
||||
use crate::error::ProjectResult;
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::parse;
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||
use crate::representations::sourcefile::{normalize_namespaces, FileEntry};
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn parse_file(
|
||||
ops_cache: &ExportedOpsCache,
|
||||
i: &Interner,
|
||||
prelude: &[FileEntry],
|
||||
) -> Result<Vec<FileEntry>, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<Vec<FileEntry>> {
|
||||
let ld = &loaded[path];
|
||||
// let ops_cache = collect_ops::mk_cache(loaded, i);
|
||||
let ops = collect_ops_for(path, loaded, ops_cache, i)?;
|
||||
@@ -34,8 +34,10 @@ pub fn parse_file(
|
||||
ops: &ops_vec,
|
||||
file: Rc::new(i.extern_all(path)),
|
||||
};
|
||||
let entries = parse::parse(ld.text.as_str(), ctx)
|
||||
.expect("This error should have been caught during loading");
|
||||
let entries = i.expect(
|
||||
parse::parse2(ld.text.as_str(), ctx),
|
||||
"This error should have been caught during loading",
|
||||
);
|
||||
let with_prelude = add_prelude(entries, path, prelude);
|
||||
let impnormalized =
|
||||
normalize_imports(&ld.preparsed.0, with_prelude, path, ops_cache, i);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::collect_ops::ExportedOpsCache;
|
||||
use crate::ast::{Constant, Rule};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::representations::sourcefile::{FileEntry, Member, Namespace};
|
||||
use crate::representations::sourcefile::{FileEntry, Member, ModuleBlock};
|
||||
use crate::utils::Substack;
|
||||
|
||||
fn member_rec(
|
||||
@@ -19,9 +19,9 @@ fn member_rec(
|
||||
.chain(mod_stack.iter().rev_vec_clone().into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
match data {
|
||||
Member::Namespace(Namespace { name, body }) => {
|
||||
Member::Module(ModuleBlock { name, body }) => {
|
||||
let new_body = entv_rec(mod_stack.push(name), body, path, ops_cache, i);
|
||||
Member::Namespace(Namespace { name, body: new_body })
|
||||
Member::Module(ModuleBlock { name, body: new_body })
|
||||
},
|
||||
Member::Constant(constant) => Member::Constant(Constant {
|
||||
name: constant.name,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::loaded_source::{LoadedSource, LoadedSourceTable};
|
||||
use super::preparse::preparse;
|
||||
use crate::error::{
|
||||
NoTargets, ProjectError, ProjectResult, UnexpectedDirectory,
|
||||
};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{ProjectError, UnexpectedDirectory};
|
||||
use crate::pipeline::file_loader::{IOResult, Loaded};
|
||||
use crate::pipeline::import_abs_path::import_abs_path;
|
||||
use crate::representations::sourcefile::FileEntry;
|
||||
@@ -19,7 +20,7 @@ fn load_abs_path_rec(
|
||||
i: &Interner,
|
||||
get_source: &impl Fn(&[Tok<String>]) -> IOResult,
|
||||
is_injected_module: &impl Fn(&[Tok<String>]) -> bool,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<()> {
|
||||
// # Termination
|
||||
//
|
||||
// Every recursion of this function either
|
||||
@@ -40,7 +41,7 @@ fn load_abs_path_rec(
|
||||
if let Some((filename, _)) = name_split {
|
||||
// if the filename is valid, load, preparse and record this file
|
||||
let text = unwrap_or!(get_source(filename)? => Loaded::Code; {
|
||||
return Err(UnexpectedDirectory { path: i.extern_all(filename) }.rc())
|
||||
return Err(UnexpectedDirectory { path: filename.to_vec() }.rc())
|
||||
});
|
||||
let preparsed = preparse(
|
||||
filename.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
@@ -56,6 +57,9 @@ fn load_abs_path_rec(
|
||||
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
|
||||
let abs_pathv =
|
||||
import_abs_path(filename, modpath, &import.nonglob_path(i), i)?;
|
||||
if abs_path.starts_with(&abs_pathv) {
|
||||
return Ok(());
|
||||
}
|
||||
// recurse on imported module
|
||||
load_abs_path_rec(
|
||||
&abs_pathv,
|
||||
@@ -111,9 +115,11 @@ pub fn load_source<'a>(
|
||||
i: &Interner,
|
||||
get_source: &impl Fn(&[Tok<String>]) -> IOResult,
|
||||
is_injected_module: &impl Fn(&[Tok<String>]) -> bool,
|
||||
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<LoadedSourceTable> {
|
||||
let mut table = LoadedSourceTable::new();
|
||||
let mut any_target = false;
|
||||
for target in targets {
|
||||
any_target |= true;
|
||||
load_abs_path_rec(
|
||||
target,
|
||||
&mut table,
|
||||
@@ -123,5 +129,5 @@ pub fn load_source<'a>(
|
||||
is_injected_module,
|
||||
)?
|
||||
}
|
||||
Ok(table)
|
||||
if any_target { Ok(table) } else { Err(NoTargets.rc()) }
|
||||
}
|
||||
|
||||
@@ -4,11 +4,9 @@ use std::rc::Rc;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::ast::Constant;
|
||||
use crate::error::{ProjectError, ProjectResult, VisibilityMismatch};
|
||||
use crate::interner::Interner;
|
||||
use crate::parse::{self, ParsingContext};
|
||||
use crate::pipeline::error::{
|
||||
ParseErrorWithPath, ProjectError, VisibilityMismatch,
|
||||
};
|
||||
use crate::representations::sourcefile::{
|
||||
imports, normalize_namespaces, FileEntry, Member,
|
||||
};
|
||||
@@ -38,12 +36,12 @@ fn to_module(src: &[FileEntry], prelude: &[FileEntry]) -> Module<(), ()> {
|
||||
let imports = imports(all_src()).cloned().collect::<Vec<_>>();
|
||||
let mut items = all_src()
|
||||
.filter_map(|ent| match ent {
|
||||
FileEntry::Internal(Member::Namespace(ns)) => {
|
||||
FileEntry::Internal(Member::Module(ns)) => {
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude));
|
||||
let entry = ModEntry { exported: false, member };
|
||||
Some((ns.name, entry))
|
||||
},
|
||||
FileEntry::Exported(Member::Namespace(ns)) => {
|
||||
FileEntry::Exported(Member::Module(ns)) => {
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude));
|
||||
let entry = ModEntry { exported: true, member };
|
||||
Some((ns.name, entry))
|
||||
@@ -55,8 +53,8 @@ fn to_module(src: &[FileEntry], prelude: &[FileEntry]) -> Module<(), ()> {
|
||||
match file_entry {
|
||||
FileEntry::Comment(_)
|
||||
| FileEntry::Import(_)
|
||||
| FileEntry::Internal(Member::Namespace(_))
|
||||
| FileEntry::Exported(Member::Namespace(_)) => (),
|
||||
| FileEntry::Internal(Member::Module(_))
|
||||
| FileEntry::Exported(Member::Module(_)) => (),
|
||||
FileEntry::Export(tokv) =>
|
||||
for tok in tokv {
|
||||
add_export(&mut items, *tok)
|
||||
@@ -89,24 +87,13 @@ pub fn preparse(
|
||||
source: &str,
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
) -> Result<Preparsed, Rc<dyn ProjectError>> {
|
||||
) -> ProjectResult<Preparsed> {
|
||||
// Parse with no operators
|
||||
let ctx = ParsingContext::<&str>::new(&[], i, Rc::new(file.clone()));
|
||||
let entries = parse::parse(source, ctx).map_err(|error| {
|
||||
ParseErrorWithPath {
|
||||
full_source: source.to_string(),
|
||||
error,
|
||||
path: file.clone(),
|
||||
}
|
||||
.rc()
|
||||
})?;
|
||||
let entries = parse::parse2(source, ctx)?;
|
||||
let normalized = normalize_namespaces(Box::new(entries.into_iter()))
|
||||
.map_err(|ns| {
|
||||
VisibilityMismatch {
|
||||
namespace: ns.into_iter().map(|t| i.r(t)).cloned().collect(),
|
||||
file: Rc::new(file.clone()),
|
||||
}
|
||||
.rc()
|
||||
.map_err(|namespace| {
|
||||
VisibilityMismatch { namespace, file: Rc::new(file.clone()) }.rc()
|
||||
})?;
|
||||
Ok(Preparsed(to_module(&normalized, prelude)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user