Skip to content

Commit

Permalink
Merge pull request #256 from koto-lang/string-to-number-improvements
Browse files Browse the repository at this point in the history
Update string.to_number
  • Loading branch information
irh authored Dec 3, 2023
2 parents 628b284 + 5b2ac6b commit 0bd79a3
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 14 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ The Koto project adheres to

### Changed

#### Core Library

- `string.to_number` changes:
- `0x`, `0o`, and `0b` prefixes are understood for parsing hex, octal, or
binary numbers respectively.
- An overload has been added that accepts a number base between 2 and 36.
- If the string doesn't contain a number null is now returned instead of an
exception being thrown.

#### REPL

- The REPL `config.koto` settings have all been moved into a `repl` sub-map.
- e.g.
`export { edit_mode: 'vi' }` is now `export { repl: { edit_mode: 'vi' }}`
Expand Down
40 changes: 31 additions & 9 deletions core/runtime/src/core_lib/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,37 @@ pub fn make_module() -> KMap {
let expected_error = "a String";

match ctx.instance_and_args(is_string, expected_error)? {
(Value::Str(s), []) => match s.parse::<i64>() {
Ok(n) => Ok(n.into()),
Err(_) => match s.parse::<f64>() {
Ok(n) => Ok(n.into()),
Err(_) => {
runtime_error!("string.to_number: Failed to convert '{s}'")
}
},
},
(Value::Str(s), []) => {
let maybe_integer = if let Some(hex) = s.strip_prefix("0x") {
i64::from_str_radix(hex, 16)
} else if let Some(octal) = s.strip_prefix("0o") {
i64::from_str_radix(octal, 8)
} else if let Some(binary) = s.strip_prefix("0b") {
i64::from_str_radix(binary, 2)
} else {
s.parse::<i64>()
};

if let Ok(integer) = maybe_integer {
Ok(integer.into())
} else if let Ok(float) = s.parse::<f64>() {
Ok(float.into())
} else {
Ok(Value::Null)
}
}
(Value::Str(s), [Value::Number(n)]) => {
let base = n.into();
if !(2..=36).contains(&base) {
return runtime_error!("Number base must be within 2..=36");
}

if let Ok(result) = i64::from_str_radix(s, base) {
Ok(result.into())
} else {
Ok(Value::Null)
}
}
(_, unexpected) => type_error_with_slice(expected_error, unexpected),
}
});
Expand Down
27 changes: 26 additions & 1 deletion docs/core_lib/string.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,23 @@ check! o_o
|String| -> Number
```

Returns the string parsed as a number.
Returns the string converted into a number.
- `0x`, `0o`, and `0b` prefixes will cause the parsing to treat the input as
containing a hexadecimal, octal, or binary number respectively.
- Otherwise the number is assumed to be base 10, and the presence of a decimal
point will produce a float instead of an integer.

If a number can't be produced then `Null` is returned.

```kototype
|String, Integer| -> Integer
```

Returns the string converted into an integer given the specified base.

The base must be in the range `2..=36`, otherwise an error will be thrown.

If the string contains non-numerical digits then `Null` is returned.

### Example

Expand All @@ -375,6 +391,15 @@ check! 123
print! '-8.9'.to_number()
check! -8.9
print! '0x7f'.to_number()
check! 127
print! '0b10101'.to_number()
check! 21
print! '2N9C'.to_number(36)
check! 123456
```

## to_uppercase
Expand Down
14 changes: 10 additions & 4 deletions koto/tests/strings.koto
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,17 @@ zzz
assert_eq (string.to_lowercase "HÉLLÖ"), "héllö"

@test to_number: ||
x = string.to_number "42"
assert_eq x, 42
assert_eq type(x), "Int"
check_int = |s, n|
x = s.to_number()
assert_eq x, n
assert_eq type(x), 'Int'

x = string.to_number "-1.5"
check_int '42', 42
check_int '0xdeadbeef', 3735928559
check_int '0b101010', 42
check_int '0o173', 123

x = '-1.5'.to_number()
assert_eq x, -1.5
assert_eq type(x), "Float"

Expand Down

0 comments on commit 0bd79a3

Please sign in to comment.