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 + + + + + + + +
versionchanges
nextSwitched 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 ```