Working example

This commit is contained in:
2023-03-10 13:58:16 +00:00
parent 35a081162f
commit 180ebb56fa
62 changed files with 1487 additions and 372 deletions

View File

@@ -16,8 +16,8 @@ impl AssertionError {
return Err(Self { value, assertion }.into_extern())
}
pub fn into_extern(self) -> Rc<dyn ExternError> {
Rc::new(self)
pub fn ext(value: Clause, assertion: &'static str) -> Rc<dyn ExternError> {
return Self { value, assertion }.into_extern()
}
}

21
src/external/bool/boolean.rs vendored Normal file
View File

@@ -0,0 +1,21 @@
use crate::{atomic_inert, representations::{interpreted::Clause, Primitive}, foreign::Atom};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Boolean(pub bool);
atomic_inert!(Boolean);
impl From<bool> for Boolean { fn from(value: bool) -> Self { Self(value) } }
impl<'a> TryFrom<&'a Clause> for Boolean {
type Error = ();
fn try_from(value: &'a Clause) -> Result<Self, Self::Error> {
if let Clause::P(Primitive::Atom(Atom(a))) = value {
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
return Ok(*b)
}
}
return Err(())
}
}

52
src/external/bool/equals.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::foreign::Atom;
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
use super::super::assertion_error::AssertionError;
use super::boolean::Boolean;
/// Equals function
///
/// Next state: [Equals1]
#[derive(Clone)]
pub struct Equals2;
externfn_impl!(Equals2, |_: &Self, c: Clause| {Ok(Equals1{c})});
/// Partially applied Equals function
///
/// Prev state: [Equals2]; Next state: [Equals0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Equals1{ c: Clause }
atomic_redirect!(Equals1, c);
atomic_impl!(Equals1);
externfn_impl!(Equals1, |this: &Self, c: Clause| {
let a: Literal = this.c.clone().try_into()
.map_err(|_| AssertionError::ext(this.c.clone(), "a primitive"))?;
Ok(Equals0{ a, c })
});
/// Fully applied Equals function.
///
/// Prev state: [Equals1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Equals0 { a: Literal, c: Clause }
atomic_redirect!(Equals0, c);
atomic_impl!(Equals0, |Self{ a, c }: &Self| {
let b: Literal = c.clone().try_into()
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
let eqls = match (a, b) {
(Literal::Char(c1), Literal::Char(c2)) => *c1 == c2,
(Literal::Num(n1), Literal::Num(n2)) => *n1 == n2,
(Literal::Str(s1), Literal::Str(s2)) => *s1 == s2,
(Literal::Uint(i1), Literal::Uint(i2)) => *i1 == i2,
(_, _) => AssertionError::fail(c.clone(), "the expected type")?,
};
Ok(Clause::P(Primitive::Atom(Atom::new(Boolean::from(eqls)))))
});

43
src/external/bool/ifthenelse.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;
use crate::external::assertion_error::AssertionError;
use crate::representations::PathSet;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
use super::Boolean;
/// IfThenElse function
///
/// Next state: [IfThenElse0]
#[derive(Clone)]
pub struct IfThenElse1;
externfn_impl!(IfThenElse1, |_: &Self, c: Clause| {Ok(IfThenElse0{c})});
/// Partially applied IfThenElse function
///
/// Prev state: [IfThenElse1]; Next state: [IfThenElse0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct IfThenElse0{ c: Clause }
atomic_redirect!(IfThenElse0, c);
atomic_impl!(IfThenElse0, |this: &Self| {
let Boolean(b) = (&this.c).try_into()
.map_err(|_| AssertionError::ext(this.c.clone(), "a boolean"))?;
Ok(if b { Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
body: Rc::new(Clause::Lambda {
args: None,
body: Rc::new(Clause::LambdaArg)
})
}} else { Clause::Lambda {
args: None,
body: Rc::new(Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
body: Rc::new(Clause::LambdaArg)
})
}})
});

13
src/external/bool/mod.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
mod equals;
mod boolean;
mod ifthenelse;
pub use boolean::Boolean;
use crate::project::{Loader, fnlib_loader};
pub fn bool() -> impl Loader {
fnlib_loader(vec![
("ifthenelse", Box::new(ifthenelse::IfThenElse1)),
("equals", Box::new(equals::Equals2))
])
}

13
src/external/conv/mod.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
use crate::project::{fnlib_loader, Loader};
mod to_string;
mod parse_float;
mod parse_uint;
pub fn conv() -> impl Loader {
fnlib_loader(vec![
("parse_float", Box::new(parse_float::ParseFloat1)),
("parse_uint", Box::new(parse_uint::ParseUint1)),
("to_string", Box::new(to_string::ToString1))
])
}

46
src/external/conv/parse_float.rs vendored Normal file
View File

@@ -0,0 +1,46 @@
use chumsky::Parser;
use std::fmt::Debug;
use std::hash::Hash;
use super::super::assertion_error::AssertionError;
use crate::parse::float_parser;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::foreign::ExternError;
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
/// ParseFloat a number
///
/// Next state: [ParseFloat0]
#[derive(Clone)]
pub struct ParseFloat1;
externfn_impl!(ParseFloat1, |_: &Self, c: Clause| {Ok(ParseFloat0{c})});
/// Applied to_string function
///
/// Prev state: [ParseFloat1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct ParseFloat0{ c: Clause }
atomic_redirect!(ParseFloat0, c);
atomic_impl!(ParseFloat0, |Self{ c }: &Self| {
let literal: &Literal = c.try_into()
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
let number = match literal {
Literal::Str(s) => {
let parser = float_parser();
parser.parse(s.as_str()).map_err(|_| AssertionError{
value: c.clone(), assertion: "cannot be parsed into a float"
}.into_extern())?
}
Literal::Num(n) => *n,
Literal::Uint(i) => (*i as u32).into(),
Literal::Char(char) => char.to_digit(10).ok_or(AssertionError{
value: c.clone(), assertion: "is not a decimal digit"
}.into_extern())?.into()
};
Ok(Clause::P(Primitive::Literal(Literal::Num(number))))
});

46
src/external/conv/parse_uint.rs vendored Normal file
View File

@@ -0,0 +1,46 @@
use chumsky::Parser;
use std::fmt::Debug;
use std::hash::Hash;
use super::super::assertion_error::AssertionError;
use crate::parse::int_parser;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::foreign::ExternError;
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
/// Parse a number
///
/// Next state: [ParseUint0]
#[derive(Clone)]
pub struct ParseUint1;
externfn_impl!(ParseUint1, |_: &Self, c: Clause| {Ok(ParseUint0{c})});
/// Applied ParseUint function
///
/// Prev state: [ParseUint1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct ParseUint0{ c: Clause }
atomic_redirect!(ParseUint0, c);
atomic_impl!(ParseUint0, |Self{ c }: &Self| {
let literal: &Literal = c.try_into()
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
let uint = match literal {
Literal::Str(s) => {
let parser = int_parser();
parser.parse(s.as_str()).map_err(|_| AssertionError{
value: c.clone(), assertion: "cannot be parsed into an unsigned int"
}.into_extern())?
}
Literal::Num(n) => n.floor() as u64,
Literal::Uint(i) => *i,
Literal::Char(char) => char.to_digit(10).ok_or(AssertionError{
value: c.clone(), assertion: "is not a decimal digit"
}.into_extern())? as u64
};
Ok(Clause::P(Primitive::Literal(Literal::Uint(uint))))
});

35
src/external/conv/to_string.rs vendored Normal file
View File

@@ -0,0 +1,35 @@
use std::fmt::Debug;
use std::hash::Hash;
use crate::external::assertion_error::AssertionError;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
/// ToString a clause
///
/// Next state: [ToString0]
#[derive(Clone)]
pub struct ToString1;
externfn_impl!(ToString1, |_: &Self, c: Clause| {Ok(ToString0{c})});
/// Applied ToString function
///
/// Prev state: [ToString1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct ToString0{ c: Clause }
atomic_redirect!(ToString0, c);
atomic_impl!(ToString0, |Self{ c }: &Self| {
let literal: &Literal = c.try_into()
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
let string = match literal {
Literal::Char(c) => c.to_string(),
Literal::Uint(i) => i.to_string(),
Literal::Num(n) => n.to_string(),
Literal::Str(s) => s.clone()
};
Ok(Clause::P(Primitive::Literal(Literal::Str(string))))
});

11
src/external/cpsio/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use crate::project::{Loader, fnlib_loader};
mod print;
mod readline;
pub fn cpsio() -> impl Loader {
fnlib_loader(vec![
("print", Box::new(print::Print2)),
("readline", Box::new(readline::Readln2))
])
}

32
src/external/cpsio/print.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;
use crate::external::str::cls2str;
use crate::representations::PathSet;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
/// Print function
///
/// Next state: [Print1]
#[derive(Clone)]
pub struct Print2;
externfn_impl!(Print2, |_: &Self, c: Clause| {Ok(Print1{c})});
/// Partially applied Print function
///
/// Prev state: [Print2]; Next state: [Print0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Print1{ c: Clause }
atomic_redirect!(Print1, c);
atomic_impl!(Print1, |Self{ c }: &Self| {
let message = cls2str(&c)?;
print!("{}", message);
Ok(Clause::Lambda {
args: Some(PathSet{ steps: Rc::new(vec![]), next: None }),
body: Rc::new(Clause::LambdaArg)
})
});

35
src/external/cpsio/readline.rs vendored Normal file
View File

@@ -0,0 +1,35 @@
use std::fmt::Debug;
use std::io::stdin;
use std::rc::Rc;
use std::hash::Hash;
use crate::external::runtime_error::RuntimeError;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
/// Readln function
///
/// Next state: [Readln1]
#[derive(Clone)]
pub struct Readln2;
externfn_impl!(Readln2, |_: &Self, c: Clause| {Ok(Readln1{c})});
/// Partially applied Readln function
///
/// Prev state: [Readln2]; Next state: [Readln0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Readln1{ c: Clause }
atomic_redirect!(Readln1, c);
atomic_impl!(Readln1, |Self{ c }: &Self| {
let mut buf = String::new();
stdin().read_line(&mut buf).map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
buf.pop();
Ok(Clause::Apply {
f: Rc::new(c.clone()),
x: Rc::new(Clause::P(Primitive::Literal(Literal::Str(buf)))),
id: 0
})
});

10
src/external/mod.rs vendored
View File

@@ -1,4 +1,8 @@
mod numbers;
mod num;
mod assertion_error;
use numbers::Multiply2;
pub mod std;
mod conv;
mod str;
mod cpsio;
mod runtime_error;
mod bool;

15
src/external/num/mod.rs vendored Normal file
View File

@@ -0,0 +1,15 @@
mod numeric;
pub mod operators;
pub use numeric::Numeric;
use crate::project::{fnlib_loader, Loader};
pub fn num() -> impl Loader {
fnlib_loader(vec![
("add", Box::new(operators::add::Add2)),
("subtract", Box::new(operators::subtract::Subtract2)),
("multiply", Box::new(operators::multiply::Multiply2)),
("divide", Box::new(operators::divide::Divide2)),
("remainder", Box::new(operators::remainder::Remainder2))
])
}

View File

@@ -10,7 +10,7 @@ use crate::representations::interpreted::Clause;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Numeric {
Int(u64),
Uint(u64),
Num(NotNan<f64>)
}
@@ -19,9 +19,9 @@ impl Add for Numeric {
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a + b),
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a + b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a + b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() + b)
}
}
@@ -32,11 +32,11 @@ impl Sub for Numeric {
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) if b < a => Numeric::Int(a - b),
(Numeric::Int(a), Numeric::Int(b))
(Numeric::Uint(a), Numeric::Uint(b)) if b < a => Numeric::Uint(a - b),
(Numeric::Uint(a), Numeric::Uint(b))
=> Numeric::Num(NotNan::new(a as f64 - b as f64).unwrap()),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a - b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() - b)
}
}
@@ -47,9 +47,9 @@ impl Mul for Numeric {
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a * b),
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a * b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a * b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() * b)
}
}
@@ -59,8 +59,8 @@ impl Div for Numeric {
type Output = Numeric;
fn div(self, rhs: Self) -> Self::Output {
let a = match self { Numeric::Int(i) => i as f64, Numeric::Num(f) => *f };
let b = match rhs { Numeric::Int(i) => i as f64, Numeric::Num(f) => *f };
let a = match self { Numeric::Uint(i) => i as f64, Numeric::Num(f) => *f };
let b = match rhs { Numeric::Uint(i) => i as f64, Numeric::Num(f) => *f };
Numeric::Num(NotNan::new(a / b).unwrap())
}
}
@@ -70,9 +70,9 @@ impl Rem for Numeric {
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a % b),
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a % b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a % b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() % b)
}
}
@@ -85,7 +85,7 @@ impl TryFrom<Clause> for Numeric {
AssertionError::fail(value, "a literal value")?
};
match l {
Literal::Int(i) => Ok(Numeric::Int(i)),
Literal::Uint(i) => Ok(Numeric::Uint(i)),
Literal::Num(n) => Ok(Numeric::Num(n)),
_ => AssertionError::fail(value, "an integer or number")?
}
@@ -95,8 +95,17 @@ impl TryFrom<Clause> for Numeric {
impl From<Numeric> for Clause {
fn from(value: Numeric) -> Self {
Clause::P(Primitive::Literal(match value {
Numeric::Int(i) => Literal::Int(i),
Numeric::Uint(i) => Literal::Uint(i),
Numeric::Num(n) => Literal::Num(n)
}))
}
}
impl From<Numeric> for String {
fn from(value: Numeric) -> Self {
match value {
Numeric::Uint(i) => i.to_string(),
Numeric::Num(n) => n.to_string()
}
}
}

41
src/external/num/operators/add.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use super::super::Numeric;
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
/// Add function
///
/// Next state: [Add1]
#[derive(Clone)]
pub struct Add2;
externfn_impl!(Add2, |_: &Self, c: Clause| {Ok(Add1{c})});
/// Partially applied Add function
///
/// Prev state: [Add2]; Next state: [Add0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Add1{ c: Clause }
atomic_redirect!(Add1, c);
atomic_impl!(Add1);
externfn_impl!(Add1, |this: &Self, c: Clause| {
let a: Numeric = this.c.clone().try_into()?;
Ok(Add0{ a, c })
});
/// Fully applied Add function.
///
/// Prev state: [Add1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Add0 { a: Numeric, c: Clause }
atomic_redirect!(Add0, c);
atomic_impl!(Add0, |Self{ a, c }: &Self| {
let b: Numeric = c.clone().try_into()?;
Ok((*a + b).into())
});

41
src/external/num/operators/divide.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use super::super::Numeric;
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
/// Divide function
///
/// Next state: [Divide1]
#[derive(Clone)]
pub struct Divide2;
externfn_impl!(Divide2, |_: &Self, c: Clause| {Ok(Divide1{c})});
/// Partially applied Divide function
///
/// Prev state: [Divide2]; Next state: [Divide0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Divide1{ c: Clause }
atomic_redirect!(Divide1, c);
atomic_impl!(Divide1);
externfn_impl!(Divide1, |this: &Self, c: Clause| {
let a: Numeric = this.c.clone().try_into()?;
Ok(Divide0{ a, c })
});
/// Fully applied Divide function.
///
/// Prev state: [Divide1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Divide0 { a: Numeric, c: Clause }
atomic_redirect!(Divide0, c);
atomic_impl!(Divide0, |Self{ a, c }: &Self| {
let b: Numeric = c.clone().try_into()?;
Ok((*a / b).into())
});

5
src/external/num/operators/mod.rs vendored Normal file
View File

@@ -0,0 +1,5 @@
pub mod add;
pub mod divide;
pub mod multiply;
pub mod remainder;
pub mod subtract;

View File

@@ -1,14 +1,11 @@
mod numeric;
use numeric::Numeric;
use super::super::Numeric;
use std::fmt::Debug;
use std::rc::Rc;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::foreign::{ExternError, ExternFn, Atom, Atomic};
use crate::representations::Primitive;
use crate::representations::interpreted::{Clause, InternalError};
use crate::representations::interpreted::Clause;
/// Multiply function
///

41
src/external/num/operators/remainder.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use super::super::Numeric;
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
/// Remainder function
///
/// Next state: [Remainder1]
#[derive(Clone)]
pub struct Remainder2;
externfn_impl!(Remainder2, |_: &Self, c: Clause| {Ok(Remainder1{c})});
/// Partially applied Remainder function
///
/// Prev state: [Remainder2]; Next state: [Remainder0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Remainder1{ c: Clause }
atomic_redirect!(Remainder1, c);
atomic_impl!(Remainder1);
externfn_impl!(Remainder1, |this: &Self, c: Clause| {
let a: Numeric = this.c.clone().try_into()?;
Ok(Remainder0{ a, c })
});
/// Fully applied Remainder function.
///
/// Prev state: [Remainder1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Remainder0 { a: Numeric, c: Clause }
atomic_redirect!(Remainder0, c);
atomic_impl!(Remainder0, |Self{ a, c }: &Self| {
let b: Numeric = c.clone().try_into()?;
Ok((*a % b).into())
});

41
src/external/num/operators/subtract.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use super::super::Numeric;
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::{Clause};
/// Subtract function
///
/// Next state: [Subtract1]
#[derive(Clone)]
pub struct Subtract2;
externfn_impl!(Subtract2, |_: &Self, c: Clause| {Ok(Subtract1{c})});
/// Partially applied Subtract function
///
/// Prev state: [Subtract2]; Next state: [Subtract0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Subtract1{ c: Clause }
atomic_redirect!(Subtract1, c);
atomic_impl!(Subtract1);
externfn_impl!(Subtract1, |this: &Self, c: Clause| {
let a: Numeric = this.c.clone().try_into()?;
Ok(Subtract0{ a, c })
});
/// Fully applied Subtract function.
///
/// Prev state: [Subtract1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Subtract0 { a: Numeric, c: Clause }
atomic_redirect!(Subtract0, c);
atomic_impl!(Subtract0, |Self{ a, c }: &Self| {
let b: Numeric = c.clone().try_into()?;
Ok((*a - b).into())
});

27
src/external/runtime_error.rs vendored Normal file
View File

@@ -0,0 +1,27 @@
use std::{rc::Rc, fmt::Display};
use crate::foreign::ExternError;
#[derive(Clone)]
pub struct RuntimeError {
message: String,
operation: &'static str,
}
impl RuntimeError {
pub fn fail(message: String, operation: &'static str) -> Result<!, Rc<dyn ExternError>> {
return Err(Self { message, operation }.into_extern())
}
pub fn ext(message: String, operation: &'static str) -> Rc<dyn ExternError> {
return Self { message, operation }.into_extern()
}
}
impl Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error while {}: {}", self.operation, self.message)
}
}
impl ExternError for RuntimeError{}

19
src/external/std.rs vendored Normal file
View File

@@ -0,0 +1,19 @@
use std::collections::HashMap;
use crate::project::{map_loader, Loader};
use super::bool::bool;
use super::cpsio::cpsio;
use super::conv::conv;
use super::str::str;
use super::num::num;
pub fn std() -> impl Loader {
map_loader(HashMap::from([
("cpsio", cpsio().boxed()),
("conv", conv().boxed()),
("bool", bool().boxed()),
("str", str().boxed()),
("num", num().boxed()),
]))
}

47
src/external/str/char_at.rs vendored Normal file
View File

@@ -0,0 +1,47 @@
use std::fmt::Debug;
use std::hash::Hash;
use crate::external::assertion_error::AssertionError;
use crate::external::runtime_error::RuntimeError;
use crate::representations::{Literal, Primitive};
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::interpreted::Clause;
/// CharAt function
///
/// Next state: [CharAt1]
#[derive(Clone)]
pub struct CharAt2;
externfn_impl!(CharAt2, |_: &Self, c: Clause| {Ok(CharAt1{c})});
/// Partially applied CharAt function
///
/// Prev state: [CharAt2]; Next state: [CharAt0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct CharAt1{ c: Clause }
atomic_redirect!(CharAt1, c);
atomic_impl!(CharAt1);
externfn_impl!(CharAt1, |this: &Self, c: Clause| {
let s = if let Ok(Literal::Str(s)) = this.c.clone().try_into() {s}
else {AssertionError::fail(this.c.clone(), "a string")?};
Ok(CharAt0{ s, c })
});
/// Fully applied CharAt function.
///
/// Prev state: [CharAt1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct CharAt0 { s: String, c: Clause }
atomic_redirect!(CharAt0, c);
atomic_impl!(CharAt0, |Self{ s, c }: &Self| {
let i = if let Ok(Literal::Uint(i)) = c.clone().try_into() {i}
else {AssertionError::fail(c.clone(), "an uint")?};
if let Some(c) = s.chars().nth(i as usize) {
Ok(Clause::P(Primitive::Literal(Literal::Char(c))))
} else {
RuntimeError::fail("Character index out of bounds".to_string(), "indexing string")?
}
});

13
src/external/str/cls2str.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
use std::rc::Rc;
use crate::foreign::ExternError;
use crate::external::assertion_error::AssertionError;
use crate::representations::{interpreted::Clause, Literal};
pub fn cls2str(c: &Clause) -> Result<&String, Rc<dyn ExternError>> {
let literal: &Literal = c.try_into()
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
if let Literal::Str(s) = literal {Ok(s)} else {
AssertionError::fail(c.clone(), "a string")?
}
}

41
src/external/str/concatenate.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use super::cls2str;
use std::fmt::Debug;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
/// Concatenate function
///
/// Next state: [Concatenate1]
#[derive(Clone)]
pub struct Concatenate2;
externfn_impl!(Concatenate2, |_: &Self, c: Clause| {Ok(Concatenate1{c})});
/// Partially applied Concatenate function
///
/// Prev state: [Concatenate2]; Next state: [Concatenate0]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Concatenate1{ c: Clause }
atomic_redirect!(Concatenate1, c);
atomic_impl!(Concatenate1);
externfn_impl!(Concatenate1, |this: &Self, c: Clause| {
let a: String = cls2str(&this.c)?.clone();
Ok(Concatenate0{ a, c })
});
/// Fully applied Concatenate function.
///
/// Prev state: [Concatenate1]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct Concatenate0 { a: String, c: Clause }
atomic_redirect!(Concatenate0, c);
atomic_impl!(Concatenate0, |Self{ a, c }: &Self| {
let b: &String = cls2str(c)?;
Ok(Clause::P(Primitive::Literal(Literal::Str(a.to_owned() + b))))
});

11
src/external/str/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
mod concatenate;
mod cls2str;
mod char_at;
pub use cls2str::cls2str;
use crate::project::{Loader, fnlib_loader};
pub fn str() -> impl Loader {
fnlib_loader(vec![
("concatenate", Box::new(concatenate::Concatenate2))
])
}

View File

@@ -7,15 +7,17 @@ use dyn_clone::DynClone;
use crate::representations::interpreted::{Clause, RuntimeError, InternalError};
pub trait ExternError: Display {}
pub trait ExternError: Display {
fn into_extern(self) -> Rc<dyn ExternError> where Self: 'static + Sized {
Rc::new(self)
}
}
/// Represents an externally defined function from the perspective of the executor
/// Since Orchid lacks basic numerical operations, these are also external functions.
pub trait ExternFn: DynClone {
fn name(&self) -> &str;
fn apply(&self, arg: Clause) -> Result<Clause, Rc<dyn ExternError>>;
fn argstr(&self) -> &str { "clause" }
fn retstr(&self) -> &str { "clause" }
fn hash(&self, state: &mut dyn std::hash::Hasher) { state.write_str(self.name()) }
}
@@ -28,7 +30,7 @@ impl Hash for dyn ExternFn {
}
impl Debug for dyn ExternFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.argstr(), self.retstr())
write!(f, "##EXTERN[{}]##", self.name())
}
}

View File

@@ -1,147 +0,0 @@
#[allow(unused)] // for the doc comments
use crate::representations::Primitive;
#[allow(unused)] // for the doc comments
use crate::foreign::{Atomic, ExternFn};
#[allow(unused)] // for the doc comments
use std::any::Any;
#[allow(unused)] // for the doc comments
use std::hash::Hash;
#[allow(unused)] // for the doc comments
use dyn_clone::DynClone;
#[allow(unused)] // for the doc comments
use std::fmt::Debug;
/// A macro that generates the straightforward, syntactically invariant part of implementing
/// [Atomic]. Implemented fns are [Atomic::as_any], [Atomic::definitely_eq] and [Atomic::hash].
///
/// It depends on [Eq] and [Hash]
#[macro_export]
macro_rules! atomic_defaults {
() => {
fn as_any(&self) -> &dyn std::any::Any { self }
fn definitely_eq(&self, _other: &dyn std::any::Any) -> bool {
_other.downcast_ref().map(|o| self == o).unwrap_or(false)
}
fn hash(&self, mut hasher: &mut dyn std::hash::Hasher) { <Self as Hash>::hash(self, &mut hasher) }
};
}
/// A macro that generates implementations of [Atomic] to simplify the development of
/// external bindings for Orchid.
///
/// The macro depends on implementations of [AsRef<Clause>] and [From<(&Self, Clause)>] for
/// extracting the clause to be processed and then reconstructing the [Atomic]. Naturally,
/// supertraits of [Atomic] are also dependencies. These are [Any], [Debug] and [DynClone].
///
/// The simplest form just requires the typename to be specified. This additionally depends on an
/// implementation of [ExternFn] because after the clause is fully normalized it returns `Self`
/// wrapped in a [Primitive::ExternFn]. It is intended for intermediary
/// stages of the function where validation and the next state are defined in [ExternFn::apply].
///
/// ```
/// atomic_impl!(Multiply1)
/// ```
///
/// The last stage of the function should use the extended form of the macro which takes an
/// additional closure to explicitly describe what happens when the argument is fully processed.
///
/// ```
/// // excerpt from the exact implementation of Multiply
/// atomic_impl!(Multiply0, |Self(a, cls): &Self| {
/// let b: Numeric = cls.clone().try_into().map_err(AssertionError::into_extern)?;
/// Ok(*a * b).into())
/// })
/// ```
///
#[macro_export]
macro_rules! atomic_impl {
($typ:ident) => {
atomic_impl!{$typ, |this: &Self| Ok(Clause::P(Primitive::ExternFn(Box::new(this.clone()))))}
};
($typ:ident, $next_phase:expr) => {
impl Atomic for $typ {
$crate::atomic_defaults!{}
fn run_once(&self) -> Result<Clause, $crate::representations::interpreted::InternalError> {
match <Self as AsRef<Clause>>::as_ref(self).run_once() {
Err(InternalError::NonReducible) => {
($next_phase)(self)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
.map_err(InternalError::Runtime)
}
Ok(arg) => Ok(Clause::P(Primitive::Atom(Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))
)))),
Err(e) => Err(e),
}
}
fn run_n_times(&self, n: usize) -> Result<(Clause, usize), $crate::representations::interpreted::RuntimeError> {
match <Self as AsRef<Clause>>::as_ref(self).run_n_times(n) {
Ok((arg, k)) if k == n => Ok((Clause::P(Primitive::Atom(Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))))), k)),
Ok((arg, k)) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map(|cls| (cls, k))
.map_err($crate::representations::interpreted::RuntimeError::Extern)
}
Err(e) => Err(e),
}
}
fn run_to_completion(&self) -> Result<Clause, $crate::representations::interpreted::RuntimeError> {
match <Self as AsRef<Clause>>::as_ref(self).run_to_completion() {
Ok(arg) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
},
Err(e) => Err(e)
}
}
}
};
}
/// Implement the traits required by [atomic_impl] to redirect run_* functions to a field
/// with a particular name.
#[macro_export]
macro_rules! atomic_redirect {
($typ:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.0 }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, clause): (&Self, Clause)) -> Self {
Self{ 0: clause, ..old.clone() }
}
}
};
($typ:ident, $field:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.$field }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, $field): (&Self, Clause)) -> Self {
Self{ $field, ..old.clone() }
}
}
};
}
/// Implement [ExternFn] with a closure that produces an [Atomic] from a reference to self
/// and a closure. This can be used in conjunction with [atomic_impl] and [atomic_redirect]
/// to normalize the argument automatically before using it.
#[macro_export]
macro_rules! externfn_impl {
($typ:ident, $next_atomic:expr) => {
impl ExternFn for $typ {
fn name(&self) -> &str {stringify!($typ)}
fn apply(&self, c: Clause) -> Result<Clause, Rc<dyn ExternError>> {
match ($next_atomic)(self, c) { // ? casts the result but we want to strictly forward it
Ok(r) => Ok(Clause::P(Primitive::Atom(Atom::new(r)))),
Err(e) => Err(e)
}
}
}
};
}

View File

@@ -0,0 +1,19 @@
#[allow(unused)] // for the doc comments
use crate::foreign::Atomic;
/// A macro that generates the straightforward, syntactically invariant part of implementing
/// [Atomic]. Implemented fns are [Atomic::as_any], [Atomic::definitely_eq] and [Atomic::hash].
///
/// It depends on [Eq] and [Hash]
#[macro_export]
macro_rules! atomic_defaults {
() => {
fn as_any(&self) -> &dyn std::any::Any { self }
fn definitely_eq(&self, _other: &dyn std::any::Any) -> bool {
_other.downcast_ref().map(|o| self == o).unwrap_or(false)
}
fn hash(&self, mut hasher: &mut dyn std::hash::Hasher) {
<Self as std::hash::Hash>::hash(self, &mut hasher)
}
};
}

View File

@@ -0,0 +1,105 @@
#[allow(unused)] // for the doc comments
use crate::representations::Primitive;
#[allow(unused)] // for the doc comments
use crate::foreign::{Atomic, ExternFn};
#[allow(unused)] // for the doc comments
use std::any::Any;
#[allow(unused)] // for the doc comments
use dyn_clone::DynClone;
#[allow(unused)] // for the doc comments
use std::fmt::Debug;
/// A macro that generates implementations of [Atomic] to simplify the development of
/// external bindings for Orchid.
///
/// The macro depends on implementations of [AsRef<Clause>] and [From<(&Self, Clause)>] for
/// extracting the clause to be processed and then reconstructing the [Atomic]. Naturally,
/// supertraits of [Atomic] are also dependencies. These are [Any], [Debug] and [DynClone].
///
/// The simplest form just requires the typename to be specified. This additionally depends on an
/// implementation of [ExternFn] because after the clause is fully normalized it returns `Self`
/// wrapped in a [Primitive::ExternFn]. It is intended for intermediary
/// stages of the function where validation and the next state are defined in [ExternFn::apply].
///
/// ```
/// atomic_impl!(Multiply1)
/// ```
///
/// The last stage of the function should use the extended form of the macro which takes an
/// additional closure to explicitly describe what happens when the argument is fully processed.
///
/// ```
/// // excerpt from the exact implementation of Multiply
/// atomic_impl!(Multiply0, |Self(a, cls): &Self| {
/// let b: Numeric = cls.clone().try_into().map_err(AssertionError::into_extern)?;
/// Ok(*a * b).into())
/// })
/// ```
///
#[macro_export]
macro_rules! atomic_impl {
($typ:ident) => {
atomic_impl!{$typ, |this: &Self| Ok(Clause::P(
$crate::representations::Primitive::ExternFn(Box::new(this.clone()))
))}
};
($typ:ident, $next_phase:expr) => {
impl $crate::foreign::Atomic for $typ {
$crate::atomic_defaults!{}
fn run_once(&self) -> Result<
$crate::representations::interpreted::Clause,
$crate::representations::interpreted::InternalError
> {
match <Self as AsRef<$crate::representations::interpreted::Clause>>::as_ref(self).run_once() {
Err($crate::representations::interpreted::InternalError::NonReducible) => {
($next_phase)(self)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
.map_err($crate::representations::interpreted::InternalError::Runtime)
}
Ok(arg) => Ok($crate::representations::interpreted::Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))
)
)
)),
Err(e) => Err(e),
}
}
fn run_n_times(&self, n: usize) -> Result<
(
$crate::representations::interpreted::Clause,
usize
),
$crate::representations::interpreted::RuntimeError
> {
match <Self as AsRef<Clause>>::as_ref(self).run_n_times(n) {
Ok((arg, k)) if k == n => Ok((Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))
)
)
), k)),
Ok((arg, k)) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map(|cls| (cls, k))
.map_err($crate::representations::interpreted::RuntimeError::Extern)
}
Err(e) => Err(e),
}
}
fn run_to_completion(&self) -> Result<Clause, $crate::representations::interpreted::RuntimeError> {
match <Self as AsRef<Clause>>::as_ref(self).run_to_completion() {
Ok(arg) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
},
Err(e) => Err(e)
}
}
}
};
}

View File

@@ -0,0 +1,48 @@
#[allow(unused)] // for the doc comments
use crate::foreign::Atomic;
#[allow(unused)] // for the doc comments
use std::any::Any;
#[allow(unused)] // for the doc comments
use dyn_clone::DynClone;
#[allow(unused)] // for the doc comments
use std::fmt::Debug;
/// Implement [Atomic] for a structure that cannot be transformed any further. This would be optimal
/// for atomics encapsulating raw data. [Atomic] depends on [Any], [Debug] and [DynClone].
#[macro_export]
macro_rules! atomic_inert {
($typ:ident) => {
impl $crate::foreign::Atomic for $typ {
$crate::atomic_defaults!{}
fn run_once(&self) -> Result<
$crate::representations::interpreted::Clause,
$crate::representations::interpreted::InternalError
> {
Err($crate::representations::interpreted::InternalError::NonReducible)
}
fn run_n_times(&self, _: usize) -> Result<
(
$crate::representations::interpreted::Clause,
usize
),
$crate::representations::interpreted::RuntimeError
> {
Ok(($crate::representations::interpreted::Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(self.clone())
)
), 0))
}
fn run_to_completion(&self) -> Result<
$crate::representations::interpreted::Clause,
$crate::representations::interpreted::RuntimeError
> {
Ok($crate::representations::interpreted::Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(self.clone())
)
))
}
}
};
}

View File

@@ -0,0 +1,28 @@
#[allow(unused)]
use super::atomic_impl;
/// Implement the traits required by [atomic_impl] to redirect run_* functions to a field
/// with a particular name.
#[macro_export]
macro_rules! atomic_redirect {
($typ:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.0 }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, clause): (&Self, Clause)) -> Self {
Self{ 0: clause, ..old.clone() }
}
}
};
($typ:ident, $field:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.$field }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, $field): (&Self, Clause)) -> Self {
Self{ $field, ..old.clone() }
}
}
};
}

View File

@@ -0,0 +1,43 @@
#[allow(unused)] // for the doc comments
use crate::{atomic_impl, atomic_redirect};
#[allow(unused)] // for the doc comments
use crate::representations::Primitive;
#[allow(unused)] // for the doc comments
use crate::foreign::{Atomic, ExternFn};
#[allow(unused)] // for the doc comments
use std::any::Any;
#[allow(unused)] // for the doc comments
use std::hash::Hash;
#[allow(unused)] // for the doc comments
use dyn_clone::DynClone;
#[allow(unused)] // for the doc comments
use std::fmt::Debug;
/// Implement [ExternFn] with a closure that produces an [Atomic] from a reference to self
/// and a closure. This can be used in conjunction with [atomic_impl] and [atomic_redirect]
/// to normalize the argument automatically before using it.
#[macro_export]
macro_rules! externfn_impl {
($typ:ident, $next_atomic:expr) => {
impl $crate::foreign::ExternFn for $typ {
fn name(&self) -> &str {stringify!($typ)}
fn apply(&self,
c: $crate::representations::interpreted::Clause
) -> Result<
$crate::representations::interpreted::Clause,
std::rc::Rc<dyn $crate::foreign::ExternError>
> {
match ($next_atomic)(self, c) { // ? casts the result but we want to strictly forward it
Ok(r) => Ok(
$crate::representations::interpreted::Clause::P(
$crate::representations::Primitive::Atom(
$crate::foreign::Atom::new(r)
)
)
),
Err(e) => Err(e)
}
}
}
};
}

View File

@@ -0,0 +1,5 @@
mod atomic_defaults;
mod atomic_impl;
mod atomic_inert;
mod atomic_redirect;
mod externfn_impl;

View File

@@ -8,12 +8,13 @@
#![feature(arc_unwrap_or_clone)]
#![feature(hasher_prefixfree_extras)]
#![feature(closure_lifetime_binder)]
#![feature(generic_arg_infer)]
use std::{env::current_dir, io};
use std::{env::current_dir, collections::HashMap};
// mod executor;
mod parse;
mod project;
pub(crate) mod project;
mod utils;
mod representations;
mod rule;
@@ -21,16 +22,17 @@ mod scheduler;
pub(crate) mod foreign;
mod external;
mod foreign_macros;
use file_loader::LoadingError;
pub use representations::ast;
use ast::{Expr, Clause};
// use representations::typed as t;
use mappable_rc::Mrc;
use project::{rule_collector, Loaded, file_loader};
use project::{rule_collector, file_loader};
use rule::Repository;
use utils::{to_mrc_slice, mrc_empty_slice, one_mrc_slice};
use utils::to_mrc_slice;
use crate::representations::{ast_to_postmacro, postmacro_to_interpreted, interpreted};
use crate::external::std::std;
use crate::project::{map_loader, string_loader, Loader, ModuleError};
use crate::representations::{ast_to_postmacro, postmacro_to_interpreted};
fn literal(orig: &[&str]) -> Mrc<[String]> {
to_mrc_slice(vliteral(orig))
@@ -41,60 +43,68 @@ fn vliteral(orig: &[&str]) -> Vec<String> {
}
static PRELUDE:&str = r#"
export ... $name =1000=> (match_seqence $name)
export ] =1000=> conslist_carriage(none)
export , $name conslist_carriage($tail) =1000=> conslist_carriage((some (cons $name $tail)))
export [ $name conslist_carriage($tail) =1000=> (some (cons $name $tail))
export (match_sequence $lhs) >> (match_sequence $rhs) =100=> (bind ($lhs) (\_. $rhs))
export (match_sequence $lhs) >>= (match_sequence $rhs) =100=> (bind ($lhs) ($rhs))
"#;
import std::(
num::(add, subtract, multiply, remainder, divide),
bool::(equals, ifthenelse),
str::concatenate
)
export (...$a + ...$b) =1001=> (add (...$a) (...$b))
export (...$a - ...$b:1) =1001=> (subtract (...$a) (...$b))
export (...$a * ...$b) =1000=> (multiply (...$a) (...$b))
export (...$a % ...$b:1) =1000=> (remainder (...$a) (...$b))
export (...$a / ...$b:1) =1000=> (divide (...$a) (...$b))
export (...$a = ...$b) =1002=> (equals (...$a) (...$b))
export (...$a ++ ...$b) =1003=> (concatenate (...$a) (...$b))
export do { ...$statement ; ...$rest:1 } =10_001=> (
statement (...$statement) do { ...$rest }
)
export do { ...$statement } =10_000=> (...$statement)
export statement (let $_name = ...$value) ...$next =10_000=> (
(\$_name. ...$next) (...$value)
)
export statement (cps $_name = ...$operation) ...$next =10_001=> (
(...$operation) \$_name. ...$next
)
export statement (cps ...$operation) ...$next =10_000=> (
(...$operation) (...$next)
)
export if ...$cond then ...$true else ...$false:1 =5_000=> (
ifthenelse (...$cond) (...$true) (...$false)
)
"#;
fn initial_tree() -> Mrc<[Expr]> {
to_mrc_slice(vec![Expr(Clause::Name {
local: None,
qualified: literal(&["main", "main"])
qualified: literal(&["mod", "main", "main"])
}, to_mrc_slice(vec![]))])
}
#[allow(unused)]
// fn typed_notation_debug() {
// let true_ex = t::Clause::Auto(0, mrc_empty_slice(),
// t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
// t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
// t::Clause::LambdaArg(1).wrap_t(t::Clause::AutoArg(0))
// ).wrap()
// ).wrap()
// ).wrap();
// let false_ex = t::Clause::Auto(0, mrc_empty_slice(),
// t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
// t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
// t::Clause::LambdaArg(2).wrap_t(t::Clause::AutoArg(0))
// ).wrap()
// ).wrap()
// ).wrap();
// println!("{:?}", t::Clause::Apply(t::Clause::Apply(Mrc::clone(&true_ex), true_ex).wrap(), false_ex))
// }
#[allow(unused)]
fn load_project() {
let cwd = current_dir().unwrap();
let collect_rules = rule_collector(move |n| -> Result<Loaded, LoadingError> {
if n == literal(&["prelude"]) { Ok(Loaded::Module(PRELUDE.to_string())) }
else { file_loader(cwd.clone())(n) }
}, vliteral(&["...", ">>", ">>=", "[", "]", ",", "=", "=>"]));
let rules = match collect_rules.try_find(&literal(&["main"])) {
let collect_rules = rule_collector(map_loader(HashMap::from([
("std", std().boxed()),
("prelude", string_loader(PRELUDE).boxed()),
("mod", file_loader(current_dir().expect("Missing CWD!")).boxed())
])));
let rules = match collect_rules.try_find(&literal(&["mod", "main"])) {
Ok(rules) => rules,
Err(err) => panic!("{:#?}", err)
Err(err) => if let ModuleError::Syntax(pe) = err {
panic!("{}", pe);
} else {panic!("{:#?}", err)}
};
let mut tree = initial_tree();
println!("Start processing {tree:?}");
let repo = Repository::new(rules.as_ref().to_owned());
println!("Ruleset: {repo:?}");
xloop!(let mut i = 0; i < 10; i += 1; {
xloop!(let mut i = 0; i < 100; i += 1; {
match repo.step(Mrc::clone(&tree)) {
Ok(Some(phase)) => {
println!("Step {i}: {phase:?}");
//println!("Step {i}: {phase:?}");
tree = phase;
},
Ok(None) => {
@@ -116,4 +126,9 @@ fn load_project() {
fn main() {
// lambda_notation_debug();
load_project();
// let mut std = std();
// match std.load(&["parse_float"]) {
// Ok(_) => println!("wtf"),
// Err(e) => panic!("{:?}", e)
// }
}

View File

@@ -93,14 +93,14 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
let clause =
enum_parser!(Lexeme::Comment).repeated()
.ignore_then(choice((
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Primitive::Literal).map(Clause::P),
enum_parser!(Lexeme >> Literal; Uint, Num, Char, Str).map(Primitive::Literal).map(Clause::P),
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
just(Lexeme::name("...")).to(true)
.or(just(Lexeme::name("..")).to(false))
.then(placeholder_parser())
.then(
just(Lexeme::Type)
.ignore_then(enum_parser!(Lexeme::Int))
.ignore_then(enum_parser!(Lexeme::Uint))
.or_not().map(Option::unwrap_or_default)
)
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((

View File

@@ -24,7 +24,7 @@ impl From<Entry> for (Lexeme, Range<usize>) {
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Lexeme {
Num(NotNan<f64>),
Int(u64),
Uint(u64),
Char(char),
Str(String),
Name(String),
@@ -35,14 +35,16 @@ pub enum Lexeme {
BS, // Backslash
At,
Type, // type operator
Comment(String)
Comment(String),
Export,
Import,
}
impl Debug for Lexeme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Num(n) => write!(f, "{}", n),
Self::Int(i) => write!(f, "{}", i),
Self::Uint(i) => write!(f, "{}", i),
Self::Char(c) => write!(f, "{:?}", c),
Self::Str(s) => write!(f, "{:?}", s),
Self::Name(name) => write!(f, "{}", name),
@@ -59,6 +61,8 @@ impl Debug for Lexeme {
Self::At => write!(f, "@"),
Self::Type => write!(f, ":"),
Self::Comment(text) => write!(f, "--[{}]--", text),
Self::Export => write!(f, "export"),
Self::Import => write!(f, "import"),
}
}
}
@@ -118,11 +122,14 @@ fn paren_parser<'a>(
})
}
pub fn lexer<'a, T: 'a>(ops: &[T]) -> impl Parser<char, LexedText, Error=Simple<char>> + 'a
pub fn lexer<'a, T: 'a>(ops: &[T]) -> impl Parser<char, Vec<Entry>, Error=Simple<char>> + 'a
where T: AsRef<str> + Clone {
let all_ops = ops.iter().map(|o| o.as_ref().to_string())
.chain(iter::once(".".to_string())).collect::<Vec<_>>();
recursive(move |recurse: Recursive<char, LexSubres, Simple<char>>| {
.chain([",", ".", "..", "..."].into_iter().map(str::to_string))
.collect::<Vec<_>>();
just("export").padded().to(Lexeme::Export)
.or(just("import").padded().to(Lexeme::Import))
.or_not().then(recursive(move |recurse: Recursive<char, LexSubres, Simple<char>>| {
choice((
paren_parser(recurse.clone(), '(', ')'),
paren_parser(recurse.clone(), '[', ']'),
@@ -135,7 +142,7 @@ where T: AsRef<str> + Clone {
just('\\').padded().to(Lexeme::BS),
just('@').padded().to(Lexeme::At),
just(':').to(Lexeme::Type),
number::int_parser().map(Lexeme::Int), // all ints are valid floats so it takes precedence
number::int_parser().map(Lexeme::Uint), // all ints are valid floats so it takes precedence
number::float_parser().map(Lexeme::Num),
string::char_parser().map(Lexeme::Char),
string::str_parser().map(Lexeme::Str),
@@ -143,7 +150,9 @@ where T: AsRef<str> + Clone {
)).map_with_span(|lx, span| box_once(Entry(lx, span)) as LexSubres)
))
}).separated_by(one_of("\t ").repeated())
.flatten().collect()
.separated_by(just('\n').then(text::whitespace()).ignored())
.map(LexedText)
.flatten().collect())
.map(|(prefix, rest): (Option<Lexeme>, Vec<Entry>)| {
prefix.into_iter().map(|l| Entry(l, 0..6)).chain(rest.into_iter()).collect()
})
.then_ignore(text::whitespace()).then_ignore(end())
}

View File

@@ -15,4 +15,6 @@ pub use sourcefile::imports;
pub use sourcefile::exported_names;
pub use lexer::{lexer, Lexeme, Entry as LexerEntry};
pub use name::is_op;
pub use parse::{parse, reparse, ParseError};
pub use parse::{parse, reparse, ParseError};
pub use import::Import;
pub use number::{float_parser, int_parser};

View File

@@ -4,7 +4,7 @@ use chumsky::{prelude::{Simple, end}, Stream, Parser};
use itertools::Itertools;
use thiserror::Error;
use crate::{ast::Rule, parse::lexer::LexedText};
use crate::{ast::Rule, parse::{lexer::LexedText, sourcefile::split_lines}};
use super::{Lexeme, FileEntry, lexer, line_parser, LexerEntry};
@@ -17,14 +17,13 @@ pub enum ParseError {
Ast(Vec<Simple<Lexeme>>)
}
pub fn parse<'a, Iter, S, Op>(ops: &[Op], stream: S) -> Result<Vec<FileEntry>, ParseError>
where
Op: 'a + AsRef<str> + Clone,
Iter: Iterator<Item = (char, Range<usize>)> + 'a,
S: Into<Stream<'a, char, Range<usize>, Iter>> {
let lexed = lexer(ops).parse(stream).map_err(ParseError::Lex)?;
println!("Lexed:\n{:?}", lexed);
let LexedText(token_batchv) = lexed;
pub fn parse<'a, Op>(ops: &[Op], data: &str) -> Result<Vec<FileEntry>, ParseError>
where Op: 'a + AsRef<str> + Clone {
let lexie = lexer(ops);
let token_batchv = split_lines(data).map(|line| {
lexie.parse(line).map_err(ParseError::Lex)
}).collect::<Result<Vec<_>, _>>()?;
println!("Lexed:\n{:?}", LexedText(token_batchv.clone()));
let parsr = line_parser().then_ignore(end());
let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| {
!v.is_empty()
@@ -34,7 +33,7 @@ where
// Stream expects tuples, lexer outputs structs
let tuples = v.into_iter().map_into::<(Lexeme, Range<usize>)>();
parsr.parse(Stream::from_iter(end..end+1, tuples))
// ^^^^^^^^^^
// ^^^^^^^^^^
// I haven't the foggiest idea why this is needed, parsers are supposed to be lazy so the
// end of input should make little difference
}).map(|res| match res {
@@ -48,13 +47,10 @@ where
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
}
pub fn reparse<'a, Iter, S, Op>(ops: &[Op], stream: S, pre: &[FileEntry])
pub fn reparse<'a, Op>(ops: &[Op], data: &str, pre: &[FileEntry])
-> Result<Vec<FileEntry>, ParseError>
where
Op: 'a + AsRef<str> + Clone,
Iter: Iterator<Item = (char, Range<usize>)> + 'a,
S: Into<Stream<'a, char, Range<usize>, Iter>> {
let result = parse(ops, stream)?;
where Op: 'a + AsRef<str> + Clone {
let result = parse(ops, data)?;
Ok(result.into_iter().zip(pre.iter()).map(|(mut output, donor)| {
if let FileEntry::Rule(Rule{source, ..}, _) = &mut output {
if let FileEntry::Rule(Rule{source: s2, ..}, _) = donor {

View File

@@ -1,23 +1,27 @@
use std::collections::HashSet;
use std::iter;
use crate::{enum_parser, box_chain};
use crate::ast::{Expr, Clause, Rule};
use crate::utils::to_mrc_slice;
use crate::utils::{to_mrc_slice, one_mrc_slice};
use crate::utils::Stackframe;
use crate::utils::iter::box_empty;
use super::expression::xpr_parser;
use super::import;
use super::import::{self, Import};
use super::import::import_parser;
use super::lexer::Lexeme;
use chumsky::{Parser, prelude::*};
use ordered_float::NotNan;
use lazy_static::lazy_static;
/// Anything we might encounter in a file
#[derive(Debug, Clone)]
pub enum FileEntry {
Import(Vec<import::Import>),
Comment(String),
/// The bool indicates whether the rule is exported - whether tokens uniquely defined inside it
/// should be exported
Rule(Rule, bool),
Export(Vec<Vec<String>>)
}
@@ -103,10 +107,10 @@ pub fn line_parser() -> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> {
choice((
// In case the usercode wants to parse doc
enum_parser!(Lexeme >> FileEntry; Comment),
just(Lexeme::name("import"))
just(Lexeme::Import)
.ignore_then(import_parser().map(FileEntry::Import))
.then_ignore(enum_parser!(Lexeme::Comment)),
just(Lexeme::name("export")).map_err_with_span(|e, s| {
.then_ignore(enum_parser!(Lexeme::Comment).or_not()),
just(Lexeme::Export).map_err_with_span(|e, s| {
println!("{:?} could not yield an export", s); e
}).ignore_then(
just(Lexeme::NS).ignore_then(
@@ -114,13 +118,14 @@ pub fn line_parser() -> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> {
.separated_by(just(Lexeme::name(",")))
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
).map(FileEntry::Export)
).or(rule_parser().map(|(source, prio, target)| {
FileEntry::Rule(Rule {
source: to_mrc_slice(source),
prio,
target: to_mrc_slice(target)
}, true)
})),
.or(rule_parser().map(|(source, prio, target)| {
FileEntry::Rule(Rule {
source: to_mrc_slice(source),
prio,
target: to_mrc_slice(target)
}, true)
}))
),
// This could match almost anything so it has to go last
rule_parser().map(|(source, prio, target)| FileEntry::Rule(Rule{
source: to_mrc_slice(source),
@@ -153,3 +158,24 @@ where I: Iterator<Item = &'b FileEntry> + 'a {
_ => None
}).flatten()
}
pub fn split_lines(data: &str) -> impl Iterator<Item = &str> {
let mut source = data.char_indices();
let mut last_slice = 0;
iter::from_fn(move || {
let mut paren_count = 0;
while let Some((i, c)) = source.next() {
match c {
'(' | '{' | '[' => paren_count += 1,
')' | '}' | ']' => paren_count -= 1,
'\n' if paren_count == 0 => {
let begin = last_slice;
last_slice = i;
return Some(&data[begin..i]);
},
_ => (),
}
}
None
})
}

View File

@@ -1,5 +0,0 @@
#[derive(Debug, Clone)]
pub enum Loaded {
Module(String),
Namespace(Vec<String>),
}

View File

@@ -0,0 +1,7 @@
use crate::parse::FileEntry;
use super::{Loader, Loaded};
pub fn ext_loader(data: Vec<FileEntry>) -> impl Loader {
move |_: &[&str]| Ok(Loaded::External(data.clone()))
}

View File

@@ -1,27 +1,10 @@
use std::io;
use std::rc::Rc;
use std::fs::read_to_string;
use std::path::PathBuf;
use mappable_rc::Mrc;
use super::{Loaded, Loader, LoadingError};
use super::loaded::Loaded;
#[derive(Clone, Debug)]
pub enum LoadingError {
IOErr(Rc<io::Error>),
UnknownNode(String),
Missing(String)
}
impl From<io::Error> for LoadingError {
fn from(inner: io::Error) -> Self {
LoadingError::IOErr(Rc::new(inner))
}
}
pub fn file_loader(proj: PathBuf) -> impl FnMut(Mrc<[String]>) -> Result<Loaded, LoadingError> + 'static {
move |path| {
pub fn file_loader(proj: PathBuf) -> impl Loader + 'static {
move |path: &[&str]| {
let dirpath = proj.join(path.join("/"));
if dirpath.is_dir() || dirpath.is_symlink() {
return Ok(Loaded::Namespace(

View File

@@ -0,0 +1,23 @@
use itertools::Itertools;
use ordered_float::NotNan;
use crate::parse::FileEntry;
use crate::representations::Primitive;
use crate::utils::{one_mrc_slice, mrc_empty_slice};
use crate::foreign::ExternFn;
use crate::ast::{Rule, Expr, Clause};
use super::{Loader, ext_loader};
pub fn fnlib_loader(src: Vec<(&'static str, Box<dyn ExternFn>)>) -> impl Loader {
let entries = src.into_iter().map(|(name, xfn)| FileEntry::Rule(Rule {
source: one_mrc_slice(Expr(Clause::Name{
local: Some(name.to_string()),
qualified: one_mrc_slice(name.to_string())
}, mrc_empty_slice())),
prio: NotNan::try_from(0.0f64).unwrap(),
target: one_mrc_slice(Expr(Clause::P(Primitive::ExternFn(xfn)), mrc_empty_slice()))
}, true))
.collect_vec();
ext_loader(entries)
}

View File

@@ -0,0 +1,16 @@
use std::collections::HashMap;
use super::{Loader, LoadingError, Loaded};
pub fn map_loader<'a, T: Loader + 'a>(mut map: HashMap<&'a str, T>) -> impl Loader + 'a {
move |path: &[&str]| {
let (key, subpath) = if let Some(sf) = path.split_first() {sf}
else {return Ok(Loaded::Module(map.keys().cloned().collect()))};
let sub = if let Some(sub) = map.get_mut(key.to_string().as_str()) {sub}
else {return Err(
if subpath.len() == 0 {LoadingError::UnknownNode(path.join("::"))}
else {LoadingError::Missing(path.join("::"))}
)};
sub.load(subpath)
}
}

View File

@@ -0,0 +1,61 @@
mod file_loader;
mod ext_loader;
mod string_loader;
mod map_loader;
mod fnlib_loader;
mod overlay_loader;
mod prefix_loader;
pub use file_loader::file_loader;
pub use ext_loader::ext_loader;
pub use fnlib_loader::fnlib_loader;
pub use string_loader::string_loader;
pub use map_loader::map_loader;
pub use overlay_loader::overlay_loader;
pub use prefix_loader::prefix_loader;
use std::{rc::Rc, io};
use crate::parse::FileEntry;
#[derive(Clone, Debug)]
pub enum LoadingError {
/// An IO operation has failed (i.e. no read permission)
IOErr(Rc<io::Error>),
/// The leaf does not exist
UnknownNode(String),
/// The leaf and at least the immediately containing namespace don't exist
Missing(String)
}
impl From<io::Error> for LoadingError {
fn from(inner: io::Error) -> Self {
LoadingError::IOErr(Rc::new(inner))
}
}
#[derive(Debug, Clone)]
pub enum Loaded {
Module(String),
Namespace(Vec<String>),
External(Vec<FileEntry>)
}
pub trait Loader {
fn load<'s, 'a>(&'s mut self, path: &'a [&'a str]) -> Result<Loaded, LoadingError>;
fn boxed<'a>(self) -> Box<dyn 'a + Loader> where Self: 'a + Sized {
Box::new(self)
}
}
impl<T> Loader for T where T: for<'a> FnMut(&'a [&'a str]) -> Result<Loaded, LoadingError> {
fn load(&mut self, path: &[&str]) -> Result<Loaded, LoadingError> {
(self)(path)
}
}
impl Loader for Box<dyn Loader> {
fn load<'s, 'a>(&'s mut self, path: &'a [&'a str]) -> Result<Loaded, LoadingError> {
self.as_mut().load(path)
}
}

View File

@@ -0,0 +1,19 @@
use super::{Loader, LoadingError};
pub fn overlay_loader(mut base: impl Loader, mut overlay: impl Loader) -> impl Loader {
move |path: &[&str]| match overlay.load(path) {
ok@Ok(_) => ok,
e@Err(LoadingError::IOErr(_)) => e,
Err(_) => base.load(path)
}
}
#[macro_export]
macro_rules! overlay_loader {
($left:expr, $right:expr) => {
overlay_loader($left, $right)
};
($left:expr, $mid:expr, $($rest:expr),+) => {
overlay_loader($left, overlay_loader!($mid, $($rest),+))
};
}

View File

@@ -0,0 +1,10 @@
use super::Loader;
pub fn prefix_loader<'a>(
prefix: &'a [&'a str], mut loader: impl Loader + 'a
) -> impl Loader + 'a {
move |path: &[&str]| {
let full_path = prefix.iter().chain(path.iter()).map(|s| s.to_string()).clone();
loader.load(path)
}
}

View File

@@ -0,0 +1,5 @@
use super::{Loader, Loaded};
pub fn string_loader<'a>(data: &'a str) -> impl Loader + 'a {
move |_: &[&str]| Ok(Loaded::Module(data.to_string()))
}

View File

@@ -1,10 +1,14 @@
mod rule_collector;
pub use rule_collector::rule_collector;
mod loading;
mod prefix;
mod name_resolver;
mod loaded;
pub use loaded::Loaded;
mod module_error;
pub mod file_loader;
pub use file_loader::file_loader;
pub use module_error::ModuleError;
pub use rule_collector::rule_collector;
pub use loading::{
Loader, Loaded, LoadingError,
ext_loader, file_loader, string_loader, map_loader, fnlib_loader,
overlay_loader, prefix_loader
};
use crate::ast::Rule;

View File

@@ -3,49 +3,47 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::rc::Rc;
use itertools::Itertools;
use mappable_rc::Mrc;
use crate::ast::Rule;
use crate::parse::{self, FileEntry};
use crate::utils::{Cache, mrc_derive, to_mrc_slice};
use crate::utils::{Cache, mrc_derive, to_mrc_slice, one_mrc_slice};
use super::name_resolver::NameResolver;
use super::module_error::ModuleError;
use super::prefix::prefix_expr;
use super::loaded::Loaded;
use super::loading::{Loaded, Loader, LoadingError};
use crate::parse::Import;
type ParseResult<T, ELoad> = Result<T, ModuleError<ELoad>>;
type ParseResult<T> = Result<T, ModuleError<LoadingError>>;
#[derive(Debug, Clone)]
pub struct Module {
pub rules: Vec<Rule>,
pub exports: Vec<String>,
pub references: Vec<Mrc<[String]>>
pub references: HashSet<Mrc<[String]>>
}
pub type RuleCollectionResult<ELoad> = Result<Vec<super::Rule>, ModuleError<ELoad>>;
pub type RuleCollectionResult = Result<Vec<super::Rule>, ModuleError<LoadingError>>;
pub fn rule_collector<F: 'static, ELoad>(
load_mod: F,
prelude: Vec<String>
) -> Cache<'static, Mrc<[String]>, RuleCollectionResult<ELoad>>
where
F: FnMut(Mrc<[String]>) -> Result<Loaded, ELoad>,
ELoad: Clone + Debug
pub fn rule_collector<F: 'static>(
load_mod: F
) -> Cache<'static, Mrc<[String]>, RuleCollectionResult>
where F: Loader
{
let load_mod_rc = RefCell::new(load_mod);
// Map paths to a namespace with name list (folder) or module with source text (file)
let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _|
-> ParseResult<Loaded, ELoad> {
(load_mod_rc.borrow_mut())(path).map_err(ModuleError::Load)
let loaded = Rc::new(Cache::new(move |path: Mrc<[String]>, _| -> ParseResult<Loaded> {
load_mod_rc.borrow_mut().load(&path.iter().map(|s| s.as_str()).collect_vec()).map_err(ModuleError::Load)
}));
// Map names to the longest prefix that points to a valid module
// At least one segment must be in the prefix, and the prefix must not be the whole name
let modname = Rc::new(Cache::new({
let loaded = Rc::clone(&loaded);
move |symbol: Mrc<[String]>, _| -> Result<Mrc<[String]>, Vec<ModuleError<ELoad>>> {
let mut errv: Vec<ModuleError<ELoad>> = Vec::new();
let reg_err = |e, errv: &mut Vec<ModuleError<ELoad>>| {
let loaded = loaded.clone();
move |symbol: Mrc<[String]>, _| -> Result<Mrc<[String]>, Vec<ModuleError<LoadingError>>> {
let mut errv: Vec<ModuleError<LoadingError>> = Vec::new();
let reg_err = |e, errv: &mut Vec<ModuleError<LoadingError>>| {
errv.push(e);
if symbol.len() == errv.len() { Err(errv.clone()) }
else { Ok(()) }
@@ -54,8 +52,8 @@ where
let path = mrc_derive(&symbol, |s| &s[..s.len() - errv.len() - 1]);
match loaded.try_find(&path) {
Ok(imports) => match imports.as_ref() {
Loaded::Module(_) => break Ok(path),
_ => reg_err(ModuleError::None, &mut errv)?
Loaded::Module(_) | Loaded::External(_) => break Ok(path),
Loaded::Namespace(_) => reg_err(ModuleError::None, &mut errv)?
},
Err(err) => reg_err(err, &mut errv)?
}
@@ -63,21 +61,33 @@ where
}
}));
// Preliminarily parse a file, substitution rules and imports are valid
let prelude_path = one_mrc_slice("prelude".to_string());
let preparsed = Rc::new(Cache::new({
let loaded = Rc::clone(&loaded);
let prelude2 = prelude.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
let loaded = loaded.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>> {
let loaded = loaded.try_find(&path)?;
if let Loaded::Module(source) = loaded.as_ref() {
Ok(parse::parse(&prelude2, source.as_str())?)
} else {Err(ModuleError::None)}
match loaded.as_ref() {
Loaded::Module(source) => {
let mut entv = parse::parse(&[] as &[&str], source.as_str())?;
if !entv.iter().any(|ent| if let FileEntry::Import(imps) = ent {
imps.iter().any(|imp| imp.path.starts_with(&prelude_path))
} else {false}) && path != prelude_path {
entv.push(FileEntry::Import(vec![Import{
name: None, path: Mrc::clone(&prelude_path)
}]))
}
Ok(entv)
}
Loaded::External(ast) => Ok(ast.clone()),
Loaded::Namespace(_) => Err(ModuleError::None),
}
}
}));
// Collect all toplevel names exported from a given file
let exports = Rc::new(Cache::new({
let loaded = Rc::clone(&loaded);
let preparsed = Rc::clone(&preparsed);
move |path: Mrc<[String]>, _| -> ParseResult<Vec<String>, ELoad> {
let loaded = loaded.clone();
let preparsed = preparsed.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<String>> {
let loaded = loaded.try_find(&path)?;
if let Loaded::Namespace(names) = loaded.as_ref() {
return Ok(names.clone());
@@ -91,9 +101,9 @@ where
}));
// Collect all toplevel names imported by a given file
let imports = Rc::new(Cache::new({
let preparsed = Rc::clone(&preparsed);
let exports = Rc::clone(&exports);
move |path: Mrc<[String]>, _| -> ParseResult<HashMap<String, Mrc<[String]>>, ELoad> {
let preparsed = preparsed.clone();
let exports = exports.clone();
move |path: Mrc<[String]>, _| -> ParseResult<HashMap<String, Mrc<[String]>>> {
let entv = preparsed.try_find(&path)?;
let import_entries = parse::imports(entv.iter());
let mut imported_symbols: HashMap<String, Mrc<[String]>> = HashMap::new();
@@ -102,54 +112,55 @@ where
if let Some(ref name) = imp.name {
if export.contains(name) {
imported_symbols.insert(name.clone(), Mrc::clone(&imp.path));
}
} else {panic!("{:?} doesn't export {}", imp.path, name)}
} else {
for exp in export.as_ref() {
imported_symbols.insert(exp.clone(), Mrc::clone(&imp.path));
}
}
}
println!("Imports for {:?} are {:?}", path.as_ref(), imported_symbols);
Ok(imported_symbols)
}
}));
// Final parse, operators are correctly separated
let parsed = Rc::new(Cache::new({
let preparsed = Rc::clone(&preparsed);
let imports = Rc::clone(&imports);
let loaded = Rc::clone(&loaded);
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>, ELoad> {
let preparsed = preparsed.clone();
let imports = imports.clone();
let loaded = loaded.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<FileEntry>> {
let imported_ops: Vec<String> =
imports.try_find(&path)?
.keys()
.chain(prelude.iter())
.filter(|s| parse::is_op(s))
.cloned()
.collect();
// let parser = file_parser(&prelude, &imported_ops);
let pre = preparsed.try_find(&path)?;
if let Loaded::Module(source) = loaded.try_find(&path)?.as_ref() {
Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?)
} else { Err(ModuleError::None) }
match loaded.try_find(&path)?.as_ref() {
Loaded::Module(source) => Ok(parse::reparse(&imported_ops, source.as_str(), &pre)?),
Loaded::External(ast) => Ok(ast.clone()),
Loaded::Namespace(_) => Err(ModuleError::None)
}
}
}));
let name_resolver_rc = RefCell::new(NameResolver::new({
let modname = Rc::clone(&modname);
let modname = modname.clone();
move |path| {
Some(modname.try_find(&path).ok()?.as_ref().clone())
}
}, {
let imports = Rc::clone(&imports);
let imports = imports.clone();
move |path| {
imports.try_find(&path).map(|f| f.as_ref().clone())
}
}));
// Turn parsed files into a bag of rules and a list of toplevel export names
let resolved = Rc::new(Cache::new({
let parsed = Rc::clone(&parsed);
let exports = Rc::clone(&exports);
let imports = Rc::clone(&imports);
let modname = Rc::clone(&modname);
move |path: Mrc<[String]>, _| -> ParseResult<Module, ELoad> {
let parsed = parsed.clone();
let exports = exports.clone();
let imports = imports.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Module> {
let mut name_resolver = name_resolver_rc.borrow_mut();
let module = Module {
rules: parsed.try_find(&path)?
@@ -168,30 +179,26 @@ where
})
} else { None }
})
.map(|rule| Ok(super::Rule {
source: to_mrc_slice(rule.source.iter()
.map(|Rule{ source, target, prio }| Ok(super::Rule {
source: to_mrc_slice(source.iter()
.map(|ex| name_resolver.process_expression(ex))
.collect::<Result<Vec<_>, _>>()?),
target: to_mrc_slice(rule.target.iter()
target: to_mrc_slice(target.iter()
.map(|ex| name_resolver.process_expression(ex))
.collect::<Result<Vec<_>, _>>()?),
..rule
prio
}))
.collect::<ParseResult<Vec<super::Rule>, ELoad>>()?,
.collect::<ParseResult<Vec<super::Rule>>>()?,
exports: exports.try_find(&path)?.as_ref().clone(),
references: imports.try_find(&path)?
.values()
.filter_map(|imps| {
modname.try_find(imps).ok().map(|r| r.as_ref().clone())
})
.collect()
.values().cloned().collect()
};
Ok(module)
}
}));
Cache::new({
let resolved = Rc::clone(&resolved);
move |path: Mrc<[String]>, _| -> ParseResult<Vec<super::Rule>, ELoad> {
let resolved = resolved.clone();
move |path: Mrc<[String]>, _| -> ParseResult<Vec<super::Rule>> {
// Breadth-first search
let mut processed: HashSet<Mrc<[String]>> = HashSet::new();
let mut rules: Vec<super::Rule> = Vec::new();
@@ -202,12 +209,12 @@ where
processed.insert(el.clone());
pending.extend(
resolved.references.iter()
.filter(|&v| !processed.contains(v))
.cloned()
.filter(|&v| !processed.contains(v))
.cloned()
);
rules.extend(
resolved.rules.iter().cloned()
)
);
};
Ok(rules)
}

View File

@@ -46,7 +46,7 @@ pub enum Clause {
},
/// 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`
/// An explicit expression associated with the leftmost, outermost [Clause::Auto], eg. `read @Uint`
Explicit(Mrc<Expr>),
/// A function expression, eg. `\x. x + 1`
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),

View File

@@ -4,6 +4,7 @@ use std::rc::Rc;
use crate::utils::Side;
use crate::foreign::{ExternError, Atom};
use super::Literal;
use super::path_set::PathSet;
use super::primitive::Primitive;
@@ -41,6 +42,22 @@ impl Debug for Clause {
}
}
impl TryFrom<Clause> for Literal {
type Error = Clause;
fn try_from(value: Clause) -> Result<Self, Self::Error> {
if let Clause::P(Primitive::Literal(l)) = value {Ok(l)}
else {Err(value)}
}
}
impl<'a> TryFrom<&'a Clause> for &'a Literal {
type Error = ();
fn try_from(value: &'a Clause) -> Result<Self, Self::Error> {
if let Clause::P(Primitive::Literal(l)) = value {Ok(l)}
else {Err(())}
}
}
/// Problems in the process of execution
#[derive(Clone)]
pub enum RuntimeError {
@@ -114,6 +131,10 @@ fn substitute(PathSet { steps, next }: &PathSet, value: &Clause, body: &Clause)
fn apply(f: &Clause, x: Rc<Clause>, id: usize) -> Result<Clause, InternalError> {
match f {
Clause::P(Primitive::Atom(Atom(a))) => Ok(Clause::Apply { // Don't execute a pre-application
f: Rc::new(a.run_once()?), // take a step in expanding the atom instead
x, id
}),
Clause::P(Primitive::ExternFn(f)) => f.apply(x.as_ref().clone())
.map_err(|e| InternalError::Runtime(RuntimeError::Extern(e))),
fex@Clause::Apply{..} => Ok(Clause::Apply{ // Don't execute the pre-function expression

View File

@@ -5,7 +5,7 @@ use std::fmt::Debug;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Literal {
Num(NotNan<f64>),
Int(u64),
Uint(u64),
Char(char),
Str(String),
}
@@ -14,7 +14,7 @@ impl Debug for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Num(arg0) => write!(f, "{:?}", arg0),
Self::Int(arg0) => write!(f, "{:?}", arg0),
Self::Uint(arg0) => write!(f, "{:?}", arg0),
Self::Char(arg0) => write!(f, "{:?}", arg0),
Self::Str(arg0) => write!(f, "{:?}", arg0),
}

View File

@@ -7,6 +7,7 @@ pub(crate) mod interpreted;
mod postmacro;
mod primitive;
mod path_set;
pub use path_set::PathSet;
pub use primitive::Primitive;
pub mod postmacro_to_interpreted;
pub use literal::Literal;

View File

@@ -100,7 +100,7 @@ fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn It
write_slice_rec(state, body)
), out_typ.to_owned())),
Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda(
if let Some(state_key) = name.strip_prefix('$') {
if let Some(state_key) = name.strip_prefix("$_") {
if let Entry::Name(name) = &state[state_key] {
name.as_ref().to_owned()
} else {panic!("Lambda template name may only be derived from Lambda name")}

27
src/utils/interner.rs Normal file
View File

@@ -0,0 +1,27 @@
// use std::{collections::HashSet, hash::Hash};
// use hashbrown::HashMap;
// #[derive(Copy, Clone)]
// pub struct Interned<'a, T> {
// interner: &'a Interner<T>,
// data: &'a T,
// }
// impl<'a, T: Eq> Eq for Interned<'a, T> {}
// impl<'a, T: PartialEq> PartialEq for Interned<'a, T> {
// fn eq(&self, other: &Self) -> bool {
// if (self.interner as *const _) == (other.interner as *const _) {
// (self.data as *const _) == (other.data as *const _)
// } else {self.data == other.data}
// }
// }
// pub struct Interner<T> {
// data: HashSet<T>,
// hash_cache: HashMap<>
// }
// impl Interner<T> {
// }

View File

@@ -1,6 +1,7 @@
mod cache;
pub mod translate;
mod replace_first;
mod interner;
// mod visitor;
pub use replace_first::replace_first;
pub use cache::Cache;