Import and export improved

- Import paths are now vname and not sym
- Imports and exports accept multiple space-delimited operators in []

as a result, we can now reliably import and export the operator *

- error reporting ergonomics
This commit is contained in:
2023-08-18 21:10:29 +01:00
parent 3fdabc29da
commit 9186bce956
33 changed files with 269 additions and 228 deletions

View File

@@ -1,9 +1,7 @@
use std::rc::Rc;
use super::{ErrorPosition, ProjectError};
use super::ProjectError;
use crate::representations::location::Location;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, VName};
/// Error produced for the statement `import *`
@@ -22,13 +20,7 @@ impl ProjectError for ImportAll {
format!("{} imports *", i.extern_all(&self.offender_mod).join("::"))
}
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition {
location: Location::File(self.offender_file.clone()),
message: Some(format!(
"{} imports *",
i.extern_all(&self.offender_mod).join("::")
)),
})
fn one_position(&self, _i: &Interner) -> Location {
Location::File(self.offender_file.clone())
}
}

View File

@@ -1,11 +1,11 @@
use super::{ErrorPosition, ProjectError};
use std::rc::Rc;
use super::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, VName};
use crate::{Interner, Location, NameLike, Tok, VName};
/// Error produced when an import refers to a nonexistent module
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -64,7 +64,7 @@ impl ProjectError for NotFound {
i.extern_all(&self.file).join("/"),
)
}
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition::just_file(i.extern_all(&self.file)))
fn one_position(&self, i: &Interner) -> crate::Location {
Location::File(Rc::new(i.extern_all(&self.file)))
}
}

View File

@@ -2,6 +2,7 @@ use std::rc::Rc;
use crate::interner::InternedDisplay;
use crate::representations::location::Location;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::Interner;
@@ -14,13 +15,6 @@ pub struct ErrorPosition {
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 {
@@ -30,8 +24,16 @@ pub trait ProjectError {
fn message(&self, _i: &Interner) -> String {
self.description().to_string()
}
/// Code positions relevant to this error
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition>;
/// Code positions relevant to this error. If you don't implement this, you
/// must implement [ProjectError::one_position]
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.one_position(i), message: None })
}
/// Short way to provide a single location. If you don't implement this, you
/// must implement [ProjectError::positions]
fn one_position(&self, _i: &Interner) -> Location {
unimplemented!()
}
/// Convert the error into an `Rc<dyn ProjectError>` to be able to
/// handle various errors together
fn rc(self) -> Rc<dyn ProjectError>

View File

@@ -1,9 +1,7 @@
use std::rc::Rc;
use super::{ErrorPosition, ProjectError};
use super::ProjectError;
use crate::representations::location::Location;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, VName};
/// Error produced when an import path starts with more `super` segments
@@ -30,14 +28,7 @@ impl ProjectError for TooManySupers {
)
}
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition {
location: Location::File(Rc::new(i.extern_all(&self.offender_file))),
message: Some(format!(
"path {} in {} contains too many `super` steps.",
i.extern_all(&self.path).join("::"),
i.extern_all(&self.offender_mod).join("::")
)),
})
fn one_position(&self, i: &Interner) -> Location {
Location::File(Rc::new(i.extern_all(&self.offender_file)))
}
}

View File

@@ -1,7 +1,7 @@
use super::{ErrorPosition, ProjectError};
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, VName};
use std::rc::Rc;
use super::ProjectError;
use crate::{Interner, Location, VName};
/// Produced when a stage that deals specifically with code encounters
/// a path that refers to a directory
@@ -15,9 +15,10 @@ impl ProjectError for UnexpectedDirectory {
"A stage that deals specifically with code encountered a path that refers \
to a directory"
}
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition::just_file(i.extern_all(&self.path)))
fn one_position(&self, i: &Interner) -> crate::Location {
Location::File(Rc::new(i.extern_all(&self.path)))
}
fn message(&self, i: &Interner) -> String {
format!(
"{} was expected to be a file but a directory was found",

View File

@@ -1,9 +1,7 @@
use std::rc::Rc;
use super::project_error::{ErrorPosition, ProjectError};
use super::project_error::ProjectError;
use crate::representations::location::Location;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, VName};
/// Multiple occurences of the same namespace with different visibility
@@ -18,13 +16,13 @@ impl ProjectError for VisibilityMismatch {
fn description(&self) -> &str {
"Some occurences of a namespace are exported but others are not"
}
fn positions(&self, i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition {
location: Location::File(self.file.clone()),
message: Some(format!(
"{} is opened multiple times with different visibilities",
i.extern_all(&self.namespace).join("::")
)),
})
fn message(&self, i: &Interner) -> String {
format!(
"{} is opened multiple times with different visibilities",
i.extern_all(&self.namespace).join("::")
)
}
fn one_position(&self, _i: &Interner) -> Location {
Location::File(self.file.clone())
}
}

View File

@@ -4,11 +4,9 @@ use std::rc::Rc;
use hashbrown::HashMap;
use super::{Process, System};
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
use crate::error::{ProjectError, ProjectResult};
use crate::interpreter::HandlerTable;
use crate::rule::Repo;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{
ast, ast_to_interpreted, collect_consts, collect_rules, rule, Interner,
Location, ProjectTree, Sym,
@@ -128,7 +126,7 @@ impl ProjectError for MacroTimeout {
)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}

View File

@@ -1,14 +1,12 @@
use hashbrown::HashMap;
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
use crate::error::{ProjectError, ProjectResult};
use crate::interpreted::{self, ExprInst};
#[allow(unused)] // for doc
use crate::interpreter;
use crate::interpreter::{
run_handler, Context, HandlerTable, Return, RuntimeError,
};
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, Location, Sym};
/// This struct ties the state of systems to loaded code, and allows to call
@@ -84,7 +82,7 @@ impl ProjectError for MissingSymbol {
)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}

View File

@@ -41,7 +41,7 @@ use crate::Primitive;
/// ```
/// use orchidlang::{Literal};
/// use orchidlang::interpreted::ExprInst;
/// use orchidlang::stl::litconv::with_lit;
/// use orchidlang::systems::cast_exprinst::with_lit;
/// use orchidlang::{atomic_impl, atomic_redirect, externfn_impl};
///
/// /// Convert a literal to a string using Rust's conversions for floats, chars and

View File

@@ -41,7 +41,7 @@ use crate::write_fn_step;
///
/// ```
/// use orchidlang::interpreted::Clause;
/// use orchidlang::stl::litconv::with_str;
/// use orchidlang::systems::cast_exprinst::with_str;
/// use orchidlang::{define_fn, Literal, Primitive};
///
/// define_fn! {expr=x in
@@ -58,7 +58,7 @@ use crate::write_fn_step;
/// A simpler format is also offered for unary functions:
///
/// ```
/// use orchidlang::stl::litconv::with_lit;
/// use orchidlang::systems::cast_exprinst::with_lit;
/// use orchidlang::{define_fn, Literal};
///
/// define_fn! {

View File

@@ -18,14 +18,14 @@ use crate::interpreted::ExprInst;
/// discussed below. The newly bound names (here `s` and `i` before `=`) can
/// also receive type annotations.
///
/// ```no_run
/// ```
/// // FIXME this is a very old example that wouldn't compile now
/// use unicode_segmentation::UnicodeSegmentation;
///
/// use orchidlang::{write_fn_step, Literal, Primitive};
/// use orchidlang::interpreted::Clause;
/// use orchidlang::stl::litconv::{with_str, with_uint};
/// use orchidlang::stl::RuntimeError;
/// use orchidlang::systems::cast_exprinst::{with_str, with_uint};
/// use orchidlang::systems::RuntimeError;
///
/// // Initial state
/// write_fn_step!(pub CharAt2 > CharAt1);
@@ -40,7 +40,7 @@ use crate::interpreted::ExprInst;
/// i = x => with_uint(x, Ok);
/// {
/// if let Some(c) = s.graphemes(true).nth(*i as usize) {
/// Ok(Literal::Char(c.to_string()).into())
/// Ok(Literal::Str(c.to_string()).into())
/// } else {
/// RuntimeError::fail(
/// "Character index out of bounds".to_string(),

View File

@@ -1,5 +1,4 @@
use std::any::{Any, TypeId};
use std::mem;
use std::rc::Rc;
use hashbrown::HashMap;
@@ -8,7 +7,6 @@ use trait_set::trait_set;
use super::{run, Context, Return, RuntimeError};
use crate::foreign::ExternError;
use crate::interpreted::{Clause, ExprInst};
use crate::utils::unwrap_or;
use crate::Primitive;
trait_set! {
@@ -65,20 +63,15 @@ pub fn run_handler(
) -> Result<Return, RuntimeError> {
loop {
let ret = run(expr.clone(), ctx.clone())?;
if ret.gas == Some(0) {
return Ok(ret);
if let Clause::P(Primitive::Atom(a)) = &ret.state.expr().clause {
if let Some(e) = handlers.dispatch(a.0.as_any()) {
expr = e?;
ctx.gas = ret.gas;
if ret.gas.map_or(true, |g| g > 0) {
continue;
}
}
}
let state_ex = ret.state.expr();
let a = if let Clause::P(Primitive::Atom(a)) = &state_ex.clause {
a
} else {
mem::drop(state_ex);
return Ok(ret);
};
expr = unwrap_or!(handlers.dispatch(a.0.as_any()); {
mem::drop(state_ex);
return Ok(ret)
})?;
ctx.gas = ret.gas;
return Ok(ret);
}
}

View File

@@ -34,4 +34,4 @@ pub use representations::{
ast, from_const_tree, interpreted, sourcefile, tree, ConstTree, Literal,
Location, PathSet, Primitive,
};
pub use utils::{Side, Substack, ThreadPool};
pub use utils::{thread_pool, Side, Substack};

View File

@@ -6,7 +6,6 @@ use itertools::Itertools;
use super::{Entry, Lexeme};
use crate::error::{ErrorPosition, ProjectError};
use crate::interner::InternedDisplay;
use crate::utils::iter::box_once;
use crate::utils::BoxedIter;
use crate::{Interner, Location, Tok};
@@ -21,8 +20,8 @@ impl ProjectError for LineNeedsPrefix {
fn message(&self, i: &Interner) -> String {
format!("{} cannot appear at the beginning of a line", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { message: None, location: self.entry.location() })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -42,8 +41,8 @@ impl ProjectError for UnexpectedEOL {
.to_string()
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { message: None, location: self.entry.location() })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -54,8 +53,8 @@ impl ProjectError for ExpectedEOL {
fn description(&self) -> &str {
"Expected the end of the line"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}
@@ -87,8 +86,8 @@ impl ProjectError for ExpectedName {
}
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -126,8 +125,9 @@ impl ProjectError for Expected {
let or_name = if self.or_name { " or a name" } else { "" };
format!("Expected {}{} but found {}", list, or_name, self.found.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.found.location(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.found.location()
}
}
@@ -143,8 +143,8 @@ impl ProjectError for ReservedToken {
format!("{} is a reserved token", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -161,8 +161,8 @@ impl ProjectError for BadTokenInRegion {
format!("{} cannot appear in {}", self.entry.bundle(i), self.region)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -179,8 +179,8 @@ impl ProjectError for NotFound {
format!("{} was expected", self.expected)
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}
@@ -191,8 +191,8 @@ impl ProjectError for LeadingNS {
fn description(&self) -> &str {
":: can only follow a name token"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}
@@ -206,8 +206,8 @@ impl ProjectError for MisalignedParen {
fn message(&self, i: &Interner) -> String {
format!("This {} has no pair", self.entry.bundle(i))
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.entry.location(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.entry.location()
}
}
@@ -218,8 +218,20 @@ impl ProjectError for NamespacedExport {
fn description(&self) -> &str {
"Exports can only refer to unnamespaced names in the local namespace"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}
pub struct GlobExport {
pub location: Location,
}
impl ProjectError for GlobExport {
fn description(&self) -> &str {
"Exports can only refer to concrete names, globstars are not allowed"
}
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}

View File

@@ -1,18 +1,45 @@
use std::collections::VecDeque;
use std::iter;
use super::context::Context;
use super::errors::{Expected, ExpectedName};
use super::errors::Expected;
use super::stream::Stream;
use super::Lexeme;
use crate::error::{ProjectError, ProjectResult};
use crate::utils::iter::{box_chain, box_once, BoxedIterIter};
use crate::sourcefile::Import;
use crate::utils::iter::{box_chain, box_once};
use crate::utils::BoxedIter;
use crate::Tok;
struct Subresult {
glob: bool,
deque: VecDeque<Tok<String>>,
}
impl Subresult {
fn new_glob() -> Self {
Self { glob: true, deque: VecDeque::new() }
}
fn new_named(name: Tok<String>) -> Self {
Self { glob: false, deque: VecDeque::from([name]) }
}
fn push_front(mut self, name: Tok<String>) -> Self {
self.deque.push_front(name);
self
}
fn finalize(self) -> Import {
let Self { mut deque, glob } = self;
debug_assert!(glob || !deque.is_empty(), "The constructors forbid this");
let name = if glob { None } else { deque.pop_back() };
Import { name, path: deque.into() }
}
}
fn parse_multiname_branch(
cursor: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(BoxedIterIter<Tok<String>>, Stream<'_>)> {
) -> ProjectResult<(BoxedIter<Subresult>, Stream<'_>)> {
let comma = ctx.interner().i(",");
let (subnames, cursor) = parse_multiname_rec(cursor, ctx.clone())?;
let (delim, cursor) = cursor.trim().pop()?;
@@ -33,30 +60,46 @@ fn parse_multiname_branch(
}
}
pub fn parse_multiname_rec(
fn parse_multiname_rec(
curosr: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(BoxedIterIter<Tok<String>>, Stream<'_>)> {
) -> ProjectResult<(BoxedIter<Subresult>, Stream<'_>)> {
let star = ctx.interner().i("*");
let comma = ctx.interner().i(",");
let (head, cursor) = curosr.trim().pop()?;
let (head, mut cursor) = curosr.trim().pop()?;
match &head.lexeme {
Lexeme::LP('(') => parse_multiname_branch(cursor, ctx),
Lexeme::LP('[') => {
let (op_ent, cursor) = cursor.trim().pop()?;
let op = ExpectedName::expect(op_ent)?;
let (rp_ent, cursor) = cursor.trim().pop()?;
Expected::expect(Lexeme::RP('['), rp_ent)?;
Ok((box_once(box_once(op)), cursor))
let mut names = Vec::new();
loop {
let head;
(head, cursor) = cursor.trim().pop()?;
match head.lexeme {
Lexeme::Name(n) => names.push(n),
Lexeme::RP('[') => break,
_ => {
let err = Expected {
expected: vec![Lexeme::RP('[')],
or_name: true,
found: head.clone(),
};
return Err(err.rc());
},
}
}
Ok((Box::new(names.into_iter().map(Subresult::new_named)), cursor))
},
Lexeme::Name(n) if *n != comma => {
Lexeme::Name(n) if *n == star =>
Ok((box_once(Subresult::new_glob()), cursor)),
Lexeme::Name(n) if ![comma, star].contains(n) => {
let cursor = cursor.trim();
if cursor.get(0).ok().map(|e| &e.lexeme) == Some(&Lexeme::NS) {
let cursor = cursor.step()?;
let (out, cursor) = parse_multiname_rec(cursor, ctx)?;
let out = Box::new(out.map(|i| box_chain!(i, iter::once(*n))));
let out = Box::new(out.map(|sr| sr.push_front(*n)));
Ok((out, cursor))
} else {
Ok((box_once(box_once(*n)), cursor))
Ok((box_once(Subresult::new_named(*n)), cursor))
}
},
_ => Err(
@@ -73,16 +116,7 @@ pub fn parse_multiname_rec(
pub fn parse_multiname(
cursor: Stream<'_>,
ctx: impl Context,
) -> ProjectResult<(Vec<Vec<Tok<String>>>, Stream<'_>)> {
) -> ProjectResult<(Vec<Import>, Stream<'_>)> {
let (output, cont) = parse_multiname_rec(cursor, ctx)?;
let output = output
.map(|it| {
let mut deque = VecDeque::with_capacity(it.size_hint().0);
for item in it {
deque.push_front(item)
}
deque.into()
})
.collect();
Ok((output, cont))
Ok((output.map(|sr| sr.finalize()).collect(), cont))
}

View File

@@ -5,8 +5,8 @@ use itertools::Itertools;
use super::context::Context;
use super::errors::{
BadTokenInRegion, Expected, ExpectedName, LeadingNS, MisalignedParen,
NamespacedExport, ReservedToken, UnexpectedEOL,
BadTokenInRegion, Expected, ExpectedName, GlobExport, LeadingNS,
MisalignedParen, NamespacedExport, ReservedToken, UnexpectedEOL,
};
use super::lexer::Lexeme;
use super::multiname::parse_multiname;
@@ -71,18 +71,8 @@ pub fn parse_line(
Lexeme::Const | Lexeme::Macro | Lexeme::Module =>
Ok(FileEntry::Internal(parse_member(cursor, ctx)?)),
Lexeme::Import => {
let globstar = ctx.interner().i("*");
let (names, cont) = parse_multiname(cursor.step()?, ctx.clone())?;
let (imports, cont) = parse_multiname(cursor.step()?, ctx)?;
cont.expect_empty()?;
let imports = (names.into_iter())
.map(|mut nsname| {
let name = nsname.pop().expect("multinames cannot be zero-length");
Import {
path: ctx.interner().i(&nsname),
name: if name == globstar { None } else { Some(name) },
}
})
.collect();
Ok(FileEntry::Import(imports))
},
_ => {
@@ -105,9 +95,12 @@ pub fn parse_export_line(
let (names, cont) = parse_multiname(cursor.step()?, ctx)?;
cont.expect_empty()?;
let names = (names.into_iter())
.map(|i| if i.len() == 1 { Some(i[0]) } else { None })
.collect::<Option<Vec<_>>>()
.ok_or_else(|| NamespacedExport { location: cursor.location() }.rc())?;
.map(|Import { name, path }| match (name, &path[..]) {
(Some(n), []) => Ok(n),
(None, _) => Err(GlobExport { location: cursor.location() }.rc()),
_ => Err(NamespacedExport { location: cursor.location() }.rc()),
})
.collect::<Result<Vec<_>, _>>()?;
Ok(FileEntry::Export(names))
},
Lexeme::Const | Lexeme::Macro | Lexeme::Module =>

View File

@@ -6,13 +6,12 @@ use std::{fs, io};
use hashbrown::{HashMap, HashSet};
use rust_embed::RustEmbed;
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
use crate::error::{ProjectError, ProjectResult};
#[allow(unused)] // for doc
use crate::facade::System;
use crate::interner::Interner;
use crate::utils::iter::box_once;
use crate::utils::{BoxedIter, Cache};
use crate::{Stok, VName};
use crate::utils::Cache;
use crate::{Location, Stok, VName};
/// All the data available about a failed source load call
#[derive(Debug)]
@@ -25,8 +24,8 @@ impl ProjectError for FileLoadingError {
fn description(&self) -> &str {
"Neither a file nor a directory could be read from the requested path"
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition::just_file(self.path.clone()))
fn one_position(&self, _i: &Interner) -> crate::Location {
Location::File(Rc::new(self.path.clone()))
}
fn message(&self, _i: &Interner) -> String {
format!("File: {}\nDirectory: {}", self.file, self.dir)

View File

@@ -14,7 +14,10 @@ fn assert_visible(
target: &[Tok<String>], // may point to a symbol or module of any kind
project: &ProjectTree<VName>,
) -> ProjectResult<()> {
let (tgt_item, tgt_path) = unwrap_or!(target.split_last(); return Ok(()));
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();
let vis_ignored_len = usize::min(tgt_path.len(), shared_len + 1);

View File

@@ -75,7 +75,7 @@ fn source_to_module(
.collect::<Vec<_>>();
let imports_from = (imports.iter())
.map(|imp| -> ProjectResult<_> {
let mut imp_path_v = i.r(imp.path).clone();
let mut imp_path_v = 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");

View File

@@ -7,7 +7,7 @@ use crate::error::{NotFound, ProjectError, ProjectResult};
use crate::interner::{Interner, Tok};
use crate::pipeline::source_loader::LoadedSourceTable;
use crate::representations::tree::WalkErrorKind;
use crate::utils::{split_max_prefix, unwrap_or, Cache};
use crate::utils::{split_max_prefix, Cache};
use crate::Sym;
pub type OpsResult = ProjectResult<Rc<HashSet<Tok<String>>>>;
@@ -33,40 +33,43 @@ pub fn collect_exported_ops(
) -> OpsResult {
let injected = injected(path).unwrap_or_else(|| Rc::new(HashSet::new()));
let path_s = &i.r(path)[..];
let name_split = split_max_prefix(path_s, &|n| loaded.contains_key(n));
let (fpath, subpath) = unwrap_or!(name_split; return Ok(Rc::new(
(loaded.keys())
.filter_map(|modname| {
if path_s.len() == coprefix(path_s.iter(), modname.iter()) {
Some(modname[path_s.len()])
} else {
None
}
})
.chain(injected.iter().copied())
.collect::<HashSet<_>>(),
)));
let preparsed = &loaded[fpath].preparsed;
let module =
preparsed.0.walk_ref(subpath, false).map_err(
|walk_err| match walk_err.kind {
WalkErrorKind::Private => {
unreachable!("visibility is not being checked here")
match split_max_prefix(path_s, &|n| loaded.contains_key(n)) {
None => {
let ops = (loaded.keys())
.filter_map(|modname| {
if path_s.len() == coprefix(path_s.iter(), modname.iter()) {
Some(modname[path_s.len()])
} else {
None
}
})
.chain(injected.iter().copied())
.collect::<HashSet<_>>();
Ok(Rc::new(ops))
},
Some((fpath, subpath)) => {
let preparsed = &loaded[fpath].preparsed;
let module = preparsed.0.walk_ref(subpath, false).map_err(
|walk_err| match walk_err.kind {
WalkErrorKind::Private => {
unreachable!("visibility is not being checked here")
},
WalkErrorKind::Missing => NotFound {
source: None,
file: fpath.to_vec(),
subpath: subpath[..walk_err.pos].to_vec(),
}
.rc(),
},
WalkErrorKind::Missing => NotFound {
source: None,
file: fpath.to_vec(),
subpath: subpath[..walk_err.pos].to_vec(),
}
.rc(),
},
)?;
let out = (module.items.iter())
.filter(|(_, v)| v.exported)
.map(|(k, _)| *k)
.chain(injected.iter().copied())
.collect::<HashSet<_>>();
Ok(Rc::new(out))
)?;
let out = (module.items.iter())
.filter(|(_, v)| v.exported)
.map(|(k, _)| *k)
.chain(injected.iter().copied())
.collect::<HashSet<_>>();
Ok(Rc::new(out))
},
}
}
pub fn mk_cache<'a>(

View File

@@ -44,7 +44,7 @@ pub fn collect_ops_for(
ret.insert(n);
} else {
let path = i.expect(
import_abs_path(file, modpath, &i.r(import.path)[..], i),
import_abs_path(file, modpath, &import.path, i),
"This error should have been caught during loading",
);
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());

View File

@@ -59,7 +59,7 @@ fn entv_rec(
.flat_map(|import| {
if let Import { name: None, path } = import {
let p = i.expect(
import_abs_path(mod_path, mod_stack, &i.r(path)[..], i),
import_abs_path(mod_path, mod_stack, &path, i),
"Should have emerged in preparsing",
);
let names = i.expect(
@@ -67,7 +67,7 @@ fn entv_rec(
"Should have emerged in second parsing",
);
let imports = (names.iter())
.map(move |&n| Import { name: Some(n), path })
.map(|&n| Import { name: Some(n), path: path.clone() })
.collect::<Vec<_>>();
Box::new(imports.into_iter()) as BoxedIter<Import>
} else {

View File

@@ -56,7 +56,7 @@ fn load_abs_path_rec(
// recurse on all imported modules
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
let abs_pathv =
import_abs_path(filename, modpath, &import.nonglob_path(i), i)?;
import_abs_path(filename, modpath, &import.nonglob_path(), i)?;
if abs_path.starts_with(&abs_pathv) {
return Ok(());
}

View File

@@ -2,9 +2,8 @@ use std::rc::Rc;
use super::location::Location;
use super::{ast, postmacro};
use crate::error::{ErrorPosition, ProjectError};
use crate::utils::iter::box_once;
use crate::utils::{BoxedIter, Substack};
use crate::error::ProjectError;
use crate::utils::Substack;
use crate::{Interner, Sym};
#[derive(Debug, Clone)]
@@ -51,9 +50,8 @@ impl ProjectError for Error {
_ => self.description().to_string(),
}
}
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
box_once(ErrorPosition { location: self.location.clone(), message: None })
fn one_position(&self, _i: &Interner) -> Location {
self.location.clone()
}
}

View File

@@ -7,11 +7,10 @@ use super::namelike::VName;
use crate::ast::{Constant, Rule};
use crate::interner::{Interner, Tok};
use crate::utils::{unwrap_or, BoxedIter};
use crate::Sym;
/// An import pointing at another module, either specifying the symbol to be
/// imported or importing all available symbols with a globstar (*)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Import {
/// Import path, a sequence of module names. Can either start with
///
@@ -19,7 +18,7 @@ pub struct Import {
/// - any number of `super` to reference the parent module of the implied
/// `self`
/// - a root name
pub path: Sym,
pub path: VName,
/// If name is None, this is a wildcard import
pub name: Option<Tok<String>>,
}
@@ -29,8 +28,8 @@ impl Import {
///
/// Returns the path if this is a glob import, or the path plus the
/// name if this is a specific import
pub fn nonglob_path(&self, i: &Interner) -> Vec<Tok<String>> {
let mut path_vec = i.r(self.path).clone();
pub fn nonglob_path(&self) -> Vec<Tok<String>> {
let mut path_vec = self.path.clone();
if let Some(n) = self.name {
path_vec.push(n)
}

View File

@@ -145,7 +145,7 @@ impl<'a, P: MessagePort, ST: StreamTable + 'a> IntoSystem<'a>
constants: io_bindings(i, streams).unwrap_tree(),
code: embed_to_map::<IOEmbed>(".orc", i),
prelude: vec![FileEntry::Import(vec![Import {
path: i.i(&vec![i.i("system"), i.i("io"), i.i("prelude")]),
path: vec![i.i("system"), i.i("io"), i.i("prelude")],
name: None,
}])],
handlers,

View File

@@ -5,8 +5,8 @@ use hashbrown::HashMap;
use crate::foreign::ExternError;
use crate::systems::asynch::MessagePort;
use crate::utils::{take_with_output, Task};
use crate::ThreadPool;
use crate::thread_pool::{Task, ThreadPool};
use crate::utils::take_with_output;
pub trait StreamHandle: Clone + Send {
fn new(id: usize) -> Self;

View File

@@ -1,11 +1,11 @@
import std::num::*
export ::(+, -, *, /, %)
export ::[+ - * / %]
import std::str::*
export ::[++]
import std::bool::*
export ::(==, if, then, else, true, false)
export ::([==], if, then, else, true, false)
import std::fn::*
export ::($, |>, =>, identity, pass, pass2, return)
export ::([$ |> =>], identity, pass, pass2, return)
import std::list
import std::map
import std::option

View File

@@ -41,7 +41,7 @@ impl IntoSystem<'static> for StlConfig {
constants: HashMap::from([(i.i("std"), fns)]),
code: embed_to_map::<StlEmbed>(".orc", i),
prelude: vec![FileEntry::Import(vec![Import {
path: i.i(&[i.i("std"), i.i("prelude")][..]),
path: vec![i.i("std"), i.i("prelude")],
name: None,
}])],
handlers: HandlerTable::new(),

View File

@@ -5,8 +5,6 @@ use std::iter;
/// A trait object of [Iterator] to be assigned to variables that may be
/// initialized form multiple iterators of incompatible types
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
/// A [BoxedIter] of [BoxedIter].
pub type BoxedIterIter<'a, T> = BoxedIter<'a, BoxedIter<'a, T>>;
/// creates a [BoxedIter] of a single element
pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> {
Box::new(iter::once(t))

View File

@@ -11,7 +11,7 @@ mod split_max_prefix;
mod string_from_charset;
mod substack;
mod take_with_output;
mod thread_pool;
pub mod thread_pool;
mod unwrap_or;
pub use cache::Cache;
@@ -30,4 +30,3 @@ pub use iter::BoxedIter;
pub use iter_find::iter_find;
pub use string_from_charset::string_from_charset;
pub use take_with_output::take_with_output;
pub use thread_pool::{Query, QueryTask, Task, ThreadPool};

View File

@@ -1,3 +1,7 @@
//! A thread pool for executing tasks in parallel, spawning threads as workload
//! increases and terminating them as tasks finish. This is not terribly
//! efficient, its main design goal is to parallelize blocking I/O calls.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{sync_channel, SyncSender};
use std::sync::{Arc, Mutex};
@@ -6,6 +10,8 @@ use std::thread::spawn;
/// A trait for a task dispatched on a [ThreadPool]. The task owns all relevant
/// data, is safe to pass between threads and is executed only once.
pub trait Task: Send + 'static {
/// Execute the task. At a minimum, this involves signaling some other thread,
/// otherwise the task has no effect.
fn run(self);
}
@@ -15,10 +21,21 @@ impl<F: FnOnce() + Send + 'static> Task for F {
}
}
/// An async unit of work that produces some result, see [Task]. This can be
/// wrapped in a generic reporter to create a task.
pub trait Query: Send + 'static {
/// The value produced by the query
type Result: Send + 'static;
/// Execute the query, producing some value which can then be sent to another
/// thread
fn run(self) -> Self::Result;
/// Associate the query with a reporter expressed in a plain function.
/// Note that because every lambda has a distinct type and every thread pool
/// runs exactly one type of task, this can appear only once in the code for
/// a given thread pool. It is practical in a narrow set of cases, most of the
/// time however you are better off defining an explicit reporter.
fn then<F: FnOnce(Self::Result) + Send + 'static>(
self,
callback: F,
@@ -37,6 +54,8 @@ impl<F: FnOnce() -> R + Send + 'static, R: Send + 'static> Query for F {
}
}
/// A reporter that calls a statically known function with the result of a
/// query. Constructed with [Query::then]
pub struct QueryTask<Q: Query, F: FnOnce(Q::Result) + Send + 'static> {
query: Q,
callback: F,
@@ -68,16 +87,23 @@ struct ThreadPoolData<T: Task> {
/// arrive. To get rid of the last waiting thread, drop the thread pool.
///
/// ```
/// use orchidlang::ThreadPool;
/// use orchidlang::thread_pool::{Task, ThreadPool};
///
/// let pool = ThreadPool::new(|s: String, _| println!("{}", s));
/// struct MyTask(&'static str);
/// impl Task for MyTask {
/// fn run(self) {
/// println!("{}", self.0)
/// }
/// }
///
/// let pool = ThreadPool::new();
///
/// // spawns first thread
/// pool.submit("foo".to_string());
/// pool.submit(MyTask("foo"));
/// // probably spawns second thread
/// pool.submit("bar".to_string());
/// pool.submit(MyTask("bar"));
/// // either spawns third thread or reuses first
/// pool.submit("baz".to_string());
/// pool.submit(MyTask("baz"));
/// ```
pub struct ThreadPool<T: Task> {
data: Arc<ThreadPoolData<T>>,

View File

@@ -8,10 +8,14 @@
/// It also supports unwrapping concrete variants of other enums
///
/// ```ignore
/// use crate::representations::Literal;
/// use crate::Literal;
///
/// crate::unwrap_or!(Literal::Usize(2) => Literal::Number; return)
/// ```
///
/// Note: this macro influences the control flow of the surrounding code
/// without an `if`, which can be misleading. It should only be used for small,
/// straightforward jumps.
macro_rules! unwrap_or {
($m:expr; $fail:expr) => {{
if let Some(res) = ($m) { res } else { $fail }