in syntax/scan.go [910:1068]
func (sc *scanner) scanNumber(val *tokenValue, c rune) Token {
// https://github.com/google/starlark-go/blob/master/doc/spec.md#lexical-elements
//
// Python features not supported:
// - integer literals of >64 bits of precision
// - 123L or 123l long suffix
// - traditional octal: 0755
// https://docs.python.org/2/reference/lexical_analysis.html#integer-and-long-integer-literals
start := sc.pos
fraction, exponent := false, false
if c == '.' {
// dot or start of fraction
sc.readRune()
c = sc.peekRune()
if !isdigit(c) {
sc.endToken(val)
return DOT
}
fraction = true
} else if c == '0' {
// hex, octal, binary or float
sc.readRune()
c = sc.peekRune()
if c == '.' {
fraction = true
} else if c == 'x' || c == 'X' {
// hex
sc.readRune()
c = sc.peekRune()
if !isxdigit(c) {
sc.error(start, "invalid hex literal")
}
for isxdigit(c) {
sc.readRune()
c = sc.peekRune()
}
} else if c == 'o' || c == 'O' {
// octal
sc.readRune()
c = sc.peekRune()
if !isodigit(c) {
sc.error(sc.pos, "invalid octal literal")
}
for isodigit(c) {
sc.readRune()
c = sc.peekRune()
}
} else if c == 'b' || c == 'B' {
// binary
sc.readRune()
c = sc.peekRune()
if !isbdigit(c) {
sc.error(sc.pos, "invalid binary literal")
}
for isbdigit(c) {
sc.readRune()
c = sc.peekRune()
}
} else {
// float (or obsolete octal "0755")
allzeros, octal := true, true
for isdigit(c) {
if c != '0' {
allzeros = false
}
if c > '7' {
octal = false
}
sc.readRune()
c = sc.peekRune()
}
if c == '.' {
fraction = true
} else if c == 'e' || c == 'E' {
exponent = true
} else if octal && !allzeros {
sc.endToken(val)
sc.errorf(sc.pos, "obsolete form of octal literal; use 0o%s", val.raw[1:])
}
}
} else {
// decimal
for isdigit(c) {
sc.readRune()
c = sc.peekRune()
}
if c == '.' {
fraction = true
} else if c == 'e' || c == 'E' {
exponent = true
}
}
if fraction {
sc.readRune() // consume '.'
c = sc.peekRune()
for isdigit(c) {
sc.readRune()
c = sc.peekRune()
}
if c == 'e' || c == 'E' {
exponent = true
}
}
if exponent {
sc.readRune() // consume [eE]
c = sc.peekRune()
if c == '+' || c == '-' {
sc.readRune()
c = sc.peekRune()
if !isdigit(c) {
sc.error(sc.pos, "invalid float literal")
}
}
for isdigit(c) {
sc.readRune()
c = sc.peekRune()
}
}
sc.endToken(val)
if fraction || exponent {
var err error
val.float, err = strconv.ParseFloat(val.raw, 64)
if err != nil {
sc.error(sc.pos, "invalid float literal")
}
return FLOAT
} else {
var err error
s := val.raw
val.bigInt = nil
if len(s) > 2 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O') {
val.int, err = strconv.ParseInt(s[2:], 8, 64)
} else if len(s) > 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B') {
val.int, err = strconv.ParseInt(s[2:], 2, 64)
} else {
val.int, err = strconv.ParseInt(s, 0, 64)
if err != nil {
num := new(big.Int)
var ok bool
val.bigInt, ok = num.SetString(s, 0)
if ok {
err = nil
}
}
}
if err != nil {
sc.error(start, "invalid int literal")
}
return INT
}
}