diff --git a/CHANGELOG.md b/CHANGELOG.md index 847fea187b2..62e12de53c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -230,3 +230,6 @@ - Fixed a bug where the formatter would remove the `@deprecated` attribute from constants. ([Surya Rose](https://github.com/GearsDatapacks)) + +- Fix invalid JavaScript codegen in cases where underscores follow a decimal. + ([Patrick Dewey](https://github.com/ptdewey)) diff --git a/compiler-core/src/javascript/decision.rs b/compiler-core/src/javascript/decision.rs index 3e36fe73e24..54ec77d4421 100644 --- a/compiler-core/src/javascript/decision.rs +++ b/compiler-core/src/javascript/decision.rs @@ -1,6 +1,6 @@ use super::{ INDENT, bit_array_segment_int_value_to_bytes, - expression::{self, Generator, Ordering, float}, + expression::{self, Generator, Ordering, float, float_from_value}, }; use crate::{ ast::{AssignmentKind, Endianness, SrcSpan, TypedClause, TypedExpr, TypedPattern}, @@ -1024,7 +1024,7 @@ impl<'generator, 'module, 'a> Variables<'generator, 'module, 'a> { RuntimeCheck::String { value: expected } => docvec![value, equality, string(expected)], RuntimeCheck::Float { float_value: expected, - } => docvec![value, equality, expected.value()], + } => docvec![value, equality, float_from_value(expected.value())], RuntimeCheck::Int { int_value: expected, } => docvec![value, equality, expected.clone()], diff --git a/compiler-core/src/javascript/expression.rs b/compiler-core/src/javascript/expression.rs index 33cbb490620..3365f0f12e1 100644 --- a/compiler-core/src/javascript/expression.rs +++ b/compiler-core/src/javascript/expression.rs @@ -288,7 +288,7 @@ impl<'module, 'a> Generator<'module, 'a> { TypedExpr::String { value, .. } => string(value), TypedExpr::Int { value, .. } => int(value), - TypedExpr::Float { value, .. } => float(value), + TypedExpr::Float { float_value, .. } => float_from_value(float_value.value()), TypedExpr::List { elements, tail, .. } => { self.not_in_tail_position(Some(Ordering::Strict), |this| match tail { @@ -2353,6 +2353,22 @@ pub fn float(value: &str) -> Document<'_> { out.to_doc() } +pub fn float_from_value(value: f64) -> Document<'static> { + if value.is_infinite() { + if value.is_sign_positive() { + "Infinity".to_doc() + } else { + "-Infinity".to_doc() + } + } else if value.is_nan() { + // NOTE: this case is probably unnecessary, as this function is only + // invoked with `LiteralFloatValue` values, which cannot be nan. + "NaN".to_doc() + } else { + value.to_doc() + } +} + /// The context where the constant expression is used, it might be inside a /// function call, or in the definition of another constant. /// diff --git a/compiler-core/src/javascript/tests/numbers.rs b/compiler-core/src/javascript/tests/numbers.rs index f3a92291787..eed9f293105 100644 --- a/compiler-core/src/javascript/tests/numbers.rs +++ b/compiler-core/src/javascript/tests/numbers.rs @@ -431,6 +431,56 @@ pub fn main() { ); } +#[test] +fn underscore_after_decimal_point() { + assert_js!( + " +pub fn main() { + 0._1 +} +" + ); +} + +#[test] +fn underscore_after_decimal_point_case_statement() { + assert_js!( + " +pub fn main(x) { + case x { + 0._1 -> \"bar\" + _ -> \"foo\" + } +} +" + ); +} + +#[test] +fn inf_float_case_statement() { + assert_js!( + " +pub fn main(x) { + case x { + 100.001e123_456_789 -> \"bar\" + _ -> \"foo\" + } +} +" + ); +} + +#[test] +fn division_inf_by_inf_float() { + assert_js!( + " +pub fn main(x) { + -100.001e123_456_789 /. 100.001e123_456_789 +} +" + ); +} + #[test] fn division_by_zero_float() { assert_js!( diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap new file mode 100644 index 00000000000..9b33a2135e1 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/javascript/tests/numbers.rs +expression: "\npub fn main(x) {\n -100.001e123_456_789 /. 100.001e123_456_789\n}\n" +--- +----- SOURCE CODE + +pub fn main(x) { + -100.001e123_456_789 /. 100.001e123_456_789 +} + + +----- COMPILED JAVASCRIPT +export function main(x) { + return -Infinity / Infinity; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_literals.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_literals.snap index 0590a670682..68b31ce7ebb 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_literals.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_literals.snap @@ -1,8 +1,6 @@ --- source: compiler-core/src/javascript/tests/numbers.rs -assertion_line: 23 expression: "\npub fn go() {\n 1.5\n 2.0\n -0.1\n 1.\n}\n" -snapshot_kind: text --- ----- SOURCE CODE @@ -19,5 +17,5 @@ export function go() { 1.5; 2.0; -0.1; - return 1.; + return 1.0; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap index 7601d5188cd..d062626b3c5 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap @@ -1,8 +1,6 @@ --- source: compiler-core/src/javascript/tests/numbers.rs -assertion_line: 37 expression: "\npub fn go() {\n 0.01e-1\n 0.01e-0\n -10.01e-1\n -10.01e-0\n 100.001e523\n -100.001e-523\n 100.001e123_456_789\n -100.001e-123_456_789\n}\n" -snapshot_kind: text --- ----- SOURCE CODE @@ -20,12 +18,12 @@ pub fn go() { ----- COMPILED JAVASCRIPT export function go() { - 0.01e-1; - 0.01e-0; - -10.01e-1; - -10.01e-0; - 100.001e523; - -100.001e-523; - 100.001e123_456_789; - return -100.001e-123_456_789; + 0.001; + 0.01; + -1.001; + -10.01; + Infinity; + -0.0; + Infinity; + return -0.0; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap new file mode 100644 index 00000000000..c7d51378682 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap @@ -0,0 +1,22 @@ +--- +source: compiler-core/src/javascript/tests/numbers.rs +expression: "\npub fn main(x) {\n case x {\n 100.001e123_456_789 -> \"bar\"\n _ -> \"foo\"\n }\n}\n" +--- +----- SOURCE CODE + +pub fn main(x) { + case x { + 100.001e123_456_789 -> "bar" + _ -> "foo" + } +} + + +----- COMPILED JAVASCRIPT +export function main(x) { + if (x === Infinity) { + return "bar"; + } else { + return "foo"; + } +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_float.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_float.snap index 5fd9820bfa4..36f6db6f3fe 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_float.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_float.snap @@ -1,8 +1,6 @@ --- source: compiler-core/src/javascript/tests/numbers.rs -assertion_line: 264 expression: "\npub fn main() {\n 09_179.1\n}\n" -snapshot_kind: text --- ----- SOURCE CODE @@ -13,5 +11,5 @@ pub fn main() { ----- COMPILED JAVASCRIPT export function main() { - return 9_179.1; + return 9179.1; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point.snap new file mode 100644 index 00000000000..070c78d43fa --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/javascript/tests/numbers.rs +expression: "\npub fn main() {\n 0._1\n}\n" +--- +----- SOURCE CODE + +pub fn main() { + 0._1 +} + + +----- COMPILED JAVASCRIPT +export function main() { + return 0.1; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point_case_statement.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point_case_statement.snap new file mode 100644 index 00000000000..35f7f1163e9 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__underscore_after_decimal_point_case_statement.snap @@ -0,0 +1,22 @@ +--- +source: compiler-core/src/javascript/tests/numbers.rs +expression: "\npub fn main(x) {\n case x {\n 0._1 -> \"bar\"\n _ -> \"foo\"\n }\n}\n" +--- +----- SOURCE CODE + +pub fn main(x) { + case x { + 0._1 -> "bar" + _ -> "foo" + } +} + + +----- COMPILED JAVASCRIPT +export function main(x) { + if (x === 0.1) { + return "bar"; + } else { + return "foo"; + } +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__wide_float_div.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__wide_float_div.snap index cca7044fb6a..2ad43271b94 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__wide_float_div.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__wide_float_div.snap @@ -11,5 +11,5 @@ pub fn go() { ----- COMPILED JAVASCRIPT export function go() { - return 111111111111111111111111111111. / 22222222222222222222222222222222222.; + return 1.111111111111111e29 / 2.222222222222222e34; }