diff --git a/Cargo.lock b/Cargo.lock index 5d87fdb..901ed74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "ahash 0.7.6", ] +[[package]] +name = "implicit-clone" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7" + [[package]] name = "itertools" version = "0.10.3" @@ -157,6 +163,7 @@ dependencies = [ "chumsky", "derivative", "hashbrown", + "implicit-clone", "itertools", "lazy_static", "mappable-rc", diff --git a/Cargo.toml b/Cargo.toml index ce5cbf5..227c26d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ ordered-float = "3.0" itertools = "0.10" smallvec = { version = "1.10.0", features = ['const_generics'] } lazy_static = "1.4.0" +implicit-clone = "0.3.5" diff --git a/src/representations/ast.rs b/src/representations/ast.rs index 0eaef53..f94d2fb 100644 --- a/src/representations/ast.rs +++ b/src/representations/ast.rs @@ -38,17 +38,28 @@ impl Debug for Expr { /// An S-expression as read from a source file #[derive(PartialEq, Eq, Hash)] pub enum Clause { + /// A literal value, eg. `1`, `"hello"` Literal(Literal), + /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` Name{ local: Option, qualified: Mrc<[String]> }, + /// A parenthesized expression, eg. `(print out "hello")`, `[1, 2, 3]`, `{Some(t) => t}` S(char, Mrc<[Expr]>), + /// An explicit expression associated with the leftmost, outermost [Clause::Auto], eg. `read @Int` Explicit(Mrc), + /// A function expression, eg. `\x. x + 1` Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>), + /// A parameterized expression with type inference, eg. `@T. T -> T` Auto(Option, Mrc<[Expr]>, Mrc<[Expr]>), + /// An opaque function, eg. an effectful function employing CPS. + /// Preferably wrap these in an Orchid monad. ExternFn(ExternFn), + /// An opaque non-callable value, eg. a file handle. + /// Preferably wrap these in an Orchid structure. Atom(Atom), + /// A placeholder for macros, eg. `$name`, `...$body`, `...$lhs:1` Placeh{ key: String, /// None => matches one token diff --git a/src/representations/ast_to_typed.rs b/src/representations/ast_to_typed.rs index fe7ae4a..807233a 100644 --- a/src/representations/ast_to_typed.rs +++ b/src/representations/ast_to_typed.rs @@ -2,7 +2,7 @@ use mappable_rc::Mrc; use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap}; -use super::{ast, typed}; +use super::{ast, typed, get_name::get_name}; #[derive(Clone)] pub enum Error { @@ -33,43 +33,42 @@ pub enum Error { /// Try to convert an expression from AST format to typed lambda pub fn expr(expr: &ast::Expr) -> Result { - Ok(expr_rec(expr, ProtoMap::new(), &mut 0, None)?.0) + Ok(expr_rec(expr, ProtoMap::new(), None)?.0) } /// Try and convert a single clause from AST format to typed lambda pub fn clause(clause: &ast::Clause) -> Result { - Ok(clause_rec(clause, ProtoMap::new(), &mut 0, None)?.0) + Ok(clause_rec(clause, ProtoMap::new(), None)?.0) } /// Try and convert a sequence of expressions from AST format to typed lambda pub fn exprv(exprv: &[ast::Expr]) -> Result { - Ok(exprv_rec(exprv, ProtoMap::new(), &mut 0, None)?.0) + Ok(exprv_rec(exprv, ProtoMap::new(), None)?.0) } const NAMES_INLINE_COUNT:usize = 3; /// Recursive state of [exprv] -fn exprv_rec( - v: &[ast::Expr], - names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>, - next_id: &mut u64, +fn exprv_rec<'a>( + v: &'a [ast::Expr], + names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, explicits: Option<&Stackframe>>, ) -> Result<(typed::Expr, usize), Error> { let (last, rest) = v.split_last().ok_or(Error::EmptyS)?; - if rest.len() == 0 {return expr_rec(&v[0], names, next_id, explicits)} + if rest.len() == 0 {return expr_rec(&v[0], names, explicits)} if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = last { assert!(empty_slice.len() == 0, "It is assumed that Explicit nodes can never have type annotations as the \ wrapped expression node matches all trailing colons." ); - let (x, _) = expr_rec(inner.as_ref(), names, next_id, None)?; - let new_explicits = Some(&Stackframe::opush(explicits, Mrc::new(x))); - let (body, used_expls) = exprv_rec(rest, names, next_id, new_explicits)?; + let (x, _) = expr_rec(inner.as_ref(), names.clone(), None)?; + let new_explicits = Stackframe::opush(explicits, Mrc::new(x)); + let (body, used_expls) = exprv_rec(rest, names, Some(&new_explicits))?; Ok((body, used_expls.saturating_sub(1))) } else { - let (f, f_used_expls) = exprv_rec(rest, names, next_id, explicits)?; + let (f, f_used_expls) = exprv_rec(rest, names.clone(), explicits)?; let x_explicits = Stackframe::opop(explicits, f_used_expls); - let (x, x_used_expls) = expr_rec(last, names, next_id, x_explicits)?; + let (x, x_used_expls) = expr_rec(last, names, x_explicits)?; Ok((typed::Expr( typed::Clause::Apply(Mrc::new(f), Mrc::new(x)), mrc_empty_slice() @@ -78,19 +77,18 @@ fn exprv_rec( } /// Recursive state of [expr] -fn expr_rec( - ast::Expr(val, typ): &ast::Expr, - names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>, - next_id: &mut u64, +fn expr_rec<'a>( + ast::Expr(val, typ): &'a ast::Expr, + names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, explicits: Option<&Stackframe>> // known explicit values ) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits) let typ: Vec = typ.iter() - .map(|c| Ok(clause_rec(c, names, next_id, None)?.0)) + .map(|c| Ok(clause_rec(c, names.clone(), None)?.0)) .collect::>()?; if let ast::Clause::S(paren, body) = val { if *paren != '(' {return Err(Error::BadGroup(*paren))} let (typed::Expr(inner, inner_t), used_expls) = exprv_rec( - body.as_ref(), names, next_id, explicits + body.as_ref(), names, explicits )?; let new_t = if typ.len() == 0 { inner_t } else { to_mrc_slice(if inner_t.len() == 0 { typ } else { @@ -99,16 +97,15 @@ fn expr_rec( }; Ok((typed::Expr(inner, new_t), used_expls)) } else { - let (cls, used_expls) = clause_rec(&val, names, next_id, explicits)?; + let (cls, used_expls) = clause_rec(&val, names, explicits)?; Ok((typed::Expr(cls, to_mrc_slice(typ)), used_expls)) } } /// Recursive state of [clause] -fn clause_rec( - cls: &ast::Clause, - names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>, - next_id: &mut u64, +fn clause_rec<'a>( + cls: &'a ast::Clause, + mut names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, mut explicits: Option<&Stackframe>> ) -> Result<(typed::Clause, usize), Error> { match cls { // (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail @@ -116,8 +113,7 @@ fn clause_rec( ast::Clause::Atom(a) => Ok((typed::Clause::Atom(a.clone()), 0)), ast::Clause::Auto(no, t, b) => { // Allocate id - let id = *next_id; - *next_id += 1; + let id = get_name(); // Pop an explicit if available let (value, rest_explicits) = explicits.map( |Stackframe{ prev, item, .. }| { @@ -128,7 +124,7 @@ fn clause_rec( // Convert the type let typ = if t.len() == 0 {None} else { let (typed::Expr(c, t), _) = exprv_rec( - t.as_ref(), names, next_id, None + t.as_ref(), names.clone(), None )?; if t.len() > 0 {return Err(Error::ExplicitBottomKind)} else {Some(Mrc::new(c))} @@ -136,7 +132,7 @@ fn clause_rec( // Traverse body with extended context if let Some(name) = no {names.set(&&**name, id)} let (body, used_expls) = exprv_rec( - b.as_ref(), names, next_id, explicits + b.as_ref(), names, explicits )?; // Produce a binding instead of an auto if explicit was available if let Some(known_value) = value { @@ -150,20 +146,15 @@ fn clause_rec( } ast::Clause::Lambda(n, t, b) => { // Allocate id - let id = *next_id; - *next_id += 1; + let id = get_name(); // Convert the type let typ = if t.len() == 0 {None} else { - let (typed::Expr(c, t), _) = exprv_rec( - t.as_ref(), names, next_id, None - )?; + let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?; if t.len() > 0 {return Err(Error::ExplicitBottomKind)} else {Some(Mrc::new(c))} }; names.set(&&**n, id); - let (body, used_expls) = exprv_rec( - b.as_ref(), names, next_id, explicits - )?; + let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?; Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls)) } ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)), @@ -175,7 +166,7 @@ fn clause_rec( ast::Clause::S(paren, entries) => { if *paren != '(' {return Err(Error::BadGroup(*paren))} let (typed::Expr(val, typ), used_expls) = exprv_rec( - entries.as_ref(), names, next_id, explicits + entries.as_ref(), names, explicits )?; if typ.len() == 0 {Ok((val, used_expls))} else {Err(Error::ExprToClause(typed::Expr(val, typ)))} diff --git a/src/representations/get_name.rs b/src/representations/get_name.rs new file mode 100644 index 0000000..425c36f --- /dev/null +++ b/src/representations/get_name.rs @@ -0,0 +1,10 @@ +use std::sync::atomic::AtomicU64; +use lazy_static::lazy_static; + +lazy_static! { + static ref NEXT_NAME: AtomicU64 = AtomicU64::new(0); +} + +pub fn get_name() -> u64 { + NEXT_NAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed) +} \ No newline at end of file diff --git a/src/representations/mod.rs b/src/representations/mod.rs index ccabe32..78ea5a1 100644 --- a/src/representations/mod.rs +++ b/src/representations/mod.rs @@ -2,4 +2,5 @@ pub mod ast; pub mod typed; pub mod literal; pub mod ast_to_typed; +pub mod get_name; pub use literal::Literal; diff --git a/src/utils/substack.rs b/src/utils/substack.rs index 8cbf8e6..cc65112 100644 --- a/src/utils/substack.rs +++ b/src/utils/substack.rs @@ -33,7 +33,7 @@ impl<'a, T: 'a> Stackframe<'a, T> { len: self.len + 1 } } - pub fn opush(prev: Option<&Self>, item: T) -> Self { + pub fn opush(prev: Option<&'a Self>, item: T) -> Self { Self { item, prev,