diff --git a/Cargo.lock b/Cargo.lock index 1fccc541..807e8682 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,25 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "bend" +version = "0.1.0" +dependencies = [ + "TSPL 0.0.9 (git+https://github.com/developedby/TSPL.git?branch=fix-hvml-bugs)", + "clap", + "highlight_error", + "hvm-core", + "indexmap", + "insta", + "interner", + "itertools", + "loaned", + "parking_lot", + "stacker", + "stdext", + "walkdir", +] + [[package]] name = "bitflags" version = "2.5.0" @@ -205,25 +224,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "hvm-lang" -version = "0.1.0" -dependencies = [ - "TSPL 0.0.9 (git+https://github.com/developedby/TSPL.git?branch=fix-hvml-bugs)", - "clap", - "highlight_error", - "hvm-core", - "indexmap", - "insta", - "interner", - "itertools", - "loaned", - "parking_lot", - "stacker", - "stdext", - "walkdir", -] - [[package]] name = "indexmap" version = "2.2.6" diff --git a/Cargo.toml b/Cargo.toml index 4b7d2677..cd7e9243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "hvm-lang" +name = "bend" version = "0.1.0" edition = "2021" [lib] -name = "hvml" +name = "bend" path = "src/lib.rs" [[bin]] -name = "hvml" +name = "bend" path = "src/main.rs" required-features = ["cli"] diff --git a/cspell.json b/cspell.json index 1d78a959..1d0b47f3 100644 --- a/cspell.json +++ b/cspell.json @@ -19,6 +19,7 @@ "Dall", "datatypes", "Deque", + "destructures", "desugared", "desugars", "dref", @@ -29,7 +30,6 @@ "hexdigit", "hvm's", "hvmc", - "hvml", "indexmap", "inet", "inets", @@ -40,6 +40,7 @@ "insta", "interner", "itertools", + "ITRS", "kwargs", "lcons", "linearization", @@ -58,10 +59,11 @@ "opre", "oprune", "oref", + "parallelizable", "peekable", "postcondition", - "prec", "powi", + "prec", "readback", "recursively", "redex", @@ -77,6 +79,7 @@ "scrutinee", "snil", "stdext", + "struct", "subcmd", "submatch", "subpattern", @@ -94,6 +97,13 @@ "walkdir", "wopts" ], - "files": ["**/*.rs", "**/*.md"], - "ignoreRegExpList": ["HexValues", "/λ/g", "/-O/g"] + "files": [ + "**/*.rs", + "**/*.md" + ], + "ignoreRegExpList": [ + "HexValues", + "/λ/g", + "/-O/g" + ] } diff --git a/docs/automatic-vectorization-with-tagged-lambdas.md b/docs/automatic-vectorization-with-tagged-lambdas.md deleted file mode 100644 index 548fc730..00000000 --- a/docs/automatic-vectorization-with-tagged-lambdas.md +++ /dev/null @@ -1,59 +0,0 @@ -# Automatic vectorization with tagged lambdas - -We have seen in [Dups and Sups](dups-and-sups.md) that duplications and superpositions can have labels. In HVM, lambdas and applications can have labels too. - -Tagged applications will only annihilate lambdas with the same tag. -```rs - -// V application's tag - #A(#A λx(body) arg) -// ^ lambda's tag -// The tag must go before the term. -// This reduces to -x = arg; body -``` - -For example, data types can be encoded as tagged lambdas: - -```rs -// data Bool = T | F -T = #Bool λt #Bool λf t -F = #Bool λt #Bool λf f - -// data List = (Cons x xs) | Nil -Cons = λx λxs #List λc #List λn #List.Cons.xs(#List.Cons.x(c x) xs) -Nil = #List λc #List λn n -``` - -When encoding the pattern matching, the application can then use the same label: - -```rs -// not = λbool match bool { T: (F) F: (T) } -not = λbool #Bool(bool F T) -``` - -In fact, `match` is syntax sugar for a tagged application like the one above. This means that it is not possible to match without using tagged applications. - -When an application and a lambda with different tags interact, the application "commutes" through the lambda instead of beta-reducing it. Here is how it works, roughly: - -```rs -#A (#B λx (b x) a) -// Reduces to -#B λc #A((b #A λ$d c) #B(a $d)) -``` - -This reduction can be hard to grasp, but an accurate way to understand it is that "the application goes through the lambda". - -This allows, in some limited scenarios, automatic vectorization. See "limitations" for a description of the limitations. -```rs -// vectorizes to: (Cons F (Cons T (Cons F Nil))) -main = (not (Cons T (Cons F (Cons T Nil)))) -``` -This works because the `Bool`-tagged application in `not` passes through the `List`-tagged lambdas in `Cons` until it gets to `T` and `F`. - -The tagged lambda and applications are compiled to `inet` nodes with different tag values for each data type. This allows them to commute, read [HVM-Core](https://github.com/HigherOrderCO/hvm-core/tree/main#language) to learn more about it. - -### Limitations -To be able to vectorize as described here: -- The function must not be recursive -- There must not be labels in common between the function and what you want to vectorize over diff --git a/docs/cli-arguments.md b/docs/cli-arguments.md index e786b0b1..d73def2f 100644 --- a/docs/cli-arguments.md +++ b/docs/cli-arguments.md @@ -1,33 +1,41 @@ # CLI arguments -It's possible to pass arguments to a program executed with `hvml run`: +It's possible to pass arguments to a program executed with `bend run` or `bend norm`: ```sh -hvml run [Arguments in expression form]... +bend run [Arguments in expression form]... ``` -It accepts any expression that would also be valid inside an hvm-lang function. +It accepts any expression that would also be valid inside a bend function. Arguments are passed to programs by applying them to the entrypoint function: -```js -main x1 x2 x3 = (MainBody x1 x2 x3) +```py +// Core syntax +main(x1, x2, x3): + MainBody(x1 x2 x3) -// Calling with `hvml run arg1 arg2 arg3 argN`, it becomes: - -main = (λx1 λx2 λx3 (MainBody x1 x2 x3) arg1 arg2 arg3 argN) +// Calling with `bend run arg1 arg2 arg3 argN`, it becomes (in core syntax): +main = (x1 λx2 λx3 (MainBody x1 x2 x3) arg1 arg2 arg3 argN) ``` There are no restrictions on the number of arguments passed to the program. - +You can even pass more arguments than the function expects, although that can lead to unexpected results. ```rust -// Can receive 2 CLI arguments -main x y = (+ x y) - -// Can't receive CLI arguments -main = λx λy (+ x y) +// Expects 2 CLI arguments +def main(x, y): + {x - y, y - x} // Calling with just one argument -hvml run 5 -λa (+ a 5) +bend norm +5 +λa {(- a 5) (- a +5)} + +// Calling with two argument +bend norm +5 +3 +{+2 -2} + +// Calling with three argument +// In this case, the third argument doesn't do anything due to the underlying interaction rules. +bend norm +5 +3 +1 +{+2 -2} ``` diff --git a/docs/compilation-and-readback.md b/docs/compilation-and-readback.md index 878c8a06..94088ef3 100644 --- a/docs/compilation-and-readback.md +++ b/docs/compilation-and-readback.md @@ -2,10 +2,10 @@ How are terms compiled to interaction net nodes? -HVM-Core has a bunch of useful nodes to write IC programs. +HVM has a bunch of useful nodes to write IC programs. Every node contains one `main` port `0` and two `auxiliary` ports, `1` and `2`. -There are 6 kinds of nodes, Eraser, Constructor, Reference, Number, Operation and Match. +There are 7 kinds of nodes, Eraser, Constructor, Duplicator, Reference, Number, Operation and Match. A lambda `λx x` compiles into a Constructor node. An application `((λx x) (λx x))` also compiles into a Constructor node. @@ -23,13 +23,10 @@ Points to the lambda variable Points to the argument When reading back, if we visit a Constructor via port 0 then we know it's a lambda, and if we visit it via port 2 it's an application. - The `Number` node uses the label to store it's number. -- An `Op2` node uses the label to store it's operation. -- And a `Constructor` node can have a label too! This is used for `dup` and [lambda tags](automatic-vectorization-with-tagged-lambdas.md. +- An `Operation` node uses the label to store it's operation. -A duplication `dup a b = x` compiles into a Constructor node too, but with a different label. -A superposition `{a b}` compiles to a Constructor node too. The difference here comes from context too. - -Additionally, nodes have labels. We use the label to store data in the node's memory, which can be used for various purposes. +A duplication `let {a b} = x` compiles into a Duplicator node. +A superposition `{a b}` compiles to a Duplicator node too. The difference here comes from context too. ``` 0 - Points to the sup occurrence 0 - Points to the duplicated value diff --git a/docs/writing-fusing-functions.md b/docs/writing-fusing-functions.md index 0005c92e..b58cbbd9 100644 --- a/docs/writing-fusing-functions.md +++ b/docs/writing-fusing-functions.md @@ -17,7 +17,7 @@ main = (if true 42 37) // so (true 42 37) will do the same thing. ``` -This is how a`Not` function that acts on this encoding can be defined +This is how a `Not` function that acts on this encoding can be defined ```rs not = λboolean (boolean false true) main = (not true) // Outputs λtλf f. @@ -120,37 +120,33 @@ Broadly speaking, a good rule of thumb in HVM is **push linear lambdas to the to ## Example -To show the power of fusing, here is a program that self-composes `fusing_not` 2^512 times and prints the result. `2^512` is larger than amount of atoms in the observable universe, and yet HVM is still able to work with it due to its optimal sharing capabilities. +To show the power of fusing, here is a program that self-composes `fusing_not` 2^24 times and prints the result. +Currently hvm is not able to handle operations between church numbers so we explicitly convert the native number to a church number in this example (which is very slow). This program uses [native numbers, which are described here](native-numbers.md). ```rs true = λt λf t false = λt λf f + not = λboolean (boolean false true) + fusing_not = λboolean λt λf (boolean f t) + // Creates a Church numeral out of a native number to_church n = switch n { 0: λf λx x _: λf λx (f (to_church n-1 f x)) } + main = - let two = λf λx (f (f x)) - let two_pow_512 = ((to_church 512) two) // Composition of church-encoded numbers is equivalent to exponentiation. - // Self-composes `not` 2^512 times and prints the result. - (two_pow_512 fusing_not) // try replacing this by regular not. Will it still work? + ((to_church 0xFFFFFF) fusing_not) // try replacing this by regular not. Will it still work? ``` Here is the program's output: ```bash -$ hvml run -s fuse_magic.hvm -λa λb λc (a b c) - -RWTS : 15374 -- ANNI : 8193 -- COMM : 5116 -- ERAS : 521 -- DREF : 1031 -- OPER : 513 -TIME : 0.002 s -RPS : 9.537 m +$ bend norm -s fuse_magic.hvm +Result: λa λb λc (a c b) +- ITRS: 285212661 +- TIME: 5.67s +- MIPS: 50.28 ``` -Only 15374 rewrites! Fusing is really powerful. +A lot of rewrites, but most of those are just to create the church number. diff --git a/src/main.rs b/src/main.rs index c2cd38d8..d6b56fed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -use clap::{Args, CommandFactory, Parser, Subcommand}; -use hvml::{ +use bend::{ check_book, compile_book, desugar_book, diagnostics::{Diagnostics, DiagnosticsConfig, Severity}, load_file_to_book, run_book, term::{Book, Name}, CompileOpts, OptLevel, RunOpts, }; +use clap::{Args, CommandFactory, Parser, Subcommand}; use std::path::{Path, PathBuf}; #[derive(Parser, Debug)] @@ -80,8 +80,8 @@ enum Mode { #[arg(help = "Path to the input file")] path: PathBuf, - #[arg(value_parser = |arg: &str| hvml::term::parser::TermParser::new(arg).parse_term())] - arguments: Option>, + #[arg(value_parser = |arg: &str| bend::term::parser::TermParser::new(arg).parse_term())] + arguments: Option>, }, /// Runs the lambda-term level desugaring passes. Desugar { diff --git a/src/net/mod.rs b/src/net/mod.rs index 4870b489..f97daa97 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,7 +1,7 @@ pub mod hvmc_to_net; use crate::term::Name; -pub type HvmlLab = u16; +pub type BendLab = u16; use NodeKind::*; #[derive(Debug, Clone)] @@ -41,13 +41,13 @@ pub enum NodeKind { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CtrKind { - Con(Option), - Tup(Option), - Dup(HvmlLab), + Con(Option), + Tup(Option), + Dup(BendLab), } impl CtrKind { - pub fn to_lab(self) -> HvmlLab { + pub fn to_lab(self) -> BendLab { #[allow(clippy::identity_op)] match self { CtrKind::Con(None) => 0, diff --git a/src/term/flavour_py/parser.rs b/src/term/flavour_py/parser.rs index 381b3378..c227df10 100644 --- a/src/term/flavour_py/parser.rs +++ b/src/term/flavour_py/parser.rs @@ -85,7 +85,7 @@ impl<'a> PyParser<'a> { } else if self.try_consume("False") { return Ok(Term::Num { val: 0 }); } - Term::Var { nam: self.parse_hvml_name()? } + Term::Var { nam: self.parse_bend_name()? } } }; Ok(res) @@ -95,7 +95,7 @@ impl<'a> PyParser<'a> { self.consume("[")?; let head = self.parse_term_py()?; if self.try_consume_keyword("for") { - let bind = self.parse_hvml_name()?; + let bind = self.parse_bend_name()?; self.consume("in")?; let iter = self.parse_term_py()?; let mut cond = None; @@ -116,7 +116,7 @@ impl<'a> PyParser<'a> { fn parse_term_py(&mut self) -> Result { self.skip_trivia(); if self.try_consume_keyword("lambda") { - let names = self.list_like(|p| p.parse_hvml_name(), "", ":", ",", true, 1)?; + let names = self.list_like(|p| p.parse_bend_name(), "", ":", ",", true, 1)?; let bod = self.parse_term_py()?; Ok(Term::Lam { names, bod: Box::new(bod) }) } else { @@ -254,7 +254,7 @@ impl<'a> PyParser<'a> { if self.starts_with("(") { self.parse_assignment_py(indent) } else { - let name = self.parse_hvml_name()?; + let name = self.parse_bend_name()?; if self.skip_starts_with("=") { // it's actually an assignment self.consume("=")?; @@ -316,7 +316,7 @@ impl<'a> PyParser<'a> { fn parse_as_bind(&mut self) -> Result, String> { let mut bind = None; if self.try_consume_keyword("as") { - bind = Some(self.parse_hvml_name()?); + bind = Some(self.parse_bend_name()?); } Ok(bind) } @@ -343,7 +343,7 @@ impl<'a> PyParser<'a> { if p.try_consume("_") { Ok(None) } else { - let nam = p.parse_hvml_name()?; + let nam = p.parse_bend_name()?; Ok(Some(nam)) } }, @@ -405,7 +405,7 @@ impl<'a> PyParser<'a> { } fn parse_fold_py(&mut self, indent: &mut Indent) -> Result { - let fun = self.parse_hvml_name()?; + let fun = self.parse_bend_name()?; let arg = self.parse_term_py()?; let bind = self.parse_as_bind()?; self.consume(":")?; @@ -422,7 +422,7 @@ impl<'a> PyParser<'a> { } fn parse_do_py(&mut self, indent: &mut Indent) -> Result { - let fun = self.parse_hvml_name()?; + let fun = self.parse_bend_name()?; self.consume(":")?; let mut block = Vec::new(); @@ -458,14 +458,14 @@ impl<'a> PyParser<'a> { fn parse_assign_pattern_py(&mut self) -> Result { if self.skip_starts_with("(") { - let mut binds = self.list_like(|p| p.parse_hvml_name(), "(", ")", ",", true, 1)?; + let mut binds = self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 1)?; if binds.len() == 1 { Ok(AssignPattern::Var(std::mem::take(&mut binds[0]))) } else { Ok(AssignPattern::Tup(binds)) } } else { - self.parse_hvml_name().map(AssignPattern::Var) + self.parse_bend_name().map(AssignPattern::Var) } } @@ -481,8 +481,8 @@ impl<'a> PyParser<'a> { } fn parse_def_py(&mut self, indent: &mut Indent) -> Result { - let name = self.parse_hvml_name()?; - let params = self.list_like(|p| p.parse_hvml_name(), "(", ")", ",", true, 0)?; + let name = self.parse_bend_name()?; + let params = self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 0)?; self.consume(":")?; indent.enter_level(); let body = self.parse_stmt_py(indent)?; @@ -491,7 +491,7 @@ impl<'a> PyParser<'a> { } fn parse_enum_py(&mut self, indent: &mut Indent) -> Result { - let name = self.parse_hvml_name()?; + let name = self.parse_bend_name()?; let mut variants = Vec::new(); self.consume(":")?; indent.enter_level(); @@ -499,10 +499,10 @@ impl<'a> PyParser<'a> { if !self.skip_exact_indent(indent, true)? { break; } - let name = self.parse_hvml_name()?; + let name = self.parse_bend_name()?; let mut fields = Vec::new(); if self.skip_starts_with("(") { - fields = self.list_like(|p| p.parse_hvml_name(), "(", ")", ",", true, 0)?; + fields = self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 0)?; } variants.push((name.clone(), Variant { name, fields })); } diff --git a/src/term/parser.rs b/src/term/parser.rs index b3807f85..9d34b5cb 100644 --- a/src/term/parser.rs +++ b/src/term/parser.rs @@ -8,7 +8,7 @@ use crate::{ use highlight_error::highlight_error; use TSPL::Parser; -// hvml grammar description: +// Bend grammar description: // ::= ( | )* // ::= "data" "=" ( | "(" ()* ")" )+ // ::= ("(" * ")" | *) "=" @@ -95,7 +95,7 @@ impl<'a> TermParser<'a> { if self.try_consume("(") { // (name field*) let name = self.parse_top_level_name()?; - let field_parser = |p: &mut Self| p.labelled(|p| p.parse_hvml_name(), "datatype constructor field"); + let field_parser = |p: &mut Self| p.labelled(|p| p.parse_bend_name(), "datatype constructor field"); let fields = self.list_like(field_parser, "", ")", "", false, 0)?; Ok((name, fields)) } else { @@ -194,7 +194,7 @@ impl<'a> TermParser<'a> { if self.starts_with("$") { unexpected_tag(self)?; self.advance_one(); - let name = self.parse_hvml_name()?; + let name = self.parse_bend_name()?; return Ok(Pattern::Chn(name)); } @@ -286,7 +286,7 @@ impl<'a> TermParser<'a> { if self.starts_with("$") { self.consume("$")?; unexpected_tag(self)?; - let nam = self.parse_hvml_name()?; + let nam = self.parse_bend_name()?; return Ok(Term::Lnk { nam }); } @@ -376,7 +376,7 @@ impl<'a> TermParser<'a> { // Use if self.try_consume_keyword("use") { unexpected_tag(self)?; - let nam = self.parse_hvml_name()?; + let nam = self.parse_bend_name()?; self.consume("=")?; let val = self.parse_term()?; self.try_consume(";"); @@ -421,7 +421,7 @@ impl<'a> TermParser<'a> { // Var unexpected_tag(self)?; - let nam = self.labelled(|p| p.parse_hvml_name(), "term")?; + let nam = self.labelled(|p| p.parse_bend_name(), "term")?; Ok(Term::Var { nam }) }) } @@ -443,7 +443,7 @@ impl<'a> TermParser<'a> { fn parse_top_level_name(&mut self) -> Result { let ini_idx = *self.index(); - let nam = self.parse_hvml_name()?; + let nam = self.parse_bend_name()?; let end_idx = *self.index(); if nam.contains("__") { let ctx = highlight_error(ini_idx, end_idx, self.input()); @@ -459,7 +459,7 @@ impl<'a> TermParser<'a> { if p.try_consume("*") { Ok(None) } else { - let nam = p.parse_hvml_name()?; + let nam = p.parse_bend_name()?; Ok(Some(nam)) } }, @@ -477,9 +477,6 @@ impl<'a> TermParser<'a> { { let ctx = highlight_error(index, index + 1, self.input); return Err(format!("Tagged terms not supported for hvm32.\n{ctx}")); - /* self.advance_one(); - let nam = self.labelled(|p| p.parse_hvml_name(), "tag name")?; - Some(Tag::Named(nam)) */ } else { None }; @@ -496,13 +493,13 @@ impl<'a> TermParser<'a> { } fn parse_match_arg(&mut self) -> Result<(Name, Term, Vec), String> { - let bnd = self.parse_hvml_name()?; + let bnd = self.parse_bend_name()?; let arg = if self.try_consume("=") { self.parse_term()? } else { Term::Var { nam: bnd.clone() } }; let with = if self.try_consume_keyword("with") { - let mut with = vec![self.parse_hvml_name()?]; + let mut with = vec![self.parse_bend_name()?]; while !self.skip_starts_with("{") { self.try_consume(","); - with.push(self.parse_hvml_name()?); + with.push(self.parse_bend_name()?); } with } else { @@ -755,7 +752,7 @@ pub trait ParserCommons<'a>: Parser<'a> { } } - fn parse_hvml_name(&mut self) -> Result { + fn parse_bend_name(&mut self) -> Result { let nam = self.parse_name()?; Ok(Name::new(nam)) } diff --git a/src/term/transform/apply_args.rs b/src/term/transform/apply_args.rs index 31e42070..b36785aa 100644 --- a/src/term/transform/apply_args.rs +++ b/src/term/transform/apply_args.rs @@ -10,7 +10,7 @@ impl Ctx<'_> { /// ```hvm /// main x1 x2 x3 = (MainBody x1 x2 x3) /// ``` - /// Calling with `hvml run arg1 arg2 arg3`, it becomes: + /// Calling with `bend run arg1 arg2 arg3`, it becomes: /// ```hvm /// main = (λx1 λx2 λx3 (MainBody x1 x2 x3) arg1 arg2 arg3) /// ``` diff --git a/src/term/transform/apply_use.rs b/src/term/transform/apply_use.rs index e80f7040..5554d2b1 100644 --- a/src/term/transform/apply_use.rs +++ b/src/term/transform/apply_use.rs @@ -7,7 +7,7 @@ impl Book { /// Inline copies of the declared bind in the `use` expression. /// /// Example: - /// ```hvml + /// ```bend /// use id = λx x /// (id id id) /// diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index f067ab58..8fb9d26b 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -1,4 +1,4 @@ -use hvml::{ +use bend::{ compile_book, desugar_book, diagnostics::{Diagnostics, DiagnosticsConfig, Severity}, net::hvmc_to_net::hvmc_to_net, @@ -107,7 +107,7 @@ fn compile_term() { term.make_var_names_unique(); term.linearize_vars(); - let net = hvml::term::term_to_net(&term, &mut Default::default()).map_err(|e| e.to_string())?; + let net = bend::term::term_to_net(&term, &mut Default::default()).map_err(|e| e.to_string())?; Ok(format!("{}", net)) }) @@ -369,7 +369,7 @@ fn cli() { let args = args_buf.lines(); let output = - std::process::Command::new(env!("CARGO_BIN_EXE_hvml")).args(args).output().expect("Run command"); + std::process::Command::new(env!("CARGO_BIN_EXE_bend")).args(args).output().expect("Run command"); let res = format!("{}{}", String::from_utf8_lossy(&output.stderr), String::from_utf8_lossy(&output.stdout)); Ok(res) diff --git a/tests/snapshots/cli__debug_list_map.hvm.snap b/tests/snapshots/cli__debug_list_map.hvm.snap index 9ef9fb8d..97d76427 100644 --- a/tests/snapshots/cli__debug_list_map.hvm.snap +++ b/tests/snapshots/cli__debug_list_map.hvm.snap @@ -6,6 +6,6 @@ error: unexpected argument '-d' found tip: to pass '-d' as a value, use '-- -d' -Usage: hvml run [OPTIONS] [ARGUMENTS]... +Usage: bend run [OPTIONS] [ARGUMENTS]... For more information, try '--help'. diff --git a/tests/snapshots/cli__debug_u60_to_nat.hvm.snap b/tests/snapshots/cli__debug_u60_to_nat.hvm.snap index 42a717e8..3004a76c 100644 --- a/tests/snapshots/cli__debug_u60_to_nat.hvm.snap +++ b/tests/snapshots/cli__debug_u60_to_nat.hvm.snap @@ -6,6 +6,6 @@ error: unexpected argument '-d' found tip: to pass '-d' as a value, use '-- -d' -Usage: hvml run [OPTIONS] [ARGUMENTS]... +Usage: bend run [OPTIONS] [ARGUMENTS]... For more information, try '--help'.