First steps for the macro system

This commit is contained in:
2025-08-01 18:32:55 +02:00
parent f87185ef88
commit 051b5e666f
18 changed files with 356 additions and 166 deletions

View File

@@ -13,22 +13,24 @@ use async_stream::try_stream;
use camino::Utf8PathBuf;
use clap::{Parser, Subcommand};
use futures::{Stream, TryStreamExt, io};
use itertools::Itertools;
use orchid_base::error::Reporter;
use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::location::SrcRange;
use orchid_base::logging::{LogStrategy, Logger};
use orchid_base::parse::Snippet;
use orchid_base::name::{NameLike, VPath};
use orchid_base::parse::{Import, Snippet};
use orchid_base::sym;
use orchid_base::tree::ttv_fmt;
use orchid_base::tree::{Token, ttv_fmt};
use orchid_host::ctx::Ctx;
use orchid_host::execute::{ExecCtx, ExecResult};
use orchid_host::expr::PathSetBuilder;
use orchid_host::expr::ExprKind;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::parse::{HostParseCtxImpl, parse_expr, parse_items};
use orchid_host::parsed::{Item, ItemKind, ParsedMember, ParsedMemberKind, ParsedModule};
use orchid_host::parse::{HostParseCtxImpl, parse_item, parse_items};
use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModule};
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use orchid_host::tree::Root;
use substack::Substack;
use tokio::task::{LocalSet, spawn_local};
@@ -147,56 +149,90 @@ async fn main() -> io::Result<ExitCode> {
println!("{}", take_first(&item.print(&FmtCtxImpl { i }).await, true))
}
},
Commands::Repl => loop {
let (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();
eprintln!("lexing");
let lexemes =
lex(i.i(prompt.trim()).await, sym!(usercode; i).await, &systems, ctx).await.unwrap();
eprintln!("lexed");
if args.logs {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let path = sym!(usercode; i).await;
let reporter = Reporter::new();
let parse_ctx = HostParseCtxImpl {
ctx: ctx.clone(),
rep: &reporter,
src: path.clone(),
systems: &systems[..],
};
let parse_res = parse_expr(
&parse_ctx,
path.clone(),
PathSetBuilder::new(),
Snippet::new(&lexemes[0], &lexemes),
)
.await;
eprintln!("parsed");
let expr = match reporter.merge(parse_res) {
Ok(expr) => expr,
Err(e) => {
eprintln!("{e}");
Commands::Repl => {
let mut counter = 0;
let mut imports = Vec::new();
let usercode_path = sym!(usercode; i).await;
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 = i.i(&format!("_{counter}")).await;
let path = usercode_path.suffix([name.clone()], i).await;
let mut lexemes =
lex(i.i(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap();
let Some(discr) = lexemes.first() else { continue };
if args.logs {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let prefix_sr = SrcRange::zw(path.clone(), 0);
let process_lexemes = async |lexemes: &[ParsTokTree]| {
let snippet = Snippet::new(&lexemes[0], lexemes);
let reporter = Reporter::new();
let parse_ctx = HostParseCtxImpl {
ctx: ctx.clone(),
rep: &reporter,
src: path.clone(),
systems: &systems[..],
};
let parse_result = parse_item(&parse_ctx, Substack::Bottom, vec![], snippet).await;
match reporter.merge(parse_result) {
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(i.i("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;
},
};
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root.clone(), expr).await;
eprintln!("executed");
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!"),
}
if !discr.is_kw(i.i("let").await) {
let prefix = [i.i("export").await, i.i("let").await, name.clone(), i.i("=").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);
let reporter = Reporter::new();
root = root.add_parsed(&new_module, path.clone(), &reporter).await;
eprintln!("parsed");
let entrypoint =
ExprKind::Const(path.suffix([const_name.clone()], i).await).at(input_sr.pos());
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), 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 { i }).await, false)),
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
}
},
Commands::Execute { proj, code } => {
let reporter = Reporter::new();
let path = sym!(usercode::entrypoint; i).await;
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 = PathBuf::from(proj_path.into_std_path_buf());
@@ -209,8 +245,7 @@ async fn main() -> io::Result<ExitCode> {
},
}
}
let lexemes = lex(i.i(code.trim()).await, path.clone(), &systems, ctx).await.unwrap();
let snippet = Snippet::new(&lexemes[0], &lexemes);
let mut lexemes = lex(i.i(code.trim()).await, path.clone(), &systems, ctx).await.unwrap();
if args.logs {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
@@ -220,21 +255,22 @@ async fn main() -> io::Result<ExitCode> {
src: path.clone(),
systems: &systems[..],
};
let parse_res = parse_expr(&parse_ctx, path.clone(), PathSetBuilder::new(), snippet).await;
let expr = match reporter.merge(parse_res) {
Ok(expr) => expr,
let prefix =
[i.i("export").await, i.i("let").await, i.i("entrypoint").await, i.i("=").await];
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
let snippet = Snippet::new(&lexemes[0], &lexemes);
let parse_res = parse_item(&parse_ctx, Substack::Bottom, vec![], snippet).await;
let entrypoint = match reporter.merge(parse_res) {
Ok(items) => ParsedModule::new(true, items),
Err(e) => {
eprintln!("{e}");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
return;
},
};
let parsed_root = ParsedModule::new(true, [Item::new(
snippet.sr(),
ParsedMember::new(true, i.i("entrypoint").await, expr.clone()),
)]);
let reporter = Reporter::new();
let root = root.add_parsed(&parsed_root, sym!(usercode; i).await, &reporter).await;
let root = root.add_parsed(&entrypoint, path.clone(), &reporter).await;
let expr = ExprKind::Const(sym!(usercode::entrypoint; i).await).at(prefix_sr.pos());
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root, expr).await;
xctx.set_gas(Some(1000));
xctx.execute().await;

View File

@@ -39,7 +39,7 @@ pub async fn parse_folder(
));
};
let name = ctx.i.i(os_str_to_string(name_os, &ctx.i, [sr]).await?).await;
let ns = ns.push(name.clone(), &ctx.i).await;
let ns = ns.suffix([name.clone()], &ctx.i).await;
let sr = SrcRange::new(0..0, &ns);
let mut items = Vec::new();
let mut stream = match fs::read_dir(path).await {
@@ -67,7 +67,7 @@ pub async fn parse_folder(
} else if path.extension() == Some(OsStr::new("orc")) {
let name_os = path.file_stem().expect("If there is an extension, there must be a stem");
let name = ctx.i.i(os_str_to_string(name_os, &ctx.i, [sr]).await?).await;
let ns = ns.push(name, &ctx.i).await;
let ns = ns.suffix([name], &ctx.i).await;
let sr = SrcRange::new(0..0, &ns);
let mut file = match File::open(path).await {
Err(e) => return Err(async_io_err(e, &ctx.i, [sr]).await),