Working example
This commit is contained in:
4
src/external/assertion_error.rs
vendored
4
src/external/assertion_error.rs
vendored
@@ -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
21
src/external/bool/boolean.rs
vendored
Normal 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
52
src/external/bool/equals.rs
vendored
Normal 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
43
src/external/bool/ifthenelse.rs
vendored
Normal 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
13
src/external/bool/mod.rs
vendored
Normal 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
13
src/external/conv/mod.rs
vendored
Normal 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
46
src/external/conv/parse_float.rs
vendored
Normal 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
46
src/external/conv/parse_uint.rs
vendored
Normal 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
35
src/external/conv/to_string.rs
vendored
Normal 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
11
src/external/cpsio/mod.rs
vendored
Normal 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
32
src/external/cpsio/print.rs
vendored
Normal 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
35
src/external/cpsio/readline.rs
vendored
Normal 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
10
src/external/mod.rs
vendored
@@ -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
15
src/external/num/mod.rs
vendored
Normal 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))
|
||||
])
|
||||
}
|
||||
@@ -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
41
src/external/num/operators/add.rs
vendored
Normal 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
41
src/external/num/operators/divide.rs
vendored
Normal 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
5
src/external/num/operators/mod.rs
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod add;
|
||||
pub mod divide;
|
||||
pub mod multiply;
|
||||
pub mod remainder;
|
||||
pub mod subtract;
|
||||
@@ -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
41
src/external/num/operators/remainder.rs
vendored
Normal 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
41
src/external/num/operators/subtract.rs
vendored
Normal 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
27
src/external/runtime_error.rs
vendored
Normal 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
19
src/external/std.rs
vendored
Normal 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
47
src/external/str/char_at.rs
vendored
Normal 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
13
src/external/str/cls2str.rs
vendored
Normal 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
41
src/external/str/concatenate.rs
vendored
Normal 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
11
src/external/str/mod.rs
vendored
Normal 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))
|
||||
])
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
19
src/foreign_macros/atomic_defaults.rs
Normal file
19
src/foreign_macros/atomic_defaults.rs
Normal 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)
|
||||
}
|
||||
};
|
||||
}
|
||||
105
src/foreign_macros/atomic_impl.rs
Normal file
105
src/foreign_macros/atomic_impl.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
48
src/foreign_macros/atomic_inert.rs
Normal file
48
src/foreign_macros/atomic_inert.rs
Normal 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())
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
28
src/foreign_macros/atomic_redirect.rs
Normal file
28
src/foreign_macros/atomic_redirect.rs
Normal 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() }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
43
src/foreign_macros/externfn_impl.rs
Normal file
43
src/foreign_macros/externfn_impl.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
5
src/foreign_macros/mod.rs
Normal file
5
src/foreign_macros/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod atomic_defaults;
|
||||
mod atomic_impl;
|
||||
mod atomic_inert;
|
||||
mod atomic_redirect;
|
||||
mod externfn_impl;
|
||||
99
src/main.rs
99
src/main.rs
@@ -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)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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((
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Loaded {
|
||||
Module(String),
|
||||
Namespace(Vec<String>),
|
||||
}
|
||||
7
src/project/loading/ext_loader.rs
Normal file
7
src/project/loading/ext_loader.rs
Normal 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()))
|
||||
}
|
||||
@@ -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(
|
||||
23
src/project/loading/fnlib_loader.rs
Normal file
23
src/project/loading/fnlib_loader.rs
Normal 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)
|
||||
}
|
||||
16
src/project/loading/map_loader.rs
Normal file
16
src/project/loading/map_loader.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
61
src/project/loading/mod.rs
Normal file
61
src/project/loading/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
19
src/project/loading/overlay_loader.rs
Normal file
19
src/project/loading/overlay_loader.rs
Normal 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),+))
|
||||
};
|
||||
}
|
||||
10
src/project/loading/prefix_loader.rs
Normal file
10
src/project/loading/prefix_loader.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
5
src/project/loading/string_loader.rs
Normal file
5
src/project/loading/string_loader.rs
Normal 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()))
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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]>),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
27
src/utils/interner.rs
Normal 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> {
|
||||
|
||||
// }
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user