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",
|
"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]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -23,7 +34,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -76,6 +87,15 @@ dependencies = [
|
|||||||
"wasi",
|
"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]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -88,12 +108,19 @@ version = "0.2.126"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchid"
|
name = "orchid"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"derivative",
|
"derivative",
|
||||||
|
"hashbrown",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -167,6 +194,12 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ edition = "2021"
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
chumsky = "0.8"
|
chumsky = "0.8"
|
||||||
derivative = "2.2"
|
derivative = "2.2"
|
||||||
|
hashbrown = "0.12"
|
||||||
@@ -17,6 +17,7 @@ pub enum Expr {
|
|||||||
S(Vec<Expr>),
|
S(Vec<Expr>),
|
||||||
Lambda(String, Option<Box<Expr>>, Vec<Expr>),
|
Lambda(String, Option<Box<Expr>>, Vec<Expr>),
|
||||||
Auto(Option<String>, Option<Box<Expr>>, Vec<Expr>),
|
Auto(Option<String>, Option<Box<Expr>>, Vec<Expr>),
|
||||||
|
|
||||||
Typed(Box<Expr>, Box<Expr>)
|
Typed(Box<Expr>, Box<Expr>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ mod name;
|
|||||||
mod substitution;
|
mod substitution;
|
||||||
mod sourcefile;
|
mod sourcefile;
|
||||||
|
|
||||||
|
pub use substitution::Substitution;
|
||||||
pub use expression::Expr;
|
pub use expression::Expr;
|
||||||
pub use expression::expression_parser;
|
pub use expression::expression_parser;
|
||||||
pub use sourcefile::FileEntry;
|
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;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
mod resolve_names;
|
mod resolve_names;
|
||||||
|
mod prefix;
|
||||||
|
mod name_resolver;
|
||||||
|
mod expr;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
pub modules: HashMap<Vec<String>, Module>,
|
pub modules: HashMap<Vec<String>, Module>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Export {
|
|
||||||
isSymbol: bool,
|
|
||||||
subpaths: HashMap<String, Export>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
pub substitutions: Vec<Substitution>,
|
pub substitutions: Vec<Substitution>,
|
||||||
pub exports: HashMap<String, Export>,
|
pub exports: Vec<String>,
|
||||||
pub all_ops: Vec<String>
|
pub references: Vec<Vec<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Substitution {
|
pub struct Substitution {
|
||||||
pub source: Expr,
|
pub source: expr::Expr,
|
||||||
pub priority: f64,
|
pub priority: f64,
|
||||||
pub target: Expr
|
pub target: expr::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>
|
|
||||||
}
|
}
|
||||||
|
|||||||
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::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::rc::Rc;
|
use std::error;
|
||||||
use std::{iter, clone};
|
|
||||||
|
|
||||||
use chumsky::{Parser, prelude::Simple};
|
use chumsky::{prelude::Simple, Parser};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::parse::{self, file_parser, exported_names, FileEntry};
|
use crate::parse::{self, file_parser, FileEntry};
|
||||||
use crate::utils::Cache;
|
use crate::utils::{Cache, as_modpath};
|
||||||
|
|
||||||
|
use super::expr;
|
||||||
|
use super::name_resolver::{NameResolver, ResolutionError};
|
||||||
|
use super::prefix::prefix;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Loaded {
|
pub enum Loaded {
|
||||||
Module(String),
|
Module(String),
|
||||||
Namespace(Vec<String>)
|
Namespace(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug, Clone)]
|
||||||
pub enum ParseError {
|
pub enum ParseError<ELoad> where ELoad: Clone {
|
||||||
#[error("Not found: {0}")]
|
#[error("Resolution cycle")]
|
||||||
NotFound(String),
|
ResolutionCycle,
|
||||||
#[error("Failed to parse {file}: {errors:?}")]
|
#[error("File not found: {0}")]
|
||||||
Syntax {
|
Load(ELoad),
|
||||||
file: String,
|
#[error("Failed to parse: {0:?}")]
|
||||||
errors: Vec<Simple<char>>
|
Syntax(Vec<Simple<char>>),
|
||||||
},
|
#[error("Not a module")]
|
||||||
#[error("Expected {0}, found {1}")]
|
None
|
||||||
Mismatch(String, String),
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseError {
|
impl<T> From<Vec<Simple<char>>> for ParseError<T> where T: Clone {
|
||||||
pub fn not_found(name: &str) -> ParseError { ParseError::NotFound(name.to_string()) }
|
fn from(simp: Vec<Simple<char>>) -> Self { Self::Syntax(simp) }
|
||||||
pub fn syntax(file: &str, errors: Vec<Simple<char>>) -> ParseError {
|
}
|
||||||
ParseError::Syntax { file: file.to_string(), errors }
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
pub fn mismatch(expected: &str, found: &str) -> ParseError {
|
|
||||||
ParseError::Mismatch(expected.to_string(), found.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading a module:
|
type ImportMap = HashMap<String, Vec<String>>;
|
||||||
// 1. [x] Parse the imports
|
type ParseResult<T, ELoad> = Result<T, ParseError<ELoad>>;
|
||||||
// 2. [x] Build a mapping of all imported symbols to full paths
|
type AnyParseResult<T, ELoad> = Result<T, Vec<ParseError<ELoad>>>;
|
||||||
// -> [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 GetLoaded<'a> = dyn FnMut(&'a [&str]) -> &'a Option<Loaded>;
|
pub fn load_project<'a, F, ELoad>(
|
||||||
type GetPreparsed<'a> = dyn FnMut(&'a [&str]) -> &'a Option<Vec<FileEntry>>;
|
mut load_mod: F,
|
||||||
|
prelude: &[&'a str],
|
||||||
pub fn load_project<'a, F>(
|
entry: (Vec<String>, expr::Expr),
|
||||||
mut load_mod: F, prelude: &[&'a str], entry: &str
|
) -> Result<super::Project, ParseError<ELoad>>
|
||||||
) -> Result<super::Project, ParseError>
|
where
|
||||||
where F: FnMut(&[&str]) -> Option<Loaded> {
|
F: FnMut(&[&str]) -> Result<Loaded, ELoad>,
|
||||||
// TODO: Welcome to Kamino!
|
ELoad: Clone
|
||||||
|
{
|
||||||
let prelude_vec: Vec<String> = prelude.iter().map(|s| s.to_string()).collect();
|
let prelude_vec: Vec<String> = prelude.iter().map(|s| s.to_string()).collect();
|
||||||
let preparser = file_parser(prelude, &[]);
|
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<_>>())
|
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 modname_cell = RefCell::new(Cache::new(|symbol: Vec<String>|
|
||||||
let mut loaded = loaded_cell.borrow_mut();
|
-> AnyParseResult<Vec<String>, ELoad> {
|
||||||
loaded.by_clone(path).as_ref().map(|loaded| match loaded {
|
let mut local_loaded = loaded_cell.borrow_mut();
|
||||||
Loaded::Module(source) => Some(preparser.parse(source.as_str()).ok()?),
|
let mut errv: Vec<ParseError<ELoad>> = Vec::new();
|
||||||
_ => return None
|
loop {
|
||||||
}).flatten()
|
let (path, name) = symbol.split_at(symbol.len() - errv.len());
|
||||||
}));
|
let pathv = path.to_vec();
|
||||||
let exports_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
match local_loaded.by_clone_fallible(&pathv) {
|
||||||
let mut loaded = loaded_cell.borrow_mut();
|
Ok(imports) => break Ok(pathv.clone()),
|
||||||
loaded.by_clone(path.clone()).as_ref().map(|data| {
|
Err(err) => {
|
||||||
let mut preparsed = preparsed_cell.borrow_mut();
|
errv.push(err);
|
||||||
match data {
|
if symbol.len() == errv.len() {
|
||||||
Loaded::Namespace(names) => Some(names.clone()),
|
break Err(errv);
|
||||||
Loaded::Module(source) => preparsed.by_clone(path).as_ref().map(|data| {
|
}
|
||||||
parse::exported_names(&data).into_iter()
|
}
|
||||||
.map(|n| n[0].clone())
|
}
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
_ => None
|
|
||||||
}
|
}
|
||||||
}).flatten()
|
|
||||||
}));
|
}));
|
||||||
let imports_cell = RefCell::new(Cache::new(|path: Vec<String>| {
|
// Preliminarily parse a file, substitution patterns and imports are valid
|
||||||
let mut preparsed = preparsed_cell.borrow_mut();
|
let preparsed_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||||
let entv = preparsed.by_clone(path).clone()?;
|
-> 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 import_entries = parse::imports(entv.iter());
|
||||||
let mut imported_symbols: HashMap<String, Vec<String>> = HashMap::new();
|
let mut imported_symbols: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
for imp in import_entries {
|
for imp in import_entries {
|
||||||
let mut exports = exports_cell.borrow_mut();
|
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 let Some(ref name) = imp.name {
|
||||||
if export.contains(&name) {
|
if export.contains(&name) {
|
||||||
imported_symbols.insert(name.clone(), imp.path.clone());
|
imported_symbols.insert(name.clone(), imp.path.clone());
|
||||||
@@ -100,22 +127,87 @@ where F: FnMut(&[&str]) -> Option<Loaded> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(imported_symbols)
|
Ok(imported_symbols)
|
||||||
}));
|
}));
|
||||||
let parsed = RefCell::new(Cache::new(|path: Vec<String>| {
|
// Final parse, operators are correctly separated
|
||||||
let mut imports = imports_cell.borrow_mut();
|
let parsed_cell = RefCell::new(Cache::new(|path: Vec<String>|
|
||||||
let mut loaded = loaded_cell.borrow_mut();
|
-> ParseResult<Vec<FileEntry>, ELoad> {
|
||||||
let data = loaded.by_clone(path.clone()).as_ref()?;
|
let mut local_imports = imports_cell.borrow_mut();
|
||||||
let text = match data { Loaded::Module(s) => Some(s), _ => None }?;
|
let imports = local_imports.by_clone_fallible(&path)?;
|
||||||
let imported_symbols = imports.by_clone(path).as_ref()?;
|
let mut local_loaded = loaded_cell.borrow_mut();
|
||||||
let imported_ops: Vec<&str> = imported_symbols.keys()
|
let imported_ops: Vec<&str> = imports
|
||||||
|
.keys()
|
||||||
.chain(prelude_vec.iter())
|
.chain(prelude_vec.iter())
|
||||||
.map(|s| s.as_str())
|
.map(|s| s.as_str())
|
||||||
.filter(|s| parse::is_op(s))
|
.filter(|s| parse::is_op(s))
|
||||||
.collect();
|
.collect();
|
||||||
let file_parser = file_parser(prelude, &imported_ops);
|
let parser = file_parser(prelude, &imported_ops);
|
||||||
file_parser.parse(text.as_str()).ok()
|
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]);
|
// let main = preparsed.get(&[entry]);
|
||||||
// for imp in parse::imports(main) {
|
// for imp in parse::imports(main) {
|
||||||
// if !modules.contains_key(&imp.path) {
|
// if !modules.contains_key(&imp.path) {
|
||||||
@@ -125,7 +217,5 @@ where F: FnMut(&[&str]) -> Option<Loaded> {
|
|||||||
// let mut project = super::Project {
|
// let mut project = super::Project {
|
||||||
// modules: HashMap::new()
|
// modules: HashMap::new()
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// Some(project)
|
|
||||||
todo!("Finish this function")
|
todo!("Finish this function")
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
/// Cache the return values of an effectless closure in a hashmap
|
/// Cache the return values of an effectless closure in a hashmap
|
||||||
/// Inspired by the closure_cacher crate.
|
/// Inspired by the closure_cacher crate.
|
||||||
@@ -8,22 +8,26 @@ pub struct Cache<I, O, F> {
|
|||||||
closure: 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,
|
I: Eq + Hash,
|
||||||
F: FnMut(I) -> O
|
F: FnMut(I) -> O
|
||||||
{
|
{
|
||||||
pub fn new(closure: F) -> Self {
|
pub fn new(closure: F) -> Self {
|
||||||
Self { store: HashMap::new(), closure }
|
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;
|
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;
|
let closure = &mut self.closure;
|
||||||
// Make sure we only clone if necessary
|
self.store.raw_entry_mut().from_key(i)
|
||||||
self.store.entry(i).or_insert_with_key(|k| closure(k.clone()))
|
.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> {
|
pub fn known(&self, i: &I) -> Option<&O> {
|
||||||
self.store.get(i)
|
self.store.get(i)
|
||||||
}
|
}
|
||||||
@@ -32,3 +36,36 @@ impl<I, O, F> Cache<I, O, F> where
|
|||||||
self.store.remove(i).is_some()
|
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;
|
mod cache;
|
||||||
|
mod substack;
|
||||||
pub use cache::Cache;
|
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