Merge pull request #217 from HigherOrderCO/feature/sc-483/add-an-option-to-pass-arguments-to-hvm-lang

[sc-483] Add an option to pass arguments to hvm-lang programs
This commit is contained in:
imaqtkatt 2024-03-01 11:42:49 -03:00 committed by GitHub
commit 2e9b3d3949
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 138 additions and 55 deletions

52
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "ahash"
version = "0.8.9"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b"
dependencies = [
"cfg-if",
"once_cell",
@ -16,9 +16,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.12"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
@ -70,9 +70,9 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "cc"
version = "1.0.86"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730"
checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
[[package]]
name = "cfg-if"
@ -228,9 +228,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.3"
version = "2.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
@ -402,9 +402,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
version = "2.0.50"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@ -481,9 +481,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -496,45 +496,45 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.3"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "yaml-rust"

View File

@ -5,7 +5,8 @@ use crate::term::{
},
display::DisplayFn,
transform::{
encode_pattern_matching::MatchErr, resolve_refs::ReferencedMainErr, simplify_ref_to_ref::CyclicDefErr,
apply_args::ArgError, encode_pattern_matching::MatchErr, resolve_refs::ReferencedMainErr,
simplify_ref_to_ref::CyclicDefErr,
},
Name,
};
@ -59,8 +60,8 @@ impl Info {
self.err_counter = 0;
}
/// Checks if any error was emitted since the start of the pass,
/// Returning all the current information as a `Err(Info)`, replacing `&mut self` with an empty one.
/// Checks if any error was emitted since the start of the pass,
/// Returning all the current information as a `Err(Info)`, replacing `&mut self` with an empty one.
/// Otherwise, returns the given arg as an `Ok(T)`.
pub fn fatal<T>(&mut self, t: T) -> Result<T, Info> {
if self.err_counter == 0 { Ok(t) } else { Err(std::mem::take(self)) }
@ -110,6 +111,7 @@ pub enum Error {
EntryPoint(EntryErr),
TopLevel(TopLevelErr),
Custom(String),
ArgError(ArgError),
}
impl Display for Error {
@ -129,6 +131,7 @@ impl Error {
Error::EntryPoint(err) => write!(f, "{err}"),
Error::TopLevel(err) => write!(f, "{err}"),
Error::Custom(err) => write!(f, "{err}"),
Error::ArgError(err) => write!(f, "{err}"),
})
}
}
@ -175,6 +178,12 @@ impl From<TopLevelErr> for Error {
}
}
impl From<ArgError> for Error {
fn from(value: ArgError) -> Self {
Self::ArgError(value)
}
}
#[derive(Debug, Clone)]
pub enum Warning {
MatchOnlyVars(Name),

View File

@ -95,13 +95,18 @@ pub fn create_host(book: Arc<Book>, labels: Arc<Labels>, compile_opts: CompileOp
pub fn check_book(book: &mut Book) -> Result<(), Info> {
// TODO: Do the checks without having to do full compilation
// TODO: Shouldn't the check mode show warnings?
compile_book(book, CompileOpts::light())?;
compile_book(book, CompileOpts::light(), None)?;
Ok(())
}
pub fn compile_book(book: &mut Book, opts: CompileOpts) -> Result<CompileResult, Info> {
let warns = desugar_book(book, opts)?;
pub fn compile_book(
book: &mut Book,
opts: CompileOpts,
args: Option<Vec<Term>>,
) -> Result<CompileResult, Info> {
let warns = desugar_book(book, opts, args)?;
let (nets, labels) = book_to_nets(book);
let mut core_book = nets_to_hvmc(nets)?;
if opts.pre_reduce {
pre_reduce_book(&mut core_book, book.hvmc_entrypoint())?;
@ -112,11 +117,16 @@ pub fn compile_book(book: &mut Book, opts: CompileOpts) -> Result<CompileResult,
Ok(CompileResult { core_book, labels, warns })
}
pub fn desugar_book(book: &mut Book, opts: CompileOpts) -> Result<Vec<Warning>, Info> {
pub fn desugar_book(
book: &mut Book,
opts: CompileOpts,
args: Option<Vec<Term>>,
) -> Result<Vec<Warning>, Info> {
let mut ctx = Ctx::new(book);
ctx.check_shared_names();
ctx.set_entrypoint();
ctx.apply_args(args)?;
ctx.book.encode_adts(opts.adt_encoding);
ctx.book.encode_builtins();
@ -184,8 +194,9 @@ pub fn run_book(
run_opts: RunOpts,
warning_opts: WarningOpts,
compile_opts: CompileOpts,
args: Option<Vec<Term>>,
) -> Result<(Term, RunInfo), Info> {
let CompileResult { core_book, labels, warns } = compile_book(&mut book, compile_opts)?;
let CompileResult { core_book, labels, warns } = compile_book(&mut book, compile_opts, args)?;
// Turn the book into an Arc so that we can use it for logging, debugging, etc.
// from anywhere else in the program

View File

@ -86,6 +86,13 @@ enum Mode {
)]
comp_opts: Vec<OptArgs>,
#[arg(value_parser = |arg: &str| hvml::term::parser::parse_term(arg)
.map_err(|e| match e[0].reason() {
chumsky::error::RichReason::Many(errs) => format!("{}", &errs[0]),
_ => format!("{}", e[0].reason()),
}))]
arguments: Option<Vec<hvml::term::Term>>,
#[command(flatten)]
warn_opts: CliWarnOpts,
},
@ -192,7 +199,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Info> {
}
let mut book = load_book(&path)?;
let compiled = compile_book(&mut book, opts)?;
let compiled = compile_book(&mut book, opts, None)?;
println!("{}", compiled.display_with_warns(warning_opts)?);
}
Mode::Desugar { path, comp_opts, lazy_mode } => {
@ -202,7 +209,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Info> {
}
let mut book = load_book(&path)?;
// TODO: Shouldn't the desugar have `warn_opts` too? maybe WarningOpts::allow_all() by default
let _warns = desugar_book(&mut book, opts)?;
let _warns = desugar_book(&mut book, opts, None)?;
println!("{}", book);
}
Mode::Run {
@ -216,6 +223,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Info> {
comp_opts,
warn_opts,
lazy_mode,
arguments,
} => {
if debug && lazy_mode {
return Err("Unsupported configuration, can not use debug mode `-d` with lazy mode `-L`".into());
@ -235,7 +243,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Info> {
let run_opts =
RunOpts { single_core, debug, linear, lazy_mode, max_memory: max_mem, max_rewrites: max_rwts };
let (res_term, RunInfo { stats, readback_errors, net, book: _, labels: _ }) =
run_book(book, max_mem as usize, run_opts, warning_opts, opts)?;
run_book(book, max_mem as usize, run_opts, warning_opts, opts, arguments)?;
let total_rewrites = stats.rewrites.total() as f64;
let rps = total_rewrites / stats.run_time / 1_000_000.0;

View File

@ -9,7 +9,6 @@ pub enum EntryErr {
NotFound(Name),
Multiple(Vec<Name>),
MultipleRules,
Arguments,
}
impl Display for EntryErr {
@ -23,7 +22,6 @@ impl Display for EntryErr {
write!(f, "File has '{}', '{}' and '{}' definitions.", fnd[0], fnd[1], fnd[2])
}
EntryErr::MultipleRules => write!(f, "Main definition can't have more than one rule."),
EntryErr::Arguments => write!(f, "Main definition can't have any arguments."),
}
}
}
@ -69,13 +67,7 @@ impl Ctx<'_> {
}
fn validate_entry_point(entry: &Definition) -> Result<Name, EntryErr> {
if entry.rules.len() > 1 {
Err(EntryErr::MultipleRules)
} else if !entry.rules[0].pats.is_empty() {
Err(EntryErr::Arguments)
} else {
Ok(entry.name.clone())
}
if entry.rules.len() > 1 { Err(EntryErr::MultipleRules) } else { Ok(entry.name.clone()) }
}
impl Book {

View File

@ -0,0 +1,50 @@
use std::fmt::Display;
use crate::{
diagnostics::Info,
term::{Ctx, Pattern, Term},
};
#[derive(Clone, Debug)]
pub enum ArgError {
PatternArgError,
ArityArgError { expected: usize, got: usize },
}
impl Display for ArgError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ArgError::PatternArgError => write!(f, ""),
ArgError::ArityArgError { expected, got } => write!(f, "Expected {expected} arguments, got {got}."),
}
}
}
impl Ctx<'_> {
pub fn apply_args(&mut self, args: Option<Vec<Term>>) -> Result<(), Info> {
self.info.start_pass();
if let Some(entrypoint) = &self.book.entrypoint {
let main_def = &mut self.book.defs[entrypoint];
if !main_def.rules[0].pats.iter().all(|pat| matches!(pat, Pattern::Var(Some(..)))) {
self.info.def_error(entrypoint.clone(), ArgError::PatternArgError);
}
if let Some(args) = args {
let expected = main_def.rules[0].pats.len();
let got = args.len();
if expected != got {
self.info.error(ArgError::ArityArgError { expected, got });
}
main_def.convert_match_def_to_term();
let main_body = &mut self.book.defs[entrypoint].rule_mut().body;
*main_body = Term::call(main_body.clone(), args);
}
}
self.info.fatal(())
}
}

View File

@ -1,3 +1,4 @@
pub mod apply_args;
pub mod definition_merge;
pub mod definition_pruning;
pub mod desugar_implicit_match_binds;

View File

@ -109,7 +109,7 @@ fn compile_term() {
fn compile_file_o_all() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let compiled = compile_book(&mut book, CompileOpts::heavy())?;
let compiled = compile_book(&mut book, CompileOpts::heavy(), None)?;
Ok(format!("{:?}", compiled))
})
}
@ -117,7 +117,7 @@ fn compile_file_o_all() {
fn compile_file() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let compiled = compile_book(&mut book, CompileOpts::light())?;
let compiled = compile_book(&mut book, CompileOpts::light(), None)?;
Ok(format!("{:?}", compiled))
})
}
@ -132,6 +132,7 @@ fn linear_readback() {
RunOpts { linear: true, ..Default::default() },
WarningOpts::deny_all(),
CompileOpts::heavy(),
None,
)?;
Ok(format!("{}{}", display_readback_errors(&info.readback_errors), res))
});
@ -143,14 +144,14 @@ fn run_file() {
let book = do_parse_book(code, path)?;
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) =
run_book(book, 1 << 24, RunOpts::lazy(), WarningOpts::deny_all(), CompileOpts::heavy())?;
run_book(book, 1 << 24, RunOpts::lazy(), WarningOpts::deny_all(), CompileOpts::heavy(), None)?;
Ok(format!("{}{}", display_readback_errors(&info.readback_errors), res))
}),
(&|code, path| {
let book = do_parse_book(code, path)?;
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) =
run_book(book, 1 << 24, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy())?;
run_book(book, 1 << 24, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy(), None)?;
Ok(format!("{}{}", display_readback_errors(&info.readback_errors), res))
}),
])
@ -166,7 +167,7 @@ fn run_lazy() {
desugar_opts.lazy_mode();
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) = run_book(book, 1 << 24, run_opts, WarningOpts::deny_all(), desugar_opts)?;
let (res, info) = run_book(book, 1 << 24, run_opts, WarningOpts::deny_all(), desugar_opts, None)?;
Ok(format!("{}{}", display_readback_errors(&info.readback_errors), res))
})
}
@ -257,7 +258,7 @@ fn encode_pattern_match() {
fn desugar_file() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
desugar_book(&mut book, CompileOpts::light())?;
desugar_book(&mut book, CompileOpts::light(), None)?;
Ok(book.to_string())
})
}
@ -273,7 +274,8 @@ fn hangs() {
let lck = Arc::new(RwLock::new(false));
let got = lck.clone();
std::thread::spawn(move || {
let _ = run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy());
let _ =
run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy(), None);
*got.write().unwrap() = true;
});
std::thread::sleep(std::time::Duration::from_secs(expected_normalization_time));
@ -287,7 +289,7 @@ fn compile_entrypoint() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
book.entrypoint = Some(Name::from("foo"));
let compiled = compile_book(&mut book, CompileOpts::light())?;
let compiled = compile_book(&mut book, CompileOpts::light(), None)?;
Ok(format!("{:?}", compiled))
})
}
@ -299,7 +301,7 @@ fn run_entrypoint() {
book.entrypoint = Some(Name::from("foo"));
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) =
run_book(book, 1 << 24, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy())?;
run_book(book, 1 << 24, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy(), None)?;
Ok(format!("{}{}", display_readback_errors(&info.readback_errors), res))
})
}

View File

@ -0,0 +1,3 @@
add x y = (+ x y)
main x y = (add x y)

View File

@ -0,0 +1,7 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/add_args.hvm
---
@add = (<+ a b> (a b))
@main = (a (b c))
& @add ~ (a (b c))