Skip to content

Commit

Permalink
std::conv: use exceptionals instead of returning error code
Browse files Browse the repository at this point in the history
  • Loading branch information
mertcandav committed Mar 5, 2024
1 parent c208d92 commit f3d6dd0
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 81 deletions.
10 changes: 5 additions & 5 deletions std/conv/atob.jule
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@

// Returns the boolean value represented by the string.
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error.
pub fn conv_bool(s: str): (bool, ConvError) {
// Any other value throws exception.
pub fn conv_bool(s: str)!: bool {
match s {
| "1" | "t" | "T" | "true" | "TRUE" | "True":
ret true, ConvError.Ok
ret true
| "0" | "f" | "F" | "false" | "FALSE" | "False":
ret false, ConvError.Ok
ret false
|:
ret false, ConvError.InvalidSyntax
error(ConvError.InvalidSyntax)
}
}

Expand Down
15 changes: 9 additions & 6 deletions std/conv/atof.jule
Original file line number Diff line number Diff line change
Expand Up @@ -753,20 +753,23 @@ fn parse_float_prefix(s: str, bit_size: int): (f64, int, ConvError) {
// there are more bits in the hexadecimal representation than
// will fit in the mantissa.)
//
// The errors that returns have concrete type ConvError.
// The exceptional errors that have concrete type ConvError.
//
// If s is not syntactically well-formed, returns err = ConvError.InvalidSyntax.
// If s is not syntactically well-formed, throws exception = ConvError.InvalidSyntax.
//
// If s is syntactically well-formed but is more than 1/2 ULP
// away from the largest floating point number of the given size,
// Returns f = ±inf, err = ConvError.OutOfRange.
// Exceptional = ConvError.OutOfRange.
//
// Recognizes the string "nan", and the (possibly signed) strings "inf" and "infinity"
// as their respective special floating point values. It ignores case when matching.
pub fn parse_float(s: str, bit_size: int): (f64, ConvError) {
pub fn parse_float(s: str, bit_size: int)!: f64 {
let (f, n, err) = parse_float_prefix(s, bit_size)
if n != s.len && (err == ConvError.Ok || err != ConvError.InvalidSyntax) {
ret 0, ConvError.InvalidSyntax
error(ConvError.InvalidSyntax)
}
ret f, err
if err != ConvError.Ok {
error(err)
}
ret f
}
49 changes: 24 additions & 25 deletions std/conv/atoi.jule
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ fn lower(c: byte): byte {
ret c | ('x' - 'X')
}

// Is like parse_int but for unsigned numbers.
//
// A sign prefix is not permitted.
pub fn parse_uint(mut s: str, mut base: int, mut bit_size: int): (u64, ConvError) {
fn __parse_uint(mut s: str, mut base: int, mut bit_size: int): (u64, ConvError) {
if s == "" {
ret 0, ConvError.InvalidSyntax
}
Expand Down Expand Up @@ -107,12 +104,6 @@ pub fn parse_uint(mut s: str, mut base: int, mut bit_size: int): (u64, ConvError
| 64:
max_val = u64.MAX
}
/*
let mut prec = 1
if bit_size == 64 {
prec = 2
}
let max_val = u64(1)<<uint(bit_size) - prec*/

let mut n: u64 = 0
for _, c in s {
Expand Down Expand Up @@ -149,6 +140,17 @@ pub fn parse_uint(mut s: str, mut base: int, mut bit_size: int): (u64, ConvError
ret n, ConvError.Ok
}

// Is like parse_int but for unsigned numbers.
//
// A sign prefix is not permitted.
pub fn parse_uint(mut s: str, mut base: int, mut bit_size: int)!: u64 {
let (un, err) = __parse_uint(s, base, bit_size)
if err != ConvError.Ok {
error(err)
}
ret un
}

// Interprets a string s in the given base (0, 2 to 36) and
// bit size (0 to 64) and returns the corresponding value i.
//
Expand All @@ -161,17 +163,15 @@ pub fn parse_uint(mut s: str, mut base: int, mut bit_size: int): (u64, ConvError
// The bit_size argument specifies the integer type
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
// correspond to int, i8, i16, i32, and i64.
// If bit_size is below 0 or above 64, an error is returned.
// If bit_size is below 0 or above 64, throws exception.
//
// The errors that parse_int returns have concrete type ConvError.
// If s is empty or contains invalid digits, err = ConvError.InvalidSyntax
// and the returned value is 0; if the value corresponding to s cannot be
// represented by a signed integer of the given size, err = ConvError.OutOfRange
// and the returned value is the maximum magnitude integer of the
// appropriate bit_size and sign.
pub fn parse_int(mut s: str, base: int, mut bit_size: int): (i: i64, err: ConvError) {
// The exception errors that parse_int throws have concrete type ConvError.
// If s is empty or contains invalid digits, exception = ConvError.InvalidSyntax;
// if the value corresponding to s cannot be represented by a signed integer of
// the given size, exception = ConvError.OutOfRange.
pub fn parse_int(mut s: str, base: int, mut bit_size: int)!: i64 {
if s == "" {
ret 0, ConvError.InvalidSyntax
error(ConvError.InvalidSyntax)
}

// Pick off leading sign.
Expand All @@ -184,10 +184,9 @@ pub fn parse_int(mut s: str, base: int, mut bit_size: int): (i: i64, err: ConvEr
}

// Convert unsigned and check range.
let mut un: u64 = 0
un, err = parse_uint(s, base, bit_size)
let (mut un, err) = __parse_uint(s, base, bit_size)
if err != ConvError.Ok && err != ConvError.OutOfRange {
ret 0, err
error(err)
}

if bit_size == 0 {
Expand All @@ -196,14 +195,14 @@ pub fn parse_int(mut s: str, base: int, mut bit_size: int): (i: i64, err: ConvEr

let cutoff = u64(1 << uint(bit_size-1))
if !neg && un >= cutoff {
ret i64(cutoff - 1), ConvError.OutOfRange
error(ConvError.OutOfRange)
}
if neg && un > cutoff {
ret -i64(cutoff), ConvError.OutOfRange
error(ConvError.OutOfRange)
}
let mut n = i64(un)
if neg {
n = -n
}
ret n, ConvError.Ok
ret n
}
2 changes: 1 addition & 1 deletion std/conv/error.jule
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// Error codes of conv package.
pub enum ConvError {
Ok, // No problem.
Ok, // No problem. Defined to using internally, any exceptional is not be this code.
OutOfRange, // Indicates that a value is out of range for the target type.
InvalidSyntax, // Indicates that a value does not have the right syntax for the target type.
InvalidBase, // Indicates that a base is invalid.
Expand Down
2 changes: 1 addition & 1 deletion std/conv/itoa.jule
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ fn fmt_bits(mut dst: []byte, mut u: u64, base: int, neg: bool, append_: bool): (
// the constant 7 we tell the compiler that the shift count is always
// less than 8 which is smaller than any register width. This allows
// the compiler to generate better code for the shift operation.
let shift = uint(bits::trailing_zeros(uint(base))) & 7
let shift = uint(bits::trailing_zeros(uint(base))) & 0b111
let b = u64(base)
let m = uint(base) - 1 // == 1<<shift - 1
for u >= b {
Expand Down
17 changes: 3 additions & 14 deletions std/flag/flag.jule
Original file line number Diff line number Diff line change
Expand Up @@ -187,19 +187,14 @@ impl ArgParser {
match {
| s == "":
ret 0, false

| strings::has_prefix(s, "0x"): // Hexadecimal
x, ok = parser(s[2:], 0b00010000)

| strings::has_prefix(s, "0b"): // Binary
x, ok = parser(s[2:], 0b10)

| strings::has_prefix(s, "0o"): // Octal
x, ok = parser(s[2:], 0b1000)

| s[0] == '0': // Octal
x, ok = parser(s[1:], 0b1000)

|: // Decimal
x, ok = parser(s, 0b1010)
}
Expand Down Expand Up @@ -231,30 +226,24 @@ impl ArgParser {
| IntFlag:
let mut intf = IntFlag(flag)
let ((*intf._data), ok) = self.parse_integer[i64](data, fn(s: str, base: int): (i64, bool) {
let (x, err) = conv::parse_int(s, base, BIT64)
ret x, err == conv::ConvError.Ok
ret conv::parse_int(s, base, BIT64) else { ret 0, false }, true
})
if !ok {
error("--" + flag.name() + ": expression must be valid signed integer")
}

| UintFlag:
let mut uintf = UintFlag(flag)
let ((*uintf._data), ok) = self.parse_integer[u64](data, fn(s: str, base: int): (u64, bool) {
let (x, err) = conv::parse_uint(s, base, BIT64)
ret x, err == conv::ConvError.Ok
ret conv::parse_uint(s, base, BIT64) else { ret 0, false }, true
})
if !ok {
error("--" + flag.name() + ": expression must be valid unsigned integer")
}

| FloatFlag:
let mut ff = FloatFlag(flag)
let ((*ff._data), err) = conv::parse_float(data, BIT64)
if err != conv::ConvError.Ok {
*ff._data = conv::parse_float(data, BIT64) else {
error("--" + flag.name() + ": expression must be valid floating-point")
}

| StrFlag:
*StrFlag(flag)._data = data
}
Expand Down
15 changes: 4 additions & 11 deletions std/jule/constant/lit/bytes.jule
Original file line number Diff line number Diff line change
Expand Up @@ -92,31 +92,24 @@ fn rune_from_esq_seq(bytes: []byte, mut &i: int): rune {
match bytes[i] {
| 'u':
const SEQ_LEN = 5
let (rc, _) = conv::parse_uint(str(bytes[i+1:i+SEQ_LEN]), 16, 64)
let r = rune(conv::parse_uint(str(bytes[i+1:i+SEQ_LEN]), 16, 64)!)
i += SEQ_LEN
let r = rune(rc)
ret r

| 'U':
const SEQ_LEN = 9
let (rc, _) = conv::parse_uint(str(bytes[i+1:i+SEQ_LEN]), 16, 64)
let r = rune(conv::parse_uint(str(bytes[i+1:i+SEQ_LEN]), 16, 64)!)
i += SEQ_LEN
let r = rune(rc)
ret r

| 'x':
const SEQ_LEN = 3
let seq = bytes[i+1:i+SEQ_LEN]
i += SEQ_LEN
let (b, _) = conv::parse_uint(str(seq), 16, 64)
ret rune(b)

ret rune(conv::parse_uint(str(seq), 16, 64)!)
|:
const SEQ_LEN = 3
let seq = bytes[i : i+SEQ_LEN]
i += SEQ_LEN
let (b, _) = conv::parse_uint(str(seq[1:]), 8, 64)
ret rune(b)
ret rune(conv::parse_uint(str(seq[1:]), 8, 64)!)
}
}

Expand Down
18 changes: 9 additions & 9 deletions std/jule/sema/eval.jule
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ impl Eval {
fn lit_float(self, &l: &LitExpr): &Data {
const FLOAT_KIND: str = PrimKind.F64

let (f, _) = parse_float(l.value, 64)
let f = parse_float(l.value, 64) else { use f64.MAX }
let mut constant = Const.new_f64(f)

ret &Data{
Expand All @@ -489,19 +489,15 @@ impl Eval {
| strings::has_prefix(lit, "0x"): // Hexadecimal
lit = lit[2:]
base = 0b00010000

| strings::has_prefix(lit, "0b"): // Binary
lit = lit[2:]
base = 0b10

| strings::has_prefix(lit, "0o"): // Ocatal
lit = lit[2:]
base = 0b1000

| lit[0] == '0' && lit.len > 1: // Octal
lit = lit[1:]
base = 0b1000

|: // Decimal
base = 0b1010
}
Expand All @@ -512,17 +508,21 @@ impl Eval {
decl: false,
}

let (sig, err) = parse_int(lit, base, BIT_SIZE)
if err == ConvError.Ok {
let mut ok = true
let sig = parse_int(lit, base, BIT_SIZE) else {
ok = false
use 0
}
if ok {
d.constant = Const.new_i64(sig)
d.kind = &TypeKind{
kind: build_prim_type(PrimKind.Int),
}
} else {
let (unsig, err) = parse_uint(lit, base, BIT_SIZE)
if err != ConvError.Ok {
let unsig = parse_uint(lit, base, BIT_SIZE) else {
self.push_err(l.token, LogMsg.InvalidNumericRange)
self.push_suggestion(LogMsg.TryFloatingPoint)
use u64.MAX
}
d.constant = Const.new_u64(unsig)
d.kind = &TypeKind{
Expand Down
12 changes: 6 additions & 6 deletions std/jule/types/bits.jule
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,23 @@ pub fn float_from_bits(bits: u64): str {
// Reports whether signed integer literal is compatible given bit-size.
pub fn check_bit_int(v: str, bit: int): bool {
ret check_bit(v, bit, fn(v: str, base: int, bit: int): bool {
let (_, err) = conv::parse_int(v, base, bit)
ret err == conv::ConvError.Ok
_ = conv::parse_int(v, base, bit) else { ret false }
ret true
})
}

// Reports whether unsigned integer literal is compatible given bit-size.
pub fn check_bit_uint(v: str, bit: int): bool {
ret check_bit(v, bit, fn(v: str, base: int, bit: int): bool {
let (_, err) = conv::parse_uint(v, base, bit)
ret err == conv::ConvError.Ok
_ = conv::parse_uint(v, base, bit) else { ret false }
ret true
})
}

// Reports whether float literal is compatible given bit-size.
pub fn check_bit_float(val: str, bit: int): bool {
let (_, err) = conv::parse_float(val, bit)
ret err == conv::ConvError.Ok
_ = conv::parse_float(val, bit) else { ret false }
ret true
}

// Reports minimum bit-size of given floating-point.
Expand Down
12 changes: 9 additions & 3 deletions tests/basic_calculator/main.jule
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ use io for std::io

fn readln(): str {
let scanner = io::Scanner.newf(io::stdin())
ret if (scanner.scan() else { use false }) { scanner.text() } else { "" }
if (scanner.scan() else { use false }) {
ret scanner.text()
}
ret ""
}

fn numeric_input(msg: str): (f64, ok: bool) {
fmt::print(msg)
ok = true
let input = readln()
let (flt, err) = conv::parse_float(input, 64)
ok = err == conv::ConvError.Ok
let flt = conv::parse_float(input, 64) else {
ok = false
use 0
}
ret flt, ok
}

Expand Down

0 comments on commit f3d6dd0

Please sign in to comment.