Public API and docs

This commit is contained in:
2023-05-26 15:23:15 +01:00
parent 3c1a6e2be2
commit fdf18e6ff8
99 changed files with 503 additions and 406 deletions

View File

@@ -0,0 +1,34 @@
use std::fmt::Display;
use std::rc::Rc;
use crate::foreign::ExternError;
use crate::representations::interpreted::ExprInst;
/// Some expectation (usually about the argument types of a function) did not
/// hold.
#[derive(Clone)]
pub struct AssertionError {
pub value: ExprInst,
pub assertion: &'static str,
}
impl AssertionError {
pub fn fail<T>(
value: ExprInst,
assertion: &'static str,
) -> Result<T, Rc<dyn ExternError>> {
return Err(Self { value, assertion }.into_extern());
}
pub fn ext(value: ExprInst, assertion: &'static str) -> Rc<dyn ExternError> {
return Self { value, assertion }.into_extern();
}
}
impl Display for AssertionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error: {:?} is not {}", self.value, self.assertion)
}
}
impl ExternError for AssertionError {}

29
src/stl/bool/boolean.rs Normal file
View File

@@ -0,0 +1,29 @@
use crate::atomic_inert;
use crate::foreign::Atom;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::Primitive;
/// Booleans exposed to Orchid
#[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 TryFrom<ExprInst> for Boolean {
type Error = ();
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
let expr = value.expr();
if let Clause::P(Primitive::Atom(Atom(a))) = &expr.clause {
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
return Ok(*b);
}
}
Err(())
}
}

54
src/stl/bool/equals.rs Normal file
View File

@@ -0,0 +1,54 @@
use std::fmt::Debug;
use super::super::assertion_error::AssertionError;
use super::super::litconv::with_lit;
use super::boolean::Boolean;
use crate::representations::interpreted::ExprInst;
use crate::representations::Literal;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Compares the inner values if
///
/// - both values are char,
/// - both are string,
/// - both are either uint or num
///
/// Next state: [Equals1]
#[derive(Clone)]
pub struct Equals2;
externfn_impl!(Equals2, |_: &Self, x: ExprInst| Ok(Equals1 { x }));
/// Prev state: [Equals2]; Next state: [Equals0]
#[derive(Debug, Clone)]
pub struct Equals1 {
x: ExprInst,
}
atomic_redirect!(Equals1, x);
atomic_impl!(Equals1);
externfn_impl!(Equals1, |this: &Self, x: ExprInst| {
with_lit(&this.x, |l| Ok(Equals0 { a: l.clone(), x }))
});
/// Prev state: [Equals1]
#[derive(Debug, Clone)]
pub struct Equals0 {
a: Literal,
x: ExprInst,
}
atomic_redirect!(Equals0, x);
atomic_impl!(Equals0, |Self { a, x }: &Self, _| {
let eqls = with_lit(x, |l| {
Ok(match (a, l) {
(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,
(Literal::Num(n1), Literal::Uint(u1)) => *n1 == (*u1 as f64),
(Literal::Uint(u1), Literal::Num(n1)) => *n1 == (*u1 as f64),
(..) => AssertionError::fail(x.clone(), "the expected type")?,
})
})?;
Ok(Boolean::from(eqls).to_atom_cls())
});

View File

@@ -0,0 +1,46 @@
use std::fmt::Debug;
use std::rc::Rc;
use super::super::assertion_error::AssertionError;
use super::Boolean;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::PathSet;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Takes a boolean and two branches, runs the first if the bool is true, the
/// second if it's false.
///
/// Next state: [IfThenElse0]
#[derive(Clone)]
pub struct IfThenElse1;
externfn_impl!(IfThenElse1, |_: &Self, x: ExprInst| Ok(IfThenElse0 { x }));
/// Prev state: [IfThenElse1]
#[derive(Debug, Clone)]
pub struct IfThenElse0 {
x: ExprInst,
}
atomic_redirect!(IfThenElse0, x);
atomic_impl!(IfThenElse0, |this: &Self, _| {
let Boolean(b) = this
.x
.clone()
.try_into()
.map_err(|_| AssertionError::ext(this.x.clone(), "a boolean"))?;
Ok(if b {
Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
body: Clause::Lambda { args: None, body: Clause::LambdaArg.wrap() }
.wrap(),
}
} else {
Clause::Lambda {
args: None,
body: Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
body: Clause::LambdaArg.wrap(),
}
.wrap(),
}
})
});

16
src/stl/bool/mod.rs Normal file
View File

@@ -0,0 +1,16 @@
mod boolean;
mod equals;
mod ifthenelse;
pub use boolean::Boolean;
use crate::interner::Interner;
use crate::pipeline::ConstTree;
pub fn bool(i: &Interner) -> ConstTree {
ConstTree::tree([
(i.i("ifthenelse"), ConstTree::xfn(ifthenelse::IfThenElse1)),
(i.i("equals"), ConstTree::xfn(equals::Equals2)),
(i.i("true"), ConstTree::atom(Boolean(true))),
(i.i("false"), ConstTree::atom(Boolean(false))),
])
}

14
src/stl/conv/mod.rs Normal file
View File

@@ -0,0 +1,14 @@
use crate::interner::Interner;
use crate::pipeline::ConstTree;
mod parse_float;
mod parse_uint;
mod to_string;
pub fn conv(i: &Interner) -> ConstTree {
ConstTree::tree([
(i.i("parse_float"), ConstTree::xfn(parse_float::ParseFloat1)),
(i.i("parse_uint"), ConstTree::xfn(parse_uint::ParseUint1)),
(i.i("to_string"), ConstTree::xfn(to_string::ToString1)),
])
}

View File

@@ -0,0 +1,43 @@
use std::fmt::Debug;
use chumsky::Parser;
use super::super::assertion_error::AssertionError;
use super::super::litconv::with_lit;
use crate::parse::float_parser;
use crate::representations::interpreted::ExprInst;
use crate::representations::Literal;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// parse a number. Accepts the same syntax Orchid does
///
/// Next state: [ParseFloat0]
#[derive(Clone)]
pub struct ParseFloat1;
externfn_impl!(ParseFloat1, |_: &Self, x: ExprInst| Ok(ParseFloat0 { x }));
/// Prev state: [ParseFloat1]
#[derive(Debug, Clone)]
pub struct ParseFloat0 {
x: ExprInst,
}
atomic_redirect!(ParseFloat0, x);
atomic_impl!(ParseFloat0, |Self { x }: &Self, _| {
let number = with_lit(x, |l| {
Ok(match l {
Literal::Str(s) => {
let parser = float_parser();
parser.parse(s.as_str()).map_err(|_| {
AssertionError::ext(x.clone(), "cannot be parsed into a float")
})?
},
Literal::Num(n) => *n,
Literal::Uint(i) => (*i as u32).into(),
Literal::Char(char) => char
.to_digit(10)
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
.into(),
})
})?;
Ok(number.into())
});

View File

@@ -0,0 +1,47 @@
use std::fmt::Debug;
use chumsky::Parser;
use super::super::assertion_error::AssertionError;
use super::super::litconv::with_lit;
use crate::parse::int_parser;
use crate::representations::interpreted::ExprInst;
use crate::representations::Literal;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
/// input is a number, floors it.
///
/// Next state: [ParseUint0]
#[derive(Clone)]
pub struct ParseUint1;
externfn_impl!(ParseUint1, |_: &Self, x: ExprInst| Ok(ParseUint0 { x }));
/// Prev state: [ParseUint1]
#[derive(Debug, Clone)]
pub struct ParseUint0 {
x: ExprInst,
}
atomic_redirect!(ParseUint0, x);
atomic_impl!(ParseUint0, |Self { x }: &Self, _| {
let uint = with_lit(x, |l| {
Ok(match l {
Literal::Str(s) => {
let parser = int_parser();
parser.parse(s.as_str()).map_err(|_| {
AssertionError::ext(
x.clone(),
"cannot be parsed into an unsigned int",
)
})?
},
Literal::Num(n) => n.floor() as u64,
Literal::Uint(i) => *i,
Literal::Char(char) => char
.to_digit(10)
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
.into(),
})
})?;
Ok(uint.into())
});

32
src/stl/conv/to_string.rs Normal file
View File

@@ -0,0 +1,32 @@
use std::fmt::Debug;
use super::super::litconv::with_lit;
use crate::representations::interpreted::ExprInst;
use crate::representations::Literal;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Convert a literal to a string using Rust's conversions for floats, chars and
/// uints respectively
///
/// Next state: [ToString0]
#[derive(Clone)]
pub struct ToString1;
externfn_impl!(ToString1, |_: &Self, x: ExprInst| Ok(ToString0 { x }));
/// Prev state: [ToString1]
#[derive(Debug, Clone)]
pub struct ToString0 {
x: ExprInst,
}
atomic_redirect!(ToString0, x);
atomic_impl!(ToString0, |Self { x }: &Self, _| {
let string = with_lit(x, |l| {
Ok(match l {
Literal::Char(c) => c.to_string(),
Literal::Uint(i) => i.to_string(),
Literal::Num(n) => n.to_string(),
Literal::Str(s) => s.clone(),
})
})?;
Ok(string.into())
});

32
src/stl/cpsio/debug.rs Normal file
View File

@@ -0,0 +1,32 @@
use std::fmt::Debug;
use crate::foreign::{Atomic, AtomicReturn};
use crate::interner::InternedDisplay;
use crate::interpreter::Context;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_defaults, externfn_impl};
/// Print and return whatever expression is in the argument without normalizing
/// it.
///
/// Next state: [Debug1]
#[derive(Clone)]
pub struct Debug2;
externfn_impl!(Debug2, |_: &Self, x: ExprInst| Ok(Debug1 { x }));
/// Prev state: [Debug2]
#[derive(Debug, Clone)]
pub struct Debug1 {
x: ExprInst,
}
impl Atomic for Debug1 {
atomic_defaults!();
fn run(&self, ctx: Context) -> crate::foreign::AtomicResult {
println!("{}", self.x.bundle(ctx.interner));
Ok(AtomicReturn {
clause: self.x.expr().clause.clone(),
gas: ctx.gas.map(|g| g - 1),
inert: false,
})
}
}

41
src/stl/cpsio/io.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::io::{self, Write};
use super::super::runtime_error::RuntimeError;
use crate::atomic_inert;
use crate::interpreter::{HandlerParm, HandlerRes};
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::{Literal, Primitive};
use crate::utils::unwrap_or;
/// An IO command to be handled by the host application.
#[derive(Clone, Debug)]
pub enum IO {
Print(String, ExprInst),
Readline(ExprInst),
}
atomic_inert!(IO);
/// Default xommand handler for IO actions
pub fn handle(effect: HandlerParm) -> HandlerRes {
// Downcast command
let io: &IO = unwrap_or!(effect.as_any().downcast_ref(); Err(effect)?);
// Interpret and execute
Ok(match io {
IO::Print(str, cont) => {
print!("{}", str);
io::stdout()
.flush()
.map_err(|e| RuntimeError::ext(e.to_string(), "writing to stdout"))?;
cont.clone()
},
IO::Readline(cont) => {
let mut buf = String::new();
io::stdin()
.read_line(&mut buf)
.map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
buf.pop();
let x = Clause::P(Primitive::Literal(Literal::Str(buf))).wrap();
Clause::Apply { f: cont.clone(), x }.wrap()
},
})
}

19
src/stl/cpsio/mod.rs Normal file
View File

@@ -0,0 +1,19 @@
use crate::interner::Interner;
use crate::pipeline::ConstTree;
mod debug;
mod io;
mod panic;
mod print;
mod readline;
pub use io::{handle, IO};
pub fn cpsio(i: &Interner) -> ConstTree {
ConstTree::tree([
(i.i("print"), ConstTree::xfn(print::Print2)),
(i.i("readline"), ConstTree::xfn(readline::Readln2)),
(i.i("debug"), ConstTree::xfn(debug::Debug2)),
(i.i("panic"), ConstTree::xfn(panic::Panic1)),
])
}

35
src/stl/cpsio/panic.rs Normal file
View File

@@ -0,0 +1,35 @@
use std::fmt::Display;
use super::super::litconv::with_str;
use crate::foreign::ExternError;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Takes a message, returns an [ExternError] unconditionally.
///
/// Next state: [Panic0]
#[derive(Clone)]
pub struct Panic1;
externfn_impl!(Panic1, |_: &Self, x: ExprInst| Ok(Panic0 { x }));
/// Prev state: [Panic1]
#[derive(Debug, Clone)]
pub struct Panic0 {
x: ExprInst,
}
atomic_redirect!(Panic0, x);
atomic_impl!(Panic0, |Self { x }: &Self, _| {
with_str(x, |s| Err(OrchidPanic(s.clone()).into_extern()))
});
/// An unrecoverable error in Orchid land. Of course, because Orchid is lazy, it
/// only applies to the expressions that use the one that generated it.
pub struct OrchidPanic(String);
impl Display for OrchidPanic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Orchid code panicked: {}", self.0)
}
}
impl ExternError for OrchidPanic {}

40
src/stl/cpsio/print.rs Normal file
View File

@@ -0,0 +1,40 @@
use std::fmt::Debug;
use super::super::litconv::with_str;
use super::io::IO;
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
use crate::interpreter::Context;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_defaults, atomic_impl, atomic_redirect, externfn_impl};
/// Wrap a string and the continuation into an [IO] event to be evaluated by the
/// embedder.
///
/// Next state: [Print1]
#[derive(Clone)]
pub struct Print2;
externfn_impl!(Print2, |_: &Self, x: ExprInst| Ok(Print1 { x }));
/// Prev state: [Print2]; Next state: [Print0]
#[derive(Debug, Clone)]
pub struct Print1 {
x: ExprInst,
}
atomic_redirect!(Print1, x);
atomic_impl!(Print1);
externfn_impl!(Print1, |this: &Self, x: ExprInst| {
with_str(&this.x, |s| Ok(Print0 { s: s.clone(), x }))
});
/// Prev state: [Print1]
#[derive(Debug, Clone)]
pub struct Print0 {
s: String,
x: ExprInst,
}
impl Atomic for Print0 {
atomic_defaults!();
fn run(&self, ctx: Context) -> AtomicResult {
Ok(AtomicReturn::from_data(IO::Print(self.s.clone(), self.x.clone()), ctx))
}
}

27
src/stl/cpsio/readline.rs Normal file
View File

@@ -0,0 +1,27 @@
use std::fmt::Debug;
use super::io::IO;
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
use crate::interpreter::Context;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_defaults, externfn_impl};
/// Create an [IO] event that reads a line form standard input and calls the
/// continuation with it.
///
/// Next state: [Readln1]
#[derive(Clone)]
pub struct Readln2;
externfn_impl!(Readln2, |_: &Self, x: ExprInst| Ok(Readln1 { x }));
/// Prev state: [Readln2]
#[derive(Debug, Clone)]
pub struct Readln1 {
x: ExprInst,
}
impl Atomic for Readln1 {
atomic_defaults!();
fn run(&self, ctx: Context) -> AtomicResult {
Ok(AtomicReturn::from_data(IO::Readline(self.x.clone()), ctx))
}
}

45
src/stl/litconv.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::rc::Rc;
use super::assertion_error::AssertionError;
use crate::foreign::ExternError;
use crate::representations::interpreted::ExprInst;
use crate::representations::Literal;
/// Tries to cast the [ExprInst] as a [Literal], calls the provided function on
/// it if successful. Returns a generic [AssertionError] if not.
pub fn with_lit<T>(
x: &ExprInst,
predicate: impl FnOnce(&Literal) -> Result<T, Rc<dyn ExternError>>,
) -> Result<T, Rc<dyn ExternError>> {
x.with_literal(predicate)
.map_err(|_| AssertionError::ext(x.clone(), "a literal value"))
.and_then(|r| r)
}
/// Like [with_lit] but also unwraps [Literal::Str]
pub fn with_str<T>(
x: &ExprInst,
predicate: impl FnOnce(&String) -> Result<T, Rc<dyn ExternError>>,
) -> Result<T, Rc<dyn ExternError>> {
with_lit(x, |l| {
if let Literal::Str(s) = l {
predicate(s)
} else {
AssertionError::fail(x.clone(), "a string")?
}
})
}
/// Like [with_lit] but also unwraps [Literal::Uint]
pub fn with_uint<T>(
x: &ExprInst,
predicate: impl FnOnce(u64) -> Result<T, Rc<dyn ExternError>>,
) -> Result<T, Rc<dyn ExternError>> {
with_lit(x, |l| {
if let Literal::Uint(u) = l {
predicate(*u)
} else {
AssertionError::fail(x.clone(), "an uint")?
}
})
}

11
src/stl/mk_stl.rs Normal file
View File

@@ -0,0 +1,11 @@
use super::bool::bool;
use super::conv::conv;
use super::cpsio::cpsio;
use super::num::num;
use super::str::str;
use crate::interner::Interner;
use crate::pipeline::ConstTree;
pub fn mk_stl(i: &Interner) -> ConstTree {
cpsio(i) + conv(i) + bool(i) + str(i) + num(i)
}

12
src/stl/mod.rs Normal file
View File

@@ -0,0 +1,12 @@
mod assertion_error;
mod bool;
mod conv;
mod cpsio;
mod litconv;
mod mk_stl;
mod num;
mod runtime_error;
mod str;
pub use cpsio::{handle as handleIO, IO};
pub use mk_stl::mk_stl;

16
src/stl/num/mod.rs Normal file
View File

@@ -0,0 +1,16 @@
mod numeric;
pub mod operators;
pub use numeric::Numeric;
use crate::interner::Interner;
use crate::pipeline::ConstTree;
pub fn num(i: &Interner) -> ConstTree {
ConstTree::tree([
(i.i("add"), ConstTree::xfn(operators::add::Add2)),
(i.i("subtract"), ConstTree::xfn(operators::subtract::Subtract2)),
(i.i("multiply"), ConstTree::xfn(operators::multiply::Multiply2)),
(i.i("divide"), ConstTree::xfn(operators::divide::Divide2)),
(i.i("remainder"), ConstTree::xfn(operators::remainder::Remainder2)),
])
}

133
src/stl/num/numeric.rs Normal file
View File

@@ -0,0 +1,133 @@
use std::ops::{Add, Div, Mul, Rem, Sub};
use std::rc::Rc;
use ordered_float::NotNan;
use super::super::assertion_error::AssertionError;
use super::super::litconv::with_lit;
use crate::foreign::ExternError;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::{Literal, Primitive};
/// A number, either floating point or unsigned int, visible to Orchid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Numeric {
Uint(u64),
Num(NotNan<f64>),
}
impl Numeric {
/// Wrap a f64 in a Numeric
///
/// # Panics
///
/// if the value is NaN or Infinity.try_into()
fn num<T: Into<f64>>(value: T) -> Self {
let f = value.into();
assert!(f.is_finite(), "unrepresentable number");
NotNan::try_from(f).map(Self::Num).expect("not a number")
}
}
impl Add for Numeric {
type Output = Numeric;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a + b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a + b),
(Numeric::Uint(a), Numeric::Num(b))
| (Numeric::Num(b), Numeric::Uint(a)) =>
Numeric::num::<f64>(a as f64 + *b),
}
}
}
impl Sub for Numeric {
type Output = Numeric;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) if b <= a => Numeric::Uint(a - b),
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(a as f64 - b as f64),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a - b),
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 - *b),
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a - b as f64),
}
}
}
impl Mul for Numeric {
type Output = Numeric;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a * b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a * b),
(Numeric::Uint(a), Numeric::Num(b))
| (Numeric::Num(b), Numeric::Uint(a)) =>
Numeric::Num(NotNan::new(a as f64).unwrap() * b),
}
}
}
impl Div for Numeric {
type Output = Numeric;
fn div(self, rhs: Self) -> Self::Output {
let a: f64 = self.into();
let b: f64 = rhs.into();
Numeric::num(a / b)
}
}
impl Rem for Numeric {
type Output = Numeric;
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a % b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a % b),
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 % *b),
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a % b as f64),
}
}
}
impl TryFrom<ExprInst> for Numeric {
type Error = Rc<dyn ExternError>;
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
with_lit(&value.clone(), |l| match l {
Literal::Uint(i) => Ok(Numeric::Uint(*i)),
Literal::Num(n) => Ok(Numeric::Num(*n)),
_ => AssertionError::fail(value, "an integer or number")?,
})
}
}
impl From<Numeric> for Clause {
fn from(value: Numeric) -> Self {
Clause::P(Primitive::Literal(match value {
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(),
}
}
}
impl From<Numeric> for f64 {
fn from(val: Numeric) -> Self {
match val {
Numeric::Num(n) => *n,
Numeric::Uint(i) => i as f64,
}
}
}

View File

@@ -0,0 +1,36 @@
use std::fmt::Debug;
use super::super::Numeric;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Adds two numbers
///
/// Next state: [Add1]
#[derive(Clone)]
pub struct Add2;
externfn_impl!(Add2, |_: &Self, x: ExprInst| Ok(Add1 { x }));
/// Prev state: [Add2]; Next state: [Add0]
#[derive(Debug, Clone)]
pub struct Add1 {
x: ExprInst,
}
atomic_redirect!(Add1, x);
atomic_impl!(Add1);
externfn_impl!(Add1, |this: &Self, x: ExprInst| {
let a: Numeric = this.x.clone().try_into()?;
Ok(Add0 { a, x })
});
/// Prev state: [Add1]
#[derive(Debug, Clone)]
pub struct Add0 {
a: Numeric,
x: ExprInst,
}
atomic_redirect!(Add0, x);
atomic_impl!(Add0, |Self { a, x }: &Self, _| {
let b: Numeric = x.clone().try_into()?;
Ok((*a + b).into())
});

View File

@@ -0,0 +1,37 @@
use std::fmt::Debug;
use super::super::Numeric;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Divides two numbers
///
/// Next state: [Divide1]
#[derive(Clone)]
pub struct Divide2;
externfn_impl!(Divide2, |_: &Self, x: ExprInst| Ok(Divide1 { x }));
/// Prev state: [Divide2]; Next state: [Divide0]
#[derive(Debug, Clone)]
pub struct Divide1 {
x: ExprInst,
}
atomic_redirect!(Divide1, x);
atomic_impl!(Divide1);
externfn_impl!(Divide1, |this: &Self, x: ExprInst| {
let a: Numeric = this.x.clone().try_into()?;
Ok(Divide0 { a, x })
});
/// Prev state: [Divide1]
#[derive(Debug, Clone)]
pub struct Divide0 {
a: Numeric,
x: ExprInst,
}
atomic_redirect!(Divide0, x);
atomic_impl!(Divide0, |Self { a, x }: &Self, _| {
let b: Numeric = x.clone().try_into()?;
Ok((*a / b).into())
});

View File

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

View File

@@ -0,0 +1,36 @@
use std::fmt::Debug;
use super::super::Numeric;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Multiplies two numbers
///
/// Next state: [Multiply1]
#[derive(Clone)]
pub struct Multiply2;
externfn_impl!(Multiply2, |_: &Self, x: ExprInst| Ok(Multiply1 { x }));
/// Prev state: [Multiply2]; Next state: [Multiply0]
#[derive(Debug, Clone)]
pub struct Multiply1 {
x: ExprInst,
}
atomic_redirect!(Multiply1, x);
atomic_impl!(Multiply1);
externfn_impl!(Multiply1, |this: &Self, x: ExprInst| {
let a: Numeric = this.x.clone().try_into()?;
Ok(Multiply0 { a, x })
});
/// Prev state: [Multiply1]
#[derive(Debug, Clone)]
pub struct Multiply0 {
a: Numeric,
x: ExprInst,
}
atomic_redirect!(Multiply0, x);
atomic_impl!(Multiply0, |Self { a, x }: &Self, _| {
let b: Numeric = x.clone().try_into()?;
Ok((*a * b).into())
});

View File

@@ -0,0 +1,36 @@
use std::fmt::Debug;
use super::super::Numeric;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Takes the modulus of two numbers.
///
/// Next state: [Remainder1]
#[derive(Clone)]
pub struct Remainder2;
externfn_impl!(Remainder2, |_: &Self, x: ExprInst| Ok(Remainder1 { x }));
/// Prev state: [Remainder2]; Next state: [Remainder0]
#[derive(Debug, Clone)]
pub struct Remainder1 {
x: ExprInst,
}
atomic_redirect!(Remainder1, x);
atomic_impl!(Remainder1);
externfn_impl!(Remainder1, |this: &Self, x: ExprInst| {
let a: Numeric = this.x.clone().try_into()?;
Ok(Remainder0 { a, x })
});
/// Prev state: [Remainder1]
#[derive(Debug, Clone)]
pub struct Remainder0 {
a: Numeric,
x: ExprInst,
}
atomic_redirect!(Remainder0, x);
atomic_impl!(Remainder0, |Self { a, x }: &Self, _| {
let b: Numeric = x.clone().try_into()?;
Ok((*a % b).into())
});

View File

@@ -0,0 +1,36 @@
use std::fmt::Debug;
use super::super::Numeric;
use crate::representations::interpreted::ExprInst;
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Subtracts two numbers
///
/// Next state: [Subtract1]
#[derive(Clone)]
pub struct Subtract2;
externfn_impl!(Subtract2, |_: &Self, x: ExprInst| Ok(Subtract1 { x }));
/// Prev state: [Subtract2]; Next state: [Subtract0]
#[derive(Debug, Clone)]
pub struct Subtract1 {
x: ExprInst,
}
atomic_redirect!(Subtract1, x);
atomic_impl!(Subtract1);
externfn_impl!(Subtract1, |this: &Self, x: ExprInst| {
let a: Numeric = this.x.clone().try_into()?;
Ok(Subtract0 { a, x })
});
/// Prev state: [Subtract1]
#[derive(Debug, Clone)]
pub struct Subtract0 {
a: Numeric,
x: ExprInst,
}
atomic_redirect!(Subtract0, x);
atomic_impl!(Subtract0, |Self { a, x }: &Self, _| {
let b: Numeric = x.clone().try_into()?;
Ok((*a - b).into())
});

32
src/stl/runtime_error.rs Normal file
View File

@@ -0,0 +1,32 @@
use std::fmt::Display;
use std::rc::Rc;
use crate::foreign::ExternError;
/// Some external event prevented the operation from succeeding
#[derive(Clone)]
pub struct RuntimeError {
message: String,
operation: &'static str,
}
impl RuntimeError {
pub fn fail<T>(
message: String,
operation: &'static str,
) -> Result<T, 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 {}

45
src/stl/str/char_at.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::fmt::Debug;
use super::super::litconv::{with_str, with_uint};
use super::super::runtime_error::RuntimeError;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::{Literal, Primitive};
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Takes an uint and a string, finds the char in a string at a 0-based index
///
/// Next state: [CharAt1]
#[derive(Clone)]
pub struct CharAt2;
externfn_impl!(CharAt2, |_: &Self, x: ExprInst| Ok(CharAt1 { x }));
/// Prev state: [CharAt2]; Next state: [CharAt0]
#[derive(Debug, Clone)]
pub struct CharAt1 {
x: ExprInst,
}
atomic_redirect!(CharAt1, x);
atomic_impl!(CharAt1);
externfn_impl!(CharAt1, |this: &Self, x: ExprInst| {
with_str(&this.x, |s| Ok(CharAt0 { s: s.clone(), x }))
});
/// Prev state: [CharAt1]
#[derive(Debug, Clone)]
pub struct CharAt0 {
s: String,
x: ExprInst,
}
atomic_redirect!(CharAt0, x);
atomic_impl!(CharAt0, |Self { s, x }: &Self, _| {
with_uint(x, |i| {
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",
)?
}
})
});

View File

@@ -0,0 +1,37 @@
use std::fmt::Debug;
use super::super::litconv::with_str;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::{Literal, Primitive};
use crate::{atomic_impl, atomic_redirect, externfn_impl};
/// Concatenates two strings
///
/// Next state: [Concatenate1]
#[derive(Clone)]
pub struct Concatenate2;
externfn_impl!(Concatenate2, |_: &Self, c: ExprInst| Ok(Concatenate1 { c }));
/// Prev state: [Concatenate2]; Next state: [Concatenate0]
#[derive(Debug, Clone)]
pub struct Concatenate1 {
c: ExprInst,
}
atomic_redirect!(Concatenate1, c);
atomic_impl!(Concatenate1);
externfn_impl!(Concatenate1, |this: &Self, c: ExprInst| {
with_str(&this.c, |a| Ok(Concatenate0 { a: a.clone(), c }))
});
/// Prev state: [Concatenate1]
#[derive(Debug, Clone)]
pub struct Concatenate0 {
a: String,
c: ExprInst,
}
atomic_redirect!(Concatenate0, c);
atomic_impl!(Concatenate0, |Self { a, c }: &Self, _| {
with_str(c, |b| {
Ok(Clause::P(Primitive::Literal(Literal::Str(a.to_owned() + b))))
})
});

12
src/stl/str/mod.rs Normal file
View File

@@ -0,0 +1,12 @@
mod char_at;
mod concatenate;
use crate::interner::Interner;
use crate::pipeline::ConstTree;
pub fn str(i: &Interner) -> ConstTree {
ConstTree::tree([(
i.i("concatenate"),
ConstTree::xfn(concatenate::Concatenate2),
)])
}