Skip to content

Commit

Permalink
fix(eval): parse octal and hexadecimal intergers as numbers in `std.p…
Browse files Browse the repository at this point in the history
…arseYaml`
  • Loading branch information
eduardosm committed Aug 4, 2024
1 parent e52d5b4 commit 11ac2f5
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
in `std.parseYaml`.
- Allow explicit `+` in floating point numbers in `std.parseYaml`.
- Allow leading zeros in floating point numbers in `std.parseYaml`.
- Parse octal and hexadecimal intergers as numbers in `std.parseYaml`.

Note: YAML changes are considered fixes because they where overlooked features.

## 0.1.2 (2024-07-20)

Expand Down
63 changes: 62 additions & 1 deletion rsjsonnet-lang/src/program/eval/parse_yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,10 @@ fn scalar_to_value(
"true" | "True" | "TRUE" => Ok(ValueData::Bool(true)),
"false" | "False" | "FALSE" => Ok(ValueData::Bool(false)),
_ => {
if let Some(number) = try_parse_number(value) {
let number = try_parse_number(value)
.or_else(|| try_parse_octal_number(value))
.or_else(|| try_parse_hex_number(value));
if let Some(number) = number {
if !number.is_finite() {
return Err(ParseError::NumberOverflow);
}
Expand Down Expand Up @@ -362,3 +365,61 @@ fn try_parse_number(s: &str) -> Option<f64> {
let number = s.parse().unwrap();
Some(number)
}

fn try_parse_octal_number(s: &str) -> Option<f64> {
let digits = s.strip_prefix("0o")?;
if digits.is_empty() {
return None;
}

let mut int = 0u128;
let mut chars = digits.chars().peekable();
while let Some(chr) = chars.peek() {
let digit = chr.to_digit(8)?;
let new_int = int.checked_mul(8).and_then(|v| v.checked_add(digit.into()));
if let Some(new_int) = new_int {
int = new_int;
chars.next();
} else {
break;
}
}

let mut number = int as f64;
for chr in chars {
let digit = chr.to_digit(8)?;
number = number.mul_add(8.0, f64::from(digit));
}

Some(number)
}

fn try_parse_hex_number(s: &str) -> Option<f64> {
let digits = s.strip_prefix("0x")?;
if digits.is_empty() {
return None;
}

let mut int = 0u128;
let mut chars = digits.chars().peekable();
while let Some(chr) = chars.peek() {
let digit = chr.to_digit(16)?;
let new_int = int
.checked_mul(16)
.and_then(|v| v.checked_add(digit.into()));
if let Some(new_int) = new_int {
int = new_int;
chars.next();
} else {
break;
}
}

let mut number = int as f64;
for chr in chars {
let digit = chr.to_digit(16)?;
number = number.mul_add(16.0, f64::from(digit));
}

Some(number)
}
29 changes: 29 additions & 0 deletions ui-tests/pass/stdlib/parseYaml.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ std.assertEqual(std.parseYaml("8.5e+2"), 850) &&
std.assertEqual(std.parseYaml("25e-2"), 0.25) &&
std.assertEqual(std.parseYaml("0.5e+10"), 5000000000) &&

std.assertEqual(std.parseYaml("0o1"), 1) &&
std.assertEqual(std.parseYaml("0o7"), 7) &&

std.assertEqual(std.parseYaml("0x1"), 1) &&
std.assertEqual(std.parseYaml("0x9"), 9) &&
std.assertEqual(std.parseYaml("0xF"), 15) &&

std.assertEqual(std.parseYaml("."), ".") &&
std.assertEqual(std.parseYaml("-."), "-.") &&
std.assertEqual(std.parseYaml("+."), "+.") &&
Expand All @@ -101,6 +108,28 @@ std.assertEqual(std.parseYaml("1e-"), "1e-") &&
std.assertEqual(std.parseYaml("-1e-"), "-1e-") &&
std.assertEqual(std.parseYaml("+1e-"), "+1e-") &&

std.assertEqual(std.parseYaml("0o"), "0o") &&
std.assertEqual(std.parseYaml("+0o"), "+0o") &&
std.assertEqual(std.parseYaml("-0o"), "-0o") &&

std.assertEqual(std.parseYaml("0o8"), "0o8") &&
std.assertEqual(std.parseYaml("+0o8"), "+0o8") &&
std.assertEqual(std.parseYaml("-0o8"), "-0o8") &&

std.assertEqual(std.parseYaml("+0o1"), "+0o1") &&
std.assertEqual(std.parseYaml("-0o1"), "-0o1") &&

std.assertEqual(std.parseYaml("0x"), "0x") &&
std.assertEqual(std.parseYaml("+0x"), "+0x") &&
std.assertEqual(std.parseYaml("-0x"), "-0x") &&

std.assertEqual(std.parseYaml("0xG"), "0xG") &&
std.assertEqual(std.parseYaml("+0xG"), "+0xG") &&
std.assertEqual(std.parseYaml("-0xG"), "-0xG") &&

std.assertEqual(std.parseYaml("+0x1"), "+0x1") &&
std.assertEqual(std.parseYaml("-0x1"), "-0x1") &&

std.assertEqual(std.parseYaml('"string"'), "string") &&
std.assertEqual(std.parseYaml("'string'"), "string") &&
std.assertEqual(std.parseJson('"\\uABCD"'), "\uABCD") &&
Expand Down

0 comments on commit 11ac2f5

Please sign in to comment.