Allow pattern-matching on bytearrays

- Doesn't allow pattern-matching on G1/G2 elements and strings,
    because the use cases for those is unclear and it adds complexity to
    the feature.

  - We still _parse_ patterns on G1/G2 elements and strings, but emit an
    error in those cases.

  - The syntax is the same as for bytearray literals (i.e. supports hex,
    utf-8 strings or plain arrays of bytes).
This commit is contained in:
KtorZ
2024-08-02 13:28:07 +02:00
parent ea032c90f2
commit f14dfdf8e1
24 changed files with 605 additions and 52 deletions

View File

@@ -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
}
"#
);
}
}

View File

@@ -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)

View File

@@ -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",
},
},
],
}

View File

@@ -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",
),
},
]

View File

@@ -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",
),
},
]

View File

@@ -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",
),
},
]

View File

@@ -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
}
"#
);
}
}