Skip to content

Commit c390fff

Browse files
giacomocavalierilpil
authored andcommitted
Support alias patterns on JS target
1 parent e47bb8e commit c390fff

9 files changed

+376
-15
lines changed

compiler-core/src/exhaustiveness.rs

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ impl Branch {
271271
// any size test, so if we find a `ReadAction` that is the first
272272
// test to perform in a bit array pattern we know it's always
273273
// going to match and can be safely moved into the branch's body.
274-
Pattern::BitArray { tests } => match tests.front() {
274+
Pattern::BitArray { tests } => match tests.front_mut() {
275275
Some(BitArrayTest::Match(MatchTest {
276276
value: BitArrayMatchedValue::Variable(name),
277277
read_action,
@@ -285,15 +285,37 @@ impl Branch {
285285
let _ = tests.pop_front();
286286
}
287287

288-
// Discards are removed directly without even binding them
289-
// in the branch's body.
290-
Some(test) if test.is_discard() => {
291-
let _ = tests.pop_front();
292-
}
293-
294-
// Otherwise there's no unconditional test to pop, we
295-
// keep the pattern without changing it.
296-
Some(_) => return true,
288+
Some(test) => match test {
289+
// Just like regular assigns, those patterns are unrefutable
290+
// and will become assignments in the branch's body.
291+
BitArrayTest::Match(MatchTest {
292+
value: BitArrayMatchedValue::Assign { name, value },
293+
read_action,
294+
}) => {
295+
let bit_array = check.var.clone();
296+
self.body.assign_bit_array_slice(
297+
name.clone(),
298+
bit_array,
299+
read_action.clone(),
300+
);
301+
// We will still need to check the aliased value!
302+
*test = BitArrayTest::Match(MatchTest {
303+
value: value.as_ref().clone(),
304+
read_action: read_action.clone(),
305+
});
306+
continue;
307+
}
308+
309+
// Discards are removed directly without even binding them
310+
// in the branch's body.
311+
_ if test.is_discard() => {
312+
let _ = tests.pop_front();
313+
}
314+
315+
// Otherwise there's no unconditional test to pop, we
316+
// keep the pattern without changing it.
317+
_ => return true,
318+
},
297319

298320
// If a bit array pattern has no tests then it's always
299321
// going to match, no matter what. We just remove it.
@@ -955,7 +977,7 @@ pub enum BitArrayMatchedValue {
955977
Discard(EcoString),
956978
Assign {
957979
name: EcoString,
958-
pattern_match: Box<BitArrayMatchedValue>,
980+
value: Box<BitArrayMatchedValue>,
959981
},
960982
}
961983

@@ -2791,16 +2813,16 @@ impl CaseToCompile {
27912813
}
27922814
}
27932815

2794-
fn segment_matched_value(segment: &TypedPatternBitArraySegment) -> BitArrayMatchedValue {
2795-
match segment.value.as_ref() {
2816+
fn segment_matched_value(pattern: &TypedPattern) -> BitArrayMatchedValue {
2817+
match pattern {
27962818
ast::Pattern::Int { int_value, .. } => BitArrayMatchedValue::LiteralInt(int_value.clone()),
27972819
ast::Pattern::Float { value, .. } => BitArrayMatchedValue::LiteralFloat(value.clone()),
27982820
ast::Pattern::String { value, .. } => BitArrayMatchedValue::LiteralString(value.clone()),
27992821
ast::Pattern::Variable { name, .. } => BitArrayMatchedValue::Variable(name.clone()),
28002822
ast::Pattern::Discard { name, .. } => BitArrayMatchedValue::Discard(name.clone()),
28012823
ast::Pattern::Assign { name, pattern, .. } => BitArrayMatchedValue::Assign {
28022824
name: name.clone(),
2803-
pattern_match: Box::new(segment_matched_value(pattern)),
2825+
value: Box::new(segment_matched_value(pattern)),
28042826
},
28052827
x => panic!("unexpected segment value pattern {:?}", x),
28062828
}

compiler-core/src/javascript/decision.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,9 @@ impl<'generator, 'module, 'a> Variables<'generator, 'module, 'a> {
911911
read_action,
912912
negation,
913913
)?,
914-
BitArrayMatchedValue::Variable(..) | BitArrayMatchedValue::Discard(..) => {
914+
BitArrayMatchedValue::Variable(..)
915+
| BitArrayMatchedValue::Discard(..)
916+
| BitArrayMatchedValue::Assign { .. } => {
915917
panic!("unreachable")
916918
}
917919
},

compiler-core/src/javascript/tests/bit_arrays.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,3 +1999,115 @@ fn go(x) {
19991999
"#
20002000
)
20012001
}
2002+
2003+
// https://github.com/gleam-lang/gleam/issues/3375
2004+
#[test]
2005+
fn bit_array_assignment_int() {
2006+
assert_js!(
2007+
"
2008+
pub fn main() {
2009+
let assert <<1 as a>> = <<1>>
2010+
a
2011+
}
2012+
"
2013+
);
2014+
}
2015+
2016+
#[test]
2017+
fn case_bit_array_assignment_int() {
2018+
assert_js!(
2019+
"
2020+
pub fn go(x) {
2021+
case x {
2022+
<<1 as n>>
2023+
| <<2 as n, _:bytes>> -> n
2024+
_ -> 1
2025+
}
2026+
}
2027+
"
2028+
);
2029+
}
2030+
2031+
// https://github.com/gleam-lang/gleam/issues/3375
2032+
#[test]
2033+
fn bit_array_assignment_float() {
2034+
assert_js!(
2035+
"
2036+
pub fn main() {
2037+
let assert <<3.14 as pi:float>> = <<3.14>>
2038+
pi
2039+
}
2040+
"
2041+
);
2042+
}
2043+
2044+
#[test]
2045+
fn case_bit_array_assignment_float() {
2046+
assert_js!(
2047+
"
2048+
pub fn go(x) {
2049+
case x {
2050+
<<3.14 as pi:float>>
2051+
| <<1.1 as pi:float, _:bytes>> -> pi
2052+
_ -> 1.1
2053+
}
2054+
}
2055+
"
2056+
);
2057+
}
2058+
2059+
// https://github.com/gleam-lang/gleam/issues/3375
2060+
#[test]
2061+
fn bit_array_assignment_string() {
2062+
assert_js!(
2063+
r#"
2064+
pub fn main() {
2065+
let assert <<"Hello, world!" as message:utf8>> = <<"Hello, world!">>
2066+
message
2067+
}
2068+
"#
2069+
);
2070+
}
2071+
2072+
#[test]
2073+
fn case_bit_array_assignment_string() {
2074+
assert_js!(
2075+
r#"
2076+
pub fn go(x) {
2077+
case x {
2078+
<<"Hello" as message>>
2079+
| <<"Jak" as message, _:bytes>> -> message
2080+
_ -> "wibble"
2081+
}
2082+
}
2083+
"#
2084+
);
2085+
}
2086+
2087+
// https://github.com/gleam-lang/gleam/issues/3375
2088+
#[test]
2089+
fn bit_array_assignment_discard() {
2090+
assert_js!(
2091+
r#"
2092+
pub fn main() {
2093+
let assert <<_ as number>> = <<10>>
2094+
number
2095+
}
2096+
"#
2097+
);
2098+
}
2099+
2100+
#[test]
2101+
fn case_bit_array_assignment_discard() {
2102+
assert_js!(
2103+
r#"
2104+
pub fn go(x) {
2105+
case x {
2106+
<<_ as n>>
2107+
| <<_ as n, _:bytes>> -> n
2108+
_ -> 1
2109+
}
2110+
}
2111+
"#
2112+
);
2113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn main() {\n let assert <<_ as number>> = <<10>>\n number\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn main() {
8+
let assert <<_ as number>> = <<10>>
9+
number
10+
}
11+
12+
13+
----- COMPILED JAVASCRIPT
14+
import { makeError, toBitArray } from "../gleam.mjs";
15+
16+
export function main() {
17+
let $ = toBitArray([10]);
18+
if ($.bitSize !== 8) {
19+
throw makeError(
20+
"let_assert",
21+
"my/mod",
22+
3,
23+
"main",
24+
"Pattern match failed, no pattern matched the value.",
25+
{ value: $ }
26+
)
27+
}
28+
let number = $.byteAt(0);
29+
return number;
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn main() {\n let assert <<3.14 as pi:float>> = <<3.14>>\n pi\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn main() {
8+
let assert <<3.14 as pi:float>> = <<3.14>>
9+
pi
10+
}
11+
12+
13+
----- COMPILED JAVASCRIPT
14+
import { makeError, toBitArray, bitArraySliceToFloat, sizedFloat } from "../gleam.mjs";
15+
16+
export function main() {
17+
let $ = toBitArray([sizedFloat(3.14, 64, true)]);
18+
if ($.bitSize !== 64 || bitArraySliceToFloat($, 0, 64, true) !== 3.14) {
19+
throw makeError(
20+
"let_assert",
21+
"my/mod",
22+
3,
23+
"main",
24+
"Pattern match failed, no pattern matched the value.",
25+
{ value: $ }
26+
)
27+
}
28+
let pi = bitArraySliceToFloat($, 0, 64, true);
29+
return pi;
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn main() {\n let assert <<1 as a>> = <<1>>\n a\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn main() {
8+
let assert <<1 as a>> = <<1>>
9+
a
10+
}
11+
12+
13+
----- COMPILED JAVASCRIPT
14+
import { makeError, toBitArray } from "../gleam.mjs";
15+
16+
export function main() {
17+
let $ = toBitArray([1]);
18+
if ($.bitSize !== 8 || $.byteAt(0) !== 1) {
19+
throw makeError(
20+
"let_assert",
21+
"my/mod",
22+
3,
23+
"main",
24+
"Pattern match failed, no pattern matched the value.",
25+
{ value: $ }
26+
)
27+
}
28+
let a = $.byteAt(0);
29+
return a;
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn go(x) {\n case x {\n <<_ as n>>\n | <<_ as n, _:bytes>> -> n\n _ -> 1\n }\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn go(x) {
8+
case x {
9+
<<_ as n>>
10+
| <<_ as n, _:bytes>> -> n
11+
_ -> 1
12+
}
13+
}
14+
15+
16+
----- COMPILED JAVASCRIPT
17+
export function go(x) {
18+
if (x.bitSize === 8) {
19+
let n = x.byteAt(0);
20+
return n;
21+
} else {
22+
if (x.bitSize >= 8) {
23+
if ((x.bitSize - 8) % 8 === 0) {
24+
let n = x.byteAt(0);
25+
return n;
26+
} else {
27+
return 1;
28+
}
29+
} else {
30+
return 1;
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn go(x) {\n case x {\n <<3.14 as pi:float>>\n | <<1.1 as pi:float, _:bytes>> -> pi\n _ -> 1.1\n }\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn go(x) {
8+
case x {
9+
<<3.14 as pi:float>>
10+
| <<1.1 as pi:float, _:bytes>> -> pi
11+
_ -> 1.1
12+
}
13+
}
14+
15+
16+
----- COMPILED JAVASCRIPT
17+
import { bitArraySliceToFloat } from "../gleam.mjs";
18+
19+
export function go(x) {
20+
if (x.bitSize === 64) {
21+
if (bitArraySliceToFloat(x, 0, 64, true) === 3.14) {
22+
let pi = bitArraySliceToFloat(x, 0, 64, true);
23+
return pi;
24+
} else {
25+
if (bitArraySliceToFloat(x, 0, 64, true) === 1.1) {
26+
if ((x.bitSize - 64) % 8 === 0) {
27+
let pi = bitArraySliceToFloat(x, 0, 64, true);
28+
return pi;
29+
} else {
30+
return 1.1;
31+
}
32+
} else {
33+
return 1.1;
34+
}
35+
}
36+
} else {
37+
if (x.bitSize >= 64) {
38+
if (bitArraySliceToFloat(x, 0, 64, true) === 1.1) {
39+
if ((x.bitSize - 64) % 8 === 0) {
40+
let pi = bitArraySliceToFloat(x, 0, 64, true);
41+
return pi;
42+
} else {
43+
return 1.1;
44+
}
45+
} else {
46+
return 1.1;
47+
}
48+
} else {
49+
return 1.1;
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)