114 lines
2.5 KiB
Go
114 lines
2.5 KiB
Go
package ast
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func parseFloat(b []byte) (float64, error) {
|
|
// TODO: inefficient
|
|
if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' {
|
|
return math.NaN(), nil
|
|
}
|
|
|
|
tok := string(b)
|
|
err := numberContainsInvalidUnderscore(tok)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
cleanedVal := cleanupNumberToken(tok)
|
|
return strconv.ParseFloat(cleanedVal, 64)
|
|
}
|
|
|
|
func parseIntHex(b []byte) (int64, error) {
|
|
tok := string(b)
|
|
cleanedVal := cleanupNumberToken(tok)
|
|
err := hexNumberContainsInvalidUnderscore(cleanedVal)
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
return strconv.ParseInt(cleanedVal[2:], 16, 64)
|
|
}
|
|
|
|
func parseIntOct(b []byte) (int64, error) {
|
|
tok := string(b)
|
|
cleanedVal := cleanupNumberToken(tok)
|
|
err := numberContainsInvalidUnderscore(cleanedVal)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return strconv.ParseInt(cleanedVal[2:], 8, 64)
|
|
}
|
|
|
|
func parseIntBin(b []byte) (int64, error) {
|
|
tok := string(b)
|
|
cleanedVal := cleanupNumberToken(tok)
|
|
err := numberContainsInvalidUnderscore(cleanedVal)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return strconv.ParseInt(cleanedVal[2:], 2, 64)
|
|
}
|
|
|
|
func parseIntDec(b []byte) (int64, error) {
|
|
tok := string(b)
|
|
cleanedVal := cleanupNumberToken(tok)
|
|
err := numberContainsInvalidUnderscore(cleanedVal)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return strconv.ParseInt(cleanedVal, 10, 64)
|
|
}
|
|
|
|
func numberContainsInvalidUnderscore(value string) error {
|
|
// For large numbers, you may use underscores between digits to enhance
|
|
// readability. Each underscore must be surrounded by at least one digit on
|
|
// each side.
|
|
|
|
hasBefore := false
|
|
for idx, r := range value {
|
|
if r == '_' {
|
|
if !hasBefore || idx+1 >= len(value) {
|
|
// can't end with an underscore
|
|
return errInvalidUnderscore
|
|
}
|
|
}
|
|
hasBefore = isDigitRune(r)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func hexNumberContainsInvalidUnderscore(value string) error {
|
|
hasBefore := false
|
|
for idx, r := range value {
|
|
if r == '_' {
|
|
if !hasBefore || idx+1 >= len(value) {
|
|
// can't end with an underscore
|
|
return errInvalidUnderscoreHex
|
|
}
|
|
}
|
|
hasBefore = isHexDigit(r)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cleanupNumberToken(value string) string {
|
|
cleanedVal := strings.Replace(value, "_", "", -1)
|
|
return cleanedVal
|
|
}
|
|
|
|
func isHexDigit(r rune) bool {
|
|
return isDigitRune(r) ||
|
|
(r >= 'a' && r <= 'f') ||
|
|
(r >= 'A' && r <= 'F')
|
|
}
|
|
|
|
func isDigitRune(r rune) bool {
|
|
return r >= '0' && r <= '9'
|
|
}
|
|
|
|
var errInvalidUnderscore = errors.New("invalid use of _ in number")
|
|
var errInvalidUnderscoreHex = errors.New("invalid use of _ in hex number")
|