From b9f1bb74d76204673e6ad9ef1168b31904a024df Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Thu, 22 Jan 2026 20:56:02 +0100 Subject: [PATCH] Fixed a very nasty deadlock --- .cargo/config.toml | 4 +- examples/hello-world/main.orc | 6 +- orchid-extension/src/atom.rs | 16 +- orchid-extension/src/atom_owned.rs | 4 +- orchid-extension/src/atom_thin.rs | 4 +- orchid-extension/src/conv.rs | 14 +- orchid-host/src/tree.rs | 8 +- orchid-std/src/macros/macro_lib.rs | 4 +- orchid-std/src/macros/macro_system.rs | 6 +- orchid-std/src/macros/match_macros.rs | 12 +- orchid-std/src/macros/mod.rs | 2 +- orchid-std/src/macros/stdlib/funnctional.rs | 12 + orchid-std/src/macros/stdlib/mod.rs | 20 + orchid-std/src/macros/stdlib/option.rs | 56 ++ orchid-std/src/macros/stdlib/record.rs | 45 ++ .../macros/{std_macros.rs => stdlib/tuple.rs} | 73 +-- orchid-std/src/macros/utils.rs | 9 + orchid-std/src/std/record/record_atom.rs | 24 +- orchid-std/src/std/record/record_lib.rs | 14 +- orchid-std/src/std/std_system.rs | 18 +- orcx/src/main.rs | 510 ++++++++++-------- 21 files changed, 506 insertions(+), 355 deletions(-) create mode 100644 orchid-std/src/macros/stdlib/funnctional.rs create mode 100644 orchid-std/src/macros/stdlib/mod.rs create mode 100644 orchid-std/src/macros/stdlib/option.rs create mode 100644 orchid-std/src/macros/stdlib/record.rs rename orchid-std/src/macros/{std_macros.rs => stdlib/tuple.rs} (66%) diff --git a/.cargo/config.toml b/.cargo/config.toml index e452f15..8c33870 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,7 @@ [alias] xtask = "run --quiet --package xtask --" -orcx = "xtask orcx" -orcxdb = "xtask orcxdb" +orcx = "xtask orcx --" +orcxdb = "xtask orcxdb --" [env] CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/examples/hello-world/main.orc b/examples/hello-world/main.orc index 7bc2e5b..adfaa05 100644 --- a/examples/hello-world/main.orc +++ b/examples/hello-world/main.orc @@ -1,5 +1,7 @@ -let user = "lorinc" + " " + "bethlenfalvy" +let user = string::concat "lorinc" (string::concat " " "bethlenfalvy") let number = 1 + 2 let interpolated = "Hello $user $number" -let user = +let user = r[ "foo" 1, "bar" 3 ] +let ffmain = user . "foo" |> option::expect "missing value" +let main = 1 diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index 92ff882..375b09c 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -13,7 +13,7 @@ use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, stream}; use orchid_api_derive::Coding; use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec}; use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating}; -use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt}; +use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt, take_first}; use orchid_base::interner::is; use orchid_base::location::Pos; use orchid_base::name::Sym; @@ -320,12 +320,18 @@ impl Format for AtomFactory { } } -pub async fn err_not_callable() -> OrcErrv { - mk_errv_floating(is("This atom is not callable").await, "Attempted to apply value as function") +pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv { + mk_errv_floating( + is("This atom is not callable").await, + format!("Attempted to apply {} as function", take_first(unit, false)), + ) } -pub async fn err_not_command() -> OrcErrv { - mk_errv_floating(is("This atom is not a command").await, "Settled on an inactionable value") +pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv { + mk_errv_floating( + is("This atom is not a command").await, + format!("Settled on {} which is an inactionable value", take_first(unit, false)), + ) } /// Read the type ID prefix from an atom, return type information and the rest diff --git a/orchid-extension/src/atom_owned.rs b/orchid-extension/src/atom_owned.rs index 8db4d1a..bdd163d 100644 --- a/orchid-extension/src/atom_owned.rs +++ b/orchid-extension/src/atom_owned.rs @@ -228,7 +228,7 @@ pub trait OwnedAtom: Atomic + Any + Clone + 'static { fn val(&self) -> impl Future>; #[allow(unused_variables)] fn call_ref(&self, arg: Expr) -> impl Future { - async move { bot(err_not_callable().await) } + async move { bot(err_not_callable(&self.dyn_print().await).await) } } fn call(self, arg: Expr) -> impl Future { async { @@ -239,7 +239,7 @@ pub trait OwnedAtom: Atomic + Any + Clone + 'static { } #[allow(unused_variables)] fn command(self) -> impl Future>> { - async move { Err(err_not_command().await) } + async move { Err(err_not_command(&self.dyn_print().await).await) } } #[allow(unused_variables)] fn free(self) -> impl Future { async {} } diff --git a/orchid-extension/src/atom_thin.rs b/orchid-extension/src/atom_thin.rs index 5d9e729..61b83ee 100644 --- a/orchid-extension/src/atom_thin.rs +++ b/orchid-extension/src/atom_thin.rs @@ -99,11 +99,11 @@ pub trait ThinAtom: { #[allow(unused_variables)] fn call(&self, arg: Expr) -> impl Future { - async move { bot(err_not_callable().await) } + async move { bot(err_not_callable(&self.print().await).await) } } #[allow(unused_variables)] fn command(&self) -> impl Future>> { - async move { Err(err_not_command().await) } + async move { Err(err_not_command(&self.print().await).await) } } #[allow(unused_variables)] fn print(&self) -> impl Future { diff --git a/orchid-extension/src/conv.rs b/orchid-extension/src/conv.rs index 8aebdad..649f613 100644 --- a/orchid-extension/src/conv.rs +++ b/orchid-extension/src/conv.rs @@ -4,12 +4,13 @@ use std::pin::Pin; use dyn_clone::DynClone; use never::Never; use orchid_base::error::{OrcErrv, OrcRes, mk_errv}; +use orchid_base::format::{Format, fmt}; use orchid_base::interner::is; use orchid_base::location::Pos; use trait_set::trait_set; use crate::atom::{AtomicFeatures, ForeignAtom, TAtom, ToAtom}; -use crate::expr::Expr; +use crate::expr::{Expr, ExprKind}; use crate::gen_expr::{GExpr, atom, bot}; pub trait TryFromExpr: Sized { @@ -26,14 +27,17 @@ impl TryFromExpr for (T, U) { } } -async fn err_not_atom(pos: Pos) -> OrcErrv { - mk_errv(is("Expected an atom").await, "This expression is not an atom", [pos]) +async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv { + mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos]) } impl TryFromExpr for ForeignAtom { async fn try_from_expr(expr: Expr) -> OrcRes { - match expr.atom().await { - Err(ex) => Err(err_not_atom(ex.data().await.pos.clone()).await), + if let ExprKind::Bottom(err) = &expr.data().await.kind { + return Err(err.clone()); + } + match expr.clone().atom().await { + Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &expr).await), Ok(f) => Ok(f), } } diff --git a/orchid-host/src/tree.rs b/orchid-host/src/tree.rs index e9e77b6..093bf38 100644 --- a/orchid-host/src/tree.rs +++ b/orchid-host/src/tree.rs @@ -63,7 +63,7 @@ impl Root { pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym) -> Self { let mut ref_this = self.0.write().await; let this = &mut *ref_this; - let mut deferred_consts = HashMap::new(); + let mut deferred_consts = Vec::new(); let consts = this.consts.clone(); let mut tfpctx = FromParsedCtx { pars_root: parsed, @@ -86,7 +86,7 @@ impl Root { .expect("Merge conflict between parsed and existing module"); let new = Root(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() }))); *this.ctx.root.write().await = new.downgrade(); - for (path, (sys_id, pc_id)) in deferred_consts { + for (path, sys_id, pc_id) in deferred_consts { let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing"); let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap(); let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), this.ctx.clone()).await; @@ -383,7 +383,7 @@ pub struct FromParsedCtx<'a> { root: &'a Module, ctx: &'a Ctx, consts: &'a MemoMap, - deferred_consts: &'a mut HashMap, + deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>, } impl Tree for Module { @@ -435,7 +435,7 @@ impl MemberKind { async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self { match parsed { ParsedMemberKind::Const(id, sys) => { - ctx.deferred_consts.insert(path, (sys.id(), *id)); + ctx.deferred_consts.push((path, sys.id(), *id)); MemberKind::Const }, ParsedMemberKind::Mod(m) => diff --git a/orchid-std/src/macros/macro_lib.rs b/orchid-std/src/macros/macro_lib.rs index 6675a43..a215941 100644 --- a/orchid-std/src/macros/macro_lib.rs +++ b/orchid-std/src/macros/macro_lib.rs @@ -15,7 +15,7 @@ pub async fn gen_macro_lib() -> Vec { prefix("macros", [ fun(true, "resolve", async |tpl: TAtom| resolve(own(&tpl).await).await), prefix("common", [ - build_macro(None, ["..", "_"]).finish(), + build_macro(None, ["..", "_", "="]).finish(), build_macro(Some(1), ["+"]) .rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| { call(sym_ref(sym!(std::ops::add::resolve)), [resolve(lhs).await, resolve(rhs).await]) @@ -41,7 +41,7 @@ pub async fn gen_macro_lib() -> Vec { call(sym_ref(sym!(std::ops::mod::resolve)), [resolve(lhs).await, resolve(rhs).await]) }]) .finish(), - build_macro(Some(10), ["."]) + build_macro(Some(3), ["."]) .rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| { call(sym_ref(sym!(std::ops::get::resolve)), [resolve(lhs).await, resolve(rhs).await]) }]) diff --git a/orchid-std/src/macros/macro_system.rs b/orchid-std/src/macros/macro_system.rs index 34e4c89..1588bc4 100644 --- a/orchid-std/src/macros/macro_system.rs +++ b/orchid-std/src/macros/macro_system.rs @@ -18,7 +18,7 @@ use crate::macros::macro_value::Macro; use crate::macros::mactree_lexer::MacTreeLexer; use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib}; use crate::macros::ph_lexer::{PhAtom, PhLexer}; -use crate::macros::std_macros::gen_std_macro_lib; +use crate::macros::stdlib::gen_std_macro_lib; use crate::macros::utils::MacroBodyArgCollector; use crate::{MacTree, StdSystem}; @@ -55,10 +55,14 @@ impl System for MacroSystem { sym!(macros::common::;), sym!(macros::common::..), sym!(macros::common::_), + sym!(macros::common::=), + sym!(macros::common::.), sym!(std::tuple::t), + sym!(std::record::r), sym!(pattern::match), sym!(pattern::ref), sym!(pattern::=>), + Sym::literal("std::fn::|>").await, ] } fn lexers() -> Vec { vec![&MacTreeLexer, &PhLexer] } diff --git a/orchid-std/src/macros/match_macros.rs b/orchid-std/src/macros/match_macros.rs index 169e535..ca9fa00 100644 --- a/orchid-std/src/macros/match_macros.rs +++ b/orchid-std/src/macros/match_macros.rs @@ -21,7 +21,6 @@ use orchid_extension::tree::{GenMember, fun, prefix}; use crate::macros::resolve::resolve; use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::std::reflection::sym_atom::SymAtom; -use crate::std::tuple::Tuple; use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api}; #[derive(Clone, Coding)] @@ -92,20 +91,15 @@ pub async fn gen_match_macro_lib() -> Vec { async |[value, rules]| { exec(async move |mut h| { let rule_lines = h - .exec::>(call(sym_ref(sym!(macros::resolve)), [ + .exec::>>(call(sym_ref(sym!(macros::resolve)), [ mactree!(macros::common::semi_list "push" rules.clone();).to_gen().await, ])) .await?; let mut rule_atoms = Vec::<(TAtom, Expr)>::new(); - for line_exprh in rule_lines.iter() { - let line_mac = h - .exec::>(Expr::from_handle( - ExprHandle::from_ticket(*line_exprh).await, - )) - .await?; + 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, + mactree!(pattern::_row "push" own(line_mac).await ;).to_gen().await, ])) .await?; rule_atoms.push((matcher, body)); diff --git a/orchid-std/src/macros/mod.rs b/orchid-std/src/macros/mod.rs index 774b75c..dbe97a3 100644 --- a/orchid-std/src/macros/mod.rs +++ b/orchid-std/src/macros/mod.rs @@ -10,7 +10,7 @@ pub mod match_macros; mod ph_lexer; mod resolve; mod rule; -pub mod std_macros; +pub mod stdlib; mod utils; use mactree::{MacTok, MacTree}; diff --git a/orchid-std/src/macros/stdlib/funnctional.rs b/orchid-std/src/macros/stdlib/funnctional.rs new file mode 100644 index 0000000..03931df --- /dev/null +++ b/orchid-std/src/macros/stdlib/funnctional.rs @@ -0,0 +1,12 @@ +use orchid_extension::tree::{GenMember, prefix}; + +use crate::macros::resolve::resolve; +use crate::macros::utils::{build_macro, mactree, mactreev}; + +pub async fn gen_functional_macro_lib() -> Vec { + prefix("std::fn", [build_macro(Some(4), ["|>"]) + .rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [async |[lhs, fun, rhs]| { + resolve(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)).await + }]) + .finish()]) +} diff --git a/orchid-std/src/macros/stdlib/mod.rs b/orchid-std/src/macros/stdlib/mod.rs new file mode 100644 index 0000000..94ea316 --- /dev/null +++ b/orchid-std/src/macros/stdlib/mod.rs @@ -0,0 +1,20 @@ +pub mod funnctional; +pub mod option; +pub mod record; +pub mod tuple; + +use orchid_extension::tree::{GenMember, merge_trivial}; + +use crate::macros::stdlib::funnctional::gen_functional_macro_lib; +use crate::macros::stdlib::option::gen_option_macro_lib; +use crate::macros::stdlib::record::gen_record_macro_lib; +use crate::macros::stdlib::tuple::gen_tuple_macro_lib; + +pub async fn gen_std_macro_lib() -> Vec { + merge_trivial([ + gen_functional_macro_lib().await, + gen_option_macro_lib().await, + gen_tuple_macro_lib().await, + gen_record_macro_lib().await, + ]) +} diff --git a/orchid-std/src/macros/stdlib/option.rs b/orchid-std/src/macros/stdlib/option.rs new file mode 100644 index 0000000..b95274f --- /dev/null +++ b/orchid-std/src/macros/stdlib/option.rs @@ -0,0 +1,56 @@ +use futures::StreamExt; +use orchid_base::sym; +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::tree::{GenMember, fun, prefix}; + +use crate::macros::match_macros::MatcherAtom; +use crate::macros::resolve::resolve; +use crate::macros::utils::{build_macro, mactree, mactreev}; +use crate::{OrcOpt, Tpl}; + +pub async fn gen_option_macro_lib() -> Vec { + prefix("std::option", [ + fun(false, "is_some_body", |sub: TAtom, val: OrcOpt| { + exec(async move |mut h| { + let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) }; + sub.run_matcher(&mut h, sub_val).await + }) + }), + fun( + false, + "is_none_body", + async |val: OrcOpt| { + if val.0.is_none() { OrcOpt(Some(Tpl(()))) } else { OrcOpt(None) } + }, + ), + build_macro(None, ["some", "none"]) + .rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [ + |[sub]: [_; _]| { + exec(async move |mut h| { + let sub = h + .exec::>(resolve(mactree!(pattern::match_rule "push" sub;)).await) + .await?; + Ok(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 { + keys: vec![], + matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await, + }) + }) + }]) + .finish(), + ]) +} diff --git a/orchid-std/src/macros/stdlib/record.rs b/orchid-std/src/macros/stdlib/record.rs new file mode 100644 index 0000000..2400dd0 --- /dev/null +++ b/orchid-std/src/macros/stdlib/record.rs @@ -0,0 +1,45 @@ +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::expr::Expr; +use orchid_extension::gen_expr::{call, sym_ref}; +use orchid_extension::tree::{GenMember, prefix}; + +use crate::macros::resolve::resolve; +use crate::macros::utils::{build_macro, mactree, mactreev}; +use crate::std::string::str_atom::IntStrAtom; +use crate::{HomoTpl, MacTree, Tpl}; + +pub async fn gen_record_macro_lib() -> Vec { + prefix("std::record", [build_macro(None, ["r", "_row"]) + .rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_; _]| { + exec(async move |mut h| { + let tup = h + .exec::>>(call(sym_ref(sym!(macros::resolve)), [ + mactree!((macros::common::comma_list "push" elements ;)).to_gen().await, + ])) + .await?; + let mut record = sym_ref(sym!(std::record::empty)); + for item_exprh in tup.0 { + let Tpl((key, value)) = h + .exec::, Expr)>>( + resolve(mactree!(std::record::_row "push" own(&item_exprh).await ;)).await, + ) + .await?; + record = call(sym_ref(sym!(std::record::set)), [ + record.to_gen().await, + key.to_gen().await, + value.to_gen().await, + ]); + } + Ok(record) + }) + .await + }]) + .rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |[name, value]| { + Ok(Tpl((resolve(name).await, resolve(value).await))) + }]) + .finish()]) +} diff --git a/orchid-std/src/macros/std_macros.rs b/orchid-std/src/macros/stdlib/tuple.rs similarity index 66% rename from orchid-std/src/macros/std_macros.rs rename to orchid-std/src/macros/stdlib/tuple.rs index aef34a6..a994c27 100644 --- a/orchid-std/src/macros/std_macros.rs +++ b/orchid-std/src/macros/stdlib/tuple.rs @@ -10,53 +10,11 @@ use orchid_extension::gen_expr::{GExpr, call, sym_ref}; use orchid_extension::tree::{GenMember, fun, prefix}; use crate::macros::match_macros::MatcherAtom; -use crate::macros::resolve::resolve; use crate::macros::utils::{build_macro, mactree, mactreev}; -use crate::{HomoTpl, MacTree, OrcOpt, Tpl}; +use crate::{HomoTpl, MacTree, OrcOpt}; -pub async fn gen_std_macro_lib() -> Vec { - prefix("std", [ - prefix("option", [ - fun(false, "is_some_body", |sub: TAtom, val: OrcOpt| { - exec(async move |mut h| { - let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) }; - sub.run_matcher(&mut h, sub_val).await - }) - }), - fun(false, "is_none_body", async |val: OrcOpt| { - if val.0.is_none() { OrcOpt(Some(Tpl(()))) } else { OrcOpt(None) } - }), - build_macro(None, ["some", "none"]) - .rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [ - |[sub]: [_; _]| { - exec(async move |mut h| { - let sub = h - .exec::>( - resolve(mactree!(pattern::match_rule "push" sub;)).await, - ) - .await?; - Ok(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 { - keys: vec![], - matcher: h.register(sym_ref(sym!(std::option::is_none_body))).await, - }) - }) - }]) - .finish(), - ]), - prefix("tuple", [ +pub async fn gen_tuple_macro_lib() -> Vec { + prefix("std::tuple", [ build_macro(None, ["t"]) .rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| { exec(async move |mut h| { @@ -94,32 +52,7 @@ pub async fn gen_std_macro_lib() -> Vec { ]) .finish(), fun(false, "matcher_body", tuple_matcher_body), - ]), - prefix("record", [ - build_macro(None, ["r"]) - .rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_;_]| { - exec(async move |mut h| { - let tup = h - .exec::>>(call(sym_ref(sym!(macros::resolve)), [ - mactree!((macros::common::comma_list "push" elements ;)).to_gen().await, - ])) - .await?; - let val = stream::iter(&tup.0[..]) - .fold(sym_ref(sym!(std::tuple::empty)), async |head, new| { - call(sym_ref(sym!(std::tuple::cat)), [ - head, - call(sym_ref(sym!(std::tuple::one)), [call( - sym_ref(sym!(macros::resolve)), - [new.clone().to_gen().await], - )]), - ]) - }) - .await; - Ok(val) - }).await - }]).finish(), ]) - ]) } fn parse_tpl(elements: MacTree, tail_matcher: Option) -> impl Future { diff --git a/orchid-std/src/macros/utils.rs b/orchid-std/src/macros/utils.rs index 0b3a8f4..f14c1f0 100644 --- a/orchid-std/src/macros/utils.rs +++ b/orchid-std/src/macros/utils.rs @@ -188,6 +188,15 @@ macro_rules! mactreev_impl { $ret.push($arg); $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); }; + (@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => { + let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else { + panic!("pushv used with non-vec value") + }; + for item in body.items.iter() { + $ret.push(item.clone()); + } + $crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*); + }; (@RECUR $ret:ident "l_" $arg:expr ; ($($body:tt)*) $($tail:tt)*) => { $ret.push(MacTok::Lambda( MacTok::Name($arg).at(orchid_base::location::Pos::Inherit), diff --git a/orchid-std/src/std/record/record_atom.rs b/orchid-std/src/std/record/record_atom.rs index 038a38a..97e4930 100644 --- a/orchid-std/src/std/record/record_atom.rs +++ b/orchid-std/src/std/record/record_atom.rs @@ -5,11 +5,12 @@ use std::rc::Rc; use futures::AsyncWrite; use futures::future::join_all; use hashbrown::HashMap; +use orchid_api_derive::Coding; use orchid_api_traits::{Encode, Request}; use orchid_base::interner::{IStr, es}; use orchid_base::name::Sym; use orchid_base::sym; -use orchid_extension::atom::{Atomic, Supports}; +use orchid_extension::atom::{Atomic, MethodSetBuilder, Supports}; use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use orchid_extension::expr::Expr; use orchid_extension::gen_expr::sym_ref; @@ -18,12 +19,15 @@ use crate::api; use crate::std::protocol::types::{GetImplMethod, GetTagIdMethod}; #[derive(Clone)] -pub struct Record(pub Rc>); -impl Atomic for Record { +pub struct RecordAtom(pub Rc>); +impl Atomic for RecordAtom { type Data = (); type Variant = OwnedVariant; + fn reg_reqs() -> MethodSetBuilder { + MethodSetBuilder::new().handle::().handle::() + } } -impl OwnedAtom for Record { +impl OwnedAtom for RecordAtom { type Refs = Vec; async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs { let (keys, values) = @@ -34,17 +38,17 @@ impl OwnedAtom for Record { async fn deserialize(mut dctx: impl DeserializeCtx, refs: Self::Refs) -> Self { let keys = join_all(dctx.decode::>().await.iter().map(|t| async { es(*t).await })).await; - Record(Rc::new(keys.into_iter().zip(refs).collect())) + RecordAtom(Rc::new(keys.into_iter().zip(refs).collect())) } async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } } -impl Supports for Record { +impl Supports for RecordAtom { async fn handle(&self, _: GetTagIdMethod) -> ::Response { Sym::literal("std::record::Record").await.to_api() } } -impl Supports for Record { +impl Supports for RecordAtom { async fn handle(&self, req: GetImplMethod) -> ::Response { let name = Sym::from_api(req.0).await; let val = if name == sym!(std::ops::get) { @@ -57,3 +61,9 @@ impl Supports for Record { Some(val.create().await.serialize().await) } } + +#[derive(Clone, Debug, Coding)] +pub struct CreateRecord(pub std::collections::HashMap); +impl Request for CreateRecord { + type Response = api::ExprTicket; +} diff --git a/orchid-std/src/std/record/record_lib.rs b/orchid-std/src/std/record/record_lib.rs index 5e7b128..2904d66 100644 --- a/orchid-std/src/std/record/record_lib.rs +++ b/orchid-std/src/std/record/record_lib.rs @@ -7,24 +7,24 @@ use orchid_extension::expr::Expr; use orchid_extension::tree::{GenMember, cnst, fun, prefix}; use crate::std::option::OrcOpt; -use crate::std::record::record_atom::Record; +use crate::std::record::record_atom::RecordAtom; use crate::std::string::str_atom::IntStrAtom; pub fn gen_record_lib() -> Vec { prefix("std::record", [ - cnst(true, "empty", Record(Rc::new(HashMap::new()))), - fun(true, "set", async |map: TAtom, key: IntStrAtom, val: Expr| { + cnst(true, "empty", RecordAtom(Rc::new(HashMap::new()))), + fun(true, "set", async |map: TAtom, key: IntStrAtom, val: Expr| { let mut map = own(&map).await.0.as_ref().clone(); map.insert(key.0.clone(), val); - Record(Rc::new(map)) + RecordAtom(Rc::new(map)) }), - fun(true, "get", async |map: TAtom, key: IntStrAtom| { + fun(true, "get", async |map: TAtom, key: IntStrAtom| { OrcOpt(own(&map).await.0.get(&key.0).cloned()) }), - fun(true, "delete", async |map: TAtom, key: IntStrAtom| { + fun(true, "delete", async |map: TAtom, key: IntStrAtom| { let mut map = own(&map).await.0.as_ref().clone(); map.remove(&key.0); - Record(Rc::new(map)) + RecordAtom(Rc::new(map)) }), ]) } diff --git a/orchid-std/src/std/std_system.rs b/orchid-std/src/std/std_system.rs index f59aaf2..a1cb11d 100644 --- a/orchid-std/src/std/std_system.rs +++ b/orchid-std/src/std/std_system.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use futures::future::join_all; use orchid_api_derive::{Coding, Hierarchy}; +use orchid_base::interner::es; use orchid_base::name::Sym; use orchid_base::reqnot::{Receipt, ReqHandle, ReqHandleExt}; use orchid_base::sym; @@ -23,7 +24,7 @@ use crate::std::option::{OptAtom, gen_option_lib}; use crate::std::protocol::proto_parser::{AsProtoParser, ProtoParser}; use crate::std::protocol::type_parser::{AsTypeParser, TypeParser}; use crate::std::protocol::types::{CreateTag, Tag, Tagged, gen_protocol_lib}; -use crate::std::record::record_atom::Record; +use crate::std::record::record_atom::{CreateRecord, RecordAtom}; use crate::std::record::record_lib::gen_record_lib; use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib}; use crate::std::string::str_lexer::StringLexer; @@ -36,6 +37,7 @@ use crate::{Float, Int}; pub enum StdReq { CreateTag(CreateTag), CreateTuple(CreateTuple), + CreateRecord(CreateRecord), CreateSymAtom(CreateSymAtom), } @@ -58,7 +60,7 @@ impl SystemCard for StdSystem { Some(StrAtom::dynfo()), Some(IntStrAtom::dynfo()), Some(OptAtom::dynfo()), - Some(Record::dynfo()), + Some(RecordAtom::dynfo()), Some(Tuple::dynfo()), Some(TupleBuilder::dynfo()), Some(Tag::dynfo()), @@ -74,6 +76,14 @@ impl System for StdSystem { let tk = tpl.to_expr().await.serialize().await; xreq.reply(req, &tk).await.unwrap() }, + StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => { + let values = + 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; + 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() @@ -109,5 +119,7 @@ impl System for StdSystem { gen_ops_lib(), ]) } - async fn prelude() -> Vec { vec![sym!(std), sym!(std::tuple), sym!(std::option)] } + async fn prelude() -> Vec { + vec![sym!(std), sym!(std::tuple), sym!(std::option), sym!(std::record)] + } } diff --git a/orcx/src/main.rs b/orcx/src/main.rs index d434548..84ba4ee 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -4,10 +4,13 @@ use tokio::time::Instant; pub mod parse_folder; use std::cell::RefCell; +use std::collections::HashMap; use std::fs::File; use std::io::{Read, Write}; +use std::pin::pin; use std::process::{Command, ExitCode}; use std::rc::Rc; +use std::time::Duration; use async_fn_stream::try_stream; use camino::{Utf8Path, Utf8PathBuf}; @@ -187,253 +190,294 @@ async fn main() -> io::Result { let logger = get_logger(&args); let logger2 = logger.clone(); unsafe { STARTUP = Some(Instant::now()) }; - local_set.spawn_local(with_stash(async move { - let ctx = &Ctx::new(SpawnerImpl, logger2); - let extensions = get_all_extensions(&args, ctx).try_collect::>().await.unwrap(); - time_print(&args, "Extensions loaded"); - match args.command { - Commands::Lex { file } => { - let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); - let mut file = File::open(file.as_std_path()).unwrap(); - let mut buf = String::new(); - file.read_to_string(&mut buf).unwrap(); - let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); - println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)) - }, - Commands::Parse { file } => { - let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); - let mut file = File::open(file.as_std_path()).unwrap(); - let mut buf = String::new(); - file.read_to_string(&mut buf).unwrap(); - let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); - let Some(first) = lexemes.first() else { - println!("File empty!"); - return; - }; - let pctx = HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) }; - let snip = Snippet::new(first, &lexemes); - match with_reporter(parse_items(&pctx, Substack::Bottom, snip)).await.unwrap() { - Err(errv) => { - eprintln!("{errv}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - }, - Ok(ptree) if ptree.is_empty() => { - eprintln!("File empty only after parsing, but no errors were reported"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - }, - Ok(ptree) => - for item in ptree { - println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true)) - }, - }; - }, - Commands::Repl => { - let mut counter = 0; - let mut imports = Vec::new(); - let usercode_path = sym!(usercode); - let mut stdin = BufReader::new(stdin()); - loop { - counter += 1; - let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); - print!("\\.> "); - std::io::stdout().flush().unwrap(); - let mut prompt = String::new(); - stdin.read_line(&mut prompt).await.unwrap(); - let name = is(&format!("_{counter}")).await; - let path = usercode_path.suffix([name.clone()]).await; - let mut lexemes = - lex(is(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap(); - let Some(discr) = lexemes.first() else { continue }; - writeln!( - log("debug"), - "lexed: {}", - take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true) - ) - .await; - let prefix_sr = SrcRange::zw(path.clone(), 0); - let process_lexemes = async |lexemes: &[ParsTokTree]| { - let snippet = Snippet::new(&lexemes[0], lexemes); - let parse_ctx = - HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; - match try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet)).await - { - Ok(items) => Some(items), - Err(e) => { - eprintln!("{e}"); - None - }, - } + let ctx = Ctx::new(SpawnerImpl, logger2); + let (signal_end_main, on_end_main) = futures::channel::oneshot::channel(); + let ctx1 = ctx.clone(); + local_set.spawn_local(async move { + let ctx = &ctx1; + with_stash(async move { + let extensions = + get_all_extensions(&args, ctx).try_collect::>().await.unwrap(); + time_print(&args, "Extensions loaded"); + match args.command { + Commands::Lex { file } => { + let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); + let mut file = File::open(file.as_std_path()).unwrap(); + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); + println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)) + }, + Commands::Parse { file } => { + let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); + let mut file = File::open(file.as_std_path()).unwrap(); + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); + let Some(first) = lexemes.first() else { + println!("File empty!"); + return; }; - let add_imports = |items: &mut Vec, imports: &[Import]| { - items.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone()))); - }; - if discr.is_kw(is("import").await) { - let Some(import_lines) = process_lexemes(&lexemes).await else { continue }; - imports.extend(import_lines.into_iter().map(|it| match it.kind { - ItemKind::Import(imp) => imp, - _ => panic!("Expected imports from import line"), - })); - continue; - } - if !discr.is_kw(is("let").await) { - let prefix = [is("export").await, is("let").await, name.clone(), is("=").await]; - lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); - } - let Some(mut new_lines) = process_lexemes(&lexemes).await else { continue }; - let const_decl = new_lines.iter().exactly_one().expect("Multiple lines from let"); - let input_sr = const_decl.sr.map_range(|_| 0..0); - let const_name = match &const_decl.kind { - ItemKind::Member(ParsedMember { name: const_name, .. }) => const_name.clone(), - _ => panic!("Expected exactly one constant declaration from let"), - }; - add_imports(&mut new_lines, &imports); - imports.push(Import::new(input_sr.clone(), VPath::new(path.segs()), const_name.clone())); - let new_module = ParsedModule::new(true, new_lines); - match with_reporter(root.add_parsed(&new_module, path.clone())).await { - Ok(new) => root = new, + let pctx = HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) }; + let snip = Snippet::new(first, &lexemes); + match with_reporter(parse_items(&pctx, Substack::Bottom, snip)).await.unwrap() { Err(errv) => { eprintln!("{errv}"); *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; }, - } - eprintln!("parsed"); - let entrypoint = - ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos()); - let mut xctx = ExecCtx::new(root.clone(), entrypoint).await; - eprintln!("executed"); - xctx.set_gas(Some(1000)); - xctx.execute().await; - match xctx.result() { - ExecResult::Value(val) => println!( - "{const_name} = {}", - take_first(&val.print(&FmtCtxImpl::default()).await, false) - ), - ExecResult::Err(e) => println!("error: {e}"), - ExecResult::Gas(_) => println!("Ran out of gas!"), - } - } - }, - Commands::ModTree { proj, prefix } => { - let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap(); - if let Some(proj_path) = proj { - let path = proj_path.into_std_path_buf(); - match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { - Ok(r) => root = r, - Err(e) => { - eprintln!("{e}"); + Ok(ptree) if ptree.is_empty() => { + eprintln!("File empty only after parsing, but no errors were reported"); *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; }, - } - } - let prefix = match prefix { - Some(pref) => VPath::parse(&pref).await, - None => VPath::new([]), - }; - let root_data = root.0.read().await; - print_mod(&root_data.root, prefix, &root_data).await; - async fn print_mod(module: &Module, path: VPath, root: &RootData) { - let indent = " ".repeat(path.len()); - for (key, tgt) in &module.imports { - match tgt { - Ok(tgt) => println!("{indent}import {key} => {}", tgt.target), - Err(opts) => println!( - "{indent}import {key} conflicts between {}", - opts.iter().map(|i| &i.target).join(" ") - ), - } - } - for (key, mem) in &module.members { - let new_path = path.clone().name_with_suffix(key.clone()).to_sym().await; - match mem.kind(root.ctx.clone(), &root.consts).await { - MemberKind::Module(module) => { - println!("{indent}module {key} {{"); - print_mod(module, VPath::new(new_path.segs()), root).boxed_local().await; - println!("{indent}}}") + Ok(ptree) => + for item in ptree { + println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true)) }, - MemberKind::Const => { - let value = root.consts.get(&new_path).expect("Missing const!"); - println!("{indent}const {key} = {}", fmt(value).await) - }, - } - } - } - }, - Commands::Exec { proj, code } => { - let path = sym!(usercode); - let prefix_sr = SrcRange::zw(path.clone(), 0); - let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); - if let Some(proj_path) = proj { - let path = proj_path.into_std_path_buf(); - match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { - Ok(r) => root = r, - Err(e) => { - eprintln!("{e}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - }, - } - } - let mut lexemes = match lex(is(code.trim()).await, path.clone(), &systems, ctx).await { - Ok(lexemes) => { + }; + }, + Commands::Repl => { + let mut counter = 0; + let mut imports = Vec::new(); + let usercode_path = sym!(usercode); + let mut stdin = BufReader::new(stdin()); + loop { + counter += 1; + let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); + print!("\\.> "); + std::io::stdout().flush().unwrap(); + let mut prompt = String::new(); + stdin.read_line(&mut prompt).await.unwrap(); + let name = is(&format!("_{counter}")).await; + let path = usercode_path.suffix([name.clone()]).await; + let mut lexemes = + lex(is(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap(); + let Some(discr) = lexemes.first() else { continue }; writeln!( log("debug"), "lexed: {}", - fmt_v::(lexemes.iter()).await.join(" ") + take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true) ) .await; - lexemes - }, - Err(e) => { - eprintln!("{e}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - }, - }; - let parse_ctx = - HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; - let prefix = [is("export").await, is("let").await, is("entrypoint").await, is("=").await]; - lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); - let snippet = Snippet::new(&lexemes[0], &lexemes); - let entrypoint = match try_with_reporter(parse_item( - &parse_ctx, - Substack::Bottom, - vec![], - snippet, - )) - .await - { - Ok(items) => ParsedModule::new(true, items), - Err(e) => { - eprintln!("{e}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - }, - }; - let root = match with_reporter(root.add_parsed(&entrypoint, path.clone())).await { - Err(e) => { - eprintln!("{e}"); - *exit_code1.borrow_mut() = ExitCode::FAILURE; - return; - }, - Ok(new_root) => new_root, - }; - let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos()); - let mut xctx = ExecCtx::new(root, expr).await; - xctx.set_gas(Some(10_000)); - xctx.execute().await; - match xctx.result() { - ExecResult::Value(val) => { - println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)) - }, - ExecResult::Err(e) => println!("error: {e}"), - ExecResult::Gas(_) => println!("Ran out of gas!"), - } - }, + let prefix_sr = SrcRange::zw(path.clone(), 0); + let process_lexemes = async |lexemes: &[ParsTokTree]| { + let snippet = Snippet::new(&lexemes[0], lexemes); + let parse_ctx = + HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; + match try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet)) + .await + { + Ok(items) => Some(items), + Err(e) => { + eprintln!("{e}"); + None + }, + } + }; + let add_imports = |items: &mut Vec, imports: &[Import]| { + items + .extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone()))); + }; + if discr.is_kw(is("import").await) { + let Some(import_lines) = process_lexemes(&lexemes).await else { continue }; + imports.extend(import_lines.into_iter().map(|it| match it.kind { + ItemKind::Import(imp) => imp, + _ => panic!("Expected imports from import line"), + })); + continue; + } + if !discr.is_kw(is("let").await) { + let prefix = [is("export").await, is("let").await, name.clone(), is("=").await]; + lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); + } + let Some(mut new_lines) = process_lexemes(&lexemes).await else { continue }; + let const_decl = new_lines.iter().exactly_one().expect("Multiple lines from let"); + let input_sr = const_decl.sr.map_range(|_| 0..0); + let const_name = match &const_decl.kind { + ItemKind::Member(ParsedMember { name: const_name, .. }) => const_name.clone(), + _ => panic!("Expected exactly one constant declaration from let"), + }; + add_imports(&mut new_lines, &imports); + imports.push(Import::new( + input_sr.clone(), + VPath::new(path.segs()), + const_name.clone(), + )); + let new_module = ParsedModule::new(true, new_lines); + match with_reporter(root.add_parsed(&new_module, path.clone())).await { + Ok(new) => root = new, + Err(errv) => { + eprintln!("{errv}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + } + eprintln!("parsed"); + let entrypoint = + ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos()); + let mut xctx = ExecCtx::new(root.clone(), entrypoint).await; + eprintln!("executed"); + xctx.set_gas(Some(1000)); + xctx.execute().await; + match xctx.result() { + ExecResult::Value(val) => println!( + "{const_name} = {}", + take_first(&val.print(&FmtCtxImpl::default()).await, false) + ), + ExecResult::Err(e) => println!("error: {e}"), + ExecResult::Gas(_) => println!("Ran out of gas!"), + } + } + }, + Commands::ModTree { proj, prefix } => { + let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap(); + if let Some(proj_path) = proj { + let path = proj_path.into_std_path_buf(); + match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { + Ok(r) => root = r, + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + } + } + let prefix = match prefix { + Some(pref) => VPath::parse(&pref).await, + None => VPath::new([]), + }; + let root_data = root.0.read().await; + print_mod(&root_data.root, prefix, &root_data).await; + async fn print_mod(module: &Module, path: VPath, root: &RootData) { + let indent = " ".repeat(path.len()); + for (key, tgt) in &module.imports { + match tgt { + Ok(tgt) => println!("{indent}import {key} => {}", tgt.target), + Err(opts) => println!( + "{indent}import {key} conflicts between {}", + opts.iter().map(|i| &i.target).join(" ") + ), + } + } + for (key, mem) in &module.members { + let new_path = path.clone().name_with_suffix(key.clone()).to_sym().await; + match mem.kind(root.ctx.clone(), &root.consts).await { + MemberKind::Module(module) => { + println!("{indent}module {key} {{"); + print_mod(module, VPath::new(new_path.segs()), root).boxed_local().await; + println!("{indent}}}") + }, + MemberKind::Const => { + let value = root.consts.get(&new_path).expect("Missing const!"); + println!("{indent}const {key} = {}", fmt(value).await) + }, + } + } + } + }, + Commands::Exec { proj, code } => { + let path = sym!(usercode); + let prefix_sr = SrcRange::zw(path.clone(), 0); + let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); + if let Some(proj_path) = proj { + let path = proj_path.into_std_path_buf(); + match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await { + Ok(r) => root = r, + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + } + } + let mut lexemes = match lex(is(code.trim()).await, path.clone(), &systems, ctx).await { + Ok(lexemes) => { + writeln!( + log("debug"), + "lexed: {}", + fmt_v::(lexemes.iter()).await.join(" ") + ) + .await; + lexemes + }, + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + }; + let parse_ctx = + HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; + let prefix = [is("export").await, is("let").await, is("entrypoint").await, is("=").await]; + lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); + let snippet = Snippet::new(&lexemes[0], &lexemes); + let entrypoint = match try_with_reporter(parse_item( + &parse_ctx, + Substack::Bottom, + vec![], + snippet, + )) + .await + { + Ok(items) => ParsedModule::new(true, items), + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + }; + let root = match with_reporter(root.add_parsed(&entrypoint, path.clone())).await { + Err(e) => { + eprintln!("{e}"); + *exit_code1.borrow_mut() = ExitCode::FAILURE; + return; + }, + Ok(new_root) => new_root, + }; + let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos()); + let mut xctx = ExecCtx::new(root.clone(), expr).await; + xctx.set_gas(Some(10_000)); + xctx.execute().await; + match xctx.result() { + ExecResult::Value(val) => { + println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)) + }, + ExecResult::Err(e) => println!("error: {e}"), + ExecResult::Gas(_) => println!("Ran out of gas!"), + } + }, + }; + }) + .await; + signal_end_main.send(()).expect("cleanup should still be waiting"); + }); + let cleanup = async { + if on_end_main.await.is_err() { + return; } - })); - with_interner(local_interner(), with_logger(logger, local_set)).await; + tokio::time::sleep(Duration::from_secs(2)).await; + let mut extensions = HashMap::new(); + let systems = ctx.systems.read().await.values().filter_map(|v| v.upgrade()).collect_vec(); + let exprs = ctx.exprs.iter().collect_vec(); + for system in &systems { + extensions.insert(system.ext().name().clone(), system.ext().clone()); + } + if extensions.is_empty() && systems.is_empty() && exprs.is_empty() { + return; + } + eprintln!("Shutdown is taking long. The following language constructs are still live:"); + eprintln!("Extensions: {}", extensions.keys().join(", ")); + for sys in &systems { + eprintln!("System: {:?} = {}", sys.id(), sys.ctor().name()) + } + for (rc, expr) in &exprs { + eprintln!("{rc}x {:?} = {}", expr.id(), fmt(expr).await) + } + }; + futures::future::select( + pin!(cleanup), + pin!(with_interner(local_interner(), with_logger(logger, local_set))), + ) + .await; let x = *exit_code.borrow(); Ok(x) }