Significantly extended stdlib

This commit is contained in:
2026-01-27 20:53:45 +01:00
parent 66e5a71032
commit 534f08b45c
42 changed files with 635 additions and 211 deletions

8
Cargo.lock generated
View File

@@ -948,6 +948,7 @@ dependencies = [
"orchid-api",
"orchid-api-traits",
"orchid-base",
"orchid-extension",
"ordered-float",
"pastey",
"substack",
@@ -981,6 +982,7 @@ dependencies = [
"substack",
"test_executors",
"tokio",
"unicode-segmentation",
]
[[package]]
@@ -1708,6 +1710,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-xid"
version = "0.1.0"

View File

@@ -1,2 +1,4 @@
let user = r[ "foo" 1, "bar" t[3, 4] ]
let main = user.bar.1
let _main = user.bar.1
let main = "foo" + string::slice "hello" 1 3 + "bar"

View File

@@ -9,9 +9,9 @@ use orchid_base::interner::is;
use orchid_base::location::Pos;
use trait_set::trait_set;
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom, ToAtom};
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom};
use crate::expr::{Expr, ExprKind};
use crate::gen_expr::{GExpr, atom, bot};
use crate::gen_expr::{GExpr, bot};
pub trait TryFromExpr: Sized {
fn try_from_expr(expr: Expr) -> impl Future<Output = OrcRes<Self>>;
@@ -111,10 +111,6 @@ impl<T: ToExpr> ToExpr for OrcRes<T> {
}
}
impl<A: ToAtom> ToExpr for A {
async fn to_gen(self) -> GExpr { atom(self) }
}
impl ToExpr for Never {
async fn to_gen(self) -> GExpr { match self {} }
}

View File

@@ -13,7 +13,7 @@ use crate::atom::Atomic;
use crate::atom_owned::{OwnedAtom, OwnedVariant};
use crate::conv::{ToExpr, TryFromExpr};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
use crate::gen_expr::{GExpr, arg, call, lambda, new_atom, seq};
enum Command {
Execute(GExpr, Sender<Expr>),
@@ -34,11 +34,11 @@ impl BuilderCoroutine {
None => panic!("Before the stream ends, we should have gotten a Halt"),
Some(Command::Halt(expr)) => expr,
Some(Command::Execute(expr, reply)) => call(
lambda(0, [seq([arg(0)], call(Replier { reply, builder: self }.to_gen().await, [arg(0)]))]),
lambda(0, [seq([arg(0)], call(new_atom(Replier { reply, builder: self }), [arg(0)]))]),
[expr],
),
Some(Command::Register(expr, reply)) =>
call(Replier { reply, builder: self }.to_gen().await, [expr]),
call(new_atom(Replier { reply, builder: self }), [expr]),
}
}
}

View File

@@ -261,7 +261,8 @@ impl ExtensionBuilder {
let text = es(text).await;
let src = Sym::from_api(src).await;
let expr_store = BorrowedExprStore::new();
let trigger_char = text.chars().nth(pos as usize).unwrap();
let tail = &text[pos as usize..];
let trigger_char = tail.chars().next().unwrap();
let ekey_na = ekey_not_applicable().await;
let ekey_cascade = ekey_cascade().await;
let lexers = cted().inst().dyn_lexers();
@@ -269,7 +270,7 @@ impl ExtensionBuilder {
lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
{
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
match try_with_reporter(lx.lex(&text[pos as usize..], &ctx)).await {
match try_with_reporter(lx.lex(tail, &ctx)).await {
Err(e) if e.any(|e| *e == ekey_na) => continue,
Err(e) => {
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));

View File

@@ -6,7 +6,7 @@ use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use futures::future::LocalBoxFuture;
use futures::future::{LocalBoxFuture, join_all};
use futures::{AsyncWrite, FutureExt};
use itertools::Itertools;
use never::Never;
@@ -14,6 +14,7 @@ use orchid_api_traits::Encode;
use orchid_base::clone;
use orchid_base::error::OrcRes;
use orchid_base::format::{FmtCtx, FmtUnit};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use task_local::task_local;
use trait_set::trait_set;
@@ -24,7 +25,7 @@ use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::coroutine_exec::{ExecHandle, exec};
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::gen_expr::{GExpr, new_atom};
use crate::system::sys_id;
trait_set! {
@@ -44,6 +45,16 @@ pub fn get_arg(idx: usize) -> Expr {
.expect("get_arg called outside ExprFunc")
}
pub fn get_argc() -> usize {
ARGV.try_with(|argv| argv.len()).expect("get_arg called outside ExprFunc")
}
pub async fn get_arg_posv(idxes: impl IntoIterator<Item = usize>) -> impl Iterator<Item = Pos> {
let args = (ARGV.try_with(|argv| idxes.into_iter().map(|i| &argv[i]).cloned().collect_vec()))
.expect("get_arg_posv called outside ExprFunc");
join_all(args.iter().map(|expr| expr.pos())).await.into_iter()
}
pub trait ExprFunc<I, O>: Clone + 'static {
fn argtyps() -> &'static [TypeId];
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
@@ -123,7 +134,7 @@ impl OwnedAtom for Fun {
if new_args.len() == self.record.argtyps.len() {
(self.record.fun)(new_args).await.to_gen().await
} else {
Self { args: new_args, record: self.record.clone(), path: self.path.clone() }.to_gen().await
new_atom(Self { args: new_args, record: self.record.clone(), path: self.path.clone() })
}
}
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
@@ -169,7 +180,7 @@ impl OwnedAtom for Lambda {
if new_args.len() == self.record.argtyps.len() {
(self.record.fun)(new_args).await.to_gen().await
} else {
Self { args: new_args, record: self.record.clone() }.to_gen().await
new_atom(Self { args: new_args, record: self.record.clone() })
}
}
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
@@ -181,7 +192,7 @@ mod expr_func_derives {
use orchid_base::error::OrcRes;
use super::ExprFunc;
use super::{ARGV, ExprFunc};
use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::{ExecHandle, Expr};
use crate::gen_expr::GExpr;
@@ -200,8 +211,9 @@ mod expr_func_derives {
}
async fn apply<'a>(&self, _: ExecHandle<'a>, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::argtyps().len(), "Arity mismatch");
let argv = v.clone();
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_gen().await)
Ok(ARGV.scope(argv, self($($t::try_from_expr([< $t:lower >]).await?,)*)).await.to_gen().await)
}
}
}

View File

@@ -106,7 +106,8 @@ impl Format for GExprKind {
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) }
pub fn atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
/// Creates an expression from a new atom that we own.
pub fn new_atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
pub fn seq(deps: impl IntoIterator<Item = GExpr>, val: GExpr) -> GExpr {
fn recur(mut ops: impl Iterator<Item = GExpr>) -> Option<GExpr> {

View File

@@ -155,7 +155,10 @@ where A: AtomicFeatures {
Ok(TAtom { value: *value, untyped: foreign })
}
pub async fn dep_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
/// Make a global request to a system that supports this request type. The
/// target system must either be the system in which this function is called, or
/// one of its direct dependencies.
pub async fn sys_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
let mut msg = Vec::new();
req.into().encode_vec(&mut msg);
let cted = cted();

View File

@@ -20,7 +20,7 @@ use crate::api;
use crate::conv::ToExpr;
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
use crate::func_atom::{ExprFunc, Fun};
use crate::gen_expr::{GExpr, sym_ref};
use crate::gen_expr::{GExpr, new_atom, sym_ref};
pub type GenTokTree = TokTree<Expr, GExpr>;
pub type GenTok = Token<Expr, GExpr>;
@@ -75,7 +75,7 @@ pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (
}
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
let fac =
LazyMemberFactory::new(async move |sym| MemKind::Const(Fun::new(sym, xf).await.to_gen().await));
LazyMemberFactory::new(async move |sym| MemKind::Const(new_atom(Fun::new(sym, xf).await)));
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
}
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {

View File

@@ -22,6 +22,7 @@ num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional = true }
ordered-float = "5.1.0"
pastey = "0.2.1"
substack = "1.1.1"
@@ -33,3 +34,4 @@ unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
[features]
tokio = ["dep:tokio", "dep:tokio-util", "dep:libloading"]
orchid-extension = ["dep:orchid-extension"]

View File

@@ -33,6 +33,7 @@ rust_decimal = "1.39.0"
subslice-offset = "0.1.1"
substack = "1.1.1"
tokio = { version = "1.49.0", features = ["full"] }
unicode-segmentation = "1.12.0"
[dev-dependencies]
test_executors = "0.4.1"

View File

@@ -7,7 +7,7 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::GExpr;
use orchid_extension::gen_expr::{GExpr, new_atom};
use crate::macros::mactree::{MacTok, MacTree};
@@ -41,7 +41,7 @@ impl OwnedAtom for InstantiateTplCall {
Ok(t) => self.argv.push(own(&t).await),
};
if self.argv.len() < self.argc {
return self.to_gen().await;
return new_atom(self);
}
let mut args = self.argv.into_iter();
let ret = self.tpl.map(&mut false, &mut |mt| match mt.tok() {
@@ -49,7 +49,7 @@ impl OwnedAtom for InstantiateTplCall {
_ => None,
});
assert!(args.next().is_none(), "Too many arguments for all slots");
ret.to_gen().await
new_atom(ret)
})
.await
.to_gen()

View File

@@ -11,7 +11,7 @@ use orchid_base::sym;
use orchid_base::tree::Paren;
use orchid_extension::atom::TAtom;
use orchid_extension::conv::TryFromExpr;
use orchid_extension::gen_expr::{atom, call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
@@ -40,7 +40,7 @@ impl Parser for LetLine {
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
let macro_input =
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
Ok(call(sym_ref(sym!(macros::resolve)), [atom(macro_input)]))
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
})])
}
}

View File

@@ -1,9 +1,8 @@
use orchid_base::sym;
use orchid_extension::atom::TAtom;
use orchid_extension::atom_owned::own;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, fun, prefix};
use crate::macros::mactree::MacTree;
@@ -53,14 +52,14 @@ pub async fn gen_macro_lib() -> Vec<GenMember> {
exec(async |mut h| {
let recur = resolve(mactree!(macros::common::comma_list "push" tail ;)).await;
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
tail.0.insert(0, h.exec(head).await?);
tail.0.insert(0, h.exec(new_atom(head)).await?);
Ok(tail)
})
.await
}],
)
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [async |[tail]| {
HomoTpl(vec![tail.to_gen().await])
HomoTpl(vec![new_atom(tail)])
}])
.rule(mactreev!(macros::common::comma_list()), [async |[]| UntypedTuple(Vec::new())])
.finish(),
@@ -71,14 +70,14 @@ pub async fn gen_macro_lib() -> Vec<GenMember> {
exec(async |mut h| {
let recur = resolve(mactree!(macros::common::semi_list "push" tail ;)).await;
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
tail.0.insert(0, h.exec(head).await?);
tail.0.insert(0, h.exec(new_atom(head)).await?);
Ok(tail)
})
.await
}],
)
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [async |[tail]| {
HomoTpl(vec![tail.to_gen().await])
HomoTpl(vec![new_atom(tail)])
}])
.rule(mactreev!(macros::common::semi_list()), [async |[]| UntypedTuple(Vec::new())])
.finish(),

View File

@@ -13,7 +13,7 @@ use orchid_base::tree::{Paren, Token};
use orchid_base::{clone, sym};
use orchid_extension::atom::TAtom;
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
@@ -133,7 +133,7 @@ impl Parser for MacroLine {
let macro_input =
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?)
.at(body_sr.pos());
Ok(call(sym_ref(sym!(macros::resolve)), [macro_input.to_gen().await]))
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
}))
}
let mac_cell = Rc::new(OnceCell::new());
@@ -165,7 +165,7 @@ impl Parser for MacroLine {
rules,
})))
};
mac_cell.get_or_init(mac_future).await.clone().to_gen().await
mac_cell.get_or_init(mac_future).await.clone().map(new_atom).to_gen().await
}))
}
Ok(lines)

View File

@@ -6,6 +6,7 @@ use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::interner::is;
use orchid_base::tokens::PARENS;
use orchid_base::tree::Paren;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::parser::p_tree2gen;
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
@@ -28,11 +29,12 @@ impl Lexer for MacTreeLexer {
Ok((tail4, mactree)) => {
let range = lctx.pos_tt(tail, tail4);
let tok = match &args[..] {
[] => x_tok(mactree).await,
[] => x_tok(new_atom(mactree)).await,
_ => {
let instantiate_tpl_call =
InstantiateTplCall { argc: args.len(), argv: vec![], tpl: mactree };
let call = chain!([x_tok(instantiate_tpl_call).await.at(range.clone())], args);
let call =
chain!([x_tok(new_atom(instantiate_tpl_call)).await.at(range.clone())], args);
GenTok::S(Paren::Round, call.collect())
},
};

View File

@@ -15,7 +15,7 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::{ExecHandle, exec};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, fun, prefix};
use crate::macros::resolve::resolve;
@@ -82,25 +82,27 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
.await
},
),
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| MatcherAtom {
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
matcher,
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| {
new_atom(MatcherAtom {
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
matcher,
})
}),
build_macro(None, ["match", "match_rule", "_row", "=>"])
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [
async |[value, rules]| {
exec(async move |mut h| {
let rule_lines = h
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await,
]))
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!(macros::common::semi_list "push" rules.clone();),
)]))
.await?;
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
for line_mac in rule_lines.0.iter() {
let Tpl((matcher, body)) = h
.exec(call(sym_ref(sym!(macros::resolve)), [
mactree!(pattern::_row "push" own(line_mac).await ;).to_gen().await,
]))
.exec(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!(pattern::_row "push" own(line_mac).await ;),
)]))
.await?;
rule_atoms.push((matcher, body));
}
@@ -128,18 +130,18 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
resolve(mactree!(pattern::match_rule "push" pattern; )).await
}])
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |[]| {
Ok(MatcherAtom {
Ok(new_atom(MatcherAtom {
keys: Vec::new(),
matcher: lambda(0, [OrcOpt(Some(Tpl(()))).to_gen().await]).create().await,
})
}))
}])
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
async |[pattern, mut value]| {
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
let Ok(pat) = h
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
mactree!(pattern::match_rule "push" pattern.clone();).to_gen().await,
]))
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!(pattern::match_rule "push" pattern.clone();),
)]))
.await
else {
return Err(mk_errv(
@@ -171,10 +173,10 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
[name.pos()],
));
};
Ok(MatcherAtom {
Ok(new_atom(MatcherAtom {
keys: vec![name.clone()],
matcher: sym_ref(sym!(pattern::ref_body)).to_expr().await,
})
}))
}])
.finish(),
])

View File

@@ -5,6 +5,7 @@ use orchid_base::interner::{es, is};
use orchid_base::parse::{name_char, name_start};
use orchid_extension::atom::Atomic;
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::tree::{GenTokTree, x_tok};
@@ -72,6 +73,6 @@ impl Lexer for PhLexer {
}
};
let ph_atom = PhAtom(is(name).await.to_api(), phkind);
Ok((tail, x_tok(ph_atom).await.at(ctx.pos_tt(line, tail))))
Ok((tail, x_tok(new_atom(ph_atom)).await.at(ctx.pos_tt(line, tail))))
}
}

View File

@@ -15,7 +15,7 @@ use orchid_extension::atom::TAtom;
use orchid_extension::atom_owned::own;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::{ExecHandle, exec};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, new_atom, sym_ref};
use orchid_extension::reflection::{ReflMemKind, refl};
use subslice_offset::SubsliceOffset;
use substack::Substack;
@@ -283,9 +283,9 @@ async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos
let mut call_args = vec![];
for name in rule.ph_names.iter() {
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
StateEntry::Scalar(scal) => (**scal).clone().to_gen().await,
StateEntry::Scalar(scal) => new_atom((**scal).clone()),
StateEntry::Vec(vec) =>
MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None).to_gen().await,
new_atom(MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None)),
});
}
call(sym_ref(mac.0.module.suffix([rule.body.clone()]).await), call_args).at(pos.clone())

View File

@@ -4,7 +4,7 @@ use orchid_extension::atom::TAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, fun, prefix};
use crate::macros::match_macros::MatcherAtom;
@@ -34,21 +34,21 @@ pub async fn gen_option_macro_lib() -> Vec<GenMember> {
let sub = h
.exec::<TAtom<MatcherAtom>>(resolve(mactree!(pattern::match_rule "push" sub;)).await)
.await?;
Ok(MatcherAtom {
Ok(new_atom(MatcherAtom {
keys: sub.keys().collect().await,
matcher: h
.register(call(sym_ref(sym!(std::option::is_some_body)), [sub.to_gen().await]))
.await,
})
}))
})
},
])
.rule(mactreev!(pattern::match_rule(std::option::none)), [|[]: [_; _]| {
exec(async |mut h| {
Ok(MatcherAtom {
Ok(new_atom(MatcherAtom {
keys: vec![],
matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await,
})
}))
})
}])
.finish(),

View File

@@ -4,7 +4,7 @@ use orchid_extension::atom_owned::own;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, prefix};
use crate::macros::resolve::resolve;
@@ -17,9 +17,9 @@ pub async fn gen_record_macro_lib() -> Vec<GenMember> {
.rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_; _]| {
exec(async move |mut h| {
let tup = h
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
]))
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!((macros::common::comma_list "push" elements ;)),
)]))
.await?;
let mut record = sym_ref(sym!(std::record::empty));
for item_exprh in tup.0 {

View File

@@ -6,7 +6,7 @@ use orchid_extension::atom_owned::own;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
use orchid_extension::gen_expr::{GExpr, call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, fun, prefix};
use crate::macros::match_macros::MatcherAtom;
@@ -20,7 +20,7 @@ pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
exec(async move |mut h| {
let tup = h
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
new_atom(mactree!((macros::common::comma_list "push" elements ;))),
]))
.await?;
let val = stream::iter(&tup.0[..])
@@ -56,32 +56,32 @@ pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
}
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> {
exec(async move |mut h| -> OrcRes<MatcherAtom> {
exec(async move |mut h| -> OrcRes<GExpr> {
let tup = h
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [
mactree!((macros::common::comma_list "push" elements ;)).to_gen().await,
]))
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!((macros::common::comma_list "push" elements ;)),
)]))
.await?;
let mut subs = Vec::with_capacity(tup.0.len());
for mac_a in &tup.0[..] {
let mac = own(mac_a).await;
let sub = h
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
mactree!(pattern::match_rule ("push" mac ;)).to_gen().await,
]))
.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!(pattern::match_rule ("push" mac ;)),
)]))
.await?;
subs.push(sub);
}
let tail_matcher = match tail_matcher {
Some(mac) => Some(
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [
mactree!(pattern::match_rule "push" mac ;).to_gen().await,
]))
h.exec::<TAtom<MatcherAtom>>(call(sym_ref(sym!(macros::resolve)), [new_atom(
mactree!(pattern::match_rule "push" mac ;),
)]))
.await?,
),
None => None,
};
Ok(MatcherAtom {
Ok(new_atom(MatcherAtom {
keys: stream::iter(&subs[..])
.flat_map(|t| t.keys())
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
@@ -93,7 +93,7 @@ fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Ou
])
.to_expr()
.await,
})
}))
})
}

View File

@@ -11,7 +11,7 @@ use orchid_base::name::{NameLike, Sym, VPath};
use orchid_extension::atom::{Atomic, TAtom};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::ToExpr;
use orchid_extension::gen_expr::{GExpr, sym_ref};
use orchid_extension::gen_expr::{GExpr, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
use crate::macros::macro_value::{Macro, MacroData, Rule};
@@ -48,7 +48,7 @@ impl OwnedAtom for MacroBodyArgCollector {
if self.argc == self.args.len() {
(self.cb)(self.args).await.to_gen().await
} else {
self.to_gen().await
new_atom(self)
}
}
}
@@ -86,15 +86,20 @@ impl MacroBuilder {
let argv = [].into_iter().collect_array().expect("N is 0");
MemKind::Const(body(argv).await.to_gen().await)
}),
1.. => cnst(true, name, MacroBodyArgCollector {
argc: N,
args: Vec::new(),
cb: Rc::new(move |argv| {
let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
let body = body.clone();
Box::pin(async move { body(arr).await.to_gen().await })
1.. => cnst(
true,
name,
new_atom(MacroBodyArgCollector {
argc: N,
args: Vec::new(),
cb: Rc::new(move |argv| {
let arr =
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
let body = body.clone();
Box::pin(async move { body(arr).await.to_gen().await })
}),
}),
}),
),
});
self.patterns.push(pat);
self
@@ -105,35 +110,31 @@ impl MacroBuilder {
let main_const = lazy(true, &format!("__macro__{name}"), async move |path| {
let module = (Sym::new(path.split_last_seg().1.iter().cloned()).await)
.expect("Default macro in global root");
MemKind::Const(
Macro(Rc::new(MacroData {
canonical_name: module.suffix([is(name).await]).await,
module,
prio,
rules: stream(async |mut h| {
for (counter, pattern) in patterns.into_iter().enumerate() {
let mut placeholders = Vec::new();
pattern.map(&mut false, &mut |tt| {
if let MacTok::Ph(ph) = &*tt.tok {
placeholders.push(ph.name.clone())
}
None
});
h.emit(Rule {
matcher: Matcher::new(pattern.clone()).await.unwrap(),
pattern,
ph_names: placeholders,
body: is(&format!("({name})::{counter}")).await,
})
.await;
}
})
.collect()
.await,
}))
.to_gen()
MemKind::Const(new_atom(Macro(Rc::new(MacroData {
canonical_name: module.suffix([is(name).await]).await,
module,
prio,
rules: stream(async |mut h| {
for (counter, pattern) in patterns.into_iter().enumerate() {
let mut placeholders = Vec::new();
pattern.map(&mut false, &mut |tt| {
if let MacTok::Ph(ph) = &*tt.tok {
placeholders.push(ph.name.clone())
}
None
});
h.emit(Rule {
matcher: Matcher::new(pattern.clone()).await.unwrap(),
pattern,
ph_names: placeholders,
body: is(&format!("({name})::{counter}")).await,
})
.await;
}
})
.collect()
.await,
)
}))))
});
let kw_consts = own_kws[1..].iter().flat_map(|kw| {
lazy(true, &format!("__macro__{kw}"), async move |path| {

View File

@@ -0,0 +1,25 @@
use std::borrow::Cow;
use std::pin::Pin;
use std::rc::Rc;
use futures::AsyncWrite;
use orchid_api_traits::Encode;
use orchid_extension::atom::Atomic;
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
#[derive(Clone)]
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);
impl Atomic for BlobAtom {
type Variant = OwnedVariant;
type Data = ();
}
impl OwnedAtom for BlobAtom {
type Refs = ();
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
self.0.encode(write).await.unwrap()
}
async fn deserialize(mut dctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self(dctx.read::<Rc<Vec<u8>>>().await)
}
}

View File

@@ -0,0 +1,146 @@
use std::rc::Rc;
use orchid_base::error::{OrcErrv, mk_errv};
use orchid_base::interner::is;
use orchid_extension::atom::TAtom;
use orchid_extension::atom_owned::own;
use orchid_extension::func_atom::get_arg_posv;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::boolean::Bool;
use crate::{Int, OrcOpt, Tpl};
async fn bounds_error(
expected: String,
blob: &BlobAtom,
args: impl IntoIterator<Item = usize>,
) -> OrcErrv {
mk_errv(
is("Index out of bounds").await,
format!("Selected {expected} from blob of len {}", blob.0.len()),
get_arg_posv(args).await,
)
}
pub fn gen_binary_lib() -> Vec<GenMember> {
prefix("std", [comments(
["A Blob is a sequence of bytes stored and processed efficiently."],
prefix("binary", [
comments(
["Appends a binary blob to another", "|type: Blob -> Blob -> Blob|"],
fun(true, "concat", async |a: TAtom<BlobAtom>, b: TAtom<BlobAtom>| {
new_atom(BlobAtom(Rc::new(
own(&a).await.0.iter().chain(&own(&b).await.0[..]).copied().collect(),
)))
}),
),
comments(
[
"Copies out a subsection of the binary into a new blob \
specified by starting point and length",
"|type: Blob -> Int -> Int -> Blob|",
],
fun(true, "slice", async |a: TAtom<BlobAtom>, Int(start): Int, Int(len): Int| {
let blob = own(&a).await;
if start + len > blob.0.len() as i64 {
return Err(bounds_error(format!("{start}+{len}"), &blob, 0..3).await);
}
let sub = blob.0[start as usize..(start + len) as usize].to_vec();
Ok(new_atom(BlobAtom(Rc::new(sub))))
}),
),
comments(
[
"Return the index where the second binary appears as a subsection of the first",
"|type: Blob -> Blob -> std::option Int|",
],
fun(true, "find", async |haystack: TAtom<BlobAtom>, needle: TAtom<BlobAtom>| {
let haystack_vec = own(&haystack).await;
let needle_vec = own(&needle).await;
for i in 0..haystack_vec.0.len() - needle_vec.0.len() {
if haystack_vec.0[i..].starts_with(&needle_vec.0) {
return OrcOpt(Some(Int(i as i64)));
}
}
OrcOpt(None)
}),
),
comments(
[
"Splits the binary into two halves at the given byte index",
"|type: Blob -> Int -> std::tuple Blob Blob|",
],
fun(true, "split", async |a: TAtom<BlobAtom>, i: Int| {
let v = own(&a).await;
if v.0.len() < i.0 as usize {
return Err(bounds_error(i.0.to_string(), &v, 1..2).await);
}
let (l, r) = v.0.split_at(i.0 as usize);
Ok(Tpl((
new_atom(BlobAtom(Rc::new(l.to_vec()))),
new_atom(BlobAtom(Rc::new(r.to_vec()))),
)))
}),
),
comments(
[
"Takes a binary, a starting point, a length no greater than 8, and a boolean flag \
which is true if the number is little endian. Reads a usize from \
the specified location in the binary.",
"|type: Blob -> Int -> Int -> Bool -> Int|",
],
fun(
true,
"get_int",
async |bin: TAtom<BlobAtom>, Int(start): Int, Int(len): Int, Bool(le): Bool| {
let vec = own(&bin).await;
if start + len > vec.0.len() as i64 {
return Err(bounds_error(format!("{start}+{len}"), &vec, 1..3).await);
}
if 8 < len {
return Err(mk_errv(
is("Too many bytes for int conversion").await,
format!("At most 8 bytes fit into an Int, requested {len}"),
get_arg_posv(3..4).await,
));
}
let slice = &vec.0[start as usize..(start + len) as usize];
let mut data = [0u8; 8];
Ok(Int(if le {
data[..len as usize].copy_from_slice(slice);
i64::from_le_bytes(data)
} else {
data[(8 - len as usize)..].copy_from_slice(slice);
i64::from_be_bytes(data)
}))
},
),
),
comments(
[
"Takes a length no greater than int_bytes, a little endian flag and a number to encode. \
Turns the least significant bytes of the given int into a binary.",
"|type: Int -> Bool -> Int -> Blob|",
],
fun(true, "from_num", async |len: Int, le: Bool, val: Int| {
if 8 < len.0 {
return Err(mk_errv(
is("Too many bytes for int conversion").await,
format!("Ints are 8 bytes, attempted to write {} byte buffer", len.0),
get_arg_posv(0..1).await,
));
}
let data = if le.0 { val.0.to_le_bytes() } else { val.0.to_be_bytes() };
let data = if le.0 { &data[..len.0 as usize] } else { &data[(8 - len.0 as usize)..] };
Ok(new_atom(BlobAtom(Rc::new(data.to_vec()))))
}),
),
comments(
["Returns the number of bytes in a binary", "|type: Blob -> Int|"],
fun(true, "size", async |blob: TAtom<BlobAtom>| Int(own(&blob).await.0.len() as i64)),
),
]),
)])
}

View File

@@ -0,0 +1,2 @@
pub mod binary_atom;
pub mod binary_lib;

View File

@@ -0,0 +1,47 @@
use orchid_api_derive::Coding;
use orchid_base::error::OrcRes;
use orchid_base::format::FmtUnit;
use orchid_base::sym;
use orchid_extension::atom::{Atomic, TAtom};
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{GExpr, sym_ref};
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
pub struct Bool(pub bool);
impl Atomic for Bool {
type Variant = ThinVariant;
type Data = Self;
}
impl ThinAtom for Bool {
async fn print(&self) -> FmtUnit { self.0.to_string().into() }
}
impl TryFromExpr for Bool {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match TAtom::<Bool>::downcast(expr.handle()).await {
Err(e) => Err(e.mk_err().await),
Ok(atom) => Ok(atom.value),
}
}
}
impl ToExpr for Bool {
async fn to_gen(self) -> GExpr {
sym_ref(if self.0 { sym!(std::true) } else { sym!(std::false) })
}
}
pub fn gen_bool_lib() -> Vec<GenMember> {
prefix("std", [
comments(
[
"Returns the second argument if the bool is true, the third argument otherwise",
"|type: Bool -> T -> T -> T|",
],
fun(true, "ifthenelse", async |Bool(b): Bool, t: Expr, f: Expr| if b { t } else { f }),
),
cnst(true, "true", Bool(true)),
cnst(true, "false", Bool(false)),
])
}

View File

@@ -1,3 +1,5 @@
pub mod binary;
pub mod boolean;
pub mod number;
pub mod ops;
pub mod option;

View File

@@ -1,24 +1,32 @@
use orchid_api_derive::Coding;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::error::OrcRes;
use orchid_base::format::FmtUnit;
use orchid_base::name::Sym;
use orchid_base::number::Numeric;
use orchid_base::sym;
use orchid_extension::atom::{
AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, Supports, TAtom, ToAtom,
};
use orchid_extension::atom::{Atomic, MethodSetBuilder, Supports, TAtom};
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::conv::TryFromExpr;
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::sym_ref;
use orchid_extension::system::sys_req;
use ordered_float::NotNan;
use rust_decimal::prelude::Zero;
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
use crate::std::std_system::StdReq;
use crate::std::string::to_string::ToStringMethod;
use crate::{StdSystem, api};
#[derive(Clone, Debug, Coding)]
#[derive(Debug, Clone, Copy, Coding, Hash, PartialEq, Eq, Hierarchy)]
#[extends(StdReq)]
pub struct CreateInt(pub Int);
impl Request for CreateInt {
type Response = api::ExprTicket;
}
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq)]
pub struct Int(pub i64);
impl Atomic for Int {
type Variant = ThinVariant;
@@ -38,6 +46,11 @@ impl TryFromExpr for Int {
TAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
}
}
impl ToExpr for Int {
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
Expr::deserialize(sys_req::<StdSystem, _>(CreateInt(self)).await).await.to_gen().await
}
}
impl Supports<GetTagIdMethod> for Int {
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
sym!(std::number::Int).to_api()
@@ -68,7 +81,14 @@ impl Supports<ToStringMethod> for Int {
}
}
#[derive(Clone, Debug, Coding)]
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq, Hierarchy)]
#[extends(StdReq)]
pub struct CreateFloat(pub Float);
impl Request for CreateFloat {
type Response = api::ExprTicket;
}
#[derive(Clone, Copy, Debug, Coding, Hash, PartialEq, Eq)]
pub struct Float(pub NotNan<f64>);
impl Atomic for Float {
type Variant = ThinVariant;
@@ -88,6 +108,11 @@ impl TryFromExpr for Float {
Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
}
}
impl ToExpr for Float {
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
Expr::deserialize(sys_req::<StdSystem, _>(CreateFloat(self)).await).await.to_gen().await
}
}
impl Supports<GetTagIdMethod> for Float {
async fn handle(&self, _: GetTagIdMethod) -> <GetTagIdMethod as Request>::Response {
sym!(std::number::Float).to_api()
@@ -131,12 +156,14 @@ impl TryFromExpr for Num {
}
}
}
impl ToAtom for Num {
fn to_atom_factory(self) -> AtomFactory {
impl ToExpr for Num {
async fn to_gen(self) -> orchid_extension::gen_expr::GExpr {
match self.0 {
Numeric::Float(f) => Float(f).factory(),
Numeric::Int(i) => Int(i).factory(),
Numeric::Float(f) => Float(f).to_expr().await,
Numeric::Int(i) => Int(i).to_expr().await,
}
.to_gen()
.await
}
}

View File

@@ -2,7 +2,7 @@ use std::ops::RangeInclusive;
use orchid_base::error::OrcRes;
use orchid_base::number::{num_to_errv, parse_num};
use orchid_extension::atom::ToAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::lexer::{LexContext, Lexer};
use orchid_extension::tree::{GenTokTree, x_tok};
@@ -15,10 +15,10 @@ impl Lexer for NumLexer {
async fn lex<'a>(all: &'a str, lxcx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
let fac = match parse_num(chars) {
Ok(numeric) => Num(numeric).to_atom_factory(),
Err(e) => return Err(num_to_errv(e, lxcx.pos(all), lxcx.src()).await),
};
Ok((tail, x_tok(fac).await.at(lxcx.pos_lt(chars.len(), tail))))
match parse_num(chars) {
Ok(numeric) =>
Ok((tail, x_tok(Num(numeric).to_gen().await).await.at(lxcx.pos_lt(chars.len(), tail)))),
Err(e) => Err(num_to_errv(e, lxcx.pos(all), lxcx.src()).await),
}
}
}

View File

@@ -1,7 +1,7 @@
use orchid_base::error::OrcRes;
use orchid_base::interner::is;
use orchid_base::parse::{name_char, name_start};
use orchid_extension::conv::ToExpr;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, LexedData, Lexer, err_not_applicable};
use orchid_extension::tree::GenTok;
@@ -22,7 +22,7 @@ impl Lexer for SubscriptLexer {
let new_tail = &tail[name_len + 1..];
Ok((new_tail, [
GenTok::Name(is(".").await).at(lctx.pos_lt(1, &tail[1..])),
GenTok::NewExpr(IntStrAtom(is(&tail[1..name_len + 1]).await).to_gen().await)
GenTok::NewExpr(new_atom(IntStrAtom(is(&tail[1..name_len + 1]).await)))
.at(lctx.pos_tt(&tail[1..], new_tail)),
]))
}

View File

@@ -10,7 +10,7 @@ use orchid_extension::atom::{Atomic, ForeignAtom, TAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use crate::{OrcString, api};
@@ -59,8 +59,8 @@ impl<T: ToExpr + 'static> ToExpr for OrcOpt<T> {
pub fn gen_option_lib() -> Vec<GenMember> {
prefix("std::option", [
cnst(true, "none", OptAtom(None)),
fun(true, "some", async |ex: Expr| OptAtom(Some(ex))),
cnst(true, "none", new_atom(OptAtom(None))),
fun(true, "some", async |ex: Expr| new_atom(OptAtom(Some(ex)))),
fun(true, "expect", async |opt: ForeignAtom, msg: OrcString| {
match OrcOpt::try_from_expr(opt.clone().ex()).await? {
OrcOpt(Some(ex)) => Ok::<Expr, _>(ex),

View File

@@ -7,7 +7,7 @@ use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
use orchid_base::sym;
use orchid_base::tree::Token;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::std::protocol::parse_impls::parse_impls;
@@ -44,7 +44,7 @@ impl Parser for AsProtoParser {
for (k, v) in impls {
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await);
}
Tag { id, impls: Rc::new(new_impls) }
new_atom(Tag { id, impls: Rc::new(new_impls) })
})
.await
}));

View File

@@ -7,7 +7,7 @@ use orchid_base::parse::{Comment, Parsed, expect_end, try_pop_no_fluff};
use orchid_base::sym;
use orchid_base::tree::Token;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::std::protocol::parse_impls::parse_impls;
@@ -44,7 +44,7 @@ impl Parser for AsTypeParser {
for (k, v) in impls {
new_impls.insert(k.clone(), h.register(sym_ref(id.suffix([v]).await)).await);
}
Tag { id, impls: Rc::new(new_impls) }
new_atom(Tag { id, impls: Rc::new(new_impls) })
})
.await
}));

View File

@@ -18,8 +18,8 @@ use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::{ClonableToExprDyn, ToExpr};
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, call, sym_ref};
use orchid_extension::system::dep_req;
use orchid_extension::gen_expr::{GExpr, call, new_atom, sym_ref};
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, MemKind, cnst, fun, lazy, prefix};
use crate::std::std_system::StdReq;
@@ -135,7 +135,9 @@ pub fn gen_protocol_lib() -> Vec<GenMember> {
fun(false, "resolve", async |tag: ForeignAtom, value: ForeignAtom| {
Ok(call(get_impl(value.clone(), tag).await?.to_gen().await, [value.to_gen().await]))
}),
fun(false, "wrap", async |tag: TAtom<Tag>, value: Expr| Tagged { tag: own(&tag).await, value }),
fun(false, "wrap", async |tag: TAtom<Tag>, value: Expr| {
new_atom(Tagged { tag: own(&tag).await, value })
}),
fun(false, "unwrap", async |tag: TAtom<Tag>, value: TAtom<Tagged>| {
let own_tag = own(&tag).await;
let own_val = own(&value).await;
@@ -178,7 +180,7 @@ impl<'a> TagBuilder<'a> {
self
}
pub async fn finish(self) -> TAtom<Tag> {
let tk = dep_req::<StdSystem, _>(CreateTag {
let tk = sys_req::<StdSystem, _>(CreateTag {
name: Sym::parse(&self.name).await.unwrap().to_api(),
impls: join_all(self.impls.into_iter().map(|(s, fut)| async move {
(

View File

@@ -7,7 +7,7 @@ use orchid_base::interner::is;
use orchid_extension::atom::TAtom;
use orchid_extension::atom_owned::own;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::arg;
use orchid_extension::gen_expr::{arg, new_atom};
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use crate::std::record::record_atom::RecordAtom;
@@ -15,11 +15,11 @@ use crate::std::string::str_atom::IntStrAtom;
pub fn gen_record_lib() -> Vec<GenMember> {
prefix("std::record", [
cnst(true, "empty", RecordAtom(Rc::new(HashMap::new()))),
cnst(true, "empty", new_atom(RecordAtom(Rc::new(HashMap::new())))),
fun(true, "set", async |map: TAtom<RecordAtom>, key: IntStrAtom, val: Expr| {
let mut map = own(&map).await.0.as_ref().clone();
map.insert(key.0.clone(), val);
RecordAtom(Rc::new(map))
new_atom(RecordAtom(Rc::new(map)))
}),
fun(true, "get", async |map: TAtom<RecordAtom>, key: IntStrAtom| {
let record = own(&map).await;
@@ -35,7 +35,7 @@ pub fn gen_record_lib() -> Vec<GenMember> {
fun(true, "delete", async |map: TAtom<RecordAtom>, key: IntStrAtom| {
let mut map = own(&map).await.0.as_ref().clone();
map.remove(&key.0);
RecordAtom(Rc::new(map))
new_atom(RecordAtom(Rc::new(map)))
}),
])
}

View File

@@ -8,7 +8,8 @@ use orchid_base::name::{NameLike, Sym};
use orchid_extension::atom::{Atomic, Supports, TAtom};
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::system::dep_req;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, fun, prefix};
use crate::std::std_system::StdReq;
@@ -43,7 +44,7 @@ impl Request for CreateSymAtom {
pub async fn sym_expr(sym: Sym) -> Expr {
Expr::from_handle(ExprHandle::deserialize(
dep_req::<StdSystem, _>(CreateSymAtom(sym.to_api())).await,
sys_req::<StdSystem, _>(CreateSymAtom(sym.to_api())).await,
))
}
@@ -51,7 +52,7 @@ pub async fn gen_sym_lib() -> Vec<GenMember> {
prefix("std::refl::sym", [
fun(true, "from_str", async move |str: TAtom<IntStrAtom>| {
match Sym::parse(&es(*str).await).await {
Ok(sym) => Ok(SymAtom(sym)),
Ok(sym) => Ok(new_atom(SymAtom(sym))),
Err(_) => Err(mk_errv(
is("Cannot parse sym from empty string").await,
"Empty string passed to std::refl::sym::from_str",
@@ -60,7 +61,7 @@ pub async fn gen_sym_lib() -> Vec<GenMember> {
}
}),
fun(true, "to_tpl", async move |sym: TAtom<SymAtom>| {
HomoTpl(own(&sym).await.0.segs().map(IntStrAtom).collect())
HomoTpl(own(&sym).await.0.segs().map(|seg| new_atom(IntStrAtom(seg))).collect())
}),
])
}

View File

@@ -9,6 +9,7 @@ use orchid_base::sym;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::conv::ToExpr;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::LexerObj;
use orchid_extension::parser::ParserObj;
use orchid_extension::system::{System, SystemCard};
@@ -18,6 +19,10 @@ use orchid_extension::tree::{GenMember, merge_trivial};
use super::number::num_lib::gen_num_lib;
use super::string::str_atom::{IntStrAtom, StrAtom};
use super::string::str_lib::gen_str_lib;
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::binary::binary_lib::gen_binary_lib;
use crate::std::boolean::gen_bool_lib;
use crate::std::number::num_atom::{CreateFloat, CreateInt};
use crate::std::number::num_lexer::NumLexer;
use crate::std::ops::gen_ops_lib;
use crate::std::ops::subscript_lexer::SubscriptLexer;
@@ -36,6 +41,8 @@ use crate::{Float, Int};
#[extendable]
#[allow(clippy::enum_variant_names, reason = "For the time being there are only ctor calls")]
pub enum StdReq {
CreateInt(CreateInt),
CreateFloat(CreateFloat),
CreateTag(CreateTag),
CreateTuple(CreateTuple),
CreateRecord(CreateRecord),
@@ -56,6 +63,7 @@ impl SystemCard for StdSystem {
type Req = StdReq;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
[
Some(BlobAtom::dynfo()),
Some(Int::dynfo()),
Some(Float::dynfo()),
Some(StrAtom::dynfo()),
@@ -72,9 +80,13 @@ impl SystemCard for StdSystem {
impl System for StdSystem {
async fn request<'a>(xreq: Box<dyn ReqHandle<'a> + 'a>, req: Self::Req) -> Receipt<'a> {
match req {
StdReq::CreateInt(ref req @ CreateInt(int)) =>
xreq.reply(req, &new_atom(int).to_expr().await.serialize().await).await.unwrap(),
StdReq::CreateFloat(ref req @ CreateFloat(float)) =>
xreq.reply(req, &new_atom(float).to_expr().await.serialize().await).await.unwrap(),
StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => {
let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await));
let tk = tpl.to_expr().await.serialize().await;
let tk = new_atom(tpl).to_expr().await.serialize().await;
xreq.reply(req, &tk).await.unwrap()
},
StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => {
@@ -82,12 +94,12 @@ impl System for StdSystem {
join_all(items.iter().map(async |(k, v)| (es(*k).await, Expr::deserialize(*v).await)))
.await;
let rec = RecordAtom(Rc::new(values.into_iter().collect()));
let tk = rec.to_expr().await.serialize().await;
let tk = new_atom(rec).to_expr().await.serialize().await;
xreq.reply(req, &tk).await.unwrap()
},
StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => {
let sym_atom = SymAtom(Sym::from_api(sym_tok).await);
xreq.reply(req, &sym_atom.to_expr().await.serialize().await).await.unwrap()
xreq.reply(req, &new_atom(sym_atom).to_expr().await.serialize().await).await.unwrap()
},
StdReq::CreateTag(ref req @ CreateTag { name, ref impls }) => {
let tag_atom = Tag {
@@ -102,7 +114,7 @@ impl System for StdSystem {
.collect(),
),
};
xreq.reply(req, &tag_atom.to_expr().await.serialize().await).await.unwrap()
xreq.reply(req, &new_atom(tag_atom).to_expr().await.serialize().await).await.unwrap()
},
}
}
@@ -110,6 +122,7 @@ impl System for StdSystem {
fn parsers() -> Vec<ParserObj> { vec![&AsTypeParser, &TypeParser, &AsProtoParser, &ProtoParser] }
async fn env() -> Vec<GenMember> {
merge_trivial([
gen_bool_lib(),
gen_num_lib(),
gen_str_lib(),
gen_option_lib(),
@@ -118,9 +131,10 @@ impl System for StdSystem {
gen_protocol_lib(),
gen_sym_lib().await,
gen_ops_lib(),
gen_binary_lib(),
])
}
async fn prelude() -> Vec<Sym> {
vec![sym!(std), sym!(std::tuple), sym!(std::option), sym!(std::record)]
vec![sym!(std), sym!(std::tuple), sym!(std::option), sym!(std::record), sym!(std::string)]
}
}

View File

@@ -5,7 +5,7 @@ use orchid_base::location::SrcRange;
use orchid_base::name::Sym;
use orchid_base::sym;
use orchid_base::tree::{Paren, wrap_tokv};
use orchid_extension::gen_expr::sym_ref;
use orchid_extension::gen_expr::{new_atom, sym_ref};
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::parser::p_tree2gen;
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
@@ -99,6 +99,7 @@ impl Lexer for StringLexer {
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
async fn lex<'a>(all: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
let Some(mut tail) = all.strip_prefix('"') else {
eprintln!("Unrecognized start char {:?}", all.chars().next().unwrap());
return Err(err_not_applicable().await);
};
let mut ret = None;
@@ -115,8 +116,8 @@ impl Lexer for StringLexer {
err.extend(e.clone().into_proj(ctx.src(), ctx.pos(tail) - str.len() as u32).await);
}
let str_val = str_val_res.unwrap_or_default();
x_tok(IntStrAtom::from(is(&str_val).await)).await.at(ctx.pos_lt(str.len() as u32, tail))
as GenTokTree
let atom = new_atom(IntStrAtom::from(is(&str_val).await));
x_tok(atom).await.at(ctx.pos_lt(str.len() as u32, tail)) as GenTokTree
}
let add_frag = |prev: Option<GenTokTree>, new: GenTokTree| async {
let Some(prev) = prev else { return new };

View File

@@ -1,50 +1,174 @@
use std::rc::Rc;
use orchid_base::error::mk_errv;
use orchid_base::format::fmt;
use orchid_base::interner::is;
use orchid_base::sym;
use orchid_extension::atom::ForeignAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{call, sym_ref};
use orchid_extension::func_atom::get_arg;
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use unicode_segmentation::UnicodeSegmentation;
use super::str_atom::StrAtom;
use crate::OrcString;
use crate::std::protocol::types::{get_impl, proto};
use crate::std::string::to_string::ToStringMethod;
use crate::{Int, OrcOpt, OrcString, Tpl};
pub fn gen_str_lib() -> Vec<GenMember> {
prefix("std::string", [
comments(
["Concatenate two strings"],
fun(true, "concat", async |left: OrcString, right: OrcString| {
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
}),
),
comments(
["Converts a value to string. This function is used in interpolation. \
It supports the std::string::to_string protocol in Orchid, \
the std::string::to_string request in Rust, \
and expression debug printing as a fallback (print_atom for Atomic implementors in Rust).\n\n\
This function is infallible."],
fun(true, "to_str", async |input: Expr| {
exec(async move |mut h| {
if let Ok(atom) = h.exec::<ForeignAtom>(input.clone()).await {
if let Some(str) = atom.request(ToStringMethod).await {
return StrAtom::new(Rc::new(str)).to_gen().await;
}
let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__));
let proto = h.exec(proto_ref).await.expect("This protocol is defined in this system");
if let Ok(cb) = get_impl(atom.clone(), proto).await {
return call(cb.to_gen().await, [atom.to_gen().await]).to_gen().await;
}
prefix("std", [comments(
["There are two string types, IntStr and Str. Literals are always IntStr, which are quick to \
equality-compare but may leak, so you can't generally create them at runtime.\n\n\
All functions here operate on Unicode graphemes. This essentially means that letters with \
added diacritics and Mandarin multi-codepoint characters are treated as a single character."],
prefix("string", [
comments(
["Concatenate two strings", "|type: Str -> Str -> Str|"],
fun(true, "concat", async |left: OrcString, right: OrcString| {
new_atom(StrAtom::new(Rc::new(
left.get_string().await.to_string() + &right.get_string().await,
)))
}),
),
comments(
[
"Find the size of a string in bytes. Strings are stored in UTF-8. \
This should be used to determine the computational resource utilization of strings. \
It should not be used to determine whether to truncate text.",
"|type: Str -> Int|",
],
fun(true, "size", async |s: OrcString| Int(s.get_string().await.len().try_into().unwrap())),
),
comments(
[
"Find the number of characters in a string. This can be used for example to \
truncate text. It should not be used to limit the size of messages for security purposes.",
"|type: Str -> Int|",
],
fun(true, "len", async |s: OrcString| {
Int(s.get_string().await.graphemes(true).count().try_into().unwrap())
}),
),
comments(
[
"Takes a string, a start and a length in graphemes. \
Slices out the specified subsection of the string.",
"|type: Str -> Int -> Int -> Str|",
],
fun(true, "slice", async |s: OrcString, Int(start): Int, Int(len): Int| {
let str = s.get_string().await;
if len <= 0 {
return Ok(new_atom(StrAtom::new(Rc::default())));
}
return StrAtom::new(Rc::new(fmt(&input).await)).to_gen().await;
})
.await
}),
),
proto(true, "to_string").finish(),
])
let mut substr_iter = str.graphemes(true).skip(start.try_into().unwrap());
let new_str: String =
substr_iter.by_ref().take(usize::try_from(len).unwrap() - 1).collect();
let Some(s) = substr_iter.next() else {
let str_len = str.graphemes(true).count();
return Err(mk_errv(
is("Index out of bounds").await,
format!("Tried to select grapheme {start}+{len} from string that only has {str_len}"),
[get_arg(0).pos().await, get_arg(1).pos().await, get_arg(2).pos().await],
));
};
Ok(new_atom(StrAtom::new(Rc::new(new_str + s))))
}),
),
comments(
[
"If the first string contains the second then returns the index.",
"|type: Str -> Str -> std::option Int|",
],
fun(true, "find", async |haystack: OrcString, needle: OrcString| {
let haystack_str = haystack.get_string().await;
let needle_str = needle.get_string().await;
let mut haystack_graphs = haystack_str.graphemes(true);
let mut index = 0;
loop {
let mut needle_graphs = needle_str.graphemes(true);
// check that all chars are equal
if haystack_graphs.clone().zip(needle_graphs.by_ref()).all(|(l, r)| l == r) {
// if we exhausted the haystack but not the needle, we can't succeed
if needle_graphs.next().is_some() {
break;
}
return OrcOpt(Some(Int(index)));
}
if haystack_graphs.next().is_none() {
break;
}
index += 1;
}
OrcOpt(None)
}),
),
comments(
[
"Splits the string into two substrings at the nth grapheme.",
"|type: Str -> Int -> std::tuple Str Str|",
],
fun(true, "split", async |s: OrcString, i: Int| {
let str = s.get_string().await;
let Some((i, _)) = str.grapheme_indices(true).nth(i.0.try_into().unwrap()) else {
let len = str.graphemes(true).count();
return Err(mk_errv(
is("Index out of bounds").await,
format!("Tried to split string at {}, it only has {} graphemes", i.0, len),
[get_arg(0).pos().await, get_arg(1).pos().await],
));
};
let (left, right) = str.split_at(i);
Ok(Tpl((
new_atom(StrAtom::new(Rc::new(left.to_string()))),
new_atom(StrAtom::new(Rc::new(right.to_string()))),
)))
}),
),
comments(
["Returns the nth grapheme.", "|type: Str -> Int -> Str|"],
fun(true, "char_at", async |s: OrcString, i: Int| {
let str = s.get_string().await;
let Some(s) = str.graphemes(true).nth(i.0.try_into().unwrap()) else {
let len = str.graphemes(true).count();
return Err(mk_errv(
is("Index out of bounds").await,
format!("Tried to read grapheme {} from string, it only has {}", i.0, len),
[get_arg(0).pos().await, get_arg(1).pos().await],
));
};
Ok(new_atom(StrAtom::new(Rc::new(s.to_string()))))
}),
),
comments(
[
"Converts a value to string. This function is used in interpolation. \
It supports the std::string::to_string protocol in Orchid, \
the std::string::to_string request in Rust, \
and expression debug printing as a fallback (print_atom for Atomic implementors in Rust).\n\n\
This function is infallible.",
"|type: any -> Str|",
],
fun(true, "to_str", async |input: Expr| {
exec(async move |mut h| {
if let Ok(atom) = h.exec::<ForeignAtom>(input.clone()).await {
if let Some(str) = atom.request(ToStringMethod).await {
return new_atom(StrAtom::new(Rc::new(str)));
}
let proto_ref = sym_ref(sym!(std::string::to_string::__protocol_tag__));
let proto = h.exec(proto_ref).await.expect("This protocol is defined in this system");
if let Ok(cb) = get_impl(atom.clone(), proto).await {
return call(cb.to_gen().await, [atom.to_gen().await]).to_gen().await;
}
}
return new_atom(StrAtom::new(Rc::new(fmt(&input).await)));
})
.await
}),
),
proto(true, "to_string").finish(),
]),
)])
}

View File

@@ -17,8 +17,8 @@ use orchid_extension::atom::{Atomic, MethodSetBuilder, Supports, TAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant, own};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, sym_ref};
use orchid_extension::system::dep_req;
use orchid_extension::gen_expr::{GExpr, new_atom, sym_ref};
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod};
@@ -93,22 +93,22 @@ impl OwnedAtom for TupleBuilder {
async fn call(mut self, arg: Expr) -> GExpr {
self.items.push(arg);
if self.arity.get() == self.items.len().try_into().expect("counting up from 0") {
Tuple(Rc::new(self.items)).to_gen().await
new_atom(Tuple(Rc::new(self.items)))
} else {
self.to_gen().await
new_atom(self)
}
}
}
pub fn gen_tuple_lib() -> Vec<GenMember> {
prefix("std::tuple", [
cnst(true, "empty", Tuple(Rc::new(Vec::new()))),
fun(true, "one", async |item: Expr| Tuple(Rc::new(vec![item]))),
cnst(true, "empty", new_atom(Tuple(Rc::new(Vec::new())))),
fun(true, "one", async |item: Expr| new_atom(Tuple(Rc::new(vec![item])))),
fun(true, "new", async |arity: TAtom<Int>| {
if let Ok(arity) = u32::try_from(arity.value.0).and_then(|v| v.try_into()) {
TupleBuilder { arity, items: Vec::new() }.to_gen().await
new_atom(TupleBuilder { arity, items: Vec::new() })
} else {
Tuple(Rc::new(Vec::new())).to_gen().await
new_atom(Tuple(Rc::new(Vec::new())))
}
}),
fun(true, "get", async |tup: TAtom<Tuple>, idx: TAtom<Int>| {
@@ -128,7 +128,7 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
let mut new_vec = own(&tup).await.0.to_vec();
if let Some(slot) = new_vec.get_mut(idx) {
*slot = val;
return Ok(Tuple(Rc::new(new_vec)));
return Ok(new_atom(Tuple(Rc::new(new_vec))));
}
}
return Err(mk_errv(
@@ -141,7 +141,9 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
Int(tup.len().try_into().expect("Tuple was created with an Int length"))
}),
fun(true, "cat", async |left: TAtom<Tuple>, right: TAtom<Tuple>| {
Tuple(Rc::new(own(&left).await.0.iter().chain(own(&right).await.0.iter()).cloned().collect()))
new_atom(Tuple(Rc::new(
own(&left).await.0.iter().chain(own(&right).await.0.iter()).cloned().collect(),
)))
}),
])
}
@@ -159,7 +161,7 @@ impl TryFromExpr for UntypedTuple {
impl ToExpr for UntypedTuple {
async fn to_gen(self) -> GExpr {
let exprs = join_all(self.0.into_iter().map(async |expr| expr.serialize().await)).await;
Expr::deserialize(dep_req::<StdSystem, _>(CreateTuple(exprs)).await).await.to_gen().await
Expr::deserialize(sys_req::<StdSystem, _>(CreateTuple(exprs)).await).await.to_gen().await
}
}

View File

@@ -64,7 +64,7 @@ pub struct Args {
#[derive(Subcommand, Debug)]
pub enum Commands {
Lex {
#[arg(short, long)]
#[arg()]
file: Utf8PathBuf,
},
Parse {