127 lines
2.7 KiB
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
|
|
|
|
token := lexer.newToken()
|
|
|
|
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(1),
|
|
"unexpected character in number literal",
|
|
file.ErrorKindError)
|
|
}
|
|
} else {
|
|
number, fragment, isFloat, err = lexer.tokenizeNumber(10)
|
|
}
|
|
|
|
if err != nil { return }
|
|
|
|
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
|
|
}
|