Removed foreign macros

Converted the function integration to use template
metaprogramming instead of macros.
This commit is contained in:
2023-09-22 23:17:54 +01:00
parent 7396078304
commit ba0b155ebd
45 changed files with 854 additions and 1126 deletions

View File

@@ -19,7 +19,7 @@ impl AssertionError {
location: Location,
message: &'static str,
) -> Result<T, Rc<dyn ExternError>> {
return Err(Self { location, message }.into_extern());
return Err(Self::ext(location, message));
}
/// Construct and upcast to [ExternError]

View File

@@ -1,3 +1,5 @@
import std::panic
export const block_on := \action.\cont. (
action cont
(\e.panic "unwrapped asynch call")

View File

@@ -12,26 +12,24 @@ use rust_embed::RustEmbed;
use crate::facade::{IntoSystem, System};
use crate::foreign::cps_box::{init_cps, CPSBox};
use crate::foreign::{Atomic, ExternError, InertAtomic};
use crate::interpreted::ExprInst;
use crate::foreign::{xfn_2ary, Atomic, ExternError, InertAtomic, XfnResult};
use crate::interpreted::{Clause, ExprInst};
use crate::interpreter::HandlerTable;
use crate::pipeline::file_loader::embed_to_map;
use crate::systems::codegen::call;
use crate::systems::stl::Boolean;
use crate::utils::poller::{PollEvent, Poller};
use crate::utils::unwrap_or;
use crate::{define_fn, ConstTree, Interner};
use crate::{ConstTree, Interner};
#[derive(Debug, Clone)]
struct Timer {
recurring: Boolean,
duration: NotNan<f64>,
delay: NotNan<f64>,
}
define_fn! {expr=x in
SetTimer {
recurring: Boolean,
duration: NotNan<f64>
} => Ok(init_cps(2, Timer{ recurring, duration }))
pub fn set_timer(recurring: Boolean, delay: NotNan<f64>) -> XfnResult<Clause> {
Ok(init_cps(2, Timer { recurring, delay }))
}
#[derive(Clone)]
@@ -135,7 +133,7 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
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 duration = Duration::from_secs_f64(*timeout.delay);
let cancel_timer = if timeout.recurring.0 {
CancelTimer(Rc::new(polly.set_interval(duration, action)))
} else {
@@ -186,7 +184,7 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
constants: ConstTree::namespace(
[i.i("system"), i.i("async")],
ConstTree::tree([
(i.i("set_timer"), ConstTree::xfn(SetTimer)),
(i.i("set_timer"), ConstTree::xfn(xfn_2ary(set_timer))),
(i.i("yield"), ConstTree::atom(Yield)),
]),
)

View File

@@ -15,8 +15,10 @@ use crate::{Location, Primitive};
pub fn get_literal(
exi: ExprInst,
) -> Result<(Literal, Location), Rc<dyn ExternError>> {
(exi.get_literal())
.map_err(|exi| AssertionError::ext(exi.location(), "literal"))
(exi.get_literal()).map_err(|exi| {
eprintln!("failed to get literal from {:?}", exi.expr().clause);
AssertionError::ext(exi.location(), "literal")
})
}
// ######## Automatically ########

View File

@@ -1,38 +1,27 @@
//! Utilities for generating Orchid code in Rust
use std::rc::Rc;
use crate::interpreted::{Clause, ExprInst};
use crate::utils::unwrap_or;
use crate::{PathSet, Side};
/// Convert a rust Option into an Orchid Option
pub fn orchid_opt(x: Option<ExprInst>) -> Clause {
if let Some(x) = x { some(x) } else { none() }
}
/// Constructs an instance of the orchid value Some wrapping the given
/// [ExprInst].
///
/// Takes two expressions and calls the second with the given data
fn some(x: ExprInst) -> Clause {
Clause::Lambda {
args: None,
body: Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![Side::Left]), next: None }),
body: Clause::Apply { f: Clause::LambdaArg.wrap(), x }.wrap(),
}
.wrap(),
pub fn opt(x: Option<ExprInst>) -> Clause {
match x {
Some(x) => Clause::constfn(Clause::lambda(
PathSet::end([Side::Left]),
Clause::Apply { f: Clause::LambdaArg.wrap(), x },
)),
None => Clause::pick(Clause::constfn(Clause::LambdaArg)),
}
}
/// Constructs an instance of the orchid value None
///
/// Takes two expressions and returns the first
fn none() -> Clause {
Clause::Lambda {
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
body: Clause::Lambda { args: None, body: Clause::LambdaArg.wrap() }.wrap(),
/// Convert a rust Result into an Orchid Result
pub fn res(x: Result<ExprInst, ExprInst>) -> Clause {
let mk_body = |x| Clause::Apply { f: Clause::LambdaArg.wrap(), x };
let pick_fn = |b| Clause::lambda(PathSet::end([Side::Left]), b);
match x {
Ok(x) => Clause::constfn(pick_fn(mk_body(x))),
Err(x) => pick_fn(Clause::constfn(mk_body(x))),
}
}
@@ -40,18 +29,17 @@ fn none() -> Clause {
/// values to the callback in order.
pub fn tuple(data: impl IntoIterator<Item = ExprInst>) -> Clause {
let mut steps = Vec::new();
let mut body = Clause::LambdaArg.wrap();
let mut body = Clause::LambdaArg;
for x in data.into_iter() {
steps.push(Side::Left);
body = Clause::Apply { f: body, x }.wrap()
body = Clause::Apply { f: body.wrap(), x }
}
let path_set = PathSet { next: None, steps: Rc::new(steps) };
Clause::Lambda { args: Some(path_set), body }
Clause::lambda(PathSet::end(steps), body)
}
#[cfg(test)]
mod test {
use crate::systems::codegen::tuple;
use crate::systems::codegen::tuple;
#[test]
fn tuple_printer() {
@@ -69,5 +57,5 @@ pub fn call(f: ExprInst, args: impl IntoIterator<Item = ExprInst>) -> Clause {
/// Build an Orchid list from a Rust iterator
pub fn list(items: impl IntoIterator<Item = ExprInst>) -> Clause {
let mut iter = items.into_iter();
orchid_opt(iter.next().map(|it| tuple([it, list(iter).wrap()]).wrap()))
opt(iter.next().map(|it| tuple([it, list(iter).wrap()]).wrap()))
}

View File

@@ -1,39 +1,63 @@
use std::ffi::OsString;
use std::fs::File;
use std::io::{BufReader, Read, Write};
use std::path::Path;
use std::path::{Path, PathBuf};
use hashbrown::HashMap;
use itertools::Itertools;
use super::osstring::os_string_lib;
use crate::ddispatch::Responder;
use crate::facade::{IntoSystem, System};
use crate::foreign::cps_box::{init_cps, CPSBox};
use crate::foreign::{Atomic, InertAtomic};
use crate::foreign::{
xfn_1ary, xfn_2ary, Atomic, AtomicReturn, InertAtomic, XfnResult,
};
use crate::interpreted::{Clause, ExprInst};
use crate::interpreter::HandlerTable;
use crate::systems::codegen::{call, list, orchid_opt, tuple};
use crate::systems::io::wrap_io_error;
use crate::systems::codegen::{call, list, opt, tuple};
use crate::systems::io::{wrap_io_error, Source};
use crate::systems::scheduler::{SeqScheduler, SharedHandle};
use crate::systems::stl::Boolean;
use crate::systems::RuntimeError;
use crate::utils::unwrap_or;
use crate::{define_fn, ConstTree, OrcString};
use crate::ConstTree;
#[derive(Debug, Clone)]
pub struct ReadFileCmd(OrcString);
pub struct CurrentDir;
impl Responder for CurrentDir {}
impl Atomic for CurrentDir {
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: crate::interpreter::Context,
) -> crate::foreign::AtomicResult {
let cwd = std::env::current_dir()
.map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
Ok(AtomicReturn {
clause: cwd.into_os_string().atom_cls(),
gas: ctx.gas.map(|g| g - 1),
inert: false,
})
}
}
#[derive(Debug, Clone)]
pub struct ReadFileCmd(OsString);
impl InertAtomic for ReadFileCmd {
fn type_str() -> &'static str { "readfile command" }
}
#[derive(Debug, Clone)]
pub struct ReadDirCmd(OrcString);
pub struct ReadDirCmd(OsString);
impl InertAtomic for ReadDirCmd {
fn type_str() -> &'static str { "readdir command" }
}
#[derive(Debug, Clone)]
pub struct WriteFile {
name: OrcString,
name: OsString,
append: bool,
}
impl InertAtomic for WriteFile {
@@ -43,15 +67,14 @@ impl InertAtomic for WriteFile {
#[must_use]
fn read_file(sched: &SeqScheduler, cmd: CPSBox<ReadFileCmd>) -> ExprInst {
let (ReadFileCmd(name), succ, fail, cont) = cmd.unpack3();
let name = name.get_string();
let cancel = sched.run_orphan(
move |_| File::open(name),
|file, _| match file {
Err(e) => vec![call(fail, [wrap_io_error(e)]).wrap()],
Ok(f) => {
let source =
SharedHandle::wrap(BufReader::new(Box::new(f) as Box<dyn Read>));
vec![call(succ, [source.atom_exi()]).wrap()]
let source: Source =
BufReader::new(Box::new(f) as Box<dyn Read + Send>);
vec![call(succ, [SharedHandle::wrap(source).atom_exi()]).wrap()]
},
},
);
@@ -61,7 +84,6 @@ fn read_file(sched: &SeqScheduler, cmd: CPSBox<ReadFileCmd>) -> ExprInst {
#[must_use]
fn read_dir(sched: &SeqScheduler, cmd: CPSBox<ReadDirCmd>) -> ExprInst {
let (ReadDirCmd(name), succ, fail, cont) = cmd.unpack3();
let name = name.get_string();
let cancel = sched.run_orphan(
move |_| {
Path::new(&name)
@@ -73,9 +95,7 @@ fn read_dir(sched: &SeqScheduler, cmd: CPSBox<ReadDirCmd>) -> ExprInst {
Err(e) => vec![call(fail, [wrap_io_error(e)]).wrap()],
Ok(os_namev) => {
let converted = (os_namev.into_iter())
.map(|(n, d)| {
Ok(tuple([os_str_cls(n)?.wrap(), Boolean(d).atom_exi()]).wrap())
})
.map(|(n, d)| Ok(tuple([n.atom_exi(), Boolean(d).atom_exi()]).wrap()))
.collect::<Result<Vec<_>, Clause>>();
match converted {
Err(e) => vec![call(fail, [e.wrap()]).wrap()],
@@ -90,7 +110,6 @@ fn read_dir(sched: &SeqScheduler, cmd: CPSBox<ReadDirCmd>) -> ExprInst {
#[must_use]
pub fn write_file(sched: &SeqScheduler, cmd: CPSBox<WriteFile>) -> ExprInst {
let (WriteFile { name, append }, succ, fail, cont) = cmd.unpack3();
let name = name.get_string();
let cancel = sched.run_orphan(
move |_| File::options().write(true).append(append).open(name),
|file, _| match file {
@@ -104,61 +123,38 @@ pub fn write_file(sched: &SeqScheduler, cmd: CPSBox<WriteFile>) -> ExprInst {
call(cont, [init_cps(1, cancel).wrap()]).wrap()
}
#[derive(Debug, Clone)]
pub struct InvalidString(OsString);
impl InertAtomic for InvalidString {
fn type_str() -> &'static str { "invalidstring error" }
pub fn open_file_read_cmd(name: OsString) -> XfnResult<Clause> {
Ok(init_cps(3, ReadFileCmd(name)))
}
fn os_str_cls(str: OsString) -> Result<Clause, Clause> {
(str.into_string())
.map_err(|e| InvalidString(e).atom_cls())
.map(|s| OrcString::from(s).cls())
pub fn read_dir_cmd(name: OsString) -> XfnResult<Clause> {
Ok(init_cps(3, ReadDirCmd(name)))
}
define_fn! {
pub IsInvalidString = |x| {
Ok(Boolean(x.downcast::<InvalidString>().is_ok()).atom_cls())
};
pub OpenFileRead = |x| Ok(init_cps(3, ReadFileCmd(x.downcast()?)));
pub ReadDir = |x| Ok(init_cps(3, ReadDirCmd(x.downcast()?)));
pub OpenFileWrite = |x| {
Ok(init_cps(3, WriteFile{ name: x.downcast()?, append: false }))
};
pub OpenFileAppend = |x| {
Ok(init_cps(3, WriteFile{ name: x.downcast()?, append: true }))
};
pub fn open_file_write_cmd(name: OsString) -> XfnResult<Clause> {
Ok(init_cps(3, WriteFile { name, append: false }))
}
pub JoinPaths { root: OrcString, sub: OrcString } => {
let res = Path::new(root.as_str())
.join(sub.as_str())
.into_os_string();
os_str_cls(res.clone()).map_err(|_| RuntimeError::ext(
format!("result {res:?} contains illegal characters"),
"joining paths"
))
};
pub PopPath = |x| {
eprintln!("argument is {x}");
let arg = x.downcast::<OrcString>()?;
let full_path = Path::new(arg.as_str());
let parent = unwrap_or! {full_path.parent(); {
return Ok(orchid_opt(None))
}};
let sub = unwrap_or! {full_path.file_name(); {
return Ok(orchid_opt(None))
}};
Ok(orchid_opt(Some(tuple(
[parent.as_os_str(), sub]
.into_iter()
.map(|s| os_str_cls(s.to_owned()).map_err(|_| RuntimeError::ext(
format!("Result {s:?} contains illegal characters"),
"splitting a path"
)))
.map_ok(Clause::wrap)
.collect::<Result<Vec<_>, _>>()?
).wrap())))
}
pub fn open_file_append_cmd(name: OsString) -> XfnResult<Clause> {
Ok(init_cps(3, WriteFile { name, append: true }))
}
pub fn join_paths(root: OsString, sub: OsString) -> XfnResult<OsString> {
let mut path = PathBuf::from(root);
path.push(sub);
Ok(path.into_os_string())
}
pub fn pop_path(path: OsString) -> XfnResult<Clause> {
let mut path = PathBuf::from(path);
let sub = unwrap_or! {path.file_name(); {
return Ok(opt(None))
}}
.to_owned();
debug_assert!(path.pop(), "file_name above returned Some");
Ok(opt(Some(
tuple([path.into_os_string().atom_exi(), sub.atom_exi()]).wrap(),
)))
}
/// A rudimentary system to read and write files.
@@ -187,14 +183,14 @@ impl IntoSystem<'static> for DirectFS {
constants: ConstTree::namespace(
[i.i("system"), i.i("directfs")],
ConstTree::tree([
(i.i("is_invalid_string"), ConstTree::xfn(IsInvalidString)),
(i.i("readfile"), ConstTree::xfn(OpenFileRead)),
(i.i("readdir"), ConstTree::xfn(ReadDir)),
(i.i("writefile"), ConstTree::xfn(OpenFileWrite)),
(i.i("appendfile"), ConstTree::xfn(OpenFileAppend)),
(i.i("join_paths"), ConstTree::xfn(JoinPaths)),
(i.i("pop_path"), ConstTree::xfn(PopPath)),
]),
(i.i("read_file"), ConstTree::xfn(xfn_1ary(open_file_read_cmd))),
(i.i("read_dir"), ConstTree::xfn(xfn_1ary(read_dir_cmd))),
(i.i("write_file"), ConstTree::xfn(xfn_1ary(open_file_write_cmd))),
(i.i("append_file"), ConstTree::xfn(xfn_1ary(open_file_append_cmd))),
(i.i("join_paths"), ConstTree::xfn(xfn_2ary(join_paths))),
(i.i("pop_path"), ConstTree::xfn(xfn_1ary(pop_path))),
(i.i("cwd"), ConstTree::atom(CurrentDir)),
]) + os_string_lib(i),
)
.unwrap_tree(),
handlers,

View File

@@ -1,5 +1,6 @@
//! A rudimentary system exposing methods for Orchid to interact with the file
//! system. All paths are strings.
mod commands;
mod osstring;
pub use commands::DirectFS;

View File

@@ -0,0 +1,28 @@
use std::ffi::OsString;
use crate::foreign::{xfn_1ary, InertAtomic, XfnResult};
use crate::{ConstTree, Interner, OrcString};
impl InertAtomic for OsString {
fn type_str() -> &'static str { "OsString" }
}
pub fn os_to_string(os: OsString) -> XfnResult<Result<String, OsString>> {
Ok(os.into_string())
}
pub fn string_to_os(str: OrcString) -> XfnResult<OsString> {
Ok(str.get_string().into())
}
pub fn os_print(os: OsString) -> XfnResult<String> {
Ok(os.into_string().unwrap_or_else(|e| e.to_string_lossy().to_string()))
}
pub fn os_string_lib(i: &Interner) -> ConstTree {
ConstTree::tree([
(i.i("os_to_string"), ConstTree::xfn(xfn_1ary(os_to_string))),
(i.i("string_to_os"), ConstTree::xfn(xfn_1ary(string_to_os))),
(i.i("os_print"), ConstTree::xfn(xfn_1ary(os_print))),
])
}

View File

@@ -1,60 +1,47 @@
use super::flow::IOCmdHandlePack;
use super::instances::{
BRead, ReadCmd, SRead, WriteCmd, Sink, Source,
};
use super::instances::{BRead, ReadCmd, SRead, Sink, Source, WriteCmd};
use crate::foreign::cps_box::init_cps;
use crate::foreign::{Atom, Atomic};
use crate::foreign::{xfn_1ary, xfn_2ary, Atom, Atomic, XfnResult};
use crate::interpreted::Clause;
use crate::representations::OrcString;
use crate::systems::scheduler::SharedHandle;
use crate::systems::stl::Binary;
use crate::systems::RuntimeError;
use crate::{ast, define_fn, ConstTree, Interner, Primitive};
use crate::{ast, ConstTree, Interner, Primitive};
define_fn! {
ReadString = |x| Ok(init_cps(3, IOCmdHandlePack{
cmd: ReadCmd::RStr(SRead::All),
handle: x.downcast()?
}));
ReadLine = |x| Ok(init_cps(3, IOCmdHandlePack{
cmd: ReadCmd::RStr(SRead::Line),
handle: x.downcast()?
}));
ReadBin = |x| Ok(init_cps(3, IOCmdHandlePack{
cmd: ReadCmd::RBytes(BRead::All),
handle: x.downcast()?
}));
ReadBytes { stream: SharedHandle<Source>, n: u64 } => {
Ok(init_cps(3, IOCmdHandlePack{
cmd: ReadCmd::RBytes(BRead::N(n.try_into().unwrap())),
handle: stream.clone()
}))
};
ReadUntil { stream: SharedHandle<Source>, pattern: u64 } => {
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
}))
};
WriteStr { stream: SharedHandle<Sink>, string: OrcString } => {
Ok(init_cps(3, IOCmdHandlePack {
cmd: WriteCmd::WStr(string.get_string()),
handle: stream.clone(),
}))
};
WriteBin { stream: SharedHandle<Sink>, bytes: Binary } => {
Ok(init_cps(3, IOCmdHandlePack {
cmd: WriteCmd::WBytes(bytes),
handle: stream.clone(),
}))
};
Flush = |x| Ok(init_cps(3, IOCmdHandlePack {
cmd: WriteCmd::Flush,
handle: x.downcast()?
}))
type WriteHandle = SharedHandle<Sink>;
type ReadHandle = SharedHandle<Source>;
pub fn read_string(handle: ReadHandle) -> XfnResult<Clause> {
Ok(init_cps(3, IOCmdHandlePack { handle, cmd: ReadCmd::RStr(SRead::All) }))
}
pub fn read_line(handle: ReadHandle) -> XfnResult<Clause> {
Ok(init_cps(3, IOCmdHandlePack { handle, cmd: ReadCmd::RStr(SRead::Line) }))
}
pub fn read_bin(handle: ReadHandle) -> XfnResult<Clause> {
Ok(init_cps(3, IOCmdHandlePack { handle, cmd: ReadCmd::RBytes(BRead::All) }))
}
pub fn read_bytes(handle: ReadHandle, n: u64) -> XfnResult<Clause> {
let cmd = ReadCmd::RBytes(BRead::N(n.try_into().unwrap()));
Ok(init_cps(3, IOCmdHandlePack { cmd, handle }))
}
pub fn read_until(handle: ReadHandle, pattern: u64) -> XfnResult<Clause> {
let delim = pattern.try_into().map_err(|_| {
let msg = "greater than 255".to_string();
RuntimeError::ext(msg, "converting number to byte")
})?;
let cmd = ReadCmd::RBytes(BRead::Until(delim));
Ok(init_cps(3, IOCmdHandlePack { handle, cmd }))
}
pub fn write_str(handle: WriteHandle, string: OrcString) -> XfnResult<Clause> {
let cmd = WriteCmd::WStr(string.get_string());
Ok(init_cps(3, IOCmdHandlePack { handle, cmd }))
}
pub fn write_bin(handle: WriteHandle, bytes: Binary) -> XfnResult<Clause> {
Ok(init_cps(3, IOCmdHandlePack { handle, cmd: WriteCmd::WBytes(bytes) }))
}
pub fn flush(handle: WriteHandle) -> XfnResult<Clause> {
Ok(init_cps(3, IOCmdHandlePack { handle, cmd: WriteCmd::Flush }))
}
pub fn io_bindings<'a>(
@@ -64,14 +51,14 @@ pub fn io_bindings<'a>(
ConstTree::namespace(
[i.i("system"), i.i("io")],
ConstTree::tree([
(i.i("read_string"), ConstTree::xfn(ReadString)),
(i.i("read_line"), ConstTree::xfn(ReadLine)),
(i.i("read_bin"), ConstTree::xfn(ReadBin)),
(i.i("read_n_bytes"), ConstTree::xfn(ReadBytes)),
(i.i("read_until"), ConstTree::xfn(ReadUntil)),
(i.i("write_str"), ConstTree::xfn(WriteStr)),
(i.i("write_bin"), ConstTree::xfn(WriteBin)),
(i.i("flush"), ConstTree::xfn(Flush)),
(i.i("read_string"), ConstTree::xfn(xfn_1ary(read_string))),
(i.i("read_line"), ConstTree::xfn(xfn_1ary(read_line))),
(i.i("read_bin"), ConstTree::xfn(xfn_1ary(read_bin))),
(i.i("read_n_bytes"), ConstTree::xfn(xfn_2ary(read_bytes))),
(i.i("read_until"), ConstTree::xfn(xfn_2ary(read_until))),
(i.i("write_str"), ConstTree::xfn(xfn_2ary(write_str))),
(i.i("write_bin"), ConstTree::xfn(xfn_2ary(write_bin))),
(i.i("flush"), ConstTree::xfn(xfn_1ary(flush))),
]) + ConstTree::Tree(
std_streams
.into_iter()

View File

@@ -8,5 +8,5 @@ mod instances;
mod service;
// pub use facade::{io_system, IOStream, IOSystem};
pub use instances::{wrap_io_error, Sink, Source};
pub use service::{Service, Stream, StreamTable};
pub use instances::{wrap_io_error, Source, Sink};

View File

@@ -11,8 +11,8 @@ use super::busy::{BusyState, NextItemReportKind};
use super::Canceller;
use crate::facade::{IntoSystem, System};
use crate::foreign::cps_box::{init_cps, CPSBox};
use crate::foreign::InertAtomic;
use crate::interpreted::ExprInst;
use crate::foreign::{xfn_1ary, InertAtomic, XfnResult};
use crate::interpreted::{Clause, ExprInst};
use crate::interpreter::HandlerTable;
use crate::systems::asynch::{AsynchSystem, MessagePort};
use crate::systems::stl::Boolean;
@@ -20,7 +20,7 @@ use crate::systems::AssertionError;
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};
use crate::{ConstTree, Location};
enum SharedResource<T> {
Free(T),
@@ -127,19 +127,17 @@ 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())
pub fn take_and_drop(x: ExprInst) -> XfnResult<Clause> {
match x.request() {
Some(t) => Ok(init_cps::<TakeCmd>(1, t)),
None => AssertionError::fail(Location::Unknown, "SharedHandle"),
}
}
pub fn is_taken_error(x: ExprInst) -> XfnResult<Boolean> {
Ok(Boolean(x.downcast::<SealedOrTaken>().is_ok()))
}
trait_set! {
/// The part of processing a blocking I/O task that cannot be done on a remote
/// thread, eg. because it accesses other systems or Orchid code.
@@ -334,8 +332,8 @@ impl IntoSystem<'static> for SeqScheduler {
constants: ConstTree::namespace(
[i.i("system"), i.i("scheduler")],
ConstTree::tree([
(i.i("is_taken_error"), ConstTree::xfn(IsTakenError)),
(i.i("take_and_drop"), ConstTree::xfn(TakeAndDrop)),
(i.i("is_taken_error"), ConstTree::xfn(xfn_1ary(is_taken_error))),
(i.i("take_and_drop"), ConstTree::xfn(xfn_1ary(take_and_drop))),
]),
)
.unwrap_tree(),

View File

@@ -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))),
]),
)])
}

View File

@@ -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))),
]),

View File

@@ -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))),
])
}

View File

@@ -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())
}
}

View File

@@ -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
)

View File

@@ -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)
)

View File

@@ -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))),
]),
)])
}

View File

@@ -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)))])
}

View File

@@ -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)
)

View 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

View File

@@ -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))),
]),
)
}

View File

@@ -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))),
]),
)])
}