forked from Orchid/orchid
opportunistic move
should be way faster now
This commit is contained in:
@@ -2,35 +2,39 @@ use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::foreign::ExternError;
|
||||
use crate::representations::interpreted::ExprInst;
|
||||
use crate::Location;
|
||||
|
||||
/// Some expectation (usually about the argument types of a function) did not
|
||||
/// hold.
|
||||
#[derive(Clone)]
|
||||
pub struct AssertionError {
|
||||
value: ExprInst,
|
||||
assertion: &'static str,
|
||||
location: Location,
|
||||
message: &'static str,
|
||||
}
|
||||
|
||||
impl AssertionError {
|
||||
/// Construct, upcast and wrap in a Result that never succeeds for easy
|
||||
/// short-circuiting
|
||||
pub fn fail<T>(
|
||||
value: ExprInst,
|
||||
assertion: &'static str,
|
||||
location: Location,
|
||||
message: &'static str,
|
||||
) -> Result<T, Rc<dyn ExternError>> {
|
||||
return Err(Self { value, assertion }.into_extern());
|
||||
return Err(Self { location, message }.into_extern());
|
||||
}
|
||||
|
||||
/// Construct and upcast to [ExternError]
|
||||
pub fn ext(value: ExprInst, assertion: &'static str) -> Rc<dyn ExternError> {
|
||||
return Self { value, assertion }.into_extern();
|
||||
pub fn ext(location: Location, message: &'static str) -> Rc<dyn ExternError> {
|
||||
return Self { location, message }.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)
|
||||
write!(f, "Error: expected {}", self.message)?;
|
||||
if self.location != Location::Unknown {
|
||||
write!(f, " at {}", self.location)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,7 @@ define_fn! {expr=x in
|
||||
SetTimer {
|
||||
recurring: Boolean,
|
||||
duration: NotNan<f64>
|
||||
} => Ok(init_cps(2, Timer{
|
||||
recurring: *recurring,
|
||||
duration: *duration
|
||||
}))
|
||||
} => Ok(init_cps(2, Timer{ recurring, duration }))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -125,27 +122,27 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
|
||||
let polly = Rc::new(RefCell::new(poller));
|
||||
handler_table.register({
|
||||
let polly = polly.clone();
|
||||
move |t: &CPSBox<Timer>| {
|
||||
move |t: Box<CPSBox<Timer>>| {
|
||||
let mut polly = polly.borrow_mut();
|
||||
let (timeout, action, cont) = t.unpack2();
|
||||
let duration = Duration::from_secs_f64(*timeout.duration);
|
||||
let cancel_timer = if timeout.recurring.0 {
|
||||
CancelTimer(Rc::new(polly.set_interval(duration, action.clone())))
|
||||
CancelTimer(Rc::new(polly.set_interval(duration, action)))
|
||||
} else {
|
||||
CancelTimer(Rc::new(polly.set_timeout(duration, action.clone())))
|
||||
CancelTimer(Rc::new(polly.set_timeout(duration, action)))
|
||||
};
|
||||
Ok(call(cont.clone(), [init_cps(1, cancel_timer).wrap()]).wrap())
|
||||
Ok(call(cont, [init_cps(1, cancel_timer).wrap()]).wrap())
|
||||
}
|
||||
});
|
||||
handler_table.register(move |t: &CPSBox<CancelTimer>| {
|
||||
handler_table.register(move |t: Box<CPSBox<CancelTimer>>| {
|
||||
let (command, cont) = t.unpack1();
|
||||
command.0.as_ref()();
|
||||
Ok(cont.clone())
|
||||
Ok(cont)
|
||||
});
|
||||
handler_table.register({
|
||||
let polly = polly.clone();
|
||||
let mut microtasks = VecDeque::new();
|
||||
move |_: &Yield| {
|
||||
move |_: Box<Yield>| {
|
||||
if let Some(expr) = microtasks.pop_front() {
|
||||
return Ok(expr);
|
||||
}
|
||||
|
||||
@@ -5,88 +5,61 @@ use std::rc::Rc;
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use super::assertion_error::AssertionError;
|
||||
use crate::foreign::{Atom, Atomic, ExternError};
|
||||
use crate::interpreted::{Clause, TryFromExprInst};
|
||||
use crate::foreign::{Atom, ExternError};
|
||||
use crate::interpreted::{Clause, Expr, TryFromExprInst};
|
||||
use crate::representations::interpreted::ExprInst;
|
||||
use crate::representations::{Literal, OrcString};
|
||||
use crate::Primitive;
|
||||
use crate::{Location, Primitive};
|
||||
|
||||
/// 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(&OrcString) -> Result<T, Rc<dyn ExternError>>,
|
||||
) -> Result<T, Rc<dyn ExternError>> {
|
||||
with_lit(x, |l| match l {
|
||||
Literal::Str(s) => predicate(s),
|
||||
_ => AssertionError::fail(x.clone(), "a string"),
|
||||
})
|
||||
}
|
||||
|
||||
/// If the [ExprInst] stores an [Atom], maps the predicate over it, otherwise
|
||||
/// raises a runtime error.
|
||||
pub fn with_atom<T>(
|
||||
x: &ExprInst,
|
||||
predicate: impl FnOnce(&Atom) -> Result<T, Rc<dyn ExternError>>,
|
||||
) -> Result<T, Rc<dyn ExternError>> {
|
||||
x.inspect(|c| match c {
|
||||
Clause::P(Primitive::Atom(a)) => predicate(a),
|
||||
_ => AssertionError::fail(x.clone(), "an atom"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to cast the [ExprInst] into the specified atom type. Throws an
|
||||
/// assertion error if unsuccessful, or calls the provided function on the
|
||||
/// extracted atomic type.
|
||||
pub fn with_atomic<T: Atomic, U>(
|
||||
x: &ExprInst,
|
||||
inexact_typename: &'static str,
|
||||
predicate: impl FnOnce(&T) -> Result<U, Rc<dyn ExternError>>,
|
||||
) -> Result<U, Rc<dyn ExternError>> {
|
||||
with_atom(x, |a| match a.try_cast() {
|
||||
Some(atomic) => predicate(atomic),
|
||||
_ => AssertionError::fail(x.clone(), inexact_typename),
|
||||
})
|
||||
/// [ExprInst::get_literal] except the error is mapped to an [ExternError]
|
||||
pub fn get_literal(
|
||||
exi: ExprInst,
|
||||
) -> Result<(Literal, Location), Rc<dyn ExternError>> {
|
||||
(exi.get_literal())
|
||||
.map_err(|exi| AssertionError::ext(exi.location(), "literal"))
|
||||
}
|
||||
|
||||
// ######## Automatically ########
|
||||
|
||||
impl TryFromExprInst for Literal {
|
||||
fn from_exi(exi: &ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
with_lit(exi, |l| Ok(l.clone()))
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
get_literal(exi).map(|(l, _)| l)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExprInst for OrcString {
|
||||
fn from_exi(exi: &ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
with_str(exi, |s| Ok(s.clone()))
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
match get_literal(exi)? {
|
||||
(Literal::Str(s), _) => Ok(s),
|
||||
(_, location) => AssertionError::fail(location, "string"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExprInst for u64 {
|
||||
fn from_exi(exi: &ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
with_lit(exi, |l| match l {
|
||||
Literal::Uint(u) => Ok(*u),
|
||||
_ => AssertionError::fail(exi.clone(), "an uint"),
|
||||
})
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
match get_literal(exi)? {
|
||||
(Literal::Uint(u), _) => Ok(u),
|
||||
(_, location) => AssertionError::fail(location, "uint"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExprInst for NotNan<f64> {
|
||||
fn from_exi(exi: &ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
with_lit(exi, |l| match l {
|
||||
Literal::Num(n) => Ok(*n),
|
||||
_ => AssertionError::fail(exi.clone(), "a float"),
|
||||
})
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
match get_literal(exi)? {
|
||||
(Literal::Num(n), _) => Ok(n),
|
||||
(_, location) => AssertionError::fail(location, "float"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExprInst for Atom {
|
||||
fn from_exi(exi: ExprInst) -> Result<Self, Rc<dyn ExternError>> {
|
||||
let Expr { clause, location } = exi.expr_val();
|
||||
match clause {
|
||||
Clause::P(Primitive::Atom(a)) => Ok(a),
|
||||
_ => AssertionError::fail(location, "atom"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/systems/directfs/commands.rs
Normal file
19
src/systems/directfs/commands.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use crate::foreign::cps_box::init_cps;
|
||||
use crate::foreign::InertAtomic;
|
||||
use crate::systems::asynch::MessagePort;
|
||||
use crate::systems::scheduler::SeqScheduler;
|
||||
use crate::{define_fn, OrcString};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ReadFile(OrcString);
|
||||
impl InertAtomic for ReadFile {
|
||||
fn type_str() -> &'static str { "a readfile command" }
|
||||
}
|
||||
|
||||
pub fn read_file(port: MessagePort, cmd: ReadFile) -> Vec<ExprInst> {
|
||||
let new_file =
|
||||
}
|
||||
|
||||
define_fn! {
|
||||
pub OpenFileRead = |x| Ok(init_cps(3, ReadFile(x.downcast()?)))
|
||||
}
|
||||
2
src/systems/directfs/mod.rs
Normal file
2
src/systems/directfs/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
mod commands;
|
||||
@@ -13,63 +13,49 @@ define_fn! {
|
||||
ReadString = |x| Ok(init_cps(3, IOCmdHandlePack{
|
||||
cmd: ReadCmd::RStr(SRead::All),
|
||||
handle: x.downcast()?
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
ReadLine = |x| Ok(init_cps(3, IOCmdHandlePack{
|
||||
cmd: ReadCmd::RStr(SRead::Line),
|
||||
handle: x.downcast()?
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
ReadBin = |x| Ok(init_cps(3, IOCmdHandlePack{
|
||||
cmd: ReadCmd::RBytes(BRead::All),
|
||||
handle: x.downcast()?
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
ReadBytes {
|
||||
stream: SourceHandle,
|
||||
n: u64
|
||||
} => Ok(init_cps(3, IOCmdHandlePack{
|
||||
cmd: ReadCmd::RBytes(BRead::N((*n).try_into().unwrap())),
|
||||
cmd: ReadCmd::RBytes(BRead::N(n.try_into().unwrap())),
|
||||
handle: stream.clone()
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
ReadUntil {
|
||||
stream: SourceHandle,
|
||||
pattern: u64
|
||||
} => {
|
||||
let delim = (*pattern).try_into().map_err(|_| RuntimeError::ext(
|
||||
let delim = pattern.try_into().map_err(|_| RuntimeError::ext(
|
||||
"greater than 255".to_string(),
|
||||
"converting number to byte"
|
||||
))?;
|
||||
Ok(init_cps(3, IOCmdHandlePack{
|
||||
cmd: ReadCmd::RBytes(BRead::Until(delim)),
|
||||
handle: stream.clone()
|
||||
handle: stream
|
||||
}))
|
||||
}
|
||||
}
|
||||
define_fn! {
|
||||
};
|
||||
WriteStr {
|
||||
stream: SinkHandle,
|
||||
string: OrcString
|
||||
} => Ok(init_cps(3, IOCmdHandlePack {
|
||||
cmd: WriteCmd::WStr(string.get_string()),
|
||||
handle: stream.clone(),
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
WriteBin {
|
||||
stream: SinkHandle,
|
||||
bytes: Binary
|
||||
} => Ok(init_cps(3, IOCmdHandlePack {
|
||||
cmd: WriteCmd::WBytes(bytes.clone()),
|
||||
cmd: WriteCmd::WBytes(bytes),
|
||||
handle: stream.clone(),
|
||||
}))
|
||||
}
|
||||
define_fn! {
|
||||
}));
|
||||
Flush = |x| Ok(init_cps(3, IOCmdHandlePack {
|
||||
cmd: WriteCmd::Flush,
|
||||
handle: x.downcast()?
|
||||
|
||||
@@ -56,30 +56,29 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
||||
fn into_system(self, i: &crate::Interner) -> crate::facade::System<'static> {
|
||||
let scheduler = self.scheduler.clone();
|
||||
let mut handlers = HandlerTable::new();
|
||||
handlers.register(move |cps: &CPSBox<IOCmdHandlePack<ReadCmd>>| {
|
||||
handlers.register(move |cps: Box<CPSBox<IOCmdHandlePack<ReadCmd>>>| {
|
||||
let (IOCmdHandlePack { cmd, handle }, succ, fail, tail) = cps.unpack3();
|
||||
let (cmd, succ1, fail1) = (*cmd, succ.clone(), fail.clone());
|
||||
let fail1 = fail.clone();
|
||||
let result = scheduler.schedule(
|
||||
handle.clone(),
|
||||
handle,
|
||||
move |mut stream, cancel| {
|
||||
let ret = cmd.execute(&mut stream, cancel);
|
||||
(stream, ret)
|
||||
},
|
||||
move |stream, res, _cancel| (stream, res.dispatch(succ1, fail1)),
|
||||
move |stream, res, _cancel| (stream, res.dispatch(succ, fail1)),
|
||||
|stream| (stream, Vec::new()),
|
||||
);
|
||||
match result {
|
||||
Ok(cancel) =>
|
||||
Ok(call(tail.clone(), vec![init_cps(1, cancel).wrap()]).wrap()),
|
||||
Err(e) => Ok(call(fail.clone(), vec![e.atom_exi()]).wrap()),
|
||||
Ok(cancel) => Ok(call(tail, vec![init_cps(1, cancel).wrap()]).wrap()),
|
||||
Err(e) => Ok(call(fail, vec![e.atom_exi()]).wrap()),
|
||||
}
|
||||
});
|
||||
let scheduler = self.scheduler.clone();
|
||||
handlers.register(move |cps: &CPSBox<IOCmdHandlePack<WriteCmd>>| {
|
||||
handlers.register(move |cps: Box<CPSBox<IOCmdHandlePack<WriteCmd>>>| {
|
||||
let (IOCmdHandlePack { cmd, handle }, succ, fail, tail) = cps.unpack3();
|
||||
let (cmd, succ1, fail1) = (cmd.clone(), succ.clone(), fail.clone());
|
||||
let (succ1, fail1) = (succ, fail.clone());
|
||||
let result = scheduler.schedule(
|
||||
handle.clone(),
|
||||
handle,
|
||||
move |mut stream, cancel| {
|
||||
let ret = cmd.execute(&mut stream, cancel);
|
||||
(stream, ret)
|
||||
@@ -88,9 +87,8 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
||||
|stream| (stream, Vec::new()),
|
||||
);
|
||||
match result {
|
||||
Ok(cancel) =>
|
||||
Ok(call(tail.clone(), vec![init_cps(1, cancel).wrap()]).wrap()),
|
||||
Err(e) => Ok(call(fail.clone(), vec![e.atom_exi()]).wrap()),
|
||||
Ok(cancel) => Ok(call(tail, vec![init_cps(1, cancel).wrap()]).wrap()),
|
||||
Err(e) => Ok(call(fail, vec![e.atom_exi()]).wrap()),
|
||||
}
|
||||
});
|
||||
let streams = self.global_streams.into_iter().map(|(n, stream)| {
|
||||
|
||||
@@ -3,7 +3,7 @@ mod assertion_error;
|
||||
pub mod asynch;
|
||||
pub mod cast_exprinst;
|
||||
pub mod codegen;
|
||||
mod directfs;
|
||||
// mod directfs;
|
||||
pub mod io;
|
||||
mod runtime_error;
|
||||
pub mod scheduler;
|
||||
|
||||
@@ -15,10 +15,9 @@ use crate::foreign::InertAtomic;
|
||||
use crate::interpreted::ExprInst;
|
||||
use crate::interpreter::HandlerTable;
|
||||
use crate::systems::asynch::{AsynchSystem, MessagePort};
|
||||
use crate::systems::cast_exprinst::with_atom;
|
||||
use crate::systems::stl::Boolean;
|
||||
use crate::systems::AssertionError;
|
||||
use crate::utils::ddispatch::{request, Request};
|
||||
use crate::utils::ddispatch::Request;
|
||||
use crate::utils::thread_pool::ThreadPool;
|
||||
use crate::utils::{take_with_output, unwrap_or, IdMap};
|
||||
use crate::{define_fn, ConstTree};
|
||||
@@ -117,12 +116,6 @@ impl Debug for TakeCmd {
|
||||
write!(f, "A command to drop a shared resource")
|
||||
}
|
||||
}
|
||||
define_fn! {
|
||||
pub TakeAndDrop = |x| with_atom(x, |a| match request(a.0.as_ref()) {
|
||||
Some(t) => Ok(init_cps::<TakeCmd>(1, t)),
|
||||
None => AssertionError::fail(x.clone(), "a SharedHandle"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Error produced when an operation is scheduled or a seal placed on a resource
|
||||
/// which is either already sealed or taken.
|
||||
@@ -135,6 +128,13 @@ impl InertAtomic for SealedOrTaken {
|
||||
}
|
||||
|
||||
define_fn! {
|
||||
pub TakeAndDrop = |x| {
|
||||
let location = x.location();
|
||||
match x.request() {
|
||||
Some(t) => Ok(init_cps::<TakeCmd>(1, t)),
|
||||
None => AssertionError::fail(location, "SharedHandle"),
|
||||
}
|
||||
};
|
||||
IsTakenError = |x| {
|
||||
Ok(Boolean(x.downcast::<SealedOrTaken>().is_ok()).atom_cls())
|
||||
}
|
||||
@@ -296,15 +296,15 @@ impl SeqScheduler {
|
||||
impl IntoSystem<'static> for SeqScheduler {
|
||||
fn into_system(self, i: &crate::Interner) -> crate::facade::System<'static> {
|
||||
let mut handlers = HandlerTable::new();
|
||||
handlers.register(|cmd: &CPSBox<Canceller>| {
|
||||
handlers.register(|cmd: Box<CPSBox<Canceller>>| {
|
||||
let (canceller, cont) = cmd.unpack1();
|
||||
canceller.cancel();
|
||||
Ok(cont.clone())
|
||||
Ok(cont)
|
||||
});
|
||||
handlers.register(move |cmd: &CPSBox<TakeCmd>| {
|
||||
handlers.register(move |cmd: Box<CPSBox<TakeCmd>>| {
|
||||
let (TakeCmd(cb), cont) = cmd.unpack1();
|
||||
cb(self.clone());
|
||||
Ok(cont.clone())
|
||||
Ok(cont)
|
||||
});
|
||||
System {
|
||||
name: ["system", "scheduler"].into_iter().map_into().collect(),
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user