This repository has been archived on 2022-08-29. You can view files and clone it, but cannot push or open issues or pull requests.
arf/lexer/numbers.go

127 lines
2.7 KiB
Go

package lexer
import "git.tebibyte.media/sashakoshka/arf/file"
// tokenizeSymbolBeginning lexes a token that starts with a number.
func (lexer *LexingOperation) tokenizeNumberBeginning (negative bool) (err error) {
var number uint64
var fragment float64
var isFloat bool
if lexer.char == '0' {
lexer.nextRune()
if lexer.char == 'x' {
lexer.nextRune()
number, fragment, isFloat, err = lexer.tokenizeNumber(16)
} else if lexer.char == 'b' {
lexer.nextRune()
number, fragment, isFloat, err = lexer.tokenizeNumber(2)
} else if lexer.char == '.' {
number, fragment, isFloat, err = lexer.tokenizeNumber(10)
} else if lexer.char >= '0' && lexer.char <= '9' {
number, fragment, isFloat, err = lexer.tokenizeNumber(8)
} else {
return file.NewError (
lexer.file.Location(),
"unexpected character in number literal",
file.ErrorKindError)
}
} else {
number, fragment, isFloat, err = lexer.tokenizeNumber(10)
}
if err != nil { return }
token := Token { }
if isFloat {
floatNumber := float64(number) + fragment
token.kind = TokenKindFloat
if negative {
token.value = floatNumber * -1
} else {
token.value = floatNumber
}
} else {
if negative {
token.kind = TokenKindInt
token.value = int64(number) * -1
} else {
token.kind = TokenKindUInt
token.value = uint64(number)
}
}
lexer.addToken(token)
return
}
// runeToDigit converts a rune from 0-F to a corresponding digit, with a maximum
// radix. If the character is invalid, or the digit is too big, it will return
// false for worked.
func runeToDigit (char rune, radix uint64) (digit uint64, worked bool) {
worked = true
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 {
worked = false
}
if digit >= radix {
worked = false
}
return
}
// tokenizeNumber reads and tokenizes a number with the specified radix.
func (lexer *LexingOperation) tokenizeNumber (
radix uint64,
) (
number uint64,
fragment float64,
isFloat bool,
err error,
) {
for {
digit, worked := runeToDigit(lexer.char, radix)
if !worked { break }
number *= radix
number += digit
err = lexer.nextRune()
if err != nil { return }
}
// TODO: increase accuracy of this so that TestTokenizeNumbers is
// passed.
if lexer.char == '.' {
isFloat = true
err = lexer.nextRune()
if err != nil { return }
coef := 1 / float64(radix)
for {
digit, worked := runeToDigit(lexer.char, radix)
if !worked { break }
fragment += float64(digit) * coef
coef /= float64(radix)
err = lexer.nextRune()
if err != nil { return }
}
}
return
}