156 lines
3.2 KiB
Go
156 lines
3.2 KiB
Go
package lexer
|
|
|
|
import "strconv"
|
|
import "git.tebibyte.media/arf/arf/infoerr"
|
|
|
|
// tokenizeSymbolBeginning lexes a token that starts with a number.
|
|
func (lexer *lexingOperation) tokenizeNumberBeginning (negative bool) (err error) {
|
|
var intNumber uint64
|
|
var floatNumber float64
|
|
var isFloat bool
|
|
var amountRead int
|
|
var totalRead int
|
|
|
|
token := lexer.newToken()
|
|
|
|
if lexer.char == '0' {
|
|
lexer.nextRune()
|
|
totalRead ++
|
|
|
|
if lexer.char == 'x' {
|
|
lexer.nextRune()
|
|
totalRead ++
|
|
intNumber, floatNumber,
|
|
isFloat, amountRead,
|
|
err = lexer.tokenizeNumber(16)
|
|
|
|
} else if lexer.char == 'b' {
|
|
lexer.nextRune()
|
|
totalRead ++
|
|
intNumber, floatNumber,
|
|
isFloat, amountRead,
|
|
err = lexer.tokenizeNumber(2)
|
|
|
|
} else if lexer.char == '.' {
|
|
intNumber, floatNumber,
|
|
isFloat, amountRead,
|
|
err = lexer.tokenizeNumber(10)
|
|
|
|
} else if lexer.char >= '0' && lexer.char <= '7' {
|
|
intNumber, floatNumber,
|
|
isFloat, amountRead,
|
|
err = lexer.tokenizeNumber(8)
|
|
} else if lexer.char >= '8' && lexer.char <= '9' {
|
|
err = infoerr.NewError (
|
|
lexer.file.Location(1),
|
|
"unexpected number '" + string(lexer.char) +
|
|
"' in octal literal",
|
|
infoerr.ErrorKindError)
|
|
return
|
|
}
|
|
} else {
|
|
intNumber, floatNumber,
|
|
isFloat, amountRead,
|
|
err = lexer.tokenizeNumber(10)
|
|
}
|
|
|
|
totalRead += amountRead
|
|
if negative {
|
|
totalRead += 1
|
|
}
|
|
|
|
if err != nil { return }
|
|
|
|
if isFloat {
|
|
token.kind = TokenKindFloat
|
|
if negative {
|
|
token.value = floatNumber * -1
|
|
} else {
|
|
token.value = floatNumber
|
|
}
|
|
} else {
|
|
if negative {
|
|
token.kind = TokenKindInt
|
|
token.value = int64(intNumber) * -1
|
|
} else {
|
|
token.kind = TokenKindUInt
|
|
token.value = uint64(intNumber)
|
|
}
|
|
}
|
|
|
|
token.location.SetWidth(totalRead)
|
|
lexer.addToken(token)
|
|
return
|
|
}
|
|
|
|
// runeIsDigit checks to see if the rune is a valid digit within the given
|
|
// radix, up to 16. A '.' rune will also be treated as valid.
|
|
func runeIsDigit (char rune, radix uint64) (isDigit bool) {
|
|
isDigit = true
|
|
|
|
var digit uint64
|
|
if char >= '0' && char <= '9' {
|
|
digit = uint64(char - '0')
|
|
} else if char >= 'A' && char <= 'F' {
|
|
digit = uint64(char - 'A' + 10)
|
|
} else if char >= 'a' && char <= 'f' {
|
|
digit = uint64(char - 'a' + 10)
|
|
} else if char != '.' {
|
|
isDigit = false
|
|
}
|
|
|
|
if digit >= radix {
|
|
isDigit = false
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// tokenizeNumber reads and tokenizes a number with the specified radix.
|
|
func (lexer *lexingOperation) tokenizeNumber (
|
|
radix uint64,
|
|
) (
|
|
intNumber uint64,
|
|
floatNumber float64,
|
|
isFloat bool,
|
|
amountRead int,
|
|
err error,
|
|
) {
|
|
got := ""
|
|
for {
|
|
if !runeIsDigit(lexer.char, radix) { break }
|
|
if lexer.char == '.' {
|
|
if radix != 10 {
|
|
err = infoerr.NewError (
|
|
lexer.file.Location(1),
|
|
"floats must have radix of 10",
|
|
infoerr.ErrorKindError)
|
|
return
|
|
}
|
|
isFloat = true
|
|
}
|
|
|
|
got += string(lexer.char)
|
|
err = lexer.nextRune()
|
|
if err != nil { return }
|
|
}
|
|
|
|
amountRead = len(got)
|
|
|
|
if isFloat {
|
|
floatNumber, err = strconv.ParseFloat(got, 64)
|
|
} else {
|
|
intNumber, err = strconv.ParseUint(got, int(radix), 64)
|
|
}
|
|
|
|
if err != nil {
|
|
err = infoerr.NewError (
|
|
lexer.file.Location(1),
|
|
"could not parse number: " + err.Error(),
|
|
infoerr.ErrorKindError)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|