diff --git a/CHANGELOG.md b/CHANGELOG.md index 8282882a..366a34cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - **aiken-lang**: add support for `mk_cons` and `mk_pair_data` builtins. See [#964](https://github.com/aiken-lang/aiken/issues/964). @KtorZ +- **aiken-lang**: pattern-matching on bytearrays is now available. See [#989](https://github.com/aiken-lang/aiken/issues/989). @KtorZ ### Changed diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 34da20d2..ac297986 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1262,6 +1262,12 @@ pub enum Pattern { base: Base, }, + ByteArray { + location: Span, + value: Vec, + preferred_format: ByteArrayFormatPreference, + }, + /// The creation of a variable. /// e.g. `expect [this_is_a_var, .._] = x` /// e.g. `let foo = 42` @@ -1330,6 +1336,7 @@ impl Pattern { | Pattern::Discard { location, .. } | Pattern::Tuple { location, .. } | Pattern::Pair { location, .. } + | Pattern::ByteArray { location, .. } | Pattern::Constructor { location, .. } => *location, } } @@ -1383,6 +1390,7 @@ impl TypedPattern { Pattern::Int { .. } | Pattern::Var { .. } | Pattern::Assign { .. } + | Pattern::ByteArray { .. } | Pattern::Discard { .. } => Some(Located::Pattern(self, value.clone())), Pattern::List { elements, .. } @@ -1438,6 +1446,7 @@ impl TypedPattern { pub fn tipo(&self, value: &TypedExpr) -> Option> { match self { Pattern::Int { .. } => Some(builtins::int()), + Pattern::ByteArray { .. } => Some(builtins::byte_array()), Pattern::Constructor { tipo, .. } => Some(tipo.clone()), Pattern::Var { .. } | Pattern::Assign { .. } | Pattern::Discard { .. } => { Some(value.tipo()) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index f166ed0a..74e64122 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -1877,6 +1877,12 @@ impl<'comments> Formatter<'comments> { let doc = match pattern { Pattern::Int { value, base, .. } => self.int(value, base), + Pattern::ByteArray { + value, + preferred_format, + .. + } => self.bytearray(value, None, preferred_format), + Pattern::Var { name, .. } => name.to_doc(), Pattern::Assign { name, pattern, .. } => { diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index 07d52f35..0d2c2e1f 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -17,7 +17,7 @@ use crate::{ Span, TraceLevel, Tracing, TypedArg, TypedClause, TypedDataType, TypedFunction, TypedPattern, TypedValidator, UnOp, }, - builtins::{bool, data, int, list, void, PRELUDE}, + builtins::{bool, byte_array, data, int, list, void, PRELUDE}, expr::TypedExpr, gen_uplc::{ air::ExpectLevel, @@ -915,26 +915,30 @@ impl<'a> CodeGenerator<'a> { value: expected_int, location, .. - } => { - assert!(props.kind.is_expect()); - - let name = format!( + } => AirTree::assign_literal_pattern( + format!( "__expected_by_{}_span_{}_{}", expected_int, location.start, location.end - ); + ), + AirTree::int(expected_int), + value, + int(), + props, + then, + ), - let expect = AirTree::binop( - BinOp::Eq, - bool(), - AirTree::int(expected_int), - AirTree::local_var(&name, int()), - int(), - ); - - let expr = AirTree::let_assignment(name, value, expect); - - AirTree::assert_bool(true, expr, then, props.otherwise.clone()) - } + Pattern::ByteArray { + location, + value: expected_bytes, + .. + } => AirTree::assign_literal_pattern( + format!("__expected_bytes_span_{}_{}", location.start, location.end), + AirTree::byte_array(expected_bytes.clone()), + value, + byte_array(), + props, + then, + ), Pattern::Var { name, .. } => { if props.full_check { @@ -2321,6 +2325,10 @@ impl<'a> CodeGenerator<'a> { assert!(!props.final_clause); (AirTree::int(value), then) } + Pattern::ByteArray { value, .. } => { + assert!(!props.final_clause); + (AirTree::byte_array(value.clone()), then) + } Pattern::Var { name, .. } => ( AirTree::void(), AirTree::let_assignment( @@ -2839,6 +2847,15 @@ impl<'a> CodeGenerator<'a> { then, ) } + Pattern::ByteArray { value, .. } => { + props.complex_clause = true; + AirTree::clause_guard( + &props.original_subject_name, + AirTree::byte_array(value.clone()), + byte_array(), + then, + ) + } Pattern::Var { name, .. } => AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index a32b4df5..31b3896f 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -615,7 +615,7 @@ pub fn pattern_has_conditions( data_types: &IndexMap<&DataTypeKey, &TypedDataType>, ) -> bool { match pattern { - Pattern::List { .. } | Pattern::Int { .. } => true, + Pattern::List { .. } | Pattern::Int { .. } | Pattern::ByteArray { .. } => true, Pattern::Tuple { elems, .. } => elems .iter() .any(|elem| pattern_has_conditions(elem, data_types)), diff --git a/crates/aiken-lang/src/gen_uplc/tree.rs b/crates/aiken-lang/src/gen_uplc/tree.rs index 34847148..c716b5a6 100644 --- a/crates/aiken-lang/src/gen_uplc/tree.rs +++ b/crates/aiken-lang/src/gen_uplc/tree.rs @@ -2,6 +2,7 @@ use super::air::{Air, ExpectLevel}; use crate::{ ast::{BinOp, Curve, Span, UnOp}, builtins::{bool, byte_array, data, int, list, string, void}, + gen_uplc::AssignmentProperties, tipo::{Type, ValueConstructor, ValueConstructorVariant}, }; use indexmap::IndexSet; @@ -588,6 +589,29 @@ impl AirTree { } } + pub fn assign_literal_pattern( + name: String, + pattern: AirTree, + rhs: AirTree, + tipo: Rc, + props: AssignmentProperties, + then: AirTree, + ) -> AirTree { + assert!(props.kind.is_expect()); + + let expect = AirTree::binop( + BinOp::Eq, + bool(), + pattern, + AirTree::local_var(&name, tipo.clone()), + tipo, + ); + + let expr = AirTree::let_assignment(name, rhs, expect); + + AirTree::assert_bool(true, expr, then, props.otherwise.clone()) + } + pub fn cast_from_data( value: AirTree, tipo: Rc, diff --git a/crates/aiken-lang/src/parser/error.rs b/crates/aiken-lang/src/parser/error.rs index c2ffd0ab..3fed3993 100644 --- a/crates/aiken-lang/src/parser/error.rs +++ b/crates/aiken-lang/src/parser/error.rs @@ -148,6 +148,26 @@ impl ParseError { label: None, } } + + pub fn match_on_curve(span: Span) -> Self { + Self { + kind: ErrorKind::PatternMatchOnCurvePoint, + span, + while_parsing: None, + expected: HashSet::new(), + label: Some("cannot pattern-match on curve point"), + } + } + + pub fn match_string(span: Span) -> Self { + Self { + kind: ErrorKind::PatternMatchOnString, + span, + while_parsing: None, + expected: HashSet::new(), + label: Some("cannot pattern-match on string"), + } + } } impl PartialEq for ParseError { @@ -260,6 +280,18 @@ pub enum ErrorKind { "# }))] DeprecatedWhenClause, + + #[error("I choked on a curve point in a bytearray pattern.")] + #[diagnostic(help( + "You can pattern-match on bytearrays just fine, but not on G1 nor G2 elements. Use if/else with an equality if you have to compare those." + ))] + PatternMatchOnCurvePoint, + + #[error("I refuse to cooperate and match a utf-8 string.")] + #[diagnostic(help( + "You can pattern-match on bytearrays but not on strings. Note that I can parse utf-8 encoded bytearrays just fine, so you probably want to drop the extra '@' and only manipulate bytearrays wherever you need to. On-chain, strings shall be avoided as much as possible." + ))] + PatternMatchOnString, } fn fmt_curve_type(curve: &CurveType) -> String { diff --git a/crates/aiken-lang/src/parser/pattern/bytearray.rs b/crates/aiken-lang/src/parser/pattern/bytearray.rs new file mode 100644 index 00000000..723a19af --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/bytearray.rs @@ -0,0 +1,63 @@ +use crate::{ + ast::UntypedPattern, + parser::{error::ParseError, literal, token::Token}, +}; +use chumsky::prelude::*; + +pub fn parser() -> impl Parser { + literal::bytearray(|value, preferred_format, curve, location, emit| { + if curve.is_some() { + emit(ParseError::match_on_curve(location)); + } + + UntypedPattern::ByteArray { + location, + value, + preferred_format, + } + }) +} + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn pattern_bytearray() { + assert_expr!( + r#" + when foo is { + #"00abcd" -> True + "Aiken, rocks!" -> True + #[1, 2, 3, 4] -> True + #[0x00, 0xab, 0xcd] -> True + _ -> False + } + "# + ); + } + + #[test] + fn pattern_bytearray_g1_element() { + assert_expr!( + r#" + when foo is { + #"950dfd33da2682260c76038dfb8bad6e84ae9d599a3c151815945ac1e6ef6b1027cd917f3907479d20d636ce437a41f5" -> False + _ -> True + } + "# + ); + } + + #[test] + fn pattern_bytearray_g2_element() { + assert_expr!( + r#" + when foo is { + #"b0629fa1158c2d23a10413fe91d381a84d25e31d041cd0377d25828498fd02011b35893938ced97535395e4815201e67108bcd4665e0db25d602d76fa791fab706c54abf5e1a9e44b4ac1e6badf3d2ac0328f5e30be341677c8bac5dda7682f1" -> False + _ -> True + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/pattern/mod.rs b/crates/aiken-lang/src/parser/pattern/mod.rs index 21989a73..97c6be58 100644 --- a/crates/aiken-lang/src/parser/pattern/mod.rs +++ b/crates/aiken-lang/src/parser/pattern/mod.rs @@ -1,10 +1,12 @@ use chumsky::prelude::*; +mod bytearray; mod constructor; mod discard; mod int; mod list; mod pair; +mod string; mod tuple; mod var; @@ -12,11 +14,13 @@ use crate::{ ast::UntypedPattern, parser::{error::ParseError, token::Token}, }; +pub use bytearray::parser as bytearray; pub use constructor::parser as constructor; pub use discard::parser as discard; pub use int::parser as int; pub use list::parser as list; pub use pair::parser as pair; +pub use string::parser as string; pub use tuple::parser as tuple; pub use var::parser as var; @@ -28,8 +32,10 @@ pub fn parser() -> impl Parser { constructor(pattern.clone()), discard(), int(), + bytearray(), tuple(pattern.clone()), list(pattern), + string(), )) .then( just(Token::As) diff --git a/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray.snap b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray.snap new file mode 100644 index 00000000..a23e6a4c --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray.snap @@ -0,0 +1,115 @@ +--- +source: crates/aiken-lang/src/parser/pattern/bytearray.rs +description: "Code:\n\nwhen foo is {\n #\"00abcd\" -> True\n \"Aiken, rocks!\" -> True\n #[1, 2, 3, 4] -> True\n #[0x00, 0xab, 0xcd] -> True\n _ -> False\n}\n" +--- +When { + location: 0..138, + subject: Var { + location: 5..8, + name: "foo", + }, + clauses: [ + UntypedClause { + location: 18..35, + patterns: [ + ByteArray { + location: 18..27, + value: [ + 0, + 171, + 205, + ], + preferred_format: HexadecimalString, + }, + ], + then: Var { + location: 31..35, + name: "True", + }, + }, + UntypedClause { + location: 40..63, + patterns: [ + ByteArray { + location: 40..55, + value: [ + 65, + 105, + 107, + 101, + 110, + 44, + 32, + 114, + 111, + 99, + 107, + 115, + 33, + ], + preferred_format: Utf8String, + }, + ], + then: Var { + location: 59..63, + name: "True", + }, + }, + UntypedClause { + location: 68..89, + patterns: [ + ByteArray { + location: 68..81, + value: [ + 1, + 2, + 3, + 4, + ], + preferred_format: ArrayOfBytes( + Decimal { + numeric_underscore: false, + }, + ), + }, + ], + then: Var { + location: 85..89, + name: "True", + }, + }, + UntypedClause { + location: 94..121, + patterns: [ + ByteArray { + location: 94..113, + value: [ + 0, + 171, + 205, + ], + preferred_format: ArrayOfBytes( + Hexadecimal, + ), + }, + ], + then: Var { + location: 117..121, + name: "True", + }, + }, + UntypedClause { + location: 126..136, + patterns: [ + Discard { + name: "_", + location: 126..127, + }, + ], + then: Var { + location: 131..136, + name: "False", + }, + }, + ], +} diff --git a/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g1_element.snap b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g1_element.snap new file mode 100644 index 00000000..d7b02650 --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g1_element.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/pattern/bytearray.rs +description: "Invalid code (parse error):\n\nwhen foo is {\n #\"950dfd33da2682260c76038dfb8bad6e84ae9d599a3c151815945ac1e6ef6b1027cd917f3907479d20d636ce437a41f5\" -> False\n _ -> True\n}\n" +--- +[ + ParseError { + kind: PatternMatchOnCurvePoint, + span: 18..132, + while_parsing: None, + expected: {}, + label: Some( + "cannot pattern-match on curve point", + ), + }, +] diff --git a/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g2_element.snap b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g2_element.snap new file mode 100644 index 00000000..bd7ec372 --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_bytearray_g2_element.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/pattern/bytearray.rs +description: "Invalid code (parse error):\n\nwhen foo is {\n #\"b0629fa1158c2d23a10413fe91d381a84d25e31d041cd0377d25828498fd02011b35893938ced97535395e4815201e67108bcd4665e0db25d602d76fa791fab706c54abf5e1a9e44b4ac1e6badf3d2ac0328f5e30be341677c8bac5dda7682f1\" -> False\n _ -> True\n}\n" +--- +[ + ParseError { + kind: PatternMatchOnCurvePoint, + span: 18..228, + while_parsing: None, + expected: {}, + label: Some( + "cannot pattern-match on curve point", + ), + }, +] diff --git a/crates/aiken-lang/src/parser/pattern/snapshots/pattern_string.snap b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_string.snap new file mode 100644 index 00000000..fcd55431 --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/snapshots/pattern_string.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/pattern/string.rs +description: "Invalid code (parse error):\n\nwhen foo is {\n @\"foo\" -> True\n}\n" +--- +[ + ParseError { + kind: PatternMatchOnString, + span: 16..22, + while_parsing: None, + expected: {}, + label: Some( + "cannot pattern-match on string", + ), + }, +] diff --git a/crates/aiken-lang/src/parser/pattern/string.rs b/crates/aiken-lang/src/parser/pattern/string.rs new file mode 100644 index 00000000..2cf618f6 --- /dev/null +++ b/crates/aiken-lang/src/parser/pattern/string.rs @@ -0,0 +1,33 @@ +use crate::{ + ast::{ByteArrayFormatPreference, UntypedPattern}, + parser::{error::ParseError, literal, token::Token}, +}; +use chumsky::prelude::*; + +pub fn parser() -> impl Parser { + literal::string().validate(|_, location, emit| { + emit(ParseError::match_string(location)); + + UntypedPattern::ByteArray { + location, + value: Vec::new(), + preferred_format: ByteArrayFormatPreference::Utf8String, + } + }) +} + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn pattern_string() { + assert_expr!( + r#" + when foo is { + @"foo" -> True + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 1f8e0999..ecf1c528 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -2883,3 +2883,74 @@ fn side_effects() { unreachable!(); } } + +#[test] +fn pattern_bytearray() { + let source_code = r#" + pub fn main(foo: ByteArray) { + when foo is { + #[1, 2, 3] -> True + #"00ff" -> True + "Aiken, rocks!" -> True + _ -> False + } + } + "#; + + let result = check(parse(source_code)); + assert!(result.is_ok()); + + let (warnings, _) = result.unwrap(); + assert!(warnings.is_empty(), "no warnings: {warnings:#?}"); +} + +#[test] +fn pattern_bytearray_not_unify_clause_list() { + let source_code = r#" + pub fn main(foo: ByteArray) { + when foo is { + [1, 2, 3] -> True + _ -> False + } + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::CouldNotUnify { .. })) + )) +} + +#[test] +fn pattern_bytearray_not_unify_clause_int() { + let source_code = r#" + pub fn main(foo: ByteArray) { + when foo is { + 42 -> True + _ -> False + } + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::CouldNotUnify { .. })) + )) +} + +#[test] +fn pattern_bytearray_not_unify_subject() { + let source_code = r#" + pub fn main(foo: String) { + when foo is { + "42" -> True + _ -> False + } + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err((_, Error::CouldNotUnify { .. })) + )) +} diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index ae567e67..43e0ac47 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -1000,3 +1000,20 @@ fn format_variadic_trace() { "# ); } + +#[test] +fn format_pattern_bytearray() { + assert_format!( + r#" + fn main(foo) { + when foo is { + "Aiken, rocks!" -> True + #"00abcd" -> True + #[1, 2, 3, 4] -> True + #[0x00, 0xab, 0xcd] -> True + _ -> False + } + } + "# + ); +} diff --git a/crates/aiken-lang/src/tests/snapshots/format_pattern_bytearray.snap b/crates/aiken-lang/src/tests/snapshots/format_pattern_bytearray.snap new file mode 100644 index 00000000..5b33e707 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/format_pattern_bytearray.snap @@ -0,0 +1,13 @@ +--- +source: crates/aiken-lang/src/tests/format.rs +description: "Code:\n\nfn main(foo) {\n when foo is {\n \"Aiken, rocks!\" -> True\n #\"00abcd\" -> True\n #[1, 2, 3, 4] -> True\n #[0x00, 0xab, 0xcd] -> True\n _ -> False\n }\n}\n" +--- +fn main(foo) { + when foo is { + "Aiken, rocks!" -> True + #"00abcd" -> True + #[1, 2, 3, 4] -> True + #[0x00, 0xab, 0xcd] -> True + _ -> False + } +} diff --git a/crates/aiken-lang/src/tipo/exhaustive.rs b/crates/aiken-lang/src/tipo/exhaustive.rs index 6507f79c..88df69fc 100644 --- a/crates/aiken-lang/src/tipo/exhaustive.rs +++ b/crates/aiken-lang/src/tipo/exhaustive.rs @@ -381,6 +381,7 @@ pub(crate) enum Pattern { #[derive(Debug, Clone, PartialEq)] pub(crate) enum Literal { Int(String), + ByteArray(Vec), } impl Pattern { @@ -536,6 +537,9 @@ pub(super) fn simplify( ) -> Result { match value { ast::Pattern::Int { value, .. } => Ok(Pattern::Literal(Literal::Int(value.clone()))), + ast::Pattern::ByteArray { value, .. } => { + Ok(Pattern::Literal(Literal::ByteArray(value.clone()))) + } ast::Pattern::Assign { pattern, .. } => simplify(environment, pattern.as_ref()), ast::Pattern::List { elements, tail, .. } => { let mut p = if let Some(t) = tail { diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index cbb2dd5a..4a3064a6 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::{ ast::{CallArg, Pattern, Span, TypedPattern, UntypedPattern}, - builtins::{int, list, pair, tuple}, + builtins::{byte_array, int, list, pair, tuple}, }; use itertools::Itertools; use std::{ @@ -199,6 +199,21 @@ impl<'a, 'b> PatternTyper<'a, 'b> { }) } + Pattern::ByteArray { + location, + value, + preferred_format, + } => { + self.environment + .unify(tipo, byte_array(), location, false)?; + + Ok(Pattern::ByteArray { + location, + value, + preferred_format, + }) + } + Pattern::List { location, elements, diff --git a/crates/aiken-lsp/src/lib.rs b/crates/aiken-lsp/src/lib.rs index b1683aa6..15beac07 100644 --- a/crates/aiken-lsp/src/lib.rs +++ b/crates/aiken-lsp/src/lib.rs @@ -34,7 +34,7 @@ pub fn start() -> Result<(), Error> { let (connection, io_threads) = Connection::stdio(); // Run the server and wait for the two threads to end (typically by trigger LSP Exit event). - let server_capabilities = serde_json::to_value(&capabilities())?; + let server_capabilities = serde_json::to_value(capabilities())?; let initialization_params = connection.initialize(server_capabilities)?; let initialize_params = serde_json::from_value(initialization_params)?; diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index 1de09053..8e2bfd6e 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -62,7 +62,7 @@ fn assert_uplc(source_code: &str, expected: Term, should_fail: bool) { let expected: Program = expected.try_into().unwrap(); - assert_eq!(debruijn_program.to_pretty(), expected.to_pretty()); + assert!(debruijn_program.to_pretty() == expected.to_pretty()); let mut eval = debruijn_program.eval(ExBudget::default()); @@ -6631,3 +6631,42 @@ fn mk_pair_data() { false, ) } + +#[test] +fn pattern_bytearray() { + let src = r#" + test pattern_bytearray() { + let bytes = "foo" + when bytes is { + "bar" -> False + #[0x66, 0x6f, 0x6f] -> True + _ -> False + } + } + "#; + + let snd_clause = Term::equals_bytestring() + .apply(Term::byte_string(vec![0x66, 0x6f, 0x6f])) + .apply(Term::var("__subject")) + .delayed_if_then_else(Term::bool(true), Term::bool(false)); + + let fst_clause = Term::equals_bytestring() + .apply(Term::byte_string(vec![0x62, 0x61, 0x72])) + .apply(Term::var("__subject")) + .delayed_if_then_else(Term::bool(false), snd_clause); + + let when_clause = fst_clause + .lambda("__subject") + .apply(Term::var("__when_var")) + .lambda("__when_var") + .apply(Term::var("bytes")); + + let program = when_clause + .lambda("bytes") + .apply(Term::byte_string(vec![0x66, 0x6f, 0x6f])) + // Not sure what this extra lambda is or do? + .lambda("???") + .apply(Term::Error.delay()); + + assert_uplc(src, program, false) +} diff --git a/crates/aiken/src/cmd/build.rs b/crates/aiken/src/cmd/build.rs index a5379e0c..a5b60499 100644 --- a/crates/aiken/src/cmd/build.rs +++ b/crates/aiken/src/cmd/build.rs @@ -22,19 +22,34 @@ pub struct Args { uplc: bool, /// Filter traces to be included in the generated program(s). - /// - user-defined: only consider traces that you've explicitly defined (either through the - /// 'trace' keyword of via the trace-if-false ('?') operator. - /// - compiler-generated: only included internal traces generated by the Aiken compiler, for - /// example in usage of 'expect'. - /// - all: include both user-defined and compiler-generated traces. + /// + /// - user-defined: + /// only consider traces that you've explicitly defined + /// either through the 'trace' keyword of via the trace-if-false + /// ('?') operator. + /// + /// - compiler-generated: + /// only included internal traces generated by the + /// Aiken compiler, for example in usage of 'expect'. + /// + /// - all: + /// include both user-defined and compiler-generated traces. + /// /// [optional] [default: all] #[clap(short, long, value_parser=filter_traces_parser(), default_missing_value="all", verbatim_doc_comment)] filter_traces: Option Tracing>, /// Choose the verbosity level of traces: - /// - silent: disable traces altogether - /// - compact: only culprit line numbers are shown on failures - /// - verbose: enable full verbose traces as provided by the user or the compiler + /// + /// - silent: + /// disable traces altogether + /// + /// - compact: + /// only culprit line numbers are shown on failures + /// + /// - verbose: + /// enable full verbose traces as provided by the user or the compiler + /// /// [optional] #[clap(short, long, value_parser=trace_level_parser(), default_value_t=TraceLevel::Silent, verbatim_doc_comment)] trace_level: TraceLevel, diff --git a/crates/aiken/src/cmd/check.rs b/crates/aiken/src/cmd/check.rs index 02fc7189..92f89d3a 100644 --- a/crates/aiken/src/cmd/check.rs +++ b/crates/aiken/src/cmd/check.rs @@ -48,20 +48,35 @@ pub struct Args { #[clap(short, long)] exact_match: bool, - /// Filter traces to be considered during testing: - /// - user-defined: only consider traces that you've explicitly defined (either through the - /// 'trace' keyword of via the trace-if-false ('?') operator. - /// - compiler-generated: only included internal traces generated by the Aiken compiler, for - /// example in usage of 'expect'. - /// - all: include both user-defined and compiler-generated traces. + /// Filter traces to be included in the generated program(s). + /// + /// - user-defined: + /// only consider traces that you've explicitly defined + /// either through the 'trace' keyword of via the trace-if-false + /// ('?') operator. + /// + /// - compiler-generated: + /// only included internal traces generated by the + /// Aiken compiler, for example in usage of 'expect'. + /// + /// - all: + /// include both user-defined and compiler-generated traces. + /// /// [optional] [default: all] #[clap(short, long, value_parser=filter_traces_parser(), default_missing_value="all", verbatim_doc_comment)] filter_traces: Option Tracing>, /// Choose the verbosity level of traces: - /// - silent: disable traces altogether - /// - compact: only culprit line numbers are shown on failures - /// - verbose: enable full verbose traces as provided by the user or the compiler + /// + /// - silent: + /// disable traces altogether + /// + /// - compact: + /// only culprit line numbers are shown on failures + /// + /// - verbose: + /// enable full verbose traces as provided by the user or the compiler + /// /// [optional] #[clap(short, long, value_parser=trace_level_parser(), default_value_t=TraceLevel::Verbose, verbatim_doc_comment)] trace_level: TraceLevel, diff --git a/crates/aiken/src/cmd/export.rs b/crates/aiken/src/cmd/export.rs index 86d41058..a1c36932 100644 --- a/crates/aiken/src/cmd/export.rs +++ b/crates/aiken/src/cmd/export.rs @@ -1,9 +1,7 @@ -use std::path::PathBuf; - +use super::build::{filter_traces_parser, trace_level_parser}; use aiken_lang::ast::{TraceLevel, Tracing}; use aiken_project::{options::Options, watch::with_project}; - -use super::build::{filter_traces_parser, trace_level_parser}; +use std::path::PathBuf; #[derive(clap::Args)] pub struct Args { @@ -18,20 +16,35 @@ pub struct Args { #[clap(short, long)] name: String, - /// Filter traces to be considered during testing: - /// - user-defined: only consider traces that you've explicitly defined (either through the - /// 'trace' keyword of via the trace-if-false ('?') operator. - /// - compiler-generated: only included internal traces generated by the Aiken compiler, for - /// example in usage of 'expect'. - /// - all: include both user-defined and compiler-generated traces. + /// Filter traces to be included in the generated program(s). + /// + /// - user-defined: + /// only consider traces that you've explicitly defined + /// either through the 'trace' keyword of via the trace-if-false + /// ('?') operator. + /// + /// - compiler-generated: + /// only included internal traces generated by the + /// Aiken compiler, for example in usage of 'expect'. + /// + /// - all: + /// include both user-defined and compiler-generated traces. + /// /// [optional] [default: all] #[clap(short, long, value_parser=filter_traces_parser(), default_missing_value="all", verbatim_doc_comment)] filter_traces: Option Tracing>, /// Choose the verbosity level of traces: - /// - silent: disable traces altogether - /// - compact: only culprit line numbers are shown on failures - /// - verbose: enable full verbose traces as provided by the user or the compiler + /// + /// - silent: + /// disable traces altogether + /// + /// - compact: + /// only culprit line numbers are shown on failures + /// + /// - verbose: + /// enable full verbose traces as provided by the user or the compiler + /// /// [optional] #[clap(short, long, value_parser=trace_level_parser(), default_value_t=TraceLevel::Verbose, verbatim_doc_comment)] trace_level: TraceLevel,