This repository has been archived on 2024-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
arf/lexer/numbers.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
}