x nat // expected nat
b int // expected base
count int // expected digit count
- ok bool // expected success
+ err error // expected error
next rune // next character (or 0, if at EOF)
}{
- // invalid: no mantissa
- {},
- {s: "?"},
- {base: 10},
- {base: 36},
- {base: 62},
- {s: "?", base: 10},
- {s: "0b"},
- {s: "0o"},
- {s: "0x"},
- {s: "0b2"},
- {s: "0B2"},
- {s: "0o8"},
- {s: "0O8"},
- {s: "0xg"},
- {s: "0Xg"},
- {s: "345", base: 2},
+ // invalid: no digits
+ {"", 0, false, nil, 10, 0, errNoDigits, 0},
+ {"_", 0, false, nil, 10, 0, errNoDigits, 0},
+ {"?", 0, false, nil, 10, 0, errNoDigits, '?'},
+ {"?", 10, false, nil, 10, 0, errNoDigits, '?'},
+ {"", 10, false, nil, 10, 0, errNoDigits, 0},
+ {"", 36, false, nil, 36, 0, errNoDigits, 0},
+ {"", 62, false, nil, 62, 0, errNoDigits, 0},
+ {"0b", 0, false, nil, 2, 0, errNoDigits, 0},
+ {"0o", 0, false, nil, 8, 0, errNoDigits, 0},
+ {"0x", 0, false, nil, 16, 0, errNoDigits, 0},
+ {"0x_", 0, false, nil, 16, 0, errNoDigits, 0},
+ {"0b2", 0, false, nil, 2, 0, errNoDigits, '2'},
+ {"0B2", 0, false, nil, 2, 0, errNoDigits, '2'},
+ {"0o8", 0, false, nil, 8, 0, errNoDigits, '8'},
+ {"0O8", 0, false, nil, 8, 0, errNoDigits, '8'},
+ {"0xg", 0, false, nil, 16, 0, errNoDigits, 'g'},
+ {"0Xg", 0, false, nil, 16, 0, errNoDigits, 'g'},
+ {"345", 2, false, nil, 2, 0, errNoDigits, '3'},
// invalid: incorrect use of decimal point
- {s: ".0"},
- {s: ".0", base: 10},
- {s: ".", base: 0},
- {s: "0x.0"},
+ {"._", 0, true, nil, 10, 0, errNoDigits, 0},
+ {".0", 0, false, nil, 10, 0, errNoDigits, '.'},
+ {".0", 10, false, nil, 10, 0, errNoDigits, '.'},
+ {".", 0, true, nil, 10, 0, errNoDigits, 0},
+ {"0x.", 0, true, nil, 16, 0, errNoDigits, 0},
+ {"0x.g", 0, true, nil, 16, 0, errNoDigits, 'g'},
+ {"0x.0", 0, false, nil, 16, 0, errNoDigits, '.'},
+
+ // invalid: incorrect use of separators
+ {"_0", 0, false, nil, 10, 1, errInvalSep, 0},
+ {"0_", 0, false, nil, 10, 1, errInvalSep, 0},
+ {"0__0", 0, false, nil, 8, 1, errInvalSep, 0},
+ {"0x___0", 0, false, nil, 16, 1, errInvalSep, 0},
+ {"0_x", 0, false, nil, 10, 1, errInvalSep, 'x'},
+ {"0_8", 0, false, nil, 10, 1, errInvalSep, '8'},
+ {"123_.", 0, true, nat{123}, 10, 0, errInvalSep, 0},
+ {"._123", 0, true, nat{123}, 10, -3, errInvalSep, 0},
+ {"0b__1000", 0, false, nat{0x8}, 2, 4, errInvalSep, 0},
+ {"0o60___0", 0, false, nat{0600}, 8, 3, errInvalSep, 0},
+ {"0466_", 0, false, nat{0466}, 8, 3, errInvalSep, 0},
+ {"01234567_8", 0, false, nat{01234567}, 8, 7, errInvalSep, '8'},
+ {"1_.", 0, true, nat{1}, 10, 0, errInvalSep, 0},
+ {"0._1", 0, true, nat{1}, 10, -1, errInvalSep, 0},
+ {"2.7_", 0, true, nat{27}, 10, -1, errInvalSep, 0},
+ {"0x1.0_", 0, true, nat{0x10}, 16, -1, errInvalSep, 0},
+
+ // valid: separators are not accepted for base != 0
+ {"0_", 10, false, nil, 10, 1, nil, '_'},
+ {"1__0", 10, false, nat{1}, 10, 1, nil, '_'},
+ {"0__8", 10, false, nil, 10, 1, nil, '_'},
+ {"xy_z_", 36, false, nat{33*36 + 34}, 36, 2, nil, '_'},
// valid, no decimal point
- {"0", 0, false, nil, 10, 1, true, 0},
- {"0", 10, false, nil, 10, 1, true, 0},
- {"0", 36, false, nil, 36, 1, true, 0},
- {"0", 62, false, nil, 62, 1, true, 0},
- {"1", 0, false, nat{1}, 10, 1, true, 0},
- {"1", 10, false, nat{1}, 10, 1, true, 0},
- {"0 ", 0, false, nil, 10, 1, true, ' '},
- {"00 ", 0, false, nil, 8, 1, true, ' '}, // octal 0
- {"0b1", 0, false, nat{1}, 2, 1, true, 0},
- {"0B11000101", 0, false, nat{0xc5}, 2, 8, true, 0},
- {"0B110001012", 0, false, nat{0xc5}, 2, 8, true, '2'},
- {"07", 0, false, nat{7}, 8, 1, true, 0},
- {"08", 0, false, nil, 10, 1, true, '8'},
- {"08", 10, false, nat{8}, 10, 2, true, 0},
- {"018", 0, false, nat{1}, 8, 1, true, '8'},
- {"0o7", 0, false, nat{7}, 8, 1, true, 0},
- {"0o18", 0, false, nat{1}, 8, 1, true, '8'},
- {"0O17", 0, false, nat{017}, 8, 2, true, 0},
- {"03271", 0, false, nat{03271}, 8, 4, true, 0},
- {"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
- {"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
- {"A", 36, false, nat{10}, 36, 1, true, 0},
- {"A", 37, false, nat{36}, 37, 1, true, 0},
- {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
- {"XYZ?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
- {"XYZ?", 62, false, nat{(59*62+60)*62 + 61}, 62, 3, true, '?'},
- {"0x", 16, false, nil, 16, 1, true, 'x'},
- {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
- {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
+ {"0", 0, false, nil, 10, 1, nil, 0},
+ {"0", 36, false, nil, 36, 1, nil, 0},
+ {"0", 62, false, nil, 62, 1, nil, 0},
+ {"1", 0, false, nat{1}, 10, 1, nil, 0},
+ {"1", 10, false, nat{1}, 10, 1, nil, 0},
+ {"0 ", 0, false, nil, 10, 1, nil, ' '},
+ {"00 ", 0, false, nil, 8, 1, nil, ' '}, // octal 0
+ {"0b1", 0, false, nat{1}, 2, 1, nil, 0},
+ {"0B11000101", 0, false, nat{0xc5}, 2, 8, nil, 0},
+ {"0B110001012", 0, false, nat{0xc5}, 2, 8, nil, '2'},
+ {"07", 0, false, nat{7}, 8, 1, nil, 0},
+ {"08", 0, false, nil, 10, 1, nil, '8'},
+ {"08", 10, false, nat{8}, 10, 2, nil, 0},
+ {"018", 0, false, nat{1}, 8, 1, nil, '8'},
+ {"0o7", 0, false, nat{7}, 8, 1, nil, 0},
+ {"0o18", 0, false, nat{1}, 8, 1, nil, '8'},
+ {"0O17", 0, false, nat{017}, 8, 2, nil, 0},
+ {"03271", 0, false, nat{03271}, 8, 4, nil, 0},
+ {"10ab", 0, false, nat{10}, 10, 2, nil, 'a'},
+ {"1234567890", 0, false, nat{1234567890}, 10, 10, nil, 0},
+ {"A", 36, false, nat{10}, 36, 1, nil, 0},
+ {"A", 37, false, nat{36}, 37, 1, nil, 0},
+ {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, nil, 0},
+ {"XYZ?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, nil, '?'},
+ {"XYZ?", 62, false, nat{(59*62+60)*62 + 61}, 62, 3, nil, '?'},
+ {"0x", 16, false, nil, 16, 1, nil, 'x'},
+ {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, nil, 0},
+ {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, nil, 0},
// valid, with decimal point
- {"0.", 0, false, nil, 10, 1, true, '.'},
- {"0.", 10, true, nil, 10, 0, true, 0},
- {"0.1.2", 10, true, nat{1}, 10, -1, true, '.'},
- {".000", 10, true, nil, 10, -3, true, 0},
- {"12.3", 10, true, nat{123}, 10, -1, true, 0},
- {"012.345", 10, true, nat{12345}, 10, -3, true, 0},
- {"0.1", 0, true, nat{1}, 10, -1, true, 0},
- {"0.1", 2, true, nat{1}, 2, -1, true, 0},
- {"0.12", 2, true, nat{1}, 2, -1, true, '2'},
- {"0b0.1", 0, true, nat{1}, 2, -1, true, 0},
- {"0B0.12", 0, true, nat{1}, 2, -1, true, '2'},
- {"0o0.7", 0, true, nat{7}, 8, -1, true, 0},
- {"0O0.78", 0, true, nat{7}, 8, -1, true, '8'},
+ {"0.", 0, false, nil, 10, 1, nil, '.'},
+ {"0.", 10, true, nil, 10, 0, nil, 0},
+ {"0.1.2", 10, true, nat{1}, 10, -1, nil, '.'},
+ {".000", 10, true, nil, 10, -3, nil, 0},
+ {"12.3", 10, true, nat{123}, 10, -1, nil, 0},
+ {"012.345", 10, true, nat{12345}, 10, -3, nil, 0},
+ {"0.1", 0, true, nat{1}, 10, -1, nil, 0},
+ {"0.1", 2, true, nat{1}, 2, -1, nil, 0},
+ {"0.12", 2, true, nat{1}, 2, -1, nil, '2'},
+ {"0b0.1", 0, true, nat{1}, 2, -1, nil, 0},
+ {"0B0.12", 0, true, nat{1}, 2, -1, nil, '2'},
+ {"0o0.7", 0, true, nat{7}, 8, -1, nil, 0},
+ {"0O0.78", 0, true, nat{7}, 8, -1, nil, '8'},
+ {"0xdead.beef", 0, true, nat{0xdeadbeef}, 16, -4, nil, 0},
+
+ // valid, with separators
+ {"1_000", 0, false, nat{1000}, 10, 4, nil, 0},
+ {"0_466", 0, false, nat{0466}, 8, 3, nil, 0},
+ {"0o_600", 0, false, nat{0600}, 8, 3, nil, 0},
+ {"0x_f0_0d", 0, false, nat{0xf00d}, 16, 4, nil, 0},
+ {"0b1000_0001", 0, false, nat{0x81}, 2, 8, nil, 0},
+ {"1_000.000_1", 0, true, nat{10000001}, 10, -4, nil, 0},
+ {"0x_f00d.1e", 0, true, nat{0xf00d1e}, 16, -2, nil, 0},
+ {"0x_f00d.1E2", 0, true, nat{0xf00d1e2}, 16, -3, nil, 0},
+ {"0x_f00d.1eg", 0, true, nat{0xf00d1e}, 16, -2, nil, 'g'},
}
func TestScanBase(t *testing.T) {
for _, a := range natScanTests {
r := strings.NewReader(a.s)
x, b, count, err := nat(nil).scan(r, a.base, a.frac)
- if err == nil && !a.ok {
- t.Errorf("scan%+v\n\texpected error", a)
- }
- if err != nil {
- if a.ok {
- t.Errorf("scan%+v\n\tgot error = %s", a, err)
- }
- continue
+ if err != a.err {
+ t.Errorf("scan%+v\n\tgot error = %v; want %v", a, err, a.err)
}
if x.cmp(a.x) != 0 {
t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
import (
"bytes"
"fmt"
+ "io"
"math"
"strconv"
"strings"
"testing"
)
+var exponentTests = []struct {
+ s string // string to be scanned
+ base2ok bool // true if 'p'/'P' exponents are accepted
+ sepOk bool // true if '_' separators are accepted
+ x int64 // expected exponent
+ b int // expected exponent base
+ err error // expected error
+ next rune // next character (or 0, if at EOF)
+}{
+ // valid, without separators
+ {"", false, false, 0, 10, nil, 0},
+ {"1", false, false, 0, 10, nil, '1'},
+ {"e0", false, false, 0, 10, nil, 0},
+ {"E1", false, false, 1, 10, nil, 0},
+ {"e+10", false, false, 10, 10, nil, 0},
+ {"e-10", false, false, -10, 10, nil, 0},
+ {"e123456789a", false, false, 123456789, 10, nil, 'a'},
+ {"p", false, false, 0, 10, nil, 'p'},
+ {"P+100", false, false, 0, 10, nil, 'P'},
+ {"p0", true, false, 0, 2, nil, 0},
+ {"P-123", true, false, -123, 2, nil, 0},
+ {"p+0a", true, false, 0, 2, nil, 'a'},
+ {"p+123__", true, false, 123, 2, nil, '_'}, // '_' is not part of the number anymore
+
+ // valid, with separators
+ {"e+1_0", false, true, 10, 10, nil, 0},
+ {"e-1_0", false, true, -10, 10, nil, 0},
+ {"e123_456_789a", false, true, 123456789, 10, nil, 'a'},
+ {"P+1_00", false, true, 0, 10, nil, 'P'},
+ {"p-1_2_3", true, true, -123, 2, nil, 0},
+
+ // invalid: no digits
+ {"e", false, false, 0, 10, errNoDigits, 0},
+ {"ef", false, false, 0, 10, errNoDigits, 'f'},
+ {"e+", false, false, 0, 10, errNoDigits, 0},
+ {"E-x", false, false, 0, 10, errNoDigits, 'x'},
+ {"p", true, false, 0, 2, errNoDigits, 0},
+ {"P-", true, false, 0, 2, errNoDigits, 0},
+ {"p+e", true, false, 0, 2, errNoDigits, 'e'},
+ {"e+_x", false, true, 0, 10, errNoDigits, 'x'},
+
+ // invalid: incorrect use of separator
+ {"e0_", false, true, 0, 10, errInvalSep, 0},
+ {"e_0", false, true, 0, 10, errInvalSep, 0},
+ {"e-1_2__3", false, true, -123, 10, errInvalSep, 0},
+}
+
+func TestScanExponent(t *testing.T) {
+ for _, a := range exponentTests {
+ r := strings.NewReader(a.s)
+ x, b, err := scanExponent(r, a.base2ok, a.sepOk)
+ if err != a.err {
+ t.Errorf("scanExponent%+v\n\tgot error = %v; want %v", a, err, a.err)
+ }
+ if x != a.x {
+ t.Errorf("scanExponent%+v\n\tgot z = %v; want %v", a, x, a.x)
+ }
+ if b != a.b {
+ t.Errorf("scanExponent%+v\n\tgot b = %d; want %d", a, b, a.b)
+ }
+ next, _, err := r.ReadRune()
+ if err == io.EOF {
+ next = 0
+ err = nil
+ }
+ if err == nil && next != a.next {
+ t.Errorf("scanExponent%+v\n\tgot next = %q; want %q", a, next, a.next)
+ }
+ }
+}
+
type StringTest struct {
in, out string
ok bool
}
var setStringTests = []StringTest{
- {"0", "0", true},
- {"-0", "0", true},
- {"1", "1", true},
- {"-1", "-1", true},
- {"1.", "1", true},
- {"1e0", "1", true},
- {"1.e1", "10", true},
+ // invalid
{in: "1e"},
{in: "1.e"},
{in: "1e+14e-5"},
{in: "r"},
{in: "a/b"},
{in: "a.b"},
+ {in: "1/0"},
+ {in: "4/3/2"}, // issue 17001
+ {in: "4/3/"},
+ {in: "4/3."},
+ {in: "4/"},
+
+ // valid
+ {"0", "0", true},
+ {"-0", "0", true},
+ {"1", "1", true},
+ {"-1", "-1", true},
+ {"1.", "1", true},
+ {"1e0", "1", true},
+ {"1.e1", "10", true},
{"-0.1", "-1/10", true},
{"-.1", "-1/10", true},
{"2/4", "1/2", true},
{"106/141787961317645621392", "53/70893980658822810696", true},
{"204211327800791583.81095", "4084226556015831676219/20000", true},
{"0e9999999999", "0", true}, // issue #16176
- {in: "1/0"},
- {in: "4/3/2"}, // issue 17001
- {in: "4/3/"},
- {in: "4/3."},
- {in: "4/"},
}
// These are not supported by fmt.Fscanf.
var setStringTests2 = []StringTest{
+ // invalid
+ {in: "4/3x"},
+
+ // invalid with separators
+ // (smoke tests only - a comprehensive set of tests is in natconv_test.go)
+ {in: "10_/1"},
+ {in: "_10/1"},
+ {in: "1/1__0"},
+ {in: "1_000.0"}, // floats are base 10 which doesn't permit separators; see also issue #29799
+
+ // valid
{"0b1000/3", "8/3", true},
{"0B1000/0x8", "1", true},
- {"-010/1", "-8", true}, // TODO(gri) should we even permit octal here?
+ {"-010/1", "-8", true},
{"-010.", "-10", true},
{"-0o10/1", "-8", true},
{"0x10/1", "16", true},
{"0x10/0x20", "1/2", true},
- {in: "4/3x"},
- // TODO(gri) add more tests
+
+ // valid with separators
+ // (smoke tests only - a comprehensive set of tests is in natconv_test.go)
+ {"0b_1000/3", "8/3", true},
+ {"0B_10_00/0x8", "1", true},
+ {"0xdead/0B1101_1110_1010_1101", "1", true},
+ {"0B1101_1110_1010_1101/0XD_E_A_D", "1", true},
}
func TestRatSetString(t *testing.T) {