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:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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!(
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
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)
|
||||
})?;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
},
|
||||
Lexeme::Name(n) if *n != comma => {
|
||||
}
|
||||
}
|
||||
Ok((Box::new(names.into_iter().map(Subresult::new_named)), cursor))
|
||||
},
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,9 +33,9 @@ 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())
|
||||
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()])
|
||||
@@ -44,11 +44,12 @@ pub fn collect_exported_ops(
|
||||
}
|
||||
})
|
||||
.chain(injected.iter().copied())
|
||||
.collect::<HashSet<_>>(),
|
||||
)));
|
||||
.collect::<HashSet<_>>();
|
||||
Ok(Rc::new(ops))
|
||||
},
|
||||
Some((fpath, subpath)) => {
|
||||
let preparsed = &loaded[fpath].preparsed;
|
||||
let module =
|
||||
preparsed.0.walk_ref(subpath, false).map_err(
|
||||
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")
|
||||
@@ -67,6 +68,8 @@ pub fn collect_exported_ops(
|
||||
.chain(injected.iter().copied())
|
||||
.collect::<HashSet<_>>();
|
||||
Ok(Rc::new(out))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_cache<'a>(
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(());
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user