Added prelude, made lambdas a single-token prefix like NS, made progress on implementations, removed const line type
254 lines
7.9 KiB
Rust
254 lines
7.9 KiB
Rust
pub mod parse_folder;
|
|
|
|
use std::cell::RefCell;
|
|
use std::fs::File;
|
|
use std::io::{Read, Write};
|
|
use std::mem;
|
|
use std::process::{Command, ExitCode};
|
|
use std::rc::Rc;
|
|
|
|
use async_std::io::stdin;
|
|
use async_std::path::PathBuf;
|
|
use async_stream::try_stream;
|
|
use camino::Utf8PathBuf;
|
|
use clap::{Parser, Subcommand};
|
|
use futures::{Stream, TryStreamExt, io};
|
|
use orchid_base::error::Reporter;
|
|
use orchid_base::format::{FmtCtxImpl, Format, take_first};
|
|
use orchid_base::logging::{LogStrategy, Logger};
|
|
use orchid_base::parse::Snippet;
|
|
use orchid_base::sym;
|
|
use orchid_base::tree::ttv_fmt;
|
|
use orchid_host::ctx::Ctx;
|
|
use orchid_host::execute::{ExecCtx, ExecResult};
|
|
use orchid_host::expr::PathSetBuilder;
|
|
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::subprocess::ext_command;
|
|
use orchid_host::system::init_systems;
|
|
use orchid_host::tree::Root;
|
|
use substack::Substack;
|
|
use tokio::task::{LocalSet, spawn_local};
|
|
|
|
use crate::parse_folder::parse_folder;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(version, about, long_about)]
|
|
pub struct Args {
|
|
#[arg(short, long, env = "ORCHID_EXTENSIONS", value_delimiter = ';')]
|
|
extension: Vec<Utf8PathBuf>,
|
|
#[arg(short, long, env = "ORCHID_DEFAULT_SYSTEMS", value_delimiter = ';')]
|
|
system: Vec<String>,
|
|
#[arg(short, long)]
|
|
logs: bool,
|
|
#[arg(short, long)]
|
|
msg_logs: bool,
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
pub enum Commands {
|
|
Lex {
|
|
#[arg(short, long)]
|
|
file: Utf8PathBuf,
|
|
},
|
|
Parse {
|
|
#[arg(short, long)]
|
|
file: Utf8PathBuf,
|
|
},
|
|
Repl,
|
|
Execute {
|
|
#[arg(long)]
|
|
proj: Option<Utf8PathBuf>,
|
|
#[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! {
|
|
for ext_path in args.extension.iter() {
|
|
let exe = if cfg!(windows) {
|
|
ext_path.with_extension("exe")
|
|
} else {
|
|
ext_path.clone()
|
|
};
|
|
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(), msg_logger.clone(), ctx.clone())?;
|
|
yield ext
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> io::Result<ExitCode> {
|
|
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
|
|
let local_set = LocalSet::new();
|
|
let exit_code1 = exit_code.clone();
|
|
local_set.spawn_local(async move {
|
|
let args = Args::parse();
|
|
let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut))));
|
|
let i = &ctx.i;
|
|
let logger = Logger::new(if args.logs { LogStrategy::StdErr } else { LogStrategy::Discard });
|
|
let msg_logger =
|
|
Logger::new(if args.msg_logs { LogStrategy::StdErr } else { LogStrategy::Discard });
|
|
let extensions = get_all_extensions(&args, &logger, &msg_logger, 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(i.i(&buf).await, sym!(usercode; i).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(i.i(&buf).await, sym!(usercode; i).await, &systems, ctx).await.unwrap();
|
|
let Some(first) = lexemes.first() else {
|
|
println!("File empty!");
|
|
return;
|
|
};
|
|
let reporter = Reporter::new();
|
|
let pctx = HostParseCtxImpl {
|
|
rep: &reporter,
|
|
systems: &systems,
|
|
ctx: ctx.clone(),
|
|
src: sym!(usercode; i).await,
|
|
};
|
|
let snip = Snippet::new(first, &lexemes);
|
|
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
|
|
if let Some(errv) = reporter.errv() {
|
|
eprintln!("{errv}");
|
|
*exit_code1.borrow_mut() = ExitCode::FAILURE;
|
|
return;
|
|
}
|
|
if ptree.is_empty() {
|
|
eprintln!("File empty only after parsing, but no errors were reported");
|
|
*exit_code1.borrow_mut() = ExitCode::FAILURE;
|
|
return;
|
|
}
|
|
for item in ptree {
|
|
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}");
|
|
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!"),
|
|
}
|
|
},
|
|
Commands::Execute { proj, code } => {
|
|
let reporter = Reporter::new();
|
|
let path = sym!(usercode::entrypoint; i).await;
|
|
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());
|
|
match parse_folder(&root, path, sym!(src; i).await, &reporter, ctx.clone()).await {
|
|
Ok(r) => root = r,
|
|
Err(e) => {
|
|
eprintln!("{e}");
|
|
*exit_code1.borrow_mut() = ExitCode::FAILURE;
|
|
return;
|
|
},
|
|
}
|
|
}
|
|
let lexemes = lex(i.i(code.trim()).await, path.clone(), &systems, ctx).await.unwrap();
|
|
let snippet = Snippet::new(&lexemes[0], &lexemes);
|
|
if args.logs {
|
|
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
|
|
}
|
|
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).await;
|
|
let expr = match reporter.merge(parse_res) {
|
|
Ok(expr) => expr,
|
|
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 mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root, expr).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!"),
|
|
}
|
|
},
|
|
}
|
|
});
|
|
local_set.await;
|
|
let x = *exit_code.borrow();
|
|
Ok(x)
|
|
}
|