opportunistic move

should be way faster now
This commit is contained in:
2023-09-16 12:57:50 +01:00
parent 0bcf10659b
commit 1078835e8b
36 changed files with 535 additions and 521 deletions

View File

@@ -33,29 +33,34 @@ impl Debug for Binary {
}
}
define_fn! {expr=x in
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())
};
expr=x in
/// Convert a number into a binary blob
pub FromNum {
size: u64,
is_little_endian: Boolean,
data: u64
} => {
if size > &8 {
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()
data.to_le_bytes()[0..size as usize].to_vec()
} else {
data.to_be_bytes()[8 - *size as usize..].to_vec()
data.to_be_bytes()[8 - size as usize..].to_vec()
};
Ok(Binary(Arc::new(bytes)).atom_cls())
}
}
};
define_fn! {expr=x in
/// Read a number from a binary blob
pub GetNum {
buf: Binary,
@@ -69,34 +74,30 @@ define_fn! {expr=x in
"reading number from binary data"
)?
}
if 8 < *size {
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 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);
data[0..size as usize].copy_from_slice(section);
u64::from_le_bytes(data)
} else {
data[8 - *size as usize..].copy_from_slice(section);
data[8 - size as usize..].copy_from_slice(section);
u64::from_be_bytes(data)
};
Ok(Literal::Uint(num).into())
}
}
};
define_fn! {expr=x in
/// 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())
}
}
};
define_fn! {expr=x in
/// Extract a subsection of the binary data
pub Slice { s: Binary, i: u64, len: u64 } => {
if i + len < s.0.len() as u64 {
@@ -105,30 +106,25 @@ define_fn! {expr=x in
"indexing binary"
)?
}
let data = s.0[*i as usize..*i as usize + *len as usize].to_vec();
let data = s.0[i as usize..i as usize + len as usize].to_vec();
Ok(Binary(Arc::new(data)).atom_cls())
}
}
};
define_fn! {expr=x in
/// 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())))
}
}
define_fn! {expr=x in
};
/// Split binary data block into two smaller blocks
pub Split { bin: Binary, i: u64 } => {
if bin.0.len() < *i as usize {
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);
let (asl, bsl) = bin.0.split_at(i as usize);
Ok(tuple(vec![
Binary(Arc::new(asl.to_vec())).atom_cls().into(),
Binary(Arc::new(bsl.to_vec())).atom_cls().into(),
@@ -136,13 +132,6 @@ define_fn! {expr=x in
}
}
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())
}
}
pub fn bin(i: &Interner) -> ConstTree {
ConstTree::tree([(
i.i("bin"),

View File

@@ -4,7 +4,7 @@ use crate::foreign::InertAtomic;
use crate::interner::Interner;
use crate::representations::interpreted::Clause;
use crate::systems::AssertionError;
use crate::{define_fn, ConstTree, Literal, PathSet};
use crate::{define_fn, ConstTree, Literal, Location, PathSet};
/// Booleans exposed to Orchid
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -17,7 +17,26 @@ impl From<bool> for Boolean {
fn from(value: bool) -> Self { Self(value) }
}
define_fn! {expr=x in
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(),
}});
expr=x in
/// Compares the inner values if
///
/// - both are string,
@@ -26,34 +45,12 @@ define_fn! {expr=x in
(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(b.clone().into(), "the expected type")?,
(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())
}
// 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.
define_fn! {
/// Takes a boolean and two branches, runs the first if the bool is true, the
/// second if it's false.
IfThenElse = |x| x.downcast()
.map_err(|_| AssertionError::ext(x.clone(), "a boolean"))
.map(|b: Boolean| if b.0 {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(),
}})
}
pub fn bool(i: &Interner) -> ConstTree {
ConstTree::tree([(
i.i("bool"),

View File

@@ -6,48 +6,38 @@ use crate::foreign::ExternError;
use crate::interner::Interner;
use crate::interpreted::Clause;
use crate::parse::{float_parser, int_parser};
use crate::systems::cast_exprinst::with_lit;
use crate::systems::cast_exprinst::get_literal;
use crate::systems::AssertionError;
use crate::{define_fn, ConstTree, Literal};
define_fn! {
/// parse a number. Accepts the same syntax Orchid does.
ToFloat = |x| with_lit(x, |l| match l {
Literal::Str(s) => float_parser()
ToFloat = |x| match get_literal(x)? {
(Literal::Str(s), loc) => float_parser()
.parse(s.as_str())
.map_err(|_| AssertionError::ext(
x.clone(),
"cannot be parsed into a float"
)),
Literal::Num(n) => Ok(*n),
Literal::Uint(i) => NotNan::new(*i as f64)
.map_err(|_| AssertionError::ext(loc, "float syntax")),
(Literal::Num(n), _) => Ok(n),
(Literal::Uint(i), _) => NotNan::new(i as f64)
.map_err(|_| ArithmeticError::NaN.into_extern()),
}).map(|nn| Literal::Num(nn).into())
}
}.map(|nn| Literal::Num(nn).into());
define_fn! {
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
/// input is a number, floors it.
ToUint = |x| with_lit(x, |l| match l {
Literal::Str(s) => int_parser()
ToUint = |x| match get_literal(x)? {
(Literal::Str(s), loc) => int_parser()
.parse(s.as_str())
.map_err(|_| AssertionError::ext(
x.clone(),
"cannot be parsed into an unsigned int",
)),
Literal::Num(n) => Ok(n.floor() as u64),
Literal::Uint(i) => Ok(*i),
}).map(|u| Literal::Uint(u).into())
}
.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());
define_fn! {
/// Convert a literal to a string using Rust's conversions for floats, chars and
/// uints respectively
ToString = |x| with_lit(x, |l| 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.clone(),
})).map(Clause::from)
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),
})
}
pub fn conv(i: &Interner) -> ConstTree {

View File

@@ -18,8 +18,9 @@ struct Inspect1 {
}
impl Responder for Inspect1 {}
impl Atomic for Inspect1 {
fn as_any(&self) -> &dyn std::any::Any { self }
fn run(&self, ctx: Context) -> crate::foreign::AtomicResult {
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(),

View File

@@ -7,7 +7,7 @@ use crate::foreign::ExternError;
use crate::interpreted::TryFromExprInst;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::representations::{Literal, Primitive};
use crate::systems::cast_exprinst::with_lit;
use crate::systems::cast_exprinst::get_literal;
use crate::systems::AssertionError;
use crate::{define_fn, ConstTree, Interner};
@@ -42,12 +42,12 @@ impl Numeric {
}
}
impl TryFromExprInst for Numeric {
fn from_exi(exi: &ExprInst) -> Result<Self, Rc<dyn ExternError>> {
with_lit(exi, |l| match l {
Literal::Uint(i) => Ok(Numeric::Uint(*i)),
Literal::Num(n) => Ok(Numeric::Num(*n)),
_ => AssertionError::fail(exi.clone(), "an integer or number")?,
})
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
match get_literal(exi)? {
(Literal::Uint(i), _) => Ok(Numeric::Uint(i)),
(Literal::Num(n), _) => Ok(Numeric::Num(n)),
(_, location) => AssertionError::fail(location, "an integer or number")?,
}
}
}
@@ -69,42 +69,36 @@ define_fn! {
/// number, the output is number.
Add { a: Numeric, b: Numeric } => match (a, b) {
(Numeric::Uint(a), Numeric::Uint(b)) => {
a.checked_add(*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.into_inner() + *b as f64),
}.map(Numeric::into)
}
=> Numeric::num(*a + b as f64),
}.map(Numeric::into);
define_fn! {
/// Subtract a number from another. Always returns Number.
Subtract { a: Numeric, b: Numeric } => match (a, b) {
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(*a as f64 - *b as f64),
(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)
}
(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);
define_fn! {
/// 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)
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::num(a as f64 * *b),
}.map(Numeric::into);
define_fn! {
/// Divide a number by another. Always returns Number.
Divide { a: Numeric, b: Numeric } => {
let a: f64 = a.as_f64();
@@ -113,21 +107,19 @@ define_fn! {
return Err(ArithmeticError::DivByZero.into_extern())
}
Numeric::num(a / b).map(Numeric::into)
}
}
};
define_fn! {
/// 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)
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),
(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)
}

View File

@@ -2,8 +2,7 @@ use std::fmt::Display;
use std::rc::Rc;
use crate::foreign::ExternError;
use crate::systems::cast_exprinst::with_str;
use crate::{define_fn, ConstTree, Interner};
use crate::{define_fn, ConstTree, Interner, OrcString};
/// An unrecoverable error in Orchid land. Because Orchid is lazy, this only
/// invalidates expressions that reference the one that generated it.
@@ -19,10 +18,10 @@ impl ExternError for OrchidPanic {}
define_fn! {
/// Takes a message, returns an [ExternError] unconditionally.
Panic = |x| with_str(x, |s| {
let msg = Rc::new(s.get_string());
Panic = |x| {
let msg = Rc::new(x.downcast::<OrcString>()?.get_string());
Err(OrchidPanic(msg).into_extern())
})
}
}
pub fn panic(i: &Interner) -> ConstTree {

View File

@@ -1,4 +1,5 @@
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use crate::foreign::cps_box::{const_cps, init_cps, CPSBox};
@@ -23,31 +24,37 @@ struct SetStateCmd(State);
#[derive(Debug, Clone)]
struct GetStateCmd(State);
define_fn! { SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?))) }
define_fn! { GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?))) }
define_fn! {
SetState = |x| Ok(init_cps(2, SetStateCmd(x.downcast()?)));
GetState = |x| Ok(init_cps(2, GetStateCmd(x.downcast()?)))
}
fn new_state_handler<E>(cmd: &CPSBox<NewStateCmd>) -> Result<ExprInst, E> {
fn new_state_handler<E>(cmd: CPSBox<NewStateCmd>) -> Result<ExprInst, E> {
let (_, default, handler) = cmd.unpack2();
let state = State(Rc::new(RefCell::new(default.clone())));
Ok(call(handler.clone(), [state.atom_exi()]).wrap())
let state = State(Rc::new(RefCell::new(default)));
Ok(call(handler, [state.atom_exi()]).wrap())
}
fn set_state_handler<E>(cmd: &CPSBox<SetStateCmd>) -> Result<ExprInst, E> {
fn set_state_handler<E>(cmd: CPSBox<SetStateCmd>) -> Result<ExprInst, E> {
let (SetStateCmd(state), value, handler) = cmd.unpack2();
*state.0.as_ref().borrow_mut() = value.clone();
Ok(handler.clone())
*state.0.as_ref().borrow_mut() = value;
Ok(handler)
}
fn get_state_handler<E>(cmd: &CPSBox<GetStateCmd>) -> Result<ExprInst, E> {
fn get_state_handler<E>(cmd: CPSBox<GetStateCmd>) -> Result<ExprInst, E> {
let (GetStateCmd(state), handler) = cmd.unpack1();
Ok(call(handler.clone(), [state.0.as_ref().borrow().clone()]).wrap())
let val = match Rc::try_unwrap(state.0) {
Ok(cell) => cell.into_inner(),
Err(rc) => rc.as_ref().borrow().deref().clone(),
};
Ok(call(handler, [val]).wrap())
}
pub fn state_handlers() -> HandlerTable<'static> {
let mut handlers = HandlerTable::new();
handlers.register(new_state_handler);
handlers.register(get_state_handler);
handlers.register(set_state_handler);
handlers.register(|b| new_state_handler(*b));
handlers.register(|b| get_state_handler(*b));
handlers.register(|b| set_state_handler(*b));
handlers
}

View File

@@ -2,26 +2,37 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::interner::Interner;
use crate::representations::OrcString;
use crate::systems::cast_exprinst::with_str;
use crate::systems::codegen::{orchid_opt, tuple};
use crate::systems::RuntimeError;
use crate::utils::iter_find;
use crate::{define_fn, ConstTree, Literal};
define_fn! {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())
}
define_fn! {
pub Len = |x| Ok(Literal::Uint(
(*x.downcast::<OrcString>()?)
.graphemes(true)
.count() as u64
).into());
pub Size = |x| Ok(Literal::Uint(
(*x.downcast::<OrcString>()?)
.as_bytes()
.len() as u64
).into());
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()
);
define_fn! {expr=x in
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();
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);
let mut prefix = graphs.skip(i as usize - 1);
if prefix.next().is_none() {
RuntimeError::fail(
"Character index out of bounds".to_string(),
@@ -29,10 +40,10 @@ define_fn! {expr=x in
)
} else {
let mut count = 0;
let ret = (prefix.take(*len as usize))
let ret = (prefix.take(len as usize))
.map(|x| { count+=1; x })
.collect::<String>().into();
if count == *len {
if count == len {
Ok(Literal::Str(ret).into())
} else {
RuntimeError::fail(
@@ -42,38 +53,22 @@ define_fn! {expr=x in
}
}
}
}
}
};
define_fn! {expr=x in
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())))
}
}
};
define_fn! {expr=x in
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 a = graphs.by_ref().take(i as usize).collect::<String>();
let b = graphs.collect::<String>();
Ok(tuple(vec![a.into(), b.into()]))
}
}
define_fn! {
pub Len = |x| with_str(x, |s| {
Ok(Literal::Uint(s.graphemes(true).count() as u64).into())
})
}
define_fn! {
pub Size = |x| with_str(x, |s| {
Ok(Literal::Uint(s.as_bytes().len() as u64).into())
})
}
pub fn str(i: &Interner) -> ConstTree {
ConstTree::tree([(
i.i("str"),