Merge pull request #991 from aiken-lang/pattern-match-bytearrays
Allow pattern-matching on bytearrays
This commit is contained in:
commit
09c065d332
|
@ -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
|
||||
|
||||
|
|
|
@ -1262,6 +1262,12 @@ pub enum Pattern<Constructor, Type> {
|
|||
base: Base,
|
||||
},
|
||||
|
||||
ByteArray {
|
||||
location: Span,
|
||||
value: Vec<u8>,
|
||||
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<A, B> Pattern<A, B> {
|
|||
| 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<Rc<Type>> {
|
||||
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())
|
||||
|
|
|
@ -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, .. } => {
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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<Type>,
|
||||
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<Type>,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
use crate::{
|
||||
ast::UntypedPattern,
|
||||
parser::{error::ParseError, literal, token::Token},
|
||||
};
|
||||
use chumsky::prelude::*;
|
||||
|
||||
pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
|
||||
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 {
|
||||
#<Bls12_381, G1>"950dfd33da2682260c76038dfb8bad6e84ae9d599a3c151815945ac1e6ef6b1027cd917f3907479d20d636ce437a41f5" -> False
|
||||
_ -> True
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_bytearray_g2_element() {
|
||||
assert_expr!(
|
||||
r#"
|
||||
when foo is {
|
||||
#<Bls12_381, G2>"b0629fa1158c2d23a10413fe91d381a84d25e31d041cd0377d25828498fd02011b35893938ced97535395e4815201e67108bcd4665e0db25d602d76fa791fab706c54abf5e1a9e44b4ac1e6badf3d2ac0328f5e30be341677c8bac5dda7682f1" -> False
|
||||
_ -> True
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<Token, UntypedPattern, Error = ParseError> {
|
|||
constructor(pattern.clone()),
|
||||
discard(),
|
||||
int(),
|
||||
bytearray(),
|
||||
tuple(pattern.clone()),
|
||||
list(pattern),
|
||||
string(),
|
||||
))
|
||||
.then(
|
||||
just(Token::As)
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/pattern/bytearray.rs
|
||||
description: "Invalid code (parse error):\n\nwhen foo is {\n #<Bls12_381, G1>\"950dfd33da2682260c76038dfb8bad6e84ae9d599a3c151815945ac1e6ef6b1027cd917f3907479d20d636ce437a41f5\" -> False\n _ -> True\n}\n"
|
||||
---
|
||||
[
|
||||
ParseError {
|
||||
kind: PatternMatchOnCurvePoint,
|
||||
span: 18..132,
|
||||
while_parsing: None,
|
||||
expected: {},
|
||||
label: Some(
|
||||
"cannot pattern-match on curve point",
|
||||
),
|
||||
},
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/pattern/bytearray.rs
|
||||
description: "Invalid code (parse error):\n\nwhen foo is {\n #<Bls12_381, G2>\"b0629fa1158c2d23a10413fe91d381a84d25e31d041cd0377d25828498fd02011b35893938ced97535395e4815201e67108bcd4665e0db25d602d76fa791fab706c54abf5e1a9e44b4ac1e6badf3d2ac0328f5e30be341677c8bac5dda7682f1\" -> False\n _ -> True\n}\n"
|
||||
---
|
||||
[
|
||||
ParseError {
|
||||
kind: PatternMatchOnCurvePoint,
|
||||
span: 18..228,
|
||||
while_parsing: None,
|
||||
expected: {},
|
||||
label: Some(
|
||||
"cannot pattern-match on curve point",
|
||||
),
|
||||
},
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
},
|
||||
]
|
|
@ -0,0 +1,33 @@
|
|||
use crate::{
|
||||
ast::{ByteArrayFormatPreference, UntypedPattern},
|
||||
parser::{error::ParseError, literal, token::Token},
|
||||
};
|
||||
use chumsky::prelude::*;
|
||||
|
||||
pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
|
||||
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
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 { .. }))
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -381,6 +381,7 @@ pub(crate) enum Pattern {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Literal {
|
||||
Int(String),
|
||||
ByteArray(Vec<u8>),
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
|
@ -536,6 +537,9 @@ pub(super) fn simplify(
|
|||
) -> Result<Pattern, Error> {
|
||||
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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -62,7 +62,7 @@ fn assert_uplc(source_code: &str, expected: Term<Name>, should_fail: bool) {
|
|||
|
||||
let expected: Program<DeBruijn> = 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)
|
||||
}
|
||||
|
|
|
@ -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<fn(TraceLevel) -> 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,
|
||||
|
|
|
@ -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<fn(TraceLevel) -> 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,
|
||||
|
|
|
@ -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<fn(TraceLevel) -> 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,
|
||||
|
|
Loading…
Reference in New Issue