forked from Orchid/orchid
Removed foreign macros
Converted the function integration to use template metaprogramming instead of macros.
This commit is contained in:
@@ -4,11 +4,14 @@ use std::sync::Arc;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::Boolean;
|
||||
use crate::foreign::InertAtomic;
|
||||
use crate::systems::codegen::{orchid_opt, tuple};
|
||||
use crate::foreign::{
|
||||
xfn_1ary, xfn_2ary, xfn_3ary, xfn_4ary, Atomic, InertAtomic, XfnResult,
|
||||
};
|
||||
use crate::interpreted::Clause;
|
||||
use crate::systems::codegen::{opt, tuple};
|
||||
use crate::systems::RuntimeError;
|
||||
use crate::utils::{iter_find, unwrap_or};
|
||||
use crate::{define_fn, ConstTree, Interner, Literal};
|
||||
use crate::{ConstTree, Interner, Literal};
|
||||
|
||||
/// A block of binary data
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
@@ -33,114 +36,108 @@ impl Debug for Binary {
|
||||
}
|
||||
}
|
||||
|
||||
define_fn! {
|
||||
/// Detect the number of bytes in the binary data block
|
||||
pub Size = |x| {
|
||||
Ok(Literal::Uint(x.downcast::<Binary>()?.0.len() as u64).into())
|
||||
};
|
||||
/// Append two binary data blocks
|
||||
pub fn concatenate(a: Binary, b: Binary) -> XfnResult<Binary> {
|
||||
let data = a.0.iter().chain(b.0.iter()).copied().collect();
|
||||
Ok(Binary(Arc::new(data)))
|
||||
}
|
||||
|
||||
expr=x in
|
||||
|
||||
/// Convert a number into a binary blob
|
||||
pub FromNum {
|
||||
size: u64,
|
||||
is_little_endian: Boolean,
|
||||
data: u64
|
||||
} => {
|
||||
if size > 8 {
|
||||
RuntimeError::fail(
|
||||
"more than 8 bytes requested".to_string(),
|
||||
"converting number to binary"
|
||||
)?
|
||||
}
|
||||
let bytes = if is_little_endian.0 {
|
||||
data.to_le_bytes()[0..size as usize].to_vec()
|
||||
} else {
|
||||
data.to_be_bytes()[8 - size as usize..].to_vec()
|
||||
};
|
||||
Ok(Binary(Arc::new(bytes)).atom_cls())
|
||||
};
|
||||
|
||||
/// Read a number from a binary blob
|
||||
pub GetNum {
|
||||
buf: Binary,
|
||||
loc: u64,
|
||||
size: u64,
|
||||
is_little_endian: Boolean
|
||||
} => {
|
||||
if buf.0.len() < (loc + size) as usize {
|
||||
RuntimeError::fail(
|
||||
"section out of range".to_string(),
|
||||
"reading number from binary data"
|
||||
)?
|
||||
}
|
||||
if 8 < size {
|
||||
RuntimeError::fail(
|
||||
"more than 8 bytes provided".to_string(),
|
||||
"reading number from binary data"
|
||||
)?
|
||||
}
|
||||
let mut data = [0u8; 8];
|
||||
let section = &buf.0[loc as usize..(loc + size) as usize];
|
||||
let num = if is_little_endian.0 {
|
||||
data[0..size as usize].copy_from_slice(section);
|
||||
u64::from_le_bytes(data)
|
||||
} else {
|
||||
data[8 - size as usize..].copy_from_slice(section);
|
||||
u64::from_be_bytes(data)
|
||||
};
|
||||
Ok(Literal::Uint(num).into())
|
||||
};
|
||||
|
||||
/// Append two binary data blocks
|
||||
pub Concatenate { a: Binary, b: Binary } => {
|
||||
let data = a.0.iter().chain(b.0.iter()).copied().collect();
|
||||
Ok(Binary(Arc::new(data)).atom_cls())
|
||||
};
|
||||
|
||||
/// Extract a subsection of the binary data
|
||||
pub Slice { s: Binary, i: u64, len: u64 } => {
|
||||
if i + len < s.0.len() as u64 {
|
||||
RuntimeError::fail(
|
||||
"Byte index out of bounds".to_string(),
|
||||
"indexing binary"
|
||||
)?
|
||||
}
|
||||
let data = s.0[i as usize..i as usize + len as usize].to_vec();
|
||||
Ok(Binary(Arc::new(data)).atom_cls())
|
||||
};
|
||||
|
||||
/// Return the index where the first argument first contains the second,
|
||||
/// if any
|
||||
pub Find { haystack: Binary, needle: Binary } => {
|
||||
let found = iter_find(haystack.0.iter(), needle.0.iter());
|
||||
Ok(orchid_opt(found.map(|x| Literal::Uint(x as u64).into())))
|
||||
};
|
||||
/// Split binary data block into two smaller blocks
|
||||
pub Split { bin: Binary, i: u64 } => {
|
||||
if bin.0.len() < i as usize {
|
||||
RuntimeError::fail(
|
||||
"Byte index out of bounds".to_string(),
|
||||
"splitting binary"
|
||||
)?
|
||||
}
|
||||
let (asl, bsl) = bin.0.split_at(i as usize);
|
||||
Ok(tuple([
|
||||
Binary(Arc::new(asl.to_vec())).atom_cls().into(),
|
||||
Binary(Arc::new(bsl.to_vec())).atom_cls().into(),
|
||||
]))
|
||||
/// Extract a subsection of the binary data
|
||||
pub fn slice(s: Binary, i: u64, len: u64) -> XfnResult<Binary> {
|
||||
if i + len < s.0.len() as u64 {
|
||||
RuntimeError::fail(
|
||||
"Byte index out of bounds".to_string(),
|
||||
"indexing binary",
|
||||
)?
|
||||
}
|
||||
let data = s.0[i as usize..i as usize + len as usize].to_vec();
|
||||
Ok(Binary(Arc::new(data)))
|
||||
}
|
||||
|
||||
/// Return the index where the first argument first contains the second, if any
|
||||
pub fn find(haystack: Binary, needle: Binary) -> XfnResult<Clause> {
|
||||
let found = iter_find(haystack.0.iter(), needle.0.iter());
|
||||
Ok(opt(found.map(|x| Literal::Uint(x as u64).into())))
|
||||
}
|
||||
|
||||
/// Split binary data block into two smaller blocks
|
||||
pub fn split(bin: Binary, i: u64) -> XfnResult<Clause> {
|
||||
if bin.0.len() < i as usize {
|
||||
RuntimeError::fail(
|
||||
"Byte index out of bounds".to_string(),
|
||||
"splitting binary",
|
||||
)?
|
||||
}
|
||||
let (asl, bsl) = bin.0.split_at(i as usize);
|
||||
Ok(tuple([
|
||||
Binary(Arc::new(asl.to_vec())).atom_cls().into(),
|
||||
Binary(Arc::new(bsl.to_vec())).atom_cls().into(),
|
||||
]))
|
||||
}
|
||||
|
||||
/// Read a number from a binary blob
|
||||
pub fn get_num(
|
||||
buf: Binary,
|
||||
loc: u64,
|
||||
size: u64,
|
||||
is_le: Boolean,
|
||||
) -> XfnResult<Literal> {
|
||||
if buf.0.len() < (loc + size) as usize {
|
||||
RuntimeError::fail(
|
||||
"section out of range".to_string(),
|
||||
"reading number from binary data",
|
||||
)?
|
||||
}
|
||||
if 8 < size {
|
||||
RuntimeError::fail(
|
||||
"more than 8 bytes provided".to_string(),
|
||||
"reading number from binary data",
|
||||
)?
|
||||
}
|
||||
let mut data = [0u8; 8];
|
||||
let section = &buf.0[loc as usize..(loc + size) as usize];
|
||||
let num = if is_le.0 {
|
||||
data[0..size as usize].copy_from_slice(section);
|
||||
u64::from_le_bytes(data)
|
||||
} else {
|
||||
data[8 - size as usize..].copy_from_slice(section);
|
||||
u64::from_be_bytes(data)
|
||||
};
|
||||
Ok(Literal::Uint(num))
|
||||
}
|
||||
|
||||
/// Convert a number into a blob
|
||||
pub fn from_num(size: u64, is_le: Boolean, data: u64) -> XfnResult<Binary> {
|
||||
if size > 8 {
|
||||
RuntimeError::fail(
|
||||
"more than 8 bytes requested".to_string(),
|
||||
"converting number to binary",
|
||||
)?
|
||||
}
|
||||
let bytes = if is_le.0 {
|
||||
data.to_le_bytes()[0..size as usize].to_vec()
|
||||
} else {
|
||||
data.to_be_bytes()[8 - size as usize..].to_vec()
|
||||
};
|
||||
Ok(Binary(Arc::new(bytes)))
|
||||
}
|
||||
|
||||
/// Detect the number of bytes in the blob
|
||||
pub fn size(b: Binary) -> XfnResult<Literal> {
|
||||
Ok(Literal::Uint(b.0.len() as u64))
|
||||
}
|
||||
|
||||
pub fn bin(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([(
|
||||
i.i("bin"),
|
||||
ConstTree::tree([
|
||||
(i.i("concat"), ConstTree::xfn(Concatenate)),
|
||||
(i.i("slice"), ConstTree::xfn(Slice)),
|
||||
(i.i("find"), ConstTree::xfn(Find)),
|
||||
(i.i("split"), ConstTree::xfn(Split)),
|
||||
(i.i("size"), ConstTree::xfn(Size)),
|
||||
(i.i("concat"), ConstTree::xfn(xfn_2ary(concatenate))),
|
||||
(i.i("slice"), ConstTree::xfn(xfn_3ary(slice))),
|
||||
(i.i("find"), ConstTree::xfn(xfn_2ary(find))),
|
||||
(i.i("split"), ConstTree::xfn(xfn_2ary(split))),
|
||||
(i.i("get_num"), ConstTree::xfn(xfn_4ary(get_num))),
|
||||
(i.i("from_num"), ConstTree::xfn(xfn_3ary(from_num))),
|
||||
(i.i("size"), ConstTree::xfn(xfn_1ary(size))),
|
||||
]),
|
||||
)])
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::foreign::InertAtomic;
|
||||
use crate::foreign::{xfn_1ary, xfn_2ary, InertAtomic, XfnResult};
|
||||
use crate::interner::Interner;
|
||||
use crate::representations::interpreted::Clause;
|
||||
use crate::systems::AssertionError;
|
||||
use crate::{define_fn, ConstTree, Literal, Location, PathSet};
|
||||
use crate::{ConstTree, Literal, Location};
|
||||
|
||||
/// Booleans exposed to Orchid
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -17,46 +15,38 @@ impl From<bool> for Boolean {
|
||||
fn from(value: bool) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
define_fn! {
|
||||
/// Takes a boolean and two branches, runs the first if the bool is true, the
|
||||
/// second if it's false.
|
||||
// Even though it's a ternary function, IfThenElse is implemented as an unary
|
||||
// foreign function, as the rest of the logic can be defined in Orchid.
|
||||
IfThenElse = |x| x.downcast().map(|Boolean(b)| 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(),
|
||||
}});
|
||||
/// Takes a boolean and two branches, runs the first if the bool is true, the
|
||||
/// second if it's false.
|
||||
// Even though it's a ternary function, IfThenElse is implemented as an unary
|
||||
// foreign function, as the rest of the logic can be defined in Orchid.
|
||||
pub fn if_then_else(b: Boolean) -> XfnResult<Clause> {
|
||||
Ok(match b.0 {
|
||||
true => Clause::pick(Clause::constfn(Clause::LambdaArg)),
|
||||
false => Clause::constfn(Clause::pick(Clause::LambdaArg)),
|
||||
})
|
||||
}
|
||||
|
||||
expr=x in
|
||||
/// Compares the inner values if
|
||||
///
|
||||
/// - both are string,
|
||||
/// - both are either uint or num
|
||||
Equals { a: Literal, b: Literal } => Ok(Boolean::from(match (a, b) {
|
||||
/// Compares the inner values if
|
||||
///
|
||||
/// - both are string,
|
||||
/// - both are either uint or num
|
||||
pub fn equals(a: Literal, b: Literal) -> XfnResult<Boolean> {
|
||||
Ok(Boolean::from(match (a, b) {
|
||||
(Literal::Str(s1), Literal::Str(s2)) => s1 == s2,
|
||||
(Literal::Num(n1), Literal::Num(n2)) => n1 == n2,
|
||||
(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(Location::Unknown, "the expected type")?,
|
||||
}).atom_cls())
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn bool(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([(
|
||||
i.i("bool"),
|
||||
ConstTree::tree([
|
||||
(i.i("ifthenelse"), ConstTree::xfn(IfThenElse)),
|
||||
(i.i("equals"), ConstTree::xfn(Equals)),
|
||||
(i.i("ifthenelse"), ConstTree::xfn(xfn_1ary(if_then_else))),
|
||||
(i.i("equals"), ConstTree::xfn(xfn_2ary(equals))),
|
||||
(i.i("true"), ConstTree::atom(Boolean(true))),
|
||||
(i.i("false"), ConstTree::atom(Boolean(false))),
|
||||
]),
|
||||
|
||||
@@ -2,48 +2,53 @@ use chumsky::Parser;
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use super::ArithmeticError;
|
||||
use crate::foreign::ExternError;
|
||||
use crate::foreign::{xfn_1ary, ExternError, XfnResult};
|
||||
use crate::interner::Interner;
|
||||
use crate::interpreted::Clause;
|
||||
use crate::parse::{float_parser, int_parser};
|
||||
use crate::systems::cast_exprinst::get_literal;
|
||||
use crate::systems::AssertionError;
|
||||
use crate::{define_fn, ConstTree, Literal};
|
||||
use crate::{ConstTree, Literal, Location};
|
||||
|
||||
define_fn! {
|
||||
/// parse a number. Accepts the same syntax Orchid does.
|
||||
ToFloat = |x| match get_literal(x)? {
|
||||
(Literal::Str(s), loc) => float_parser()
|
||||
/// parse a number. Accepts the same syntax Orchid does.
|
||||
pub fn to_float(l: Literal) -> XfnResult<Literal> {
|
||||
match l {
|
||||
Literal::Str(s) => float_parser()
|
||||
.parse(s.as_str())
|
||||
.map_err(|_| AssertionError::ext(loc, "float syntax")),
|
||||
(Literal::Num(n), _) => Ok(n),
|
||||
(Literal::Uint(i), _) => NotNan::new(i as f64)
|
||||
.map(Literal::Num)
|
||||
.map_err(|_| AssertionError::ext(Location::Unknown, "float syntax")),
|
||||
n @ Literal::Num(_) => Ok(n),
|
||||
Literal::Uint(i) => NotNan::new(i as f64)
|
||||
.map(Literal::Num)
|
||||
.map_err(|_| ArithmeticError::NaN.into_extern()),
|
||||
}.map(|nn| Literal::Num(nn).into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
||||
/// input is a number, floors it.
|
||||
ToUint = |x| match get_literal(x)? {
|
||||
(Literal::Str(s), loc) => int_parser()
|
||||
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
||||
/// input is a number, floors it.
|
||||
pub fn to_uint(l: Literal) -> XfnResult<Literal> {
|
||||
match l {
|
||||
Literal::Str(s) => int_parser()
|
||||
.parse(s.as_str())
|
||||
.map_err(|_| AssertionError::ext(loc, "int syntax")),
|
||||
(Literal::Num(n), _) => Ok(n.floor() as u64),
|
||||
(Literal::Uint(i), _) => Ok(i),
|
||||
}.map(|u| Literal::Uint(u).into());
|
||||
.map(Literal::Uint)
|
||||
.map_err(|_| AssertionError::ext(Location::Unknown, "int syntax")),
|
||||
Literal::Num(n) => Ok(Literal::Uint(n.floor() as u64)),
|
||||
i @ Literal::Uint(_) => Ok(i),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a literal to a string using Rust's conversions for floats, chars and
|
||||
/// uints respectively
|
||||
ToString = |x| Ok(match get_literal(x)?.0 {
|
||||
Literal::Uint(i) => Clause::from(Literal::Str(i.to_string().into())),
|
||||
Literal::Num(n) => Clause::from(Literal::Str(n.to_string().into())),
|
||||
s@Literal::Str(_) => Clause::from(s),
|
||||
/// Convert a literal to a string using Rust's conversions for floats, chars and
|
||||
/// uints respectively
|
||||
pub fn to_string(l: Literal) -> XfnResult<Literal> {
|
||||
Ok(match l {
|
||||
Literal::Uint(i) => Literal::Str(i.to_string().into()),
|
||||
Literal::Num(n) => Literal::Str(n.to_string().into()),
|
||||
s @ Literal::Str(_) => s,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn conv(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([
|
||||
(i.i("to_float"), ConstTree::xfn(ToFloat)),
|
||||
(i.i("to_uint"), ConstTree::xfn(ToUint)),
|
||||
(i.i("to_string"), ConstTree::xfn(ToString)),
|
||||
(i.i("to_float"), ConstTree::xfn(xfn_1ary(to_float))),
|
||||
(i.i("to_uint"), ConstTree::xfn(xfn_1ary(to_uint))),
|
||||
(i.i("to_string"), ConstTree::xfn(xfn_1ary(to_string))),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,32 +1,18 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::foreign::{Atomic, AtomicReturn};
|
||||
use crate::foreign::{ExternFn, XfnResult};
|
||||
use crate::interpreted::Clause;
|
||||
use crate::interpreter::Context;
|
||||
use crate::representations::interpreted::ExprInst;
|
||||
use crate::utils::ddispatch::Responder;
|
||||
use crate::{write_fn_step, ConstTree, Interner};
|
||||
|
||||
write_fn_step! {
|
||||
/// Print and return whatever expression is in the argument without
|
||||
/// normalizing it.
|
||||
Inspect > Inspect1
|
||||
}
|
||||
use crate::{ConstTree, Interner};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Inspect1 {
|
||||
expr_inst: ExprInst,
|
||||
}
|
||||
impl Responder for Inspect1 {}
|
||||
impl Atomic for Inspect1 {
|
||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||
fn run(self: Box<Self>, ctx: Context) -> crate::foreign::AtomicResult {
|
||||
println!("{}", self.expr_inst);
|
||||
Ok(AtomicReturn {
|
||||
clause: self.expr_inst.expr().clause.clone(),
|
||||
gas: ctx.gas.map(|g| g - 1),
|
||||
inert: false,
|
||||
})
|
||||
struct Inspect;
|
||||
impl ExternFn for Inspect {
|
||||
fn name(&self) -> &str { "inspect" }
|
||||
fn apply(self: Box<Self>, arg: ExprInst, _: Context) -> XfnResult<Clause> {
|
||||
println!("{arg}");
|
||||
Ok(arg.expr().clause.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ export const reduce := \list.\f. do{
|
||||
]--
|
||||
export const filter := \list.\f. (
|
||||
pop list end \head.\tail.
|
||||
if (f el)
|
||||
then cons el (filter tail f)
|
||||
if (f head)
|
||||
then cons head (filter tail f)
|
||||
else filter tail f
|
||||
)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export const set := \m.\k.\v. (
|
||||
|
||||
-- ensure that there's only one instance of each key in the map
|
||||
export const normalize := \m. (
|
||||
recursive r (m, normal=empty) with
|
||||
recursive r (m, normal=empty)
|
||||
list::pop m normal \head.\tail.
|
||||
r tail $ set normal (fst head) (snd head)
|
||||
)
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::rc::Rc;
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use super::ArithmeticError;
|
||||
use crate::foreign::ExternError;
|
||||
use crate::foreign::{xfn_2ary, ExternError, ToClause, XfnResult};
|
||||
use crate::interpreted::TryFromExprInst;
|
||||
use crate::representations::interpreted::{Clause, ExprInst};
|
||||
use crate::representations::{Literal, Primitive};
|
||||
use crate::systems::cast_exprinst::get_literal;
|
||||
use crate::systems::AssertionError;
|
||||
use crate::{define_fn, ConstTree, Interner};
|
||||
use crate::{ConstTree, Interner};
|
||||
|
||||
// region: Numeric, type to handle floats and uints together
|
||||
|
||||
@@ -51,9 +51,9 @@ impl TryFromExprInst for Numeric {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Numeric> for Clause {
|
||||
fn from(value: Numeric) -> Self {
|
||||
Clause::P(Primitive::Literal(match value {
|
||||
impl ToClause for Numeric {
|
||||
fn to_clause(self) -> Clause {
|
||||
Clause::P(Primitive::Literal(match self {
|
||||
Numeric::Uint(i) => Literal::Uint(i),
|
||||
Numeric::Num(n) => Literal::Num(n),
|
||||
}))
|
||||
@@ -62,65 +62,66 @@ impl From<Numeric> for Clause {
|
||||
|
||||
// endregion
|
||||
|
||||
// region: operations
|
||||
|
||||
define_fn! {
|
||||
/// Add two numbers. If they're both uint, the output is uint. If either is
|
||||
/// number, the output is number.
|
||||
Add { a: Numeric, b: Numeric } => match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||
a.checked_add(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::Overflow.into_extern())
|
||||
}
|
||||
/// Add two numbers. If they're both uint, the output is uint. If either is
|
||||
/// number, the output is number.
|
||||
pub fn add(a: Numeric, b: Numeric) -> XfnResult<Numeric> {
|
||||
match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
||||
.checked_add(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::Overflow.into_extern()),
|
||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a + b)),
|
||||
(Numeric::Num(a), Numeric::Uint(b)) | (Numeric::Uint(b), Numeric::Num(a))
|
||||
=> Numeric::num(*a + b as f64),
|
||||
}.map(Numeric::into);
|
||||
(Numeric::Num(a), Numeric::Uint(b))
|
||||
| (Numeric::Uint(b), Numeric::Num(a)) => Numeric::num(*a + b as f64),
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract a number from another. Always returns Number.
|
||||
Subtract { a: Numeric, b: Numeric } => match (a, b) {
|
||||
/// Subtract a number from another. Always returns Number.
|
||||
pub fn subtract(a: Numeric, b: Numeric) -> XfnResult<Numeric> {
|
||||
match (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::Num(a), Numeric::Uint(b)) => Numeric::num(*a - b as f64),
|
||||
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 - *b),
|
||||
}.map(Numeric::into);
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply two numbers. If they're both uint, the output is uint. If either
|
||||
/// is number, the output is number.
|
||||
Multiply { a: Numeric, b: Numeric } => match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||
a.checked_mul(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::Overflow.into_extern())
|
||||
}
|
||||
/// Multiply two numbers. If they're both uint, the output is uint. If either
|
||||
/// is number, the output is number.
|
||||
pub fn multiply(a: Numeric, b: Numeric) -> XfnResult<Numeric> {
|
||||
match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
||||
.checked_mul(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::Overflow.into_extern()),
|
||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a * b)),
|
||||
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
|
||||
=> Numeric::num(a as f64 * *b),
|
||||
}.map(Numeric::into);
|
||||
(Numeric::Uint(a), Numeric::Num(b))
|
||||
| (Numeric::Num(b), Numeric::Uint(a)) => Numeric::num(a as f64 * *b),
|
||||
}
|
||||
}
|
||||
|
||||
/// Divide a number by another. Always returns Number.
|
||||
Divide { a: Numeric, b: Numeric } => {
|
||||
let a: f64 = a.as_f64();
|
||||
let b: f64 = b.as_f64();
|
||||
if b == 0.0 {
|
||||
return Err(ArithmeticError::DivByZero.into_extern())
|
||||
}
|
||||
Numeric::num(a / b).map(Numeric::into)
|
||||
};
|
||||
/// Divide a number by another. Always returns Number.
|
||||
pub fn divide(a: Numeric, b: Numeric) -> XfnResult<Numeric> {
|
||||
let a: f64 = a.as_f64();
|
||||
let b: f64 = b.as_f64();
|
||||
if b == 0.0 {
|
||||
return Err(ArithmeticError::DivByZero.into_extern());
|
||||
}
|
||||
Numeric::num(a / b)
|
||||
}
|
||||
|
||||
/// Take the remainder of two numbers. If they're both uint, the output is
|
||||
/// uint. If either is number, the output is number.
|
||||
Remainder { a: Numeric, b: Numeric } => match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||
a.checked_rem(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::DivByZero.into_extern())
|
||||
}
|
||||
/// Take the remainder of two numbers. If they're both uint, the output is
|
||||
/// uint. If either is number, the output is number.
|
||||
pub fn remainder(a: Numeric, b: Numeric) -> XfnResult<Numeric> {
|
||||
match (a, b) {
|
||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
||||
.checked_rem(b)
|
||||
.map(Numeric::Uint)
|
||||
.ok_or_else(|| ArithmeticError::DivByZero.into_extern()),
|
||||
(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),
|
||||
}.map(Numeric::into)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -129,11 +130,11 @@ pub fn num(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([(
|
||||
i.i("num"),
|
||||
ConstTree::tree([
|
||||
(i.i("add"), ConstTree::xfn(Add)),
|
||||
(i.i("subtract"), ConstTree::xfn(Subtract)),
|
||||
(i.i("multiply"), ConstTree::xfn(Multiply)),
|
||||
(i.i("divide"), ConstTree::xfn(Divide)),
|
||||
(i.i("remainder"), ConstTree::xfn(Remainder)),
|
||||
(i.i("add"), ConstTree::xfn(xfn_2ary(add))),
|
||||
(i.i("subtract"), ConstTree::xfn(xfn_2ary(subtract))),
|
||||
(i.i("multiply"), ConstTree::xfn(xfn_2ary(multiply))),
|
||||
(i.i("divide"), ConstTree::xfn(xfn_2ary(divide))),
|
||||
(i.i("remainder"), ConstTree::xfn(xfn_2ary(remainder))),
|
||||
]),
|
||||
)])
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::foreign::ExternError;
|
||||
use crate::{define_fn, ConstTree, Interner, OrcString};
|
||||
use crate::foreign::{xfn_1ary, ExternError, XfnResult};
|
||||
use crate::interpreted::Clause;
|
||||
use crate::{ConstTree, Interner, OrcString};
|
||||
|
||||
/// An unrecoverable error in Orchid land. Because Orchid is lazy, this only
|
||||
/// invalidates expressions that reference the one that generated it.
|
||||
@@ -16,14 +17,12 @@ impl Display for OrchidPanic {
|
||||
|
||||
impl ExternError for OrchidPanic {}
|
||||
|
||||
define_fn! {
|
||||
/// Takes a message, returns an [ExternError] unconditionally.
|
||||
Panic = |x| {
|
||||
let msg = Rc::new(x.downcast::<OrcString>()?.get_string());
|
||||
Err(OrchidPanic(msg).into_extern())
|
||||
}
|
||||
/// Takes a message, returns an [ExternError] unconditionally.
|
||||
pub fn orc_panic(msg: OrcString) -> XfnResult<Clause> {
|
||||
// any return value would work, but Clause is the simplest
|
||||
Err(OrchidPanic(Rc::new(msg.get_string())).into_extern())
|
||||
}
|
||||
|
||||
pub fn panic(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([(i.i("panic"), ConstTree::xfn(Panic))])
|
||||
ConstTree::tree([(i.i("panic"), ConstTree::xfn(xfn_1ary(orc_panic)))])
|
||||
}
|
||||
|
||||
@@ -10,17 +10,17 @@ export macro do {
|
||||
}
|
||||
export macro do {
|
||||
...$statement ; ...$rest:1
|
||||
} =0x2p130=> statement (...$statement) do { ...$rest }
|
||||
export macro do { ...$return } =0x1p130=> ...$return
|
||||
} =0x2p130=> statement (...$statement) (do { ...$rest })
|
||||
export macro do { ...$return } =0x1p130=> (...$return)
|
||||
|
||||
export ::do
|
||||
|
||||
export macro statement (let $name = ...$value) ...$next =0x1p230=> (
|
||||
export macro statement (let $name = ...$value) (...$next) =0x1p230=> (
|
||||
( \$name. ...$next) (...$value)
|
||||
)
|
||||
export macro statement (cps ...$names = ...$operation:1) ...$next =0x2p230=> (
|
||||
export macro statement (cps ...$names = ...$operation:1) (...$next) =0x2p230=> (
|
||||
(...$operation) ( (...$names) => ...$next )
|
||||
)
|
||||
export macro statement (cps ...$operation) ...$next =0x1p230=> (
|
||||
export macro statement (cps ...$operation) (...$next) =0x1p230=> (
|
||||
(...$operation) (...$next)
|
||||
)
|
||||
|
||||
10
src/systems/stl/result.orc
Normal file
10
src/systems/stl/result.orc
Normal file
@@ -0,0 +1,10 @@
|
||||
import std::panic
|
||||
|
||||
export const ok := \v. \fe.\fv. fv v
|
||||
export const err := \e. \fe.\fv. fe e
|
||||
|
||||
export const map := \result.\fv. result err fv
|
||||
export const map_err := \result.\fe. result fe ok
|
||||
export const flatten := \result. result err \res. res
|
||||
export const and_then := \result.\f. result err \v. f v
|
||||
export const unwrap := \result. result (\e. panic "value expected") \v.v
|
||||
@@ -3,11 +3,11 @@ use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::foreign::cps_box::{const_cps, init_cps, CPSBox};
|
||||
use crate::foreign::{Atomic, InertAtomic};
|
||||
use crate::interpreted::ExprInst;
|
||||
use crate::foreign::{xfn_1ary, Atomic, InertAtomic, XfnResult};
|
||||
use crate::interpreted::{Clause, ExprInst};
|
||||
use crate::interpreter::HandlerTable;
|
||||
use crate::systems::codegen::call;
|
||||
use crate::{define_fn, ConstTree, Interner};
|
||||
use crate::{ConstTree, Interner};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State(Rc<RefCell<ExprInst>>);
|
||||
@@ -24,10 +24,9 @@ struct SetStateCmd(State);
|
||||
#[derive(Debug, Clone)]
|
||||
struct GetStateCmd(State);
|
||||
|
||||
define_fn! {
|
||||
SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?)));
|
||||
GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?)))
|
||||
}
|
||||
fn get_state(s: State) -> XfnResult<Clause> { Ok(init_cps(2, GetStateCmd(s))) }
|
||||
|
||||
fn set_state(s: State) -> XfnResult<Clause> { Ok(init_cps(2, SetStateCmd(s))) }
|
||||
|
||||
fn new_state_handler<E>(cmd: CPSBox<NewStateCmd>) -> Result<ExprInst, E> {
|
||||
let (_, default, handler) = cmd.unpack2();
|
||||
@@ -63,8 +62,8 @@ pub fn state_lib(i: &Interner) -> ConstTree {
|
||||
[i.i("state")],
|
||||
ConstTree::tree([
|
||||
(i.i("new_state"), const_cps(2, NewStateCmd)),
|
||||
(i.i("get_state"), ConstTree::xfn(GetState)),
|
||||
(i.i("set_state"), ConstTree::xfn(SetState)),
|
||||
(i.i("get_state"), ConstTree::xfn(xfn_1ary(get_state))),
|
||||
(i.i("set_state"), ConstTree::xfn(xfn_1ary(set_state))),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,84 +1,77 @@
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::foreign::{xfn_1ary, xfn_2ary, xfn_3ary, XfnResult};
|
||||
use crate::interner::Interner;
|
||||
use crate::interpreted::Clause;
|
||||
use crate::representations::OrcString;
|
||||
use crate::systems::codegen::{orchid_opt, tuple};
|
||||
use crate::systems::codegen::{opt, tuple};
|
||||
use crate::systems::RuntimeError;
|
||||
use crate::utils::iter_find;
|
||||
use crate::{define_fn, ConstTree, Literal};
|
||||
use crate::{ConstTree, Literal};
|
||||
|
||||
define_fn! {
|
||||
pub Len = |x| Ok(Literal::Uint(
|
||||
(*x.downcast::<OrcString>()?)
|
||||
.graphemes(true)
|
||||
.count() as u64
|
||||
).into());
|
||||
pub fn len(s: OrcString) -> XfnResult<u64> {
|
||||
Ok(s.graphemes(true).count() as u64)
|
||||
}
|
||||
|
||||
pub Size = |x| Ok(Literal::Uint(
|
||||
(*x.downcast::<OrcString>()?)
|
||||
.as_bytes()
|
||||
.len() as u64
|
||||
).into());
|
||||
pub fn size(s: OrcString) -> XfnResult<u64> { Ok(s.as_bytes().len() as u64) }
|
||||
|
||||
expr=x in
|
||||
/// Append a string to another
|
||||
pub Concatenate { a: OrcString, b: OrcString } => Ok(
|
||||
Literal::Str((a.get_string() + b.as_str()).into()).into()
|
||||
);
|
||||
/// Append a string to another
|
||||
pub fn concatenate(a: OrcString, b: OrcString) -> XfnResult<String> {
|
||||
Ok(a.get_string() + b.as_str())
|
||||
}
|
||||
|
||||
pub Slice { s: OrcString, i: u64, len: u64 } => {
|
||||
let graphs = s.as_str().graphemes(true);
|
||||
if i == 0 {
|
||||
let orc_str = graphs.take(len as usize).collect::<String>().into();
|
||||
Ok(Literal::Str(orc_str).into())
|
||||
} else {
|
||||
let mut prefix = graphs.skip(i as usize - 1);
|
||||
if prefix.next().is_none() {
|
||||
RuntimeError::fail(
|
||||
"Character index out of bounds".to_string(),
|
||||
"indexing string",
|
||||
)
|
||||
} else {
|
||||
let mut count = 0;
|
||||
let ret = (prefix.take(len as usize))
|
||||
.map(|x| { count+=1; x })
|
||||
.collect::<String>().into();
|
||||
if count == len {
|
||||
Ok(Literal::Str(ret).into())
|
||||
} else {
|
||||
RuntimeError::fail(
|
||||
"Character index out of bounds".to_string(),
|
||||
"indexing string"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub Find { haystack: OrcString, needle: OrcString } => {
|
||||
let haystack_graphs = haystack.as_str().graphemes(true);
|
||||
let found = iter_find(haystack_graphs, needle.as_str().graphemes(true));
|
||||
Ok(orchid_opt(found.map(|x| Literal::Uint(x as u64).into())))
|
||||
};
|
||||
|
||||
pub Split { s: OrcString, i: u64 } => {
|
||||
let mut graphs = s.as_str().graphemes(true);
|
||||
let a = graphs.by_ref().take(i as usize).collect::<String>();
|
||||
let b = graphs.collect::<String>();
|
||||
Ok(tuple([a.into(), b.into()]))
|
||||
pub fn slice(s: OrcString, i: u64, len: u64) -> XfnResult<String> {
|
||||
let graphs = s.as_str().graphemes(true);
|
||||
if i == 0 {
|
||||
return Ok(graphs.take(len as usize).collect::<String>());
|
||||
}
|
||||
let mut prefix = graphs.skip(i as usize - 1);
|
||||
if prefix.next().is_none() {
|
||||
return Err(RuntimeError::ext(
|
||||
"Character index out of bounds".to_string(),
|
||||
"indexing string",
|
||||
));
|
||||
}
|
||||
let mut count = 0;
|
||||
let ret = (prefix.take(len as usize))
|
||||
.map(|x| {
|
||||
count += 1;
|
||||
x
|
||||
})
|
||||
.collect::<String>();
|
||||
if count == len {
|
||||
Ok(ret)
|
||||
} else {
|
||||
RuntimeError::fail(
|
||||
"Character index out of bounds".to_string(),
|
||||
"indexing string",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find(haystack: OrcString, needle: OrcString) -> XfnResult<Clause> {
|
||||
let haystack_graphs = haystack.as_str().graphemes(true);
|
||||
let found = iter_find(haystack_graphs, needle.as_str().graphemes(true));
|
||||
Ok(opt(found.map(|x| Literal::Uint(x as u64).into())))
|
||||
}
|
||||
|
||||
pub fn split(s: OrcString, i: u64) -> XfnResult<Clause> {
|
||||
let mut graphs = s.as_str().graphemes(true);
|
||||
let a = graphs.by_ref().take(i as usize).collect::<String>();
|
||||
let b = graphs.collect::<String>();
|
||||
Ok(tuple([a.into(), b.into()]))
|
||||
}
|
||||
|
||||
pub fn str(i: &Interner) -> ConstTree {
|
||||
ConstTree::tree([(
|
||||
i.i("str"),
|
||||
ConstTree::tree([
|
||||
(i.i("concat"), ConstTree::xfn(Concatenate)),
|
||||
(i.i("slice"), ConstTree::xfn(Slice)),
|
||||
(i.i("find"), ConstTree::xfn(Find)),
|
||||
(i.i("split"), ConstTree::xfn(Split)),
|
||||
(i.i("len"), ConstTree::xfn(Len)),
|
||||
(i.i("size"), ConstTree::xfn(Size)),
|
||||
(i.i("concat"), ConstTree::xfn(xfn_2ary(concatenate))),
|
||||
(i.i("slice"), ConstTree::xfn(xfn_3ary(slice))),
|
||||
(i.i("find"), ConstTree::xfn(xfn_2ary(find))),
|
||||
(i.i("split"), ConstTree::xfn(xfn_2ary(split))),
|
||||
(i.i("len"), ConstTree::xfn(xfn_1ary(len))),
|
||||
(i.i("size"), ConstTree::xfn(xfn_1ary(size))),
|
||||
]),
|
||||
)])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user