partway through fixes, macro system needs resdesign
Some checks failed
Rust / build (push) Has been cancelled

This commit is contained in:
2026-04-08 18:02:20 +02:00
parent 0909524dee
commit 9b4c7fa7d7
76 changed files with 1391 additions and 1065 deletions

View File

@@ -2,11 +2,13 @@ use orchid_base::Logger;
use orchid_host::dylib::ext_dylib;
use tokio::time::Instant;
pub mod parse_folder;
mod print_mod;
mod repl;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Read;
use std::pin::pin;
use std::process::{Command, ExitCode};
use std::rc::Rc;
@@ -20,8 +22,8 @@ use futures::{FutureExt, Stream, TryStreamExt, io};
use itertools::Itertools;
use orchid_base::local_interner::local_interner;
use orchid_base::{
FmtCtxImpl, Format, Import, NameLike, 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,
FmtCtxImpl, Format, 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};
use orchid_host::execute::{ExecCtx, ExecResult};
@@ -30,15 +32,14 @@ use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::logger::LoggerImpl;
use orchid_host::parse::{HostParseCtxImpl, parse_item, parse_items};
use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModule};
use orchid_host::parsed::{ParsTokTree, ParsedModule};
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use orchid_host::tree::{MemberKind, Module, RootData};
use substack::Substack;
use tokio::io::{AsyncBufReadExt, BufReader, stdin};
use tokio::task::{LocalSet, spawn_local};
use crate::parse_folder::parse_folder;
use crate::repl::repl;
#[derive(Parser, Debug)]
#[command(version, about, long_about)]
@@ -58,8 +59,10 @@ pub struct Args {
#[derive(Subcommand, Debug)]
pub enum Commands {
Lex {
#[arg()]
file: Utf8PathBuf,
#[arg(long)]
file: Option<Utf8PathBuf>,
#[arg(long)]
line: Option<String>,
},
Parse {
#[arg(short, long)]
@@ -174,311 +177,168 @@ impl Spawner for SpawnerImpl {
}
}
#[tokio::main]
async fn main() -> io::Result<ExitCode> {
fn main() -> io::Result<ExitCode> {
eprintln!("Orcx v0.1 is free software provided without warranty.");
let args = Args::parse();
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
let local_set = LocalSet::new();
let exit_code1 = exit_code.clone();
let logger = get_logger(&args);
let logger2 = logger.clone();
unsafe { STARTUP = Some(Instant::now()) };
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::<Vec<Extension>>().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();
match lex(is(&buf).await, sym!(usercode), &systems, ctx).await {
Ok(lexemes) =>
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)),
Err(e) => {
eprintln!("{e}");
exit_code1.replace(ExitCode::FAILURE);
// Use a 10MB stack for single-threaded, unoptimized operation
stacker::grow(10 * 1024 * 1024, || {
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
let args = Args::parse();
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
let local_set = LocalSet::new();
let exit_code1 = exit_code.clone();
let logger = get_logger(&args);
let logger2 = logger.clone();
unsafe { STARTUP = Some(Instant::now()) };
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;
let res = with_stash(async move {
let extensions =
get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
time_print(&args, "Extensions loaded");
match args.command {
Commands::Lex { file, line } => {
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
let mut buf = String::new();
match (&file, &line) {
(Some(file), None) => {
let mut file = File::open(file.as_std_path()).unwrap();
file.read_to_string(&mut buf).unwrap();
},
(None, Some(line)) => buf = line.clone(),
(None, None) | (Some(_), Some(_)) =>
return Err("`lex` expected exactly one of --file and --line".to_string()),
};
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx)
.await
.map_err(|e| e.to_string())?;
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) =>
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 first = lexemes.first().ok_or("File empty!".to_string())?;
let pctx =
HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) };
let snip = Snippet::new(first, &lexemes);
let ptree = try_with_reporter(parse_items(&pctx, Substack::Bottom, snip))
.await
.map_err(|e| e.to_string())?;
if ptree.is_empty() {
return Err(
"File empty only after parsing, but no errors were reported".to_string(),
);
}
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))
}
},
Commands::Repl => repl(&args, &extensions, ctx.clone()).await?,
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();
root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
.await
.map_err(|e| e.to_string())?
}
let prefix = match prefix {
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::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();
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
{
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));
match xctx.execute().await {
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) => {
.map_err(|e| e.to_string())?;
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 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());
let mut xctx = ExecCtx::new(root.clone(), expr).await;
xctx.set_gas(Some(10_000));
match xctx.execute().await {
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 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));
match xctx.execute().await {
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!"),
}
},
Ok(())
})
.await;
if let Err(s) = res {
eprintln!("{s}");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
}
signal_end_main.send(()).expect("cleanup should still be waiting");
});
let cleanup = async {
if on_end_main.await.is_err() {
return;
}
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)
}
std::process::abort()
};
futures::future::select(
pin!(cleanup),
pin!(with_interner(local_interner(), with_logger(logger, local_set))),
)
.await;
let x = *exit_code.borrow();
Ok(x)
})
.await;
signal_end_main.send(()).expect("cleanup should still be waiting");
});
let cleanup = async {
if on_end_main.await.is_err() {
return;
}
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)
}
std::process::abort()
};
futures::future::select(
pin!(cleanup),
pin!(with_interner(local_interner(), with_logger(logger, local_set))),
)
.await;
let x = *exit_code.borrow();
Ok(x)
})
}

View File

@@ -3,10 +3,7 @@ use std::path::{Path, PathBuf};
use futures::FutureExt;
use itertools::Itertools;
use orchid_base::SrcRange;
use orchid_base::Sym;
use orchid_base::Snippet;
use orchid_base::{OrcRes, async_io_err, is, os_str_to_string, report};
use orchid_base::{OrcRes, Snippet, SrcRange, Sym, async_io_err, is, os_str_to_string, report};
use orchid_host::ctx::Ctx;
use orchid_host::lex::lex;
use orchid_host::parse::{HostParseCtxImpl, parse_items};

31
orcx/src/print_mod.rs Normal file
View File

@@ -0,0 +1,31 @@
use futures::FutureExt;
use itertools::Itertools;
use orchid_base::{NameLike, VPath, fmt};
use orchid_host::tree::{MemKind, Mod, RootData};
pub async fn print_mod(module: &Mod, 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 {
MemKind::Module(module) => {
println!("{indent}module {key} {{");
print_mod(module, VPath::new(new_path.segs()), root).boxed_local().await;
println!("{indent}}}")
},
MemKind::Const => {
let value = root.consts.get(&new_path).expect("Missing const!");
println!("{indent}const {key} = {}", fmt(value).await)
},
}
}
}

102
orcx/src/repl.rs Normal file
View File

@@ -0,0 +1,102 @@
use std::io::Write;
use itertools::Itertools;
use orchid_base::{
FmtCtxImpl, FmtTTV, Format, Import, NameLike, Snippet, SrcRange, Token, VPath, fmt, is, log, sym,
take_first, try_with_reporter, with_reporter,
};
use orchid_host::ctx::Ctx;
use orchid_host::execute::{ExecCtx, ExecResult};
use orchid_host::expr::ExprKind;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::parse::{HostParseCtxImpl, parse_item};
use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModule};
use orchid_host::system::init_systems;
use substack::Substack;
use tokio::io::{AsyncBufReadExt, BufReader, stdin};
use crate::Args;
use crate::print_mod::print_mod;
pub async fn repl(args: &Args, extensions: &[Extension], ctx: Ctx) -> Result<(), String> {
let mut counter = 0;
let mut imports = Vec::new();
let usercode_path = sym!(usercode);
let mut stdin = BufReader::new(stdin());
let (mut root, systems) = init_systems(&args.system, extensions).await.unwrap();
loop {
counter += 1;
print!("\\.> ");
std::io::stdout().flush().unwrap();
let mut prompt = String::new();
stdin.read_line(&mut prompt).await.unwrap();
if let Some(cmdline) = prompt.trim().strip_prefix(":") {
if cmdline == "help" {
println!(
"Recognized commands are:\n\
- help print this message\n\
- modtree display the current module tree"
)
} else if cmdline == "modtree" {
let root_data = root.0.read().await;
print_mod(&root_data.root, VPath::new([]), &root_data).await
} else {
println!("Could not recognize command. Valid commands are: help")
}
continue;
}
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(&FmtTTV(&lexemes)).await).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[..] };
try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
.await
.map_err(|e| e.to_string())
};
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 import_lines = process_lexemes(&lexemes).await?;
imports.extend(import_lines.into_iter().map(|it| match it.kind {
ItemKind::Import(imp) => imp,
_ => panic!("Expected imports from import line"),
}));
continue;
}
let mut prefix = vec![is("export").await];
if !discr.is_kw(is("let").await) {
prefix.extend([is("let").await, name.clone(), is("=").await]);
}
lexemes.splice(0..0, prefix.into_iter().map(|n| Token::Name(n).at(prefix_sr.clone())));
let mut new_lines = process_lexemes(&lexemes).await?;
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);
root =
with_reporter(root.add_parsed(&new_module, path.clone())).await.map_err(|e| e.to_string())?;
let entrypoint = ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos());
let mut xctx = ExecCtx::new(root.clone(), entrypoint).await;
xctx.set_gas(Some(1000));
match xctx.execute().await {
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!"),
}
}
}