diff --git a/compiler/src/typed/oprint.re b/compiler/src/typed/oprint.re
index 48827f147..8c2140de7 100644
--- a/compiler/src/typed/oprint.re
+++ b/compiler/src/typed/oprint.re
@@ -660,8 +660,8 @@ and print_out_sig_item = ppf =>
// | Orec_first => "type"
// | Orec_next => "and"
// };
- let kwd =
- switch (td.otype_type) {
+ let rec resolve_kwd = out_type => {
+ switch (out_type) {
| Otyp_record(_) => "record"
| Otyp_sum(_) => "enum"
| Otyp_variant(_, _, _, _) =>
@@ -673,8 +673,7 @@ and print_out_sig_item = ppf =>
| Otyp_abstract
| Otyp_tuple(_)
| Otyp_constr(_, _) => "type"
- | Otyp_manifest(_, _) =>
- failwith("NYI: Otyp_manifest pretty-printer")
+ | Otyp_manifest(_, original_type) => resolve_kwd(original_type)
| Otyp_object(_, _) => failwith("NYI: Otyp_object pretty-printer")
| Otyp_stuff(_) => failwith("NYI: Otyp_stuff pretty-printer")
| Otyp_var(_, _) => failwith("NYI: Otyp_var pretty-printer")
@@ -683,6 +682,10 @@ and print_out_sig_item = ppf =>
| Otyp_attribute(_, _) =>
failwith("NYI: Otyp_attribute pretty-printer")
};
+ };
+
+ let kwd = resolve_kwd(td.otype_type);
+
print_out_type_decl(kwd, ppf, td);
}
| Osig_value(vd) => {
@@ -735,17 +738,11 @@ and print_out_type_decl = (kwd, ppf, td) => {
)
};
- let print_manifest = ppf =>
- fun
- | Otyp_manifest(ty, _) => fprintf(ppf, " =@ %a", out_type^, ty)
- | _ => ();
-
- let print_name_params = ppf =>
- fprintf(ppf, "%s %t%a", kwd, type_defined, print_manifest, td.otype_type);
+ let print_name_params = ppf => fprintf(ppf, "%s %t", kwd, type_defined);
let ty =
switch (td.otype_type) {
- | Otyp_manifest(_, ty) => ty
+ | Otyp_manifest(_, original_type) => original_type
| _ => td.otype_type
};
diff --git a/compiler/test/stdlib/number.test.gr b/compiler/test/stdlib/number.test.gr
index 90098b857..79ecf756e 100644
--- a/compiler/test/stdlib/number.test.gr
+++ b/compiler/test/stdlib/number.test.gr
@@ -624,10 +624,22 @@ assert Result.isErr(Number.parseInt("zzzzz", 37))
assert Result.isErr(Number.parseInt("zzzzz", 9223372036854775807))
assert Result.isErr(Number.parseInt("10", 1.23))
assert Result.isErr(Number.parseInt("10", 2/3))
+assert match (Number.parseInt("zzzzz", 10)) {
+ Err(Number.ParseIntInvalidDigit) => true,
+ _ => false,
+}
+assert match (Number.parseInt("", 10)) {
+ Err(Number.ParseIntEmptyString) => true,
+ _ => false,
+}
+assert match (Number.parseInt("10", 2/3)) {
+ Err(Number.ParseIntInvalidRadix) => true,
+ _ => false,
+}
// Number.parse
// These tests primarily focus on rational parsing
-assert Number.parse("") == Err("Invalid input")
+assert Result.isErr(Number.parse(""))
assert Number.parse("42") == Ok(42)
assert Number.parse("123.45") == Ok(123.45)
assert Number.parse("1/1") == Ok(1)
@@ -639,9 +651,9 @@ assert Number.parse("-3/-9") == Ok(1/3)
assert Number.parse("0x3/-9") == Ok(-1/3)
assert Number.parse("3/-0x9") == Ok(-1/3)
assert Number.parse("9223372036854775808/27_670_116_110_564_327_424") == Ok(1/3)
-assert Number.parse("1/2/") == Err("Invalid digit in input")
-assert Number.parse("1/") == Err("Invalid input")
-assert Number.parse("1//") == Err("Invalid digit in input")
+assert Result.isErr(Number.parse("1/2/"))
+assert Result.isErr(Number.parse("1/"))
+assert Result.isErr(Number.parse("1//"))
// Number.sign
assert Number.sign(-10000) == -1
diff --git a/stdlib/number.gr b/stdlib/number.gr
index 516d5061f..7ffe2ffb0 100644
--- a/stdlib/number.gr
+++ b/stdlib/number.gr
@@ -49,6 +49,9 @@ from "runtime/atof/parse" include Parse as Atof
from "runtime/unsafe/tags" include Tags
from "runtime/exception" include Exception
+use Atoi.{ type ParseIntError }
+
+provide { type ParseIntError }
/**
* Pi represented as a Number value.
*
@@ -567,13 +570,14 @@ provide let isClose = (a, b, relativeTolerance=1e-9, absoluteTolerance=0.0) => {
*
* @param string: The string to parse
* @param radix: The number system base to use when parsing the input string
- * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise
+ * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(err)` containing a variant of `ParseIntError`
*
* @example Number.parseInt("1", radix=10) == Ok(1)
* @example Number.parseInt("-1", radix=10) == Ok(-1)
* @example Number.parseInt("0xf0", radix=16) == Ok(0x0f0)
*
* @since v0.4.5
+ * @history v0.6.0: Switched from a string-based error message to a structured error enum
*/
provide let parseInt = Atoi.parseInt
diff --git a/stdlib/number.md b/stdlib/number.md
index 82409b616..503889402 100644
--- a/stdlib/number.md
+++ b/stdlib/number.md
@@ -37,6 +37,47 @@ Infinity
NaN
```
+## Types
+
+Type declarations included in the Number module.
+
+### Number.**ParseIntError**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+enum ParseIntError {
+ ParseIntEmptyString,
+ ParseIntInvalidDigit,
+ ParseIntInvalidRadix,
+}
+```
+
+Represents an error that occurred trying to parse an integer.
+
+Variants:
+
+```grain
+ParseIntEmptyString
+```
+
+Represents an error caused by trying to parse an empty string.
+
+```grain
+ParseIntInvalidDigit
+```
+
+Represents an error caused by trying to parse a string with an invalid character.
+
+```grain
+ParseIntInvalidRadix
+```
+
+Represents an error caused by trying to parse with an invalid radix.
+
## Values
Functions and constants included in the Number module.
@@ -1091,13 +1132,21 @@ Number.isClose(4, 4.1, relativeTolerance=0.024) == false
### Number.**parseInt**
-
-Added in 0.4.5
-No other changes yet.
+
+Added in 0.4.5
+
+
+
version
changes
+
+
+
next
Switched from a string-based error message to a structured error enum
+
+
```grain
-parseInt : (string: String, radix: Number) => Result
+parseInt :
+ (string: String, radix: Number) => Result
```
Parses a string representation of an integer into a `Number` using the
@@ -1119,7 +1168,7 @@ Returns:
|type|description|
|----|-----------|
-|`Result`|`Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise|
+|`Result`|`Ok(value)` containing the parsed number on a successful parse or `Err(err)` containing a variant of `ParseIntError`|
Examples:
@@ -1183,7 +1232,7 @@ No other changes yet.
```grain
-parse : (input: String) => Result
+parse : (input: String) => Result
```
Parses a string representation of an integer, float, or rational into a `Number`.
@@ -1199,7 +1248,7 @@ Returns:
|type|description|
|----|-----------|
-|`Result`|`Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise|
+|`Result`|`Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise|
Examples:
diff --git a/stdlib/runtime/atoi/parse.gr b/stdlib/runtime/atoi/parse.gr
index ba189562b..fd5f05781 100644
--- a/stdlib/runtime/atoi/parse.gr
+++ b/stdlib/runtime/atoi/parse.gr
@@ -20,6 +20,26 @@ from "runtime/bigint" include Bigint as BI
from "runtime/numbers" include Numbers
use Numbers.{ reducedInteger }
+/**
+ * Represents an error that occurred trying to parse an integer.
+ *
+ * @since v0.6.0
+ */
+provide enum ParseIntError {
+ /**
+ * Represents an error caused by trying to parse an empty string.
+ */
+ ParseIntEmptyString,
+ /**
+ * Represents an error caused by trying to parse a string with an invalid character.
+ */
+ ParseIntInvalidDigit,
+ /**
+ * Represents an error caused by trying to parse with an invalid radix.
+ */
+ ParseIntInvalidRadix,
+}
+
primitive (&&) = "@and"
primitive (||) = "@or"
@@ -58,11 +78,11 @@ provide let parseInt = (string: String, radix: Number) => {
radix < WasmI32.fromGrain(2) ||
radix > WasmI32.fromGrain(36)
) {
- return Err("Radix must be an integer between 2 and 36")
+ return Err(ParseIntInvalidRadix)
}
if (WasmI32.eqz(strLen)) {
- return Err("Invalid input")
+ return Err(ParseIntEmptyString)
}
let mut char = WasmI32.load8U(offset, 0n)
@@ -123,12 +143,12 @@ provide let parseInt = (string: String, radix: Number) => {
c when c - _CHAR_A < 26n => digit = char - _CHAR_A + 10n,
c when c - _CHAR_a < 26n => digit = char - _CHAR_a + 10n,
_ => {
- return Err("Invalid digit in input")
+ return Err(ParseIntInvalidDigit)
},
}
if (digit >= WasmI32.wrapI64(radix)) {
- return Err("Invalid digit in input")
+ return Err(ParseIntInvalidDigit)
}
let digit = WasmI64.extendI32U(digit)
@@ -174,7 +194,7 @@ provide let parseInt = (string: String, radix: Number) => {
}
use WasmI64.{ (*) }
// TODO: Verify this is suitable for handling "_"
- if (WasmI32.eqz(sawDigit)) return Err("Invalid digit in input")
+ if (WasmI32.eqz(sawDigit)) return Err(ParseIntInvalidDigit)
if (WasmI32.eqz(isBigInt)) {
let value = if (negative) value else value * -1N
diff --git a/stdlib/runtime/atoi/parse.md b/stdlib/runtime/atoi/parse.md
index 6dcaaaa93..a68af2bf2 100644
--- a/stdlib/runtime/atoi/parse.md
+++ b/stdlib/runtime/atoi/parse.md
@@ -2,6 +2,47 @@
title: Parse
---
+## Types
+
+Type declarations included in the Parse module.
+
+### Parse.**ParseIntError**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+enum ParseIntError {
+ ParseIntEmptyString,
+ ParseIntInvalidDigit,
+ ParseIntInvalidRadix,
+}
+```
+
+Represents an error that occurred trying to parse an integer.
+
+Variants:
+
+```grain
+ParseIntEmptyString
+```
+
+Represents an error caused by trying to parse an empty string.
+
+```grain
+ParseIntInvalidDigit
+```
+
+Represents an error caused by trying to parse a string with an invalid character.
+
+```grain
+ParseIntInvalidRadix
+```
+
+Represents an error caused by trying to parse with an invalid radix.
+
## Values
Functions and constants included in the Parse module.
@@ -9,6 +50,6 @@ Functions and constants included in the Parse module.
### Parse.**parseInt**
```grain
-parseInt : (string: String, radix: Number) => Result
+parseInt : (string: String, radix: Number) => Result
```