Fixed a hang when the cleanup code for an extension is too slow
This commit is contained in:
@@ -6,6 +6,7 @@ orcxdb = "xtask orcxdb"
|
|||||||
[env]
|
[env]
|
||||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||||
ORCHID_EXTENSIONS = "target/debug/orchid_std"
|
ORCHID_EXTENSIONS = "target/debug/orchid_std"
|
||||||
|
#ORCHID_EXTENSIONS = "target/debug/orchid-std-piped"
|
||||||
ORCHID_DEFAULT_SYSTEMS = "orchid::std;orchid::macros"
|
ORCHID_DEFAULT_SYSTEMS = "orchid::std;orchid::macros"
|
||||||
ORCHID_LOG_BUFFERS = "true"
|
ORCHID_LOG_BUFFERS = "true"
|
||||||
RUST_BACKTRACE = "1"
|
RUST_BACKTRACE = "1"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use memo_map::MemoMap;
|
|||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_api_traits::{Decode, Encode, enc_vec};
|
use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, take_first};
|
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, Format, take_first};
|
||||||
use orchid_base::logging::log;
|
use orchid_base::logging::log;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
@@ -93,10 +93,28 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||||
Box::pin(async move { take_atom(id.unwrap()).await.dyn_call(arg).await })
|
Box::pin(async move {
|
||||||
|
writeln!(
|
||||||
|
log("msg"),
|
||||||
|
"owned call {} {}",
|
||||||
|
take_first(&AtomReadGuard::new(id.unwrap()).await.dyn_print().await, false),
|
||||||
|
take_first(&arg.print(&FmtCtxImpl::default()).await, true),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
take_atom(id.unwrap()).await.dyn_call(arg).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn call_ref<'a>(&'a self, AtomCtx(_, id): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
fn call_ref<'a>(&'a self, AtomCtx(_, id): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_call_ref(arg).await })
|
Box::pin(async move {
|
||||||
|
writeln!(
|
||||||
|
log("msg"),
|
||||||
|
"owned call_ref {} {}",
|
||||||
|
take_first(&AtomReadGuard::new(id.unwrap()).await.dyn_print().await, false),
|
||||||
|
take_first(&arg.print(&FmtCtxImpl::default()).await, true),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
AtomReadGuard::new(id.unwrap()).await.dyn_call_ref(arg).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
||||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
||||||
|
|||||||
@@ -103,18 +103,24 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
impl Format for Expr {
|
impl Format for Expr {
|
||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
return print_expr(self, c, Substack::Bottom).await;
|
return print_expr(self, c, Substack::Bottom, &[]).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn print_expr<'a>(
|
pub async fn print_expr<'a>(
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||||
visited: Substack<'_, api::ExprTicket>,
|
visited: Substack<'_, api::ExprTicket>,
|
||||||
|
id_only: &[api::ExprTicket],
|
||||||
) -> FmtUnit {
|
) -> FmtUnit {
|
||||||
if visited.iter().any(|id| id == &expr.id()) {
|
if visited.iter().any(|id| id == &expr.id()) {
|
||||||
return "CYCLIC_EXPR".to_string().into();
|
return "CYCLIC_EXPR".to_string().into();
|
||||||
}
|
}
|
||||||
print_exprkind(&*expr.kind().read().await, c, visited.push(expr.id())).boxed_local().await
|
if id_only.iter().any(|id| id == &expr.id()) {
|
||||||
|
return format!("{:?}", expr.id()).into();
|
||||||
|
}
|
||||||
|
print_exprkind(&*expr.kind().read().await, c, visited.push(expr.id()), id_only)
|
||||||
|
.boxed_local()
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -138,13 +144,14 @@ impl ExprKind {
|
|||||||
}
|
}
|
||||||
impl Format for ExprKind {
|
impl Format for ExprKind {
|
||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
print_exprkind(self, c, Substack::Bottom).await
|
print_exprkind(self, c, Substack::Bottom, &[]).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn print_exprkind<'a>(
|
async fn print_exprkind<'a>(
|
||||||
ek: &ExprKind,
|
ek: &ExprKind,
|
||||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||||
visited: Substack<'_, api::ExprTicket>,
|
visited: Substack<'_, api::ExprTicket>,
|
||||||
|
id_only: &[api::ExprTicket],
|
||||||
) -> FmtUnit {
|
) -> FmtUnit {
|
||||||
match &ek {
|
match &ek {
|
||||||
ExprKind::Arg => "Arg".to_string().into(),
|
ExprKind::Arg => "Arg".to_string().into(),
|
||||||
@@ -156,10 +163,10 @@ async fn print_exprkind<'a>(
|
|||||||
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
.unbounded("{0b} {1l}")
|
.unbounded("{0b} {1l}")
|
||||||
.bounded("({0b} {1})")))
|
.bounded("({0b} {1})")))
|
||||||
.units([print_expr(f, c, visited).await, print_expr(x, c, visited).await]),
|
.units([print_expr(f, c, visited, id_only).await, print_expr(x, c, visited, id_only).await]),
|
||||||
ExprKind::Identity(id) =>
|
ExprKind::Identity(id) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{{0}}}"))).units([print_expr(
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{{0}}}"))).units([print_expr(
|
||||||
id, c, visited,
|
id, c, visited, id_only,
|
||||||
)
|
)
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
.await]),
|
.await]),
|
||||||
@@ -167,14 +174,14 @@ async fn print_exprkind<'a>(
|
|||||||
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
// .unbounded("\\.{0l}")
|
// .unbounded("\\.{0l}")
|
||||||
.bounded("(\\.{0b})")))
|
.bounded("(\\.{0b})")))
|
||||||
.units([print_expr(body, c, visited).await]),
|
.units([print_expr(body, c, visited, id_only).await]),
|
||||||
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
// .unbounded("\\{0b}. {1l}")
|
// .unbounded("\\{0b}. {1l}")
|
||||||
.bounded("(\\{0b}. {1b})")))
|
.bounded("(\\{0b}. {1b})")))
|
||||||
.units([format!("{path}").into(), print_expr(body, c, visited).await]),
|
.units([format!("{path}").into(), print_expr(body, c, visited, id_only).await]),
|
||||||
ExprKind::Seq(l, r) =>
|
ExprKind::Seq(l, r) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}")))
|
||||||
.units([print_expr(l, c, visited).await, print_expr(r, c, visited).await]),
|
.units([print_expr(l, c, visited, id_only).await, print_expr(r, c, visited, id_only).await]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,11 +102,7 @@ impl Extension {
|
|||||||
this.0.ctx.exprs.give_expr(target)
|
this.0.ctx.exprs.give_expr(target)
|
||||||
},
|
},
|
||||||
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => {
|
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => {
|
||||||
if this.is_own_sys(rel.0).await {
|
this.0.ctx.exprs.take_expr(rel.1);
|
||||||
this.0.ctx.exprs.take_expr(rel.1);
|
|
||||||
} else {
|
|
||||||
writeln!(log("warn"), "Not our system {:?}", rel.0).await
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
api::ExtHostNotif::Log(api::Log { category, message }) =>
|
api::ExtHostNotif::Log(api::Log { category, message }) =>
|
||||||
write!(log(&es(category).await), "{message}").await,
|
write!(log(&es(category).await), "{message}").await,
|
||||||
@@ -130,7 +126,7 @@ impl Extension {
|
|||||||
// Atom printing and interning is never reported because it generates too much
|
// Atom printing and interning is never reported because it generates too much
|
||||||
// noise
|
// noise
|
||||||
if !matches!(req, api::ExtHostReq::ExtAtomPrint(_))
|
if !matches!(req, api::ExtHostReq::ExtAtomPrint(_))
|
||||||
|| matches!(req, api::ExtHostReq::IntReq(_))
|
&& !matches!(req, api::ExtHostReq::IntReq(_))
|
||||||
{
|
{
|
||||||
writeln!(log("msg"), "Host received request {req:?}").await;
|
writeln!(log("msg"), "Host received request {req:?}").await;
|
||||||
}
|
}
|
||||||
@@ -299,14 +295,6 @@ impl Extension {
|
|||||||
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
|
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
|
||||||
pub fn system_ctors(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
|
pub fn system_ctors(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn is_own_sys(&self, id: api::SysId) -> bool {
|
|
||||||
let Some(sys) = self.ctx().system_inst(id).await else {
|
|
||||||
writeln!(log("warn"), "Invalid system ID {id:?}").await;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
Rc::ptr_eq(&self.0, &sys.ext().0)
|
|
||||||
}
|
|
||||||
#[must_use]
|
|
||||||
pub fn next_pars(&self) -> NonZeroU64 {
|
pub fn next_pars(&self) -> NonZeroU64 {
|
||||||
let mut next_pars = self.0.next_pars.borrow_mut();
|
let mut next_pars = self.0.next_pars.borrow_mut();
|
||||||
*next_pars = next_pars.checked_add(1).unwrap_or(NonZeroU64::new(1).unwrap());
|
*next_pars = next_pars.checked_add(1).unwrap_or(NonZeroU64::new(1).unwrap());
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "orchid-std-dbg"
|
name = "orchid-std-piped"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
476
orcx/src/main.rs
476
orcx/src/main.rs
@@ -187,261 +187,251 @@ async fn main() -> io::Result<ExitCode> {
|
|||||||
let logger = get_logger(&args);
|
let logger = get_logger(&args);
|
||||||
let logger2 = logger.clone();
|
let logger2 = logger.clone();
|
||||||
unsafe { STARTUP = Some(Instant::now()) };
|
unsafe { STARTUP = Some(Instant::now()) };
|
||||||
local_set.spawn_local(async move {
|
local_set.spawn_local(with_stash(async move {
|
||||||
let ctx = &Ctx::new(SpawnerImpl, logger2);
|
let ctx = &Ctx::new(SpawnerImpl, logger2);
|
||||||
with_stash(async {
|
let extensions = get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
|
||||||
let extensions =
|
time_print(&args, "Extensions loaded");
|
||||||
get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
|
match args.command {
|
||||||
time_print(&args, "Extensions loaded");
|
Commands::Lex { file } => {
|
||||||
match args.command {
|
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||||
Commands::Lex { file } => {
|
let mut file = File::open(file.as_std_path()).unwrap();
|
||||||
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
let mut buf = String::new();
|
||||||
let mut file = File::open(file.as_std_path()).unwrap();
|
file.read_to_string(&mut buf).unwrap();
|
||||||
let mut buf = String::new();
|
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap();
|
||||||
file.read_to_string(&mut buf).unwrap();
|
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true))
|
||||||
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();
|
||||||
Commands::Parse { file } => {
|
let mut file = File::open(file.as_std_path()).unwrap();
|
||||||
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
|
let mut buf = String::new();
|
||||||
let mut file = File::open(file.as_std_path()).unwrap();
|
file.read_to_string(&mut buf).unwrap();
|
||||||
let mut buf = String::new();
|
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap();
|
||||||
file.read_to_string(&mut buf).unwrap();
|
let Some(first) = lexemes.first() else {
|
||||||
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap();
|
println!("File empty!");
|
||||||
let Some(first) = lexemes.first() else {
|
return;
|
||||||
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 pctx = HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) };
|
let add_imports = |items: &mut Vec<Item>, imports: &[Import]| {
|
||||||
let snip = Snippet::new(first, &lexemes);
|
items.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone())));
|
||||||
match with_reporter(parse_items(&pctx, Substack::Bottom, snip)).await.unwrap() {
|
};
|
||||||
|
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) => {
|
Err(errv) => {
|
||||||
eprintln!("{errv}");
|
eprintln!("{errv}");
|
||||||
*exit_code1.borrow_mut() = ExitCode::FAILURE;
|
*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 add_imports = |items: &mut Vec<Item>, 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::<ParsTokTree>(lexemes.iter()).await.join(" ")
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
lexemes
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{e}");
|
|
||||||
*exit_code1.borrow_mut() = ExitCode::FAILURE;
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
let parse_ctx =
|
eprintln!("parsed");
|
||||||
HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] };
|
let entrypoint =
|
||||||
let prefix = [is("export").await, is("let").await, is("entrypoint").await, is("=").await];
|
ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos());
|
||||||
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
|
let mut xctx = ExecCtx::new(root.clone(), entrypoint).await;
|
||||||
let snippet = Snippet::new(&lexemes[0], &lexemes);
|
eprintln!("executed");
|
||||||
let entrypoint = match try_with_reporter(parse_item(
|
xctx.set_gas(Some(1000));
|
||||||
&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;
|
xctx.execute().await;
|
||||||
match xctx.result() {
|
match xctx.result() {
|
||||||
ExecResult::Value(val) =>
|
ExecResult::Value(val) => println!(
|
||||||
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false)),
|
"{const_name} = {}",
|
||||||
|
take_first(&val.print(&FmtCtxImpl::default()).await, false)
|
||||||
|
),
|
||||||
ExecResult::Err(e) => println!("error: {e}"),
|
ExecResult::Err(e) => println!("error: {e}"),
|
||||||
ExecResult::Gas(_) => println!("Ran out of gas!"),
|
ExecResult::Gas(_) => println!("Ran out of gas!"),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
})
|
Commands::ModTree { proj, prefix } => {
|
||||||
.await;
|
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::<ParsTokTree>(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, 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!"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}));
|
||||||
with_interner(local_interner(), with_logger(logger, local_set)).await;
|
with_interner(local_interner(), with_logger(logger, local_set)).await;
|
||||||
let x = *exit_code.borrow();
|
let x = *exit_code.borrow();
|
||||||
Ok(x)
|
Ok(x)
|
||||||
|
|||||||
Reference in New Issue
Block a user