All sorts of test scaffolding works now

This commit is contained in:
2025-02-20 18:06:44 +01:00
parent 9d744550c1
commit 9e7648bc72
41 changed files with 1230 additions and 436 deletions

View File

@@ -6,9 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-std = "1.13.0"
async-stream = "0.3.6"
camino = "1.1.9"
clap = { version = "4.5.24", features = ["derive", "env"] }
ctrlc = "3.4.5"
futures = "0.3.31"
itertools = "0.14.0"
orchid-base = { version = "0.1.0", path = "../orchid-base" }

View File

@@ -1,9 +1,10 @@
use std::fs::File;
use std::io::Read;
use std::io::{Read, Write};
use std::mem;
use std::process::{Command, ExitCode};
use std::rc::Rc;
use async_std::io::stdin;
use async_stream::try_stream;
use camino::Utf8PathBuf;
use clap::{Parser, Subcommand};
@@ -11,13 +12,17 @@ use futures::{Stream, TryStreamExt, io};
use orchid_base::clone;
use orchid_base::error::ReporterImpl;
use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::location::Pos;
use orchid_base::logging::{LogStrategy, Logger};
use orchid_base::macros::mtreev_fmt;
use orchid_base::parse::Snippet;
use orchid_base::tree::ttv_fmt;
use orchid_host::ctx::Ctx;
use orchid_host::execute::{ExecCtx, ExecResult};
use orchid_host::expr::mtreev_to_expr;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::parse::{ParseCtxImpl, parse_items};
use orchid_host::parse::{ParseCtxImpl, parse_items, parse_mtree};
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use substack::Substack;
@@ -30,6 +35,8 @@ pub struct Args {
extension: Vec<Utf8PathBuf>,
#[arg(short, long, env = "ORCHID_DEFAULT_SYSTEMS", value_delimiter = ';')]
system: Vec<String>,
#[arg(short, long)]
verbose: bool,
#[command(subcommand)]
command: Commands,
}
@@ -44,11 +51,17 @@ pub enum Commands {
#[arg(short, long)]
file: Utf8PathBuf,
},
Repl,
Execute {
#[arg()]
code: String,
},
}
fn get_all_extensions<'a>(
args: &'a Args,
logger: &'a Logger,
msg_logger: &'a Logger,
ctx: &'a Ctx,
) -> impl Stream<Item = io::Result<Extension>> + 'a {
try_stream! {
@@ -58,9 +71,9 @@ fn get_all_extensions<'a>(
} else {
ext_path.clone()
};
let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), ctx.clone()).await
let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()).await
.unwrap();
let ext = Extension::new(init, logger.clone(), ctx.clone())?;
let ext = Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?;
spawn_local(clone!(ext; async move { loop { ext.recv_one().await }}));
yield ext
}
@@ -74,31 +87,35 @@ async fn main() -> io::Result<ExitCode> {
.run_until(async {
let args = Args::parse();
let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut))));
let logger = Logger::new(LogStrategy::Discard);
let extensions =
get_all_extensions(&args, &logger, ctx).try_collect::<Vec<Extension>>().await.unwrap();
let i = &ctx.i;
let logger =
Logger::new(if args.verbose { LogStrategy::StdErr } else { LogStrategy::Discard });
let extensions = get_all_extensions(&args, &logger, &Logger::new(LogStrategy::Discard), ctx)
.try_collect::<Vec<Extension>>()
.await
.unwrap();
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(ctx.i.i(&buf).await, &systems, ctx).await.unwrap();
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i: &ctx.i }).await, true))
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap();
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).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(ctx.i.i(&buf).await, &systems, ctx).await.unwrap();
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap();
let Some(first) = lexemes.first() else {
println!("File empty!");
return;
};
let reporter = ReporterImpl::new();
let pctx = ParseCtxImpl { reporter: &reporter, systems: &systems };
let snip = Snippet::new(first, &lexemes, &ctx.i);
let snip = Snippet::new(first, &lexemes, i);
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
if let Some(errv) = reporter.errv() {
eprintln!("{errv}");
@@ -111,7 +128,65 @@ async fn main() -> io::Result<ExitCode> {
return;
}
for item in ptree {
println!("{}", take_first(&item.print(&FmtCtxImpl { i: &ctx.i }).await, true))
println!("{}", take_first(&item.print(&FmtCtxImpl { i }).await, true))
}
},
Commands::Repl => loop {
let 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 lexemes = lex(i.i(prompt.trim()).await, &systems, ctx).await.unwrap();
if args.verbose {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let mtreev = parse_mtree(
Snippet::new(&lexemes[0], &lexemes, i),
Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await),
)
.await
.unwrap();
if args.verbose {
let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
println!("parsed: {}", take_first(&fmt, true));
}
let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), expr.at(Pos::None)).await;
xctx.set_gas(Some(1000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) =>
println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},
Commands::Execute { code } => {
let systems = init_systems(&args.system, &extensions).await.unwrap();
let lexemes = lex(i.i(code.trim()).await, &systems, ctx).await.unwrap();
if args.verbose {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let mtreev = parse_mtree(
Snippet::new(&lexemes[0], &lexemes, i),
Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await),
)
.await
.unwrap();
if args.verbose {
let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
println!("parsed: {}", take_first(&fmt, true));
}
let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), expr.at(Pos::None)).await;
xctx.set_gas(Some(1000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) =>
println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},
}