forked from Orchid/orchid
Changed most everything
This commit is contained in:
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -11,6 +11,17 @@ dependencies = [
|
||||
"const-random",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -23,7 +34,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -76,6 +87,15 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -88,12 +108,19 @@ version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
|
||||
[[package]]
|
||||
name = "orchid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chumsky",
|
||||
"derivative",
|
||||
"hashbrown",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -167,6 +194,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
|
||||
@@ -8,4 +8,5 @@ edition = "2021"
|
||||
[dependencies]
|
||||
thiserror = "1.0"
|
||||
chumsky = "0.8"
|
||||
derivative = "2.2"
|
||||
derivative = "2.2"
|
||||
hashbrown = "0.12"
|
||||
@@ -17,6 +17,7 @@ pub enum Expr {
|
||||
S(Vec<Expr>),
|
||||
Lambda(String, Option<Box<Expr>>, Vec<Expr>),
|
||||
Auto(Option<String>, Option<Box<Expr>>, Vec<Expr>),
|
||||
|
||||
Typed(Box<Expr>, Box<Expr>)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ mod name;
|
||||
mod substitution;
|
||||
mod sourcefile;
|
||||
|
||||
pub use substitution::Substitution;
|
||||
pub use expression::Expr;
|
||||
pub use expression::expression_parser;
|
||||
pub use sourcefile::FileEntry;
|
||||
|
||||
25
src/project/expr.rs
Normal file
25
src/project/expr.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Literal {
|
||||
Num(f64),
|
||||
Int(u64),
|
||||
Char(char),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Literal(Literal),
|
||||
Name {
|
||||
qualified: Vec<String>,
|
||||
local: Option<String>
|
||||
},
|
||||
S(Vec<Expr>),
|
||||
Lambda(String, Option<Box<Expr>>, Vec<Expr>),
|
||||
Auto(Option<String>, Option<Box<Expr>>, Vec<Expr>)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expr {
|
||||
pub token: Token,
|
||||
pub typ: Option<Box<Expr>>
|
||||
}
|
||||
@@ -1,53 +1,25 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod resolve_names;
|
||||
|
||||
mod prefix;
|
||||
mod name_resolver;
|
||||
mod expr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Project {
|
||||
pub modules: HashMap<Vec<String>, Module>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Export {
|
||||
isSymbol: bool,
|
||||
subpaths: HashMap<String, Export>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub substitutions: Vec<Substitution>,
|
||||
pub exports: HashMap<String, Export>,
|
||||
pub all_ops: Vec<String>
|
||||
pub exports: Vec<String>,
|
||||
pub references: Vec<Vec<String>>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Substitution {
|
||||
pub source: Expr,
|
||||
pub source: expr::Expr,
|
||||
pub priority: f64,
|
||||
pub target: Expr
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Literal {
|
||||
Num(f64),
|
||||
Int(u64),
|
||||
Char(char),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Literal(Literal),
|
||||
Name(String),
|
||||
Bound,
|
||||
S(Vec<Expr>),
|
||||
Lambda(Vec<Vec<usize>>, Option<Box<Expr>>, Vec<Expr>),
|
||||
Auto(Option<Vec<Vec<usize>>>, Option<Box<Expr>>, Vec<Expr>)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expr {
|
||||
pub token: Token,
|
||||
pub typ: Box<Expr>
|
||||
pub target: expr::Expr
|
||||
}
|
||||
|
||||
122
src/project/name_resolver.rs
Normal file
122
src/project/name_resolver.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use std::{collections::HashMap};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::utils::Substack;
|
||||
|
||||
use super::expr::{Expr, Token};
|
||||
|
||||
type ImportMap = HashMap<String, Vec<String>>;
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum ResolutionError<Err> {
|
||||
#[error("Reference cycle at {0:?}")]
|
||||
Cycle(Vec<Vec<String>>),
|
||||
#[error("No module provides {0:?}")]
|
||||
NoModule(Vec<String>),
|
||||
#[error(transparent)]
|
||||
Delegate(#[from] Err)
|
||||
}
|
||||
|
||||
/// Recursively resolves symbols to their original names in expressions while caching every
|
||||
/// resolution. This makes the resolution process lightning fast and invalidation completely
|
||||
/// impossible since the intermediate steps of a resolution aren't stored.
|
||||
pub struct NameResolver<FSplit, FImps, E> {
|
||||
cache: HashMap<Vec<String>, Result<Vec<String>, ResolutionError<E>>>,
|
||||
get_modname: FSplit,
|
||||
get_imports: FImps
|
||||
}
|
||||
|
||||
|
||||
impl<FSplit, FImps, E> NameResolver<FSplit, FImps, E>
|
||||
where
|
||||
FSplit: FnMut(&Vec<String>) -> Option<Vec<String>>,
|
||||
FImps: FnMut(&Vec<String>) -> Result<ImportMap, E>,
|
||||
E: Clone
|
||||
{
|
||||
pub fn new(get_modname: FSplit, get_imports: FImps) -> Self {
|
||||
Self {
|
||||
cache: HashMap::new(),
|
||||
get_modname,
|
||||
get_imports
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtains a symbol's originnal name
|
||||
/// Uses a substack to detect loops
|
||||
fn find_origin_rec(
|
||||
&mut self,
|
||||
symbol: &Vec<String>,
|
||||
import_path: &Substack<'_, &Vec<String>>
|
||||
) -> Result<Vec<String>, ResolutionError<E>> {
|
||||
if let Some(cached) = self.cache.get(symbol) { return cached.clone() }
|
||||
// The imports and path of the referenced file and the local name
|
||||
let mut splitpoint = symbol.len();
|
||||
let path = (self.get_modname)(symbol).ok_or(ResolutionError::NoModule(symbol.clone()))?;
|
||||
let name = symbol.split_at(path.len()).1;
|
||||
let imports = (self.get_imports)(&path)?;
|
||||
let result = if let Some(source) = imports.get(&name[0]) {
|
||||
let new_sym: Vec<String> = source.iter().chain(name.iter()).cloned().collect();
|
||||
if import_path.iter().any(|el| el == &&new_sym) {
|
||||
Err(ResolutionError::Cycle(import_path.iter().cloned().cloned().collect()))
|
||||
} else {
|
||||
self.find_origin_rec(&new_sym, &import_path.push(symbol))
|
||||
}
|
||||
} else {
|
||||
Ok(symbol.clone()) // If not imported, it must be locally defined
|
||||
};
|
||||
self.cache.insert(symbol.clone(), result.clone());
|
||||
return result
|
||||
}
|
||||
|
||||
fn process_exprv_rec(&mut self, exv: &[Expr]) -> Result<Vec<Expr>, ResolutionError<E>> {
|
||||
exv.iter().map(|ex| self.process_expression_rec(ex)).collect()
|
||||
}
|
||||
|
||||
fn process_exprboxopt_rec(&mut self,
|
||||
exbo: &Option<Box<Expr>>
|
||||
) -> Result<Option<Box<Expr>>, ResolutionError<E>> {
|
||||
exbo.iter().map(|exb| Ok(Box::new(self.process_expression_rec(exb.as_ref())?)))
|
||||
.next().transpose()
|
||||
}
|
||||
|
||||
fn process_token_rec(&mut self, tok: &Token) -> Result<Token, ResolutionError<E>> {
|
||||
Ok(match tok {
|
||||
Token::Literal(l) => Token::Literal(l.clone()),
|
||||
Token::S(exv) => Token::S(
|
||||
exv.iter().map(|e| self.process_expression_rec(e))
|
||||
.collect::<Result<Vec<Expr>, ResolutionError<E>>>()?
|
||||
),
|
||||
Token::Lambda(name, typ, body) => Token::Lambda(name.clone(),
|
||||
self.process_exprboxopt_rec(typ)?,
|
||||
self.process_exprv_rec(body)?
|
||||
),
|
||||
Token::Auto(name, typ, body) => Token::Auto(name.clone(),
|
||||
self.process_exprboxopt_rec(typ)?,
|
||||
self.process_exprv_rec(body)?
|
||||
),
|
||||
Token::Name { qualified, local } => Token::Name {
|
||||
local: local.clone(),
|
||||
qualified: self.find_origin(qualified)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn process_expression_rec(&mut self, ex: &Expr) -> Result<Expr, ResolutionError<E>> {
|
||||
Ok(Expr {
|
||||
token: self.process_token_rec(&ex.token)?,
|
||||
typ: self.process_exprboxopt_rec(&ex.typ)?
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_origin(&mut self, symbol: &Vec<String>) -> Result<Vec<String>, ResolutionError<E>> {
|
||||
self.find_origin_rec(symbol, &Substack::new(symbol))
|
||||
}
|
||||
|
||||
pub fn process_token(&mut self, tok: &Token) -> Result<Token, ResolutionError<E>> {
|
||||
self.process_token_rec(tok)
|
||||
}
|
||||
|
||||
pub fn process_expression(&mut self, ex: &Expr) -> Result<Expr, ResolutionError<E>> {
|
||||
self.process_expression_rec(ex)
|
||||
}
|
||||
}
|
||||
61
src/project/prefix.rs
Normal file
61
src/project/prefix.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::parse;
|
||||
use super::expr;
|
||||
|
||||
/// Replaces the first element of a name with the matching prefix from a prefix map
|
||||
fn qualify(
|
||||
name: &Vec<String>,
|
||||
prefixes: &HashMap<String, Vec<String>>
|
||||
) -> Option<Vec<String>> {
|
||||
let value = prefixes.iter().find(|(k, _)| &&name[0] == k)?.1;
|
||||
Some(value.iter().chain(name.iter().skip(1)).cloned().collect())
|
||||
}
|
||||
|
||||
/// Produce a Token object for any value of parse::Expr other than Typed.
|
||||
/// Called by [#prefix] which handles Typed.
|
||||
fn prefix_token(
|
||||
expr: &parse::Expr,
|
||||
namespace: &Vec<String>
|
||||
) -> expr::Token {
|
||||
match expr {
|
||||
parse::Expr::Typed(_, _) => panic!("This function should only be called by prefix!"),
|
||||
parse::Expr::Char(c) => expr::Token::Literal(expr::Literal::Char(*c)),
|
||||
parse::Expr::Int(i) => expr::Token::Literal(expr::Literal::Int(*i)),
|
||||
parse::Expr::Num(n) => expr::Token::Literal(expr::Literal::Num(*n)),
|
||||
parse::Expr::Str(s) => expr::Token::Literal(expr::Literal::Str(s.clone())),
|
||||
parse::Expr::S(v) => expr::Token::S(v.iter().map(|e| prefix(e, namespace)).collect()),
|
||||
parse::Expr::Auto(name, typ, body) => expr::Token::Auto(
|
||||
name.clone(),
|
||||
typ.clone().map(|expr| Box::new(prefix(&expr, namespace))),
|
||||
body.iter().map(|e| prefix(e, namespace)).collect(),
|
||||
),
|
||||
parse::Expr::Lambda(name, typ, body) => expr::Token::Lambda(
|
||||
name.clone(),
|
||||
typ.clone().map(|expr| Box::new(prefix(&expr, namespace))),
|
||||
body.iter().map(|e| prefix(e, namespace)).collect(),
|
||||
),
|
||||
parse::Expr::Name(name) => expr::Token::Name {
|
||||
qualified: namespace.iter().chain(name.iter()).cloned().collect(),
|
||||
local: if name.len() == 1 {
|
||||
Some(name[0].clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce an Expr object for any value of parse::Expr
|
||||
pub fn prefix(expr: &parse::Expr, namespace: &Vec<String>) -> expr::Expr {
|
||||
match expr {
|
||||
parse::Expr::Typed(x, t) => expr::Expr {
|
||||
typ: Some(Box::new(prefix(t, namespace))),
|
||||
token: prefix_token(x, namespace),
|
||||
},
|
||||
_ => expr::Expr {
|
||||
typ: None,
|
||||
token: prefix_token(expr, namespace),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,95 +1,122 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::{iter, clone};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::error;
|
||||
|
||||
use chumsky::{Parser, prelude::Simple};
|
||||
use chumsky::{prelude::Simple, Parser};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::parse::{self, file_parser, exported_names, FileEntry};
|
||||
use crate::utils::Cache;
|
||||
use crate::parse::{self, file_parser, FileEntry};
|
||||
use crate::utils::{Cache, as_modpath};
|
||||
|
||||
use super::expr;
|
||||
use super::name_resolver::{NameResolver, ResolutionError};
|
||||
use super::prefix::prefix;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Loaded {
|
||||
Module(String),
|
||||
Namespace(Vec<String>)
|
||||
Namespace(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ParseError {
|
||||
#[error("Not found: {0}")]
|
||||
NotFound(String),
|
||||
#[error("Failed to parse {file}: {errors:?}")]
|
||||
Syntax {
|
||||
file: String,
|
||||
errors: Vec<Simple<char>>
|
||||
},
|
||||
#[error("Expected {0}, found {1}")]
|
||||
Mismatch(String, String),
|
||||
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum ParseError<ELoad> where ELoad: Clone {
|
||||
#[error("Resolution cycle")]
|
||||
ResolutionCycle,
|
||||
#[error("File not found: {0}")]
|
||||
Load(ELoad),
|
||||
#[error("Failed to parse: {0:?}")]
|
||||
Syntax(Vec<Simple<char>>),
|
||||
#[error("Not a module")]
|
||||
None
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn not_found(name: &str) -> ParseError { ParseError::NotFound(name.to_string()) }
|
||||
pub fn syntax(file: &str, errors: Vec<Simple<char>>) -> ParseError {
|
||||
ParseError::Syntax { file: file.to_string(), errors }
|
||||
}
|
||||
pub fn mismatch(expected: &str, found: &str) -> ParseError {
|
||||
ParseError::Mismatch(expected.to_string(), found.to_string())
|
||||
impl<T> From<Vec<Simple<char>>> for ParseError<T> where T: Clone {
|
||||
fn from(simp: Vec<Simple<char>>) -> Self { Self::Syntax(simp) }
|
||||
}
|
||||
|
||||
impl<T> From<ResolutionError<ParseError<T>>> for ParseError<T> where T: Clone {
|
||||
fn from(res: ResolutionError<ParseError<T>>) -> Self {
|
||||
match res {
|
||||
ResolutionError::Cycle(_) => ParseError::ResolutionCycle,
|
||||
ResolutionError::NoModule(_) => ParseError::None,
|
||||
ResolutionError::Delegate(d) => d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loading a module:
|
||||
// 1. [x] Parse the imports
|
||||
// 2. [x] Build a mapping of all imported symbols to full paths
|
||||
// -> [x] Parse the exported symbols from all imported modules
|
||||
// 3. [x] Parse everything using the full list of operators
|
||||
// 4. [ ] Traverse and remap elements
|
||||
type ImportMap = HashMap<String, Vec<String>>;
|
||||
type ParseResult<T, ELoad> = Result<T, ParseError<ELoad>>;
|
||||
type AnyParseResult<T, ELoad> = Result<T, Vec<ParseError<ELoad>>>;
|
||||
|
||||
type GetLoaded<'a> = dyn FnMut(&'a [&str]) -> &'a Option<Loaded>;
|
||||
type GetPreparsed<'a> = dyn FnMut(&'a [&str]) -> &'a Option<Vec<FileEntry>>;
|
||||
|
||||
pub fn load_project<'a, F>(
|
||||
mut load_mod: F, prelude: &[&'a str], entry: &str
|
||||
) -> Result<super::Project, ParseError>
|
||||
where F: FnMut(&[&str]) -> Option<Loaded> {
|
||||
// TODO: Welcome to Kamino!
|
||||
pub fn load_project<'a, F, ELoad>(
|
||||
mut load_mod: F,
|
||||
prelude: &[&'a str],
|
||||
entry: (Vec<String>, expr::Expr),
|
||||
) -> Result<super::Project, ParseError<ELoad>>
|
||||
where
|
||||
F: FnMut(&[&str]) -> Result<Loaded, ELoad>,
|
||||
ELoad: Clone
|
||||
{
|
||||
let prelude_vec: Vec<String> = prelude.iter().map(|s| s.to_string()).collect();
|
||||
let preparser = file_parser(prelude, &[]);
|
||||
let loaded_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
||||
// Map paths to a namespace with name list (folder) or module with source text (file)
|
||||
let loaded_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<Loaded, ELoad> {
|
||||
load_mod(&path.iter().map(|s| s.as_str()).collect::<Vec<_>>())
|
||||
.map_err(ParseError::Load)
|
||||
}));
|
||||
let preparsed_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
||||
let mut loaded = loaded_cell.borrow_mut();
|
||||
loaded.by_clone(path).as_ref().map(|loaded| match loaded {
|
||||
Loaded::Module(source) => Some(preparser.parse(source.as_str()).ok()?),
|
||||
_ => return None
|
||||
}).flatten()
|
||||
}));
|
||||
let exports_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
||||
let mut loaded = loaded_cell.borrow_mut();
|
||||
loaded.by_clone(path.clone()).as_ref().map(|data| {
|
||||
let mut preparsed = preparsed_cell.borrow_mut();
|
||||
match data {
|
||||
Loaded::Namespace(names) => Some(names.clone()),
|
||||
Loaded::Module(source) => preparsed.by_clone(path).as_ref().map(|data| {
|
||||
parse::exported_names(&data).into_iter()
|
||||
.map(|n| n[0].clone())
|
||||
.collect()
|
||||
}),
|
||||
_ => None
|
||||
let modname_cell = RefCell::new(Cache::new(|symbol: Vec<String>|
|
||||
-> AnyParseResult<Vec<String>, ELoad> {
|
||||
let mut local_loaded = loaded_cell.borrow_mut();
|
||||
let mut errv: Vec<ParseError<ELoad>> = Vec::new();
|
||||
loop {
|
||||
let (path, name) = symbol.split_at(symbol.len() - errv.len());
|
||||
let pathv = path.to_vec();
|
||||
match local_loaded.by_clone_fallible(&pathv) {
|
||||
Ok(imports) => break Ok(pathv.clone()),
|
||||
Err(err) => {
|
||||
errv.push(err);
|
||||
if symbol.len() == errv.len() {
|
||||
break Err(errv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).flatten()
|
||||
}
|
||||
}));
|
||||
let imports_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
||||
let mut preparsed = preparsed_cell.borrow_mut();
|
||||
let entv = preparsed.by_clone(path).clone()?;
|
||||
// Preliminarily parse a file, substitution patterns and imports are valid
|
||||
let preparsed_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
let mut loaded = loaded_cell.borrow_mut();
|
||||
let loaded = loaded.by_clone_fallible(&path)?;
|
||||
if let Loaded::Module(source) = loaded {
|
||||
Ok(preparser.parse(source.as_str())?)
|
||||
} else {Err(ParseError::None)}
|
||||
}));
|
||||
// Collect all toplevel names exported from a given file
|
||||
let exports_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<Vec<String>, ELoad> {
|
||||
let mut local_loaded = loaded_cell.borrow_mut();
|
||||
let loaded = local_loaded.by_clone_fallible(&path)?;
|
||||
let mut local_preparsed = preparsed_cell.borrow_mut();
|
||||
if let Loaded::Namespace(names) = loaded {
|
||||
return Ok(names.clone());
|
||||
}
|
||||
let preparsed = local_preparsed.by_clone_fallible(&path)?;
|
||||
Ok(parse::exported_names(&preparsed)
|
||||
.into_iter()
|
||||
.map(|n| n[0].clone())
|
||||
.collect())
|
||||
}));
|
||||
// Collect all toplevel names imported by a given file
|
||||
let imports_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<ImportMap, ELoad> {
|
||||
let mut local_preparsed = preparsed_cell.borrow_mut();
|
||||
let entv = local_preparsed.by_clone_fallible(&path)?.clone();
|
||||
let import_entries = parse::imports(entv.iter());
|
||||
let mut imported_symbols: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for imp in import_entries {
|
||||
let mut exports = exports_cell.borrow_mut();
|
||||
let export = exports.by_clone(imp.path.clone()).as_ref()?;
|
||||
let export = exports.by_clone_fallible(&imp.path)?;
|
||||
if let Some(ref name) = imp.name {
|
||||
if export.contains(&name) {
|
||||
imported_symbols.insert(name.clone(), imp.path.clone());
|
||||
@@ -100,32 +127,95 @@ where F: FnMut(&[&str]) -> Option<Loaded> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(imported_symbols)
|
||||
Ok(imported_symbols)
|
||||
}));
|
||||
let parsed = RefCell::new(Cache::new(|path: Vec<String>| {
|
||||
let mut imports = imports_cell.borrow_mut();
|
||||
let mut loaded = loaded_cell.borrow_mut();
|
||||
let data = loaded.by_clone(path.clone()).as_ref()?;
|
||||
let text = match data { Loaded::Module(s) => Some(s), _ => None }?;
|
||||
let imported_symbols = imports.by_clone(path).as_ref()?;
|
||||
let imported_ops: Vec<&str> = imported_symbols.keys()
|
||||
// Final parse, operators are correctly separated
|
||||
let parsed_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<Vec<FileEntry>, ELoad> {
|
||||
let mut local_imports = imports_cell.borrow_mut();
|
||||
let imports = local_imports.by_clone_fallible(&path)?;
|
||||
let mut local_loaded = loaded_cell.borrow_mut();
|
||||
let imported_ops: Vec<&str> = imports
|
||||
.keys()
|
||||
.chain(prelude_vec.iter())
|
||||
.map(|s| s.as_str())
|
||||
.filter(|s| parse::is_op(s))
|
||||
.collect();
|
||||
let file_parser = file_parser(prelude, &imported_ops);
|
||||
file_parser.parse(text.as_str()).ok()
|
||||
let parser = file_parser(prelude, &imported_ops);
|
||||
if let Loaded::Module(source) = local_loaded.by_clone_fallible(&path)? {
|
||||
Ok(parser.parse(source.as_str())?)
|
||||
} else {Err(ParseError::None)}
|
||||
}));
|
||||
let mut name_resolver = NameResolver::new(
|
||||
|path: &Vec<String>| { modname_cell.borrow_mut().by_clone_fallible(path).cloned().ok() },
|
||||
|path: &Vec<String>| { imports_cell.borrow_mut().by_clone_fallible(path).cloned() }
|
||||
);
|
||||
// Turn parsed files into a bag of substitutions and a list of toplevel export names
|
||||
let resolved_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<super::Module, ELoad> {
|
||||
let mut parsed = parsed_cell.borrow_mut();
|
||||
let parsed_entries = parsed.by_clone_fallible(&path)?;
|
||||
let subs: Vec<super::Substitution> = parsed_entries
|
||||
.iter()
|
||||
.filter_map(|ent| {
|
||||
if let FileEntry::Export(s) | FileEntry::Substitution(s) = ent {
|
||||
Some(super::Substitution {
|
||||
source: prefix(&s.source, &path),
|
||||
target: prefix(&s.target, &path),
|
||||
priority: s.priority,
|
||||
})
|
||||
} else { None }
|
||||
})
|
||||
.map(|sub| Ok(super::Substitution {
|
||||
source: name_resolver.process_expression(&sub.source)?,
|
||||
target: name_resolver.process_expression(&sub.target)?,
|
||||
..sub
|
||||
}))
|
||||
.collect::<ParseResult<Vec<super::Substitution>, ELoad>>()?;
|
||||
let module = super::Module {
|
||||
substitutions: subs,
|
||||
exports: exports_cell
|
||||
.borrow_mut()
|
||||
.by_clone_fallible(&path)?
|
||||
.clone(),
|
||||
references: imports_cell
|
||||
.borrow_mut()
|
||||
.by_clone_fallible(&path)?
|
||||
.values()
|
||||
.filter_map(|imps| modname_cell.borrow_mut().by_clone_fallible(imps).ok().cloned())
|
||||
.collect()
|
||||
};
|
||||
Ok(module)
|
||||
}));
|
||||
let all_subs_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||
-> ParseResult<Vec<super::Substitution>, ELoad> {
|
||||
let mut processed: HashSet<Vec<String>> = HashSet::new();
|
||||
let mut subs: Vec<super::Substitution> = Vec::new();
|
||||
let mut pending: VecDeque<Vec<String>> = VecDeque::new();
|
||||
while let Some(el) = pending.pop_front() {
|
||||
let mut local_resolved = resolved_cell.borrow_mut();
|
||||
let resolved = local_resolved.by_clone_fallible(&el)?;
|
||||
processed.insert(el.clone());
|
||||
pending.extend(
|
||||
resolved.references.iter()
|
||||
.filter(|&v| !processed.contains(v))
|
||||
.cloned()
|
||||
);
|
||||
subs.extend(
|
||||
resolved.substitutions.iter().cloned()
|
||||
)
|
||||
};
|
||||
Ok(subs)
|
||||
}));
|
||||
// let substitutions =
|
||||
// let main = preparsed.get(&[entry]);
|
||||
// for imp in parse::imports(main) {
|
||||
// if !modules.contains_key(&imp.path) {
|
||||
// if modules[&imp.path]
|
||||
// if modules[&imp.path]
|
||||
// }
|
||||
// }
|
||||
// let mut project = super::Project {
|
||||
// modules: HashMap::new()
|
||||
// };
|
||||
|
||||
// Some(project)
|
||||
todo!("Finish this function")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
/// Cache the return values of an effectless closure in a hashmap
|
||||
/// Inspired by the closure_cacher crate.
|
||||
@@ -8,27 +8,64 @@ pub struct Cache<I, O, F> {
|
||||
closure: F
|
||||
}
|
||||
|
||||
impl<I, O, F> Cache<I, O, F> where
|
||||
impl<I: 'static, O, F> Cache<I, O, F> where
|
||||
I: Eq + Hash,
|
||||
F: FnMut(I) -> O
|
||||
{
|
||||
pub fn new(closure: F) -> Self {
|
||||
Self { store: HashMap::new(), closure }
|
||||
}
|
||||
pub fn by_copy(&mut self, i: I) -> &O where I: Copy {
|
||||
/// Produce and cache a result by copying I if necessary
|
||||
pub fn by_copy(&mut self, i: &I) -> &O where I: Copy {
|
||||
let closure = &mut self.closure;
|
||||
self.store.entry(i).or_insert_with(|| closure(i))
|
||||
self.store.raw_entry_mut().from_key(i)
|
||||
.or_insert_with(|| (*i, closure(*i))).1
|
||||
}
|
||||
pub fn by_clone(&mut self, i: I) -> &O where I: Clone {
|
||||
/// Produce and cache a result by cloning I if necessary
|
||||
pub fn by_clone(&mut self, i: &I) -> &O where I: Clone {
|
||||
let closure = &mut self.closure;
|
||||
// Make sure we only clone if necessary
|
||||
self.store.entry(i).or_insert_with_key(|k| closure(k.clone()))
|
||||
self.store.raw_entry_mut().from_key(i)
|
||||
.or_insert_with(|| (i.clone(), closure(i.clone()))).1
|
||||
}
|
||||
/// Return the result if it has already been computed
|
||||
pub fn known(&self, i: &I) -> Option<&O> {
|
||||
self.store.get(i)
|
||||
}
|
||||
/// Forget the output for the given input
|
||||
pub fn drop(&mut self, i: &I) -> bool {
|
||||
self.store.remove(i).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: 'static, O, E, F> Cache<I, Result<O, E>, F> where
|
||||
I: Eq + Hash,
|
||||
E: Clone,
|
||||
F: FnMut(I) -> Result<O, E>
|
||||
{
|
||||
/// Sink the ref from a Result into the Ok value, such that copying only occurs on the sad path
|
||||
/// but the return value can be short-circuited
|
||||
pub fn by_copy_fallible(&mut self, i: &I) -> Result<&O, E> where I: Copy {
|
||||
self.by_clone(i).as_ref().map_err(|e| e.clone())
|
||||
}
|
||||
/// Sink the ref from a Result into the Ok value, such that cloning only occurs on the sad path
|
||||
/// but the return value can be short-circuited
|
||||
pub fn by_clone_fallible(&mut self, i: &I) -> Result<&O, E> where I: Clone {
|
||||
self.by_clone(i).as_ref().map_err(|e| e.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: 'static, O, F> Cache<I, Option<O>, F> where
|
||||
I: Eq + Hash,
|
||||
F: FnMut(I) -> Option<O>
|
||||
{
|
||||
/// Sink the ref from an Option into the Some value such that the return value can be
|
||||
/// short-circuited
|
||||
pub fn by_copy_fallible(&mut self, i: &I) -> Option<&O> where I: Copy {
|
||||
self.by_copy(i).as_ref()
|
||||
}
|
||||
/// Sink the ref from an Option into the Some value such that the return value can be
|
||||
/// short-circuited
|
||||
pub fn by_clone_fallible(&mut self, i: &I) -> Option<&O> where I: Clone {
|
||||
self.by_clone(i).as_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
mod cache;
|
||||
pub use cache::Cache;
|
||||
mod substack;
|
||||
pub use cache::Cache;
|
||||
pub use substack::Substack;
|
||||
|
||||
pub fn as_modpath(path: &Vec<String>) -> String {
|
||||
path.join("::")
|
||||
}
|
||||
43
src/utils/substack.rs
Normal file
43
src/utils/substack.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
/// Implement a FILO stack that lives on the regular call stack as a linked list.
|
||||
/// Mainly useful to detect loops in recursive algorithms where the recursion isn't
|
||||
/// deep enough to warrant a heap-allocated set
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Substack<'a, T> {
|
||||
pub item: T,
|
||||
pub prev: Option<&'a Self>
|
||||
}
|
||||
|
||||
impl<'a, T> Substack<'a, T> {
|
||||
pub fn item(&self) -> &T { &self.item }
|
||||
pub fn prev(&self) -> Option<&'a Substack<'a, T>> { self.prev }
|
||||
|
||||
pub fn new(item: T) -> Self {
|
||||
Self {
|
||||
item,
|
||||
prev: None
|
||||
}
|
||||
}
|
||||
pub fn push(&'a self, item: T) -> Self {
|
||||
Self {
|
||||
item,
|
||||
prev: Some(self)
|
||||
}
|
||||
}
|
||||
pub fn iter(&'a self) -> SubstackIterator<'a, T> {
|
||||
SubstackIterator { curr: Some(self) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubstackIterator<'a, T> {
|
||||
curr: Option<&'a Substack<'a, T>>
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for SubstackIterator<'a, T> {
|
||||
type Item = &'a T;
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
let Substack{ item, prev } = self.curr?;
|
||||
self.curr = *prev;
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user