diff --git a/Cargo.lock b/Cargo.lock index 601cf1e3a..7fcae8bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/src/diagnostics.rs b/src/diagnostics.rs index c3b9a9c73..2387bd98f 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -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(&mut self, t: T) -> Result { 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 for Error { } } +impl From for Error { + fn from(value: ArgError) -> Self { + Self::ArgError(value) + } +} + #[derive(Debug, Clone)] pub enum Warning { MatchOnlyVars(Name), diff --git a/src/lib.rs b/src/lib.rs index 033f242ea..6bd31e21e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,13 +95,18 @@ pub fn create_host(book: Arc, labels: Arc, 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 { - let warns = desugar_book(book, opts)?; +pub fn compile_book( + book: &mut Book, + opts: CompileOpts, + args: Option>, +) -> Result { + 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 Result, Info> { +pub fn desugar_book( + book: &mut Book, + opts: CompileOpts, + args: Option>, +) -> Result, 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>, ) -> 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 diff --git a/src/main.rs b/src/main.rs index 7f4f5d2a7..23413bde6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,6 +86,13 @@ enum Mode { )] comp_opts: Vec, + #[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>, + #[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; diff --git a/src/term/check/set_entrypoint.rs b/src/term/check/set_entrypoint.rs index 104300650..1f0832a97 100644 --- a/src/term/check/set_entrypoint.rs +++ b/src/term/check/set_entrypoint.rs @@ -9,7 +9,6 @@ pub enum EntryErr { NotFound(Name), Multiple(Vec), 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 { - 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 { diff --git a/src/term/transform/apply_args.rs b/src/term/transform/apply_args.rs new file mode 100644 index 000000000..c6d09092f --- /dev/null +++ b/src/term/transform/apply_args.rs @@ -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>) -> 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(()) + } +} diff --git a/src/term/transform/mod.rs b/src/term/transform/mod.rs index 5a8693c66..6c4b14d66 100644 --- a/src/term/transform/mod.rs +++ b/src/term/transform/mod.rs @@ -1,3 +1,4 @@ +pub mod apply_args; pub mod definition_merge; pub mod definition_pruning; pub mod desugar_implicit_match_binds; diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index e4e54c347..0550b4ac1 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -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)) }) } diff --git a/tests/golden_tests/compile_file/add_args.hvm b/tests/golden_tests/compile_file/add_args.hvm new file mode 100644 index 000000000..ea539fc5f --- /dev/null +++ b/tests/golden_tests/compile_file/add_args.hvm @@ -0,0 +1,3 @@ +add x y = (+ x y) + +main x y = (add x y) diff --git a/tests/snapshots/compile_file__add_args.hvm.snap b/tests/snapshots/compile_file__add_args.hvm.snap new file mode 100644 index 000000000..653e8733a --- /dev/null +++ b/tests/snapshots/compile_file__add_args.hvm.snap @@ -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))