9
"github.com/pelletier/go-toml/v2/unstable"
12
func parseInteger(b []byte) (int64, error) {
13
if len(b) > 2 && b[0] == '0' {
22
panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1]))
29
func parseLocalDate(b []byte) (LocalDate, error) {
36
if len(b) != 10 || b[4] != '-' || b[7] != '-' {
37
return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD")
42
date.Year, err = parseDecimalDigits(b[0:4])
44
return LocalDate{}, err
47
date.Month, err = parseDecimalDigits(b[5:7])
49
return LocalDate{}, err
52
date.Day, err = parseDecimalDigits(b[8:10])
54
return LocalDate{}, err
57
if !isValidDate(date.Year, date.Month, date.Day) {
58
return LocalDate{}, unstable.NewParserError(b, "impossible date")
64
func parseDecimalDigits(b []byte) (int, error) {
68
if c < '0' || c > '9' {
69
return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)")
78
func parseDateTime(b []byte) (time.Time, error) {
84
dt, b, err := parseLocalDateTime(b)
86
return time.Time{}, err
89
var zone *time.Location
93
panic("date time should have a timezone")
96
if b[0] == 'Z' || b[0] == 'z' {
100
const dateTimeByteLen = 6
101
if len(b) != dateTimeByteLen {
102
return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone")
111
return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character")
115
return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator")
118
hours, err := parseDecimalDigits(b[1:3])
120
return time.Time{}, err
123
return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours")
126
minutes, err := parseDecimalDigits(b[4:6])
128
return time.Time{}, err
131
return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes")
134
seconds := direction * (hours*3600 + minutes*60)
138
zone = time.FixedZone("", seconds)
140
b = b[dateTimeByteLen:]
144
return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone")
149
time.Month(dt.Month),
160
func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) {
163
const localDateTimeByteMinLen = 11
164
if len(b) < localDateTimeByteMinLen {
165
return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]")
168
date, err := parseLocalDate(b[:10])
175
if sep != 'T' && sep != ' ' && sep != 't' {
176
return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space")
179
t, rest, err := parseLocalTime(b[11:])
191
func parseLocalTime(b []byte) (LocalTime, []byte, error) {
193
nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0}
198
const localTimeByteLen = 8
199
if len(b) < localTimeByteLen {
200
return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]")
205
t.Hour, err = parseDecimalDigits(b[0:2])
211
return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23")
214
return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes")
217
t.Minute, err = parseDecimalDigits(b[3:5])
222
return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59")
225
return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds")
228
t.Second, err = parseDecimalDigits(b[6:8])
234
return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60")
239
if len(b) >= 1 && b[0] == '.' {
244
for i, c := range b[1:] {
247
return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point")
253
const maxFracPrecision = 9
254
if i >= maxFracPrecision {
271
return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit")
274
t.Nanosecond = frac * nspow[precision]
275
t.Precision = precision
277
return t, b[1+digits:], nil
283
func parseFloat(b []byte) (float64, error) {
284
if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' {
285
return math.NaN(), nil
288
cleaned, err := checkAndRemoveUnderscoresFloats(b)
293
if cleaned[0] == '.' {
294
return 0, unstable.NewParserError(b, "float cannot start with a dot")
297
if cleaned[len(cleaned)-1] == '.' {
298
return 0, unstable.NewParserError(b, "float cannot end with a dot")
301
dotAlreadySeen := false
302
for i, c := range cleaned {
305
return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point")
307
if !isDigit(cleaned[i-1]) {
308
return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit")
310
if !isDigit(cleaned[i+1]) {
311
return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit")
313
dotAlreadySeen = true
318
if cleaned[0] == '+' || cleaned[0] == '-' {
321
if cleaned[start] == '0' && isDigit(cleaned[start+1]) {
322
return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes")
325
f, err := strconv.ParseFloat(string(cleaned), 64)
327
return 0, unstable.NewParserError(b, "unable to parse float: %w", err)
333
func parseIntHex(b []byte) (int64, error) {
334
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
339
i, err := strconv.ParseInt(string(cleaned), 16, 64)
341
return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err)
347
func parseIntOct(b []byte) (int64, error) {
348
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
353
i, err := strconv.ParseInt(string(cleaned), 8, 64)
355
return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err)
361
func parseIntBin(b []byte) (int64, error) {
362
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
367
i, err := strconv.ParseInt(string(cleaned), 2, 64)
369
return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err)
375
func isSign(b byte) bool {
376
return b == '+' || b == '-'
379
func parseIntDec(b []byte) (int64, error) {
380
cleaned, err := checkAndRemoveUnderscoresIntegers(b)
387
if isSign(cleaned[0]) {
391
if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' {
392
return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number")
395
i, err := strconv.ParseInt(string(cleaned), 10, 64)
397
return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err)
403
func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) {
405
if b[start] == '+' || b[start] == '-' {
414
return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore")
417
if b[len(b)-1] == '_' {
418
return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
423
for ; i < len(b); i++ {
433
cleaned := make([]byte, i, len(b))
436
for i++; i < len(b); i++ {
440
return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
445
cleaned = append(cleaned, c)
452
func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) {
454
return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore")
457
if b[len(b)-1] == '_' {
458
return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
463
for ; i < len(b); i++ {
473
cleaned := make([]byte, 0, len(b))
475
for i := 0; i < len(b); i++ {
481
return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
483
if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') {
484
return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent")
489
cleaned = append(cleaned, c)
492
if i < len(b)-1 && b[i+1] == '_' {
493
return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent")
495
cleaned = append(cleaned, c)
497
if i < len(b)-1 && b[i+1] == '_' {
498
return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point")
500
if i > 0 && b[i-1] == '_' {
501
return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point")
503
cleaned = append(cleaned, c)
506
cleaned = append(cleaned, c)
514
func isValidDate(year int, month int, day int) bool {
515
return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year)
521
var daysBefore = [...]int32{
527
31 + 28 + 31 + 30 + 31,
528
31 + 28 + 31 + 30 + 31 + 30,
529
31 + 28 + 31 + 30 + 31 + 30 + 31,
530
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
531
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
532
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
533
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
534
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
537
func daysIn(m int, year int) int {
538
if m == 2 && isLeap(year) {
541
return int(daysBefore[m] - daysBefore[m-1])
544
func isLeap(year int) bool {
545
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
548
func isDigit(r byte) bool {
549
return r >= '0' && r <= '9'