Made some progress towards effectful programs
This commit is contained in:
@@ -2,20 +2,21 @@
|
||||
name = "orcx"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
authors = ["Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
camino = "1.2.2"
|
||||
clap = { version = "4.5.54", features = ["derive", "env"] }
|
||||
clap = { version = "4.5.54", features = ["derive", "env", "cargo"] }
|
||||
ctrlc = "3.5.1"
|
||||
futures = "0.3.31"
|
||||
itertools = "0.14.0"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
orchid-host = { version = "0.1.0", path = "../orchid-host", features = [
|
||||
"tokio",
|
||||
"tokio",
|
||||
] }
|
||||
stacker = "0.1.23"
|
||||
substack = "1.1.1"
|
||||
|
||||
118
orcx/src/main.rs
118
orcx/src/main.rs
@@ -41,46 +41,125 @@ use tokio::task::{LocalSet, spawn_local};
|
||||
use crate::parse_folder::parse_folder;
|
||||
use crate::repl::repl;
|
||||
|
||||
/// Native interpreter for the Orchid programming language
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about)]
|
||||
#[command(version, about, long_about, verbatim_doc_comment)]
|
||||
pub struct Args {
|
||||
#[arg(short, long, env = "ORCHID_EXTENSIONS", value_delimiter = ';')]
|
||||
/// Load an extension from a file. The file extension should be omitted, the
|
||||
/// loader checks for a range of platform-specific file extensions (foo.exe
|
||||
/// and foo.dll on Windows, libfoo.so or foo on other platforms)
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "ORCHID_EXTENSIONS",
|
||||
value_delimiter = ';',
|
||||
next_line_help = true,
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
extension: Vec<Utf8PathBuf>,
|
||||
#[arg(short, long, env = "ORCHID_DEFAULT_SYSTEMS", value_delimiter = ';')]
|
||||
/// Instantiate a system by name. The system must be provided by one of the
|
||||
/// loaded extensions
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
env = "ORCHID_DEFAULT_SYSTEMS",
|
||||
value_delimiter = ';',
|
||||
next_line_help = true,
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
system: Vec<String>,
|
||||
#[arg(short, long, default_value = "off", default_missing_value = "stderr")]
|
||||
/// Send a log stream to a specific destination.
|
||||
///
|
||||
/// Supported formats:
|
||||
/// - `--logs=msg>messaging.log`: the log channel `msg` will be routed to the
|
||||
/// file `messaging.log`. If this is used, the following format should also
|
||||
/// appear to specify what happens to other categories
|
||||
/// - `--logs=orchid.log`: all unspecified channels will be routed to the file
|
||||
/// `orchid.log`
|
||||
/// - `--logs` only once with no value: all unspecified channels will be
|
||||
/// routed to stderr
|
||||
///
|
||||
/// Some destination names have special meanings:
|
||||
/// - `stderr` designates the platform-specific standard error output of the
|
||||
/// interpreter process
|
||||
/// - `off` discards received messages
|
||||
///
|
||||
/// Defaults for specific channels
|
||||
/// - `warn` is routed the same as `debug`
|
||||
/// - `error` is routed the same as `warn`
|
||||
/// - `msg` is discarded (routed to `off`)
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
default_value = "off",
|
||||
default_missing_value = "stderr",
|
||||
next_line_help = true,
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
logs: Vec<String>,
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
/// Measure and report the timings of various events
|
||||
#[arg(long, action)]
|
||||
time: bool,
|
||||
/// Project folder for subcommand-specific purpose
|
||||
#[arg(long)]
|
||||
proj: Option<Utf8PathBuf>,
|
||||
/// Number of execution steps the interpreter is allowed to take. Use
|
||||
/// `--no-gas` to disable the limit.
|
||||
#[arg(long, default_value("10000"), next_line_help = true, verbatim_doc_comment)]
|
||||
gas: u64,
|
||||
/// Disable gas limiting, may cause infinite loops
|
||||
#[arg(long, action)]
|
||||
no_gas: bool,
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Commands {
|
||||
/// Tokenize some code using the current lexer plugins. Either --file or
|
||||
/// --line, but not both, can specify the source code input
|
||||
#[command(next_line_help = true, verbatim_doc_comment)]
|
||||
Lex {
|
||||
/// Source file input
|
||||
#[arg(long)]
|
||||
file: Option<Utf8PathBuf>,
|
||||
/// Raw source code input
|
||||
#[arg(long)]
|
||||
line: Option<String>,
|
||||
},
|
||||
/// Parse some code into module tree - the stage after tokenization but before
|
||||
/// any execution
|
||||
#[command(next_line_help = true, verbatim_doc_comment)]
|
||||
Parse {
|
||||
#[arg(short, long)]
|
||||
file: Utf8PathBuf,
|
||||
},
|
||||
/// Open an interactive shell
|
||||
Repl,
|
||||
/// Print the module tree after parsing. This is similar to `parse`, but it
|
||||
/// can traverse folders and show extension modules
|
||||
#[command(next_line_help = true, verbatim_doc_comment)]
|
||||
ModTree {
|
||||
#[arg(long)]
|
||||
proj: Option<Utf8PathBuf>,
|
||||
/// Module to show
|
||||
#[arg(long)]
|
||||
prefix: Option<String>,
|
||||
},
|
||||
Exec {
|
||||
#[arg(long)]
|
||||
proj: Option<Utf8PathBuf>,
|
||||
/// Evaluate an expression. The most obvious use case is to read project
|
||||
/// metadata from scripts written in other languages. If proj is set, the
|
||||
/// expression can refer to constants within this project by fully qualified
|
||||
/// path
|
||||
#[command(next_line_help = true, verbatim_doc_comment)]
|
||||
Eval {
|
||||
/// Expression to evaluate
|
||||
#[arg()]
|
||||
code: String,
|
||||
},
|
||||
/// Execute effectful Orchid code
|
||||
Exec {
|
||||
/// Entrypoint or startup command
|
||||
#[arg()]
|
||||
main: String,
|
||||
},
|
||||
}
|
||||
|
||||
static mut STARTUP: Option<Instant> = None;
|
||||
@@ -187,11 +266,11 @@ impl Spawner for SpawnerImpl {
|
||||
}
|
||||
|
||||
fn main() -> io::Result<ExitCode> {
|
||||
eprintln!("Orcx v0.1 is free software provided without warranty.");
|
||||
// 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();
|
||||
eprintln!("Orcx v{} is free software provided without warranty.", clap::crate_version!());
|
||||
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
|
||||
let local_set = LocalSet::new();
|
||||
let exit_code1 = exit_code.clone();
|
||||
@@ -248,9 +327,9 @@ fn main() -> io::Result<ExitCode> {
|
||||
}
|
||||
},
|
||||
Commands::Repl => repl(&args, &extensions, ctx.clone()).await?,
|
||||
Commands::ModTree { proj, prefix } => {
|
||||
Commands::ModTree { prefix } => {
|
||||
let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap();
|
||||
if let Some(proj_path) = proj {
|
||||
if let Some(proj_path) = args.proj {
|
||||
let path = proj_path.into_std_path_buf();
|
||||
root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
|
||||
.await
|
||||
@@ -263,11 +342,11 @@ fn main() -> io::Result<ExitCode> {
|
||||
let root_data = root.0.read().await;
|
||||
print_mod::print_mod(&root_data.root, prefix, &root_data).await;
|
||||
},
|
||||
Commands::Exec { proj, code } => {
|
||||
Commands::Eval { 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 {
|
||||
if let Some(proj_path) = args.proj {
|
||||
let path = proj_path.into_std_path_buf();
|
||||
root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
|
||||
.await
|
||||
@@ -298,7 +377,9 @@ fn main() -> io::Result<ExitCode> {
|
||||
.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));
|
||||
if !args.no_gas {
|
||||
xctx.set_gas(Some(args.gas));
|
||||
}
|
||||
match xctx.execute().await {
|
||||
ExecResult::Value(val, _) => {
|
||||
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
|
||||
@@ -307,6 +388,9 @@ fn main() -> io::Result<ExitCode> {
|
||||
ExecResult::Gas(_) => println!("Ran out of gas!"),
|
||||
}
|
||||
},
|
||||
Commands::Exec { main: _ } => {
|
||||
todo!("Integration of the command system is in-dev")
|
||||
},
|
||||
};
|
||||
Ok(())
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user