bit of cleanup, few steps towards commands demo
This commit is contained in:
@@ -13,11 +13,15 @@ clap = { version = "4.5.54", features = ["derive", "env", "cargo"] }
|
||||
ctrlc = "3.5.1"
|
||||
futures = "0.3.31"
|
||||
itertools = "0.14.0"
|
||||
never = "0.1.0"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
orchid-extension = { version = "0.1.0", path = "../orchid-extension" }
|
||||
orchid-host = { version = "0.1.0", path = "../orchid-host", features = [
|
||||
"tokio",
|
||||
"orchid-extension",
|
||||
] }
|
||||
stacker = "0.1.23"
|
||||
substack = "1.1.1"
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.18", features = ["compat"] }
|
||||
|
||||
183
orcx/src/main.rs
183
orcx/src/main.rs
@@ -1,6 +1,12 @@
|
||||
use orchid_base::Logger;
|
||||
use never::Never;
|
||||
use orchid_base::{IStr, Logger, NameLike, Receipt, ReqHandle, Sym};
|
||||
use orchid_extension::{self as ox, OrcReader, OrcWriter};
|
||||
use orchid_host::cmd_system::{CmdEvent, CmdRunner};
|
||||
use orchid_host::dylib::ext_dylib;
|
||||
use orchid_host::inline::ext_inline;
|
||||
use orchid_host::tree::Root;
|
||||
use tokio::time::Instant;
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
pub mod parse_folder;
|
||||
mod print_mod;
|
||||
mod repl;
|
||||
@@ -22,7 +28,7 @@ use futures::{FutureExt, Stream, TryStreamExt, io};
|
||||
use itertools::Itertools;
|
||||
use orchid_base::local_interner::local_interner;
|
||||
use orchid_base::{
|
||||
FmtCtxImpl, Format, Snippet, SrcRange, Token, VPath, fmt, fmt_v, is, log, sym, take_first,
|
||||
FmtCtxImpl, Snippet, SrcRange, Token, VPath, fmt, fmt_v, is, log, sym, take_first,
|
||||
try_with_reporter, ttv_fmt, with_interner, with_logger, with_reporter, with_stash,
|
||||
};
|
||||
use orchid_host::ctx::{Ctx, JoinHandle, Spawner};
|
||||
@@ -34,7 +40,7 @@ use orchid_host::logger::LoggerImpl;
|
||||
use orchid_host::parse::{HostParseCtxImpl, parse_item, parse_items};
|
||||
use orchid_host::parsed::{ParsTokTree, ParsedModule};
|
||||
use orchid_host::subprocess::ext_command;
|
||||
use orchid_host::system::init_systems;
|
||||
use orchid_host::system::{System, init_systems};
|
||||
use substack::Substack;
|
||||
use tokio::task::{LocalSet, spawn_local};
|
||||
|
||||
@@ -265,6 +271,90 @@ impl Spawner for SpawnerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct StdIoSystem;
|
||||
impl ox::SystemCard for StdIoSystem {
|
||||
type Ctor = Self;
|
||||
type Req = Never;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn ox::AtomOps>>> { [] }
|
||||
}
|
||||
impl ox::SystemCtor for StdIoSystem {
|
||||
const NAME: &'static str = "orcx::stdio";
|
||||
const VERSION: f64 = 0.0;
|
||||
type Card = Self;
|
||||
type Deps = ();
|
||||
type Instance = Self;
|
||||
fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance { Self }
|
||||
}
|
||||
impl ox::System for StdIoSystem {
|
||||
type Ctor = Self;
|
||||
async fn request<'a>(
|
||||
&self,
|
||||
_: Box<dyn ReqHandle<'a> + 'a>,
|
||||
req: ox::ReqForSystem<Self>,
|
||||
) -> Receipt<'a> {
|
||||
match req {}
|
||||
}
|
||||
async fn env(&self) -> Vec<ox::tree::GenMember> {
|
||||
// TODO: this is impractical, try dialogue interface eg. prompt, choice, println
|
||||
ox::tree::module(true, "stdio", [
|
||||
ox::tree::fun(true, "get_stdin", async |cb: ox::Expr| {
|
||||
ox::cmd(async move || {
|
||||
Some(ox::gen_expr::call(cb.clone(), OrcReader(tokio::io::stdin().compat())))
|
||||
})
|
||||
}),
|
||||
ox::tree::fun(true, "get_stdout", async |cb: ox::Expr| {
|
||||
ox::cmd(async move || {
|
||||
Some(ox::gen_expr::call(cb.clone(), OrcWriter(tokio::io::stdout().compat_write())))
|
||||
})
|
||||
}),
|
||||
ox::tree::fun(true, "get_stderr", async |cb: ox::Expr| {
|
||||
ox::cmd(async move || {
|
||||
Some(ox::gen_expr::call(cb.clone(), OrcWriter(tokio::io::stderr().compat_write())))
|
||||
})
|
||||
}),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_proj_if_set(root: &mut Root, args: &Args, ctx: &Ctx) -> Result<(), String> {
|
||||
if let Some(proj_path) = &args.proj {
|
||||
let path = proj_path.clone().into_std_path_buf();
|
||||
*root = try_with_reporter(parse_folder(root, path, sym!(src), ctx.clone()))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_const_at(
|
||||
root: &mut Root,
|
||||
ctx: &Ctx,
|
||||
systems: &[System],
|
||||
path: Sym,
|
||||
value: IStr,
|
||||
) -> Result<(), String> {
|
||||
let (name, parent) = path.split_last_seg();
|
||||
let path = Sym::new(parent.iter().cloned())
|
||||
.await
|
||||
.map_err(|_| format!("Const path must have two segments, found {path}"))?;
|
||||
let prefix_sr = SrcRange::zw(path.clone(), 0);
|
||||
let mut lexemes =
|
||||
lex(value.clone(), path.clone(), systems, ctx).await.map_err(|e| e.to_string())?;
|
||||
writeln!(log("debug"), "lexed: {}", fmt_v::<ParsTokTree>(lexemes.iter()).await.join(" ")).await;
|
||||
let parse_ctx = HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems };
|
||||
let prefix = [is("export").await, is("let").await, name, is("=").await];
|
||||
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
|
||||
let snippet = Snippet::new(&lexemes[0], &lexemes);
|
||||
let items = try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let entrypoint = ParsedModule::new(true, items);
|
||||
*root =
|
||||
with_reporter(root.add_parsed(&entrypoint, path.clone())).await.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> io::Result<ExitCode> {
|
||||
// Use a 10MB stack for single-threaded, unoptimized operation
|
||||
stacker::grow(10 * 1024 * 1024, || {
|
||||
@@ -283,10 +373,10 @@ fn main() -> io::Result<ExitCode> {
|
||||
local_set.spawn_local(async move {
|
||||
let ctx = &ctx1;
|
||||
let res = with_stash(async move {
|
||||
let extensions =
|
||||
let mut extensions =
|
||||
get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
|
||||
time_print(&args, "Extensions loaded");
|
||||
match args.command {
|
||||
match &args.command {
|
||||
Commands::Lex { file, line } => {
|
||||
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
let mut buf = String::new();
|
||||
@@ -323,73 +413,64 @@ fn main() -> io::Result<ExitCode> {
|
||||
);
|
||||
}
|
||||
for item in ptree {
|
||||
println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true))
|
||||
println!("{}", fmt(&item).await)
|
||||
}
|
||||
},
|
||||
Commands::Repl => repl(&args, &extensions, ctx.clone()).await?,
|
||||
Commands::ModTree { prefix } => {
|
||||
let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
if let Some(proj_path) = args.proj {
|
||||
let path = proj_path.into_std_path_buf();
|
||||
root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
load_proj_if_set(&mut root, &args, ctx).await?;
|
||||
let prefix = match prefix {
|
||||
Some(pref) => VPath::parse(&pref).await,
|
||||
Some(pref) => VPath::parse(pref).await,
|
||||
None => VPath::new([]),
|
||||
};
|
||||
let root_data = root.0.read().await;
|
||||
print_mod::print_mod(&root_data.root, prefix, &root_data).await;
|
||||
},
|
||||
Commands::Eval { code } => {
|
||||
let path = sym!(usercode);
|
||||
let prefix_sr = SrcRange::zw(path.clone(), 0);
|
||||
let path = sym!(usercode::entrypoint);
|
||||
let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
if let Some(proj_path) = args.proj {
|
||||
let path = proj_path.into_std_path_buf();
|
||||
root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
let mut lexemes = lex(is(code.trim()).await, path.clone(), &systems, ctx)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
writeln!(
|
||||
log("debug"),
|
||||
"lexed: {}",
|
||||
fmt_v::<ParsTokTree>(lexemes.iter()).await.join(" ")
|
||||
)
|
||||
.await;
|
||||
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 items =
|
||||
try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let entrypoint = ParsedModule::new(true, items);
|
||||
let root = with_reporter(root.add_parsed(&entrypoint, path.clone()))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos());
|
||||
load_proj_if_set(&mut root, &args, ctx).await?;
|
||||
add_const_at(&mut root, ctx, &systems[..], path.clone(), is(code).await).await?;
|
||||
let expr = ExprKind::Const(path.clone()).at(SrcRange::zw(path.clone(), 0).pos());
|
||||
let mut xctx = ExecCtx::new(root.clone(), expr).await;
|
||||
if !args.no_gas {
|
||||
xctx.set_gas(Some(args.gas));
|
||||
}
|
||||
match xctx.execute().await {
|
||||
ExecResult::Value(val, _) => {
|
||||
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
|
||||
},
|
||||
ExecResult::Value(val, _) => println!("{}", fmt(&val).await),
|
||||
ExecResult::Err(e, _) => println!("error: {e}"),
|
||||
ExecResult::Gas(_) => println!("Ran out of gas!"),
|
||||
ExecResult::Gas(_) => println!("Exceeded gas limit of {}", args.gas),
|
||||
}
|
||||
},
|
||||
Commands::Exec { main: _ } => {
|
||||
todo!("Integration of the command system is in-dev")
|
||||
Commands::Exec { main } => {
|
||||
let path = sym!(usercode::entrypoint);
|
||||
let (mut root, mut systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
let io_ext_builder = ox::ExtensionBuilder::new("orcx::stdio").system(StdIoSystem);
|
||||
let io_ext_init = ext_inline(io_ext_builder, ctx.clone()).await;
|
||||
let io_ext =
|
||||
Extension::new(io_ext_init, ctx.clone()).await.map_err(|e| e.to_string())?;
|
||||
let io_ctor = (io_ext.system_ctors().find(|ctor| ctor.name() == "orchid::cmd"))
|
||||
.expect("Missing command system ctor");
|
||||
let (io_root, io_system) = io_ctor.run(vec![]).await;
|
||||
root = root.merge(&io_root).await.expect("Failed to merge stdio root into tree");
|
||||
systems.push(io_system);
|
||||
extensions.push(io_ext);
|
||||
load_proj_if_set(&mut root, &args, ctx).await?;
|
||||
add_const_at(&mut root, ctx, &systems[..], path.clone(), is(main).await).await?;
|
||||
let expr = ExprKind::Const(path.clone()).at(SrcRange::zw(path.clone(), 0).pos());
|
||||
let mut crun = CmdRunner::new(root, ctx.clone(), [expr]).await;
|
||||
if !args.no_gas {
|
||||
crun.set_gas(args.gas);
|
||||
}
|
||||
match crun.execute().await {
|
||||
CmdEvent::Exit | CmdEvent::Settled => (),
|
||||
CmdEvent::Err(e) => println!("error: {e}"),
|
||||
CmdEvent::Gas => println!("Exceeded gas limit of {}", args.gas),
|
||||
CmdEvent::NonCommand(val) => {
|
||||
println!("Non-command value: {}", fmt(&val).await)
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user