From c03b1d233bee271cdcb771b993e8db5334ead7b2 Mon Sep 17 00:00:00 2001 From: FranchuFranchu <38839219+FranchuFranchu@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:01:55 -0300 Subject: [PATCH] Add `HVM.print` [sc-469] (#206) --- docs/builtin-defs.md | 27 ++++++++++++- docs/compiler-options.md | 2 +- src/lib.rs | 55 ++++++++++++++++++++------- tests/golden_tests/run_file/print.hvm | 4 ++ 4 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 tests/golden_tests/run_file/print.hvm diff --git a/docs/builtin-defs.md b/docs/builtin-defs.md index 88f20af8..dca708d6 100644 --- a/docs/builtin-defs.md +++ b/docs/builtin-defs.md @@ -27,9 +27,34 @@ Reached Root This will happen whenever there are free variables inside the logged term, or [scopeless lambdas](using-scopeless-lambdas.md) which bind variables that are used outside the logged term. +## `HVM.print` + +This is very similar to `HVM.log`. However, it can only log strings. It prints these strings directly to stdout, without wrapping them in quotes or escaping them. + +With `HVM.log`: +```rs +main = (HVM.log "Hi" "Hello world") + +// Program output: +"Hi" +"Hello world" +``` +However, with `HVM.print`: +```rs +main = (HVM.print "Hi" "Hello world") + +// Program output: +Hi +"Hello world" +``` + +This can be very useful, for example, to print strings with newlines in multiple lines. + +When the first argument is not a string `HVM.print` returns the second argument and no side effects are produced (In other words, it fails silently). + ## `HVM.black_box` -`HVM.black_box` is simply the identity function, but it does not get [pre-reduced](compiler-options#pre-reduce). This makes it possible to prevent some redexes from getting pre-reduced. +`HVM.black_box` is simply the identity function, but it does not get [pre-reduced](compiler-options.md#pre-reduce). This makes it possible to prevent some redexes from getting pre-reduced. Example without `HVM.black_box` ```rs diff --git a/docs/compiler-options.md b/docs/compiler-options.md index 0c2aa7e9..4faa016d 100644 --- a/docs/compiler-options.md +++ b/docs/compiler-options.md @@ -118,7 +118,7 @@ main = (id also_id) ## Pre-reduce -Normalizes all functions except main, dereferencing definitions in active positions, and solving annihilations and commutations. It does not reduce [builtin definitions](builtin_defs), such as `HVM.log`. +Normalizes all functions except main, dereferencing definitions in active positions, and solving annihilations and commutations. It does not reduce [builtin definitions](builtin-defs.md), such as `HVM.log`. Example: ```rs diff --git a/src/lib.rs b/src/lib.rs index 16426335..8c86a0e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,27 +37,56 @@ pub const HVM1_ENTRY_POINT: &str = "Main"; /// These are the names of builtin defs that are not in the hvm-lang book, but /// are present in the hvm-core book. They are implemented using Rust code by /// [`create_host`] and they can not be rewritten as hvm-lang functions. -pub const CORE_BUILTINS: [&str; 2] = ["HVM.log", "HVM.black_box"]; +pub const CORE_BUILTINS: [&str; 3] = ["HVM.log", "HVM.black_box", "HVM.print"]; /// Creates a host with the hvm-core primitive definitions built-in. /// This needs the book as an Arc because the closure that logs /// data needs access to the book. -pub fn create_host(hvml_book: Arc, labels: Arc, compile_opts: CompileOpts) -> Arc> { +pub fn create_host(book: Arc, labels: Arc, compile_opts: CompileOpts) -> Arc> { let host = Arc::new(Mutex::new(Host::default())); - let host_clone = host.clone(); host.lock().unwrap().insert_def( "HVM.log", - hvmc::host::DefRef::Owned(Box::new(LogDef::new(move |wire| { - let host = host_clone.lock().unwrap(); - let tree = host.readback_tree(&wire); - let net = hvmc::ast::Net { root: tree, rdex: vec![] }; - let net = hvmc_to_net(&net); - let (mut term, mut readback_errors) = net_to_term(&net, &hvml_book, &labels, false); - let resugar_errs = term.resugar_adts(&hvml_book, compile_opts.adt_encoding); - term.resugar_builtins(); + hvmc::host::DefRef::Owned(Box::new(LogDef::new({ + let host = host.clone(); + let book = book.clone(); + let labels = labels.clone(); + move |wire| { + let host = host.lock().unwrap(); + let tree = host.readback_tree(&wire); + let net = hvmc::ast::Net { root: tree, rdex: vec![] }; + let net = hvmc_to_net(&net); + let (mut term, mut readback_errors) = net_to_term(&net, &book, &labels, false); + let resugar_errs = term.resugar_adts(&book, compile_opts.adt_encoding); + term.resugar_builtins(); - readback_errors.extend(resugar_errs); - println!("{}{}", display_readback_errors(&readback_errors), term); + readback_errors.extend(resugar_errs); + println!("{}{}", display_readback_errors(&readback_errors), term); + } + }))), + ); + host.lock().unwrap().insert_def( + "HVM.print", + hvmc::host::DefRef::Owned(Box::new(LogDef::new({ + let host = host.clone(); + let book = book.clone(); + let labels = labels.clone(); + move |wire| { + let host = host.lock().unwrap(); + let tree = host.readback_tree(&wire); + let net = hvmc::ast::Net { root: tree, rdex: vec![] }; + let net = hvmc_to_net(&net); + let (mut term, mut readback_errors) = net_to_term(&net, &book, &labels, false); + let resugar_errs = term.resugar_adts(&book, compile_opts.adt_encoding); + term.resugar_builtins(); + + readback_errors.extend(resugar_errs); + match term { + Term::Str { val } => { + println!("{}", val); + }, + _ => (), + } + } }))), ); let book = ast::Book::from_str("@HVM.black_box = (x x)").unwrap(); diff --git a/tests/golden_tests/run_file/print.hvm b/tests/golden_tests/run_file/print.hvm new file mode 100644 index 00000000..4386c962 --- /dev/null +++ b/tests/golden_tests/run_file/print.hvm @@ -0,0 +1,4 @@ +concat (String.nil) str = str +concat (String.cons c rest1) str2 = (String.cons c (concat rest1 str2)) + +main = (HVM.print (concat "hello " "world") (concat "goodbye " "world")) \ No newline at end of file