mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 17:57:51 +00:00
This updates the dependencies to Kube 1.11.3 to pull in a fix allowing requestheader auth to be used without normal client auth (which makes things work on clusters that don't enable client auth normally, like EKS).
1181 lines
23 KiB
Go
1181 lines
23 KiB
Go
// Package jlexer contains a JSON lexer implementation.
|
|
//
|
|
// It is expected that it is mostly used with generated parser code, so the interface is tuned
|
|
// for a parser that knows what kind of data is expected.
|
|
package jlexer
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"unicode"
|
|
"unicode/utf16"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// tokenKind determines type of a token.
|
|
type tokenKind byte
|
|
|
|
const (
|
|
tokenUndef tokenKind = iota // No token.
|
|
tokenDelim // Delimiter: one of '{', '}', '[' or ']'.
|
|
tokenString // A string literal, e.g. "abc\u1234"
|
|
tokenNumber // Number literal, e.g. 1.5e5
|
|
tokenBool // Boolean literal: true or false.
|
|
tokenNull // null keyword.
|
|
)
|
|
|
|
// token describes a single token: type, position in the input and value.
|
|
type token struct {
|
|
kind tokenKind // Type of a token.
|
|
|
|
boolValue bool // Value if a boolean literal token.
|
|
byteValue []byte // Raw value of a token.
|
|
delimValue byte
|
|
}
|
|
|
|
// Lexer is a JSON lexer: it iterates over JSON tokens in a byte slice.
|
|
type Lexer struct {
|
|
Data []byte // Input data given to the lexer.
|
|
|
|
start int // Start of the current token.
|
|
pos int // Current unscanned position in the input stream.
|
|
token token // Last scanned token, if token.kind != tokenUndef.
|
|
|
|
firstElement bool // Whether current element is the first in array or an object.
|
|
wantSep byte // A comma or a colon character, which need to occur before a token.
|
|
|
|
UseMultipleErrors bool // If we want to use multiple errors.
|
|
fatalError error // Fatal error occurred during lexing. It is usually a syntax error.
|
|
multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors.
|
|
}
|
|
|
|
// FetchToken scans the input for the next token.
|
|
func (r *Lexer) FetchToken() {
|
|
r.token.kind = tokenUndef
|
|
r.start = r.pos
|
|
|
|
// Check if r.Data has r.pos element
|
|
// If it doesn't, it mean corrupted input data
|
|
if len(r.Data) < r.pos {
|
|
r.errParse("Unexpected end of data")
|
|
return
|
|
}
|
|
// Determine the type of a token by skipping whitespace and reading the
|
|
// first character.
|
|
for _, c := range r.Data[r.pos:] {
|
|
switch c {
|
|
case ':', ',':
|
|
if r.wantSep == c {
|
|
r.pos++
|
|
r.start++
|
|
r.wantSep = 0
|
|
} else {
|
|
r.errSyntax()
|
|
}
|
|
|
|
case ' ', '\t', '\r', '\n':
|
|
r.pos++
|
|
r.start++
|
|
|
|
case '"':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
|
|
r.token.kind = tokenString
|
|
r.fetchString()
|
|
return
|
|
|
|
case '{', '[':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
r.firstElement = true
|
|
r.token.kind = tokenDelim
|
|
r.token.delimValue = r.Data[r.pos]
|
|
r.pos++
|
|
return
|
|
|
|
case '}', ']':
|
|
if !r.firstElement && (r.wantSep != ',') {
|
|
r.errSyntax()
|
|
}
|
|
r.wantSep = 0
|
|
r.token.kind = tokenDelim
|
|
r.token.delimValue = r.Data[r.pos]
|
|
r.pos++
|
|
return
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
r.token.kind = tokenNumber
|
|
r.fetchNumber()
|
|
return
|
|
|
|
case 'n':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
|
|
r.token.kind = tokenNull
|
|
r.fetchNull()
|
|
return
|
|
|
|
case 't':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
|
|
r.token.kind = tokenBool
|
|
r.token.boolValue = true
|
|
r.fetchTrue()
|
|
return
|
|
|
|
case 'f':
|
|
if r.wantSep != 0 {
|
|
r.errSyntax()
|
|
}
|
|
|
|
r.token.kind = tokenBool
|
|
r.token.boolValue = false
|
|
r.fetchFalse()
|
|
return
|
|
|
|
default:
|
|
r.errSyntax()
|
|
return
|
|
}
|
|
}
|
|
r.fatalError = io.EOF
|
|
return
|
|
}
|
|
|
|
// isTokenEnd returns true if the char can follow a non-delimiter token
|
|
func isTokenEnd(c byte) bool {
|
|
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == ':'
|
|
}
|
|
|
|
// fetchNull fetches and checks remaining bytes of null keyword.
|
|
func (r *Lexer) fetchNull() {
|
|
r.pos += 4
|
|
if r.pos > len(r.Data) ||
|
|
r.Data[r.pos-3] != 'u' ||
|
|
r.Data[r.pos-2] != 'l' ||
|
|
r.Data[r.pos-1] != 'l' ||
|
|
(r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) {
|
|
|
|
r.pos -= 4
|
|
r.errSyntax()
|
|
}
|
|
}
|
|
|
|
// fetchTrue fetches and checks remaining bytes of true keyword.
|
|
func (r *Lexer) fetchTrue() {
|
|
r.pos += 4
|
|
if r.pos > len(r.Data) ||
|
|
r.Data[r.pos-3] != 'r' ||
|
|
r.Data[r.pos-2] != 'u' ||
|
|
r.Data[r.pos-1] != 'e' ||
|
|
(r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) {
|
|
|
|
r.pos -= 4
|
|
r.errSyntax()
|
|
}
|
|
}
|
|
|
|
// fetchFalse fetches and checks remaining bytes of false keyword.
|
|
func (r *Lexer) fetchFalse() {
|
|
r.pos += 5
|
|
if r.pos > len(r.Data) ||
|
|
r.Data[r.pos-4] != 'a' ||
|
|
r.Data[r.pos-3] != 'l' ||
|
|
r.Data[r.pos-2] != 's' ||
|
|
r.Data[r.pos-1] != 'e' ||
|
|
(r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) {
|
|
|
|
r.pos -= 5
|
|
r.errSyntax()
|
|
}
|
|
}
|
|
|
|
// fetchNumber scans a number literal token.
|
|
func (r *Lexer) fetchNumber() {
|
|
hasE := false
|
|
afterE := false
|
|
hasDot := false
|
|
|
|
r.pos++
|
|
for i, c := range r.Data[r.pos:] {
|
|
switch {
|
|
case c >= '0' && c <= '9':
|
|
afterE = false
|
|
case c == '.' && !hasDot:
|
|
hasDot = true
|
|
case (c == 'e' || c == 'E') && !hasE:
|
|
hasE = true
|
|
hasDot = true
|
|
afterE = true
|
|
case (c == '+' || c == '-') && afterE:
|
|
afterE = false
|
|
default:
|
|
r.pos += i
|
|
if !isTokenEnd(c) {
|
|
r.errSyntax()
|
|
} else {
|
|
r.token.byteValue = r.Data[r.start:r.pos]
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
r.pos = len(r.Data)
|
|
r.token.byteValue = r.Data[r.start:]
|
|
}
|
|
|
|
// findStringLen tries to scan into the string literal for ending quote char to determine required size.
|
|
// The size will be exact if no escapes are present and may be inexact if there are escaped chars.
|
|
func findStringLen(data []byte) (isValid, hasEscapes bool, length int) {
|
|
delta := 0
|
|
|
|
for i := 0; i < len(data); i++ {
|
|
switch data[i] {
|
|
case '\\':
|
|
i++
|
|
delta++
|
|
if i < len(data) && data[i] == 'u' {
|
|
delta++
|
|
}
|
|
case '"':
|
|
return true, (delta > 0), (i - delta)
|
|
}
|
|
}
|
|
|
|
return false, false, len(data)
|
|
}
|
|
|
|
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
|
|
// or it returns -1.
|
|
func getu4(s []byte) rune {
|
|
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
|
return -1
|
|
}
|
|
var val rune
|
|
for i := 2; i < len(s) && i < 6; i++ {
|
|
var v byte
|
|
c := s[i]
|
|
switch c {
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
v = c - '0'
|
|
case 'a', 'b', 'c', 'd', 'e', 'f':
|
|
v = c - 'a' + 10
|
|
case 'A', 'B', 'C', 'D', 'E', 'F':
|
|
v = c - 'A' + 10
|
|
default:
|
|
return -1
|
|
}
|
|
|
|
val <<= 4
|
|
val |= rune(v)
|
|
}
|
|
return val
|
|
}
|
|
|
|
// processEscape processes a single escape sequence and returns number of bytes processed.
|
|
func (r *Lexer) processEscape(data []byte) (int, error) {
|
|
if len(data) < 2 {
|
|
return 0, fmt.Errorf("syntax error at %v", string(data))
|
|
}
|
|
|
|
c := data[1]
|
|
switch c {
|
|
case '"', '/', '\\':
|
|
r.token.byteValue = append(r.token.byteValue, c)
|
|
return 2, nil
|
|
case 'b':
|
|
r.token.byteValue = append(r.token.byteValue, '\b')
|
|
return 2, nil
|
|
case 'f':
|
|
r.token.byteValue = append(r.token.byteValue, '\f')
|
|
return 2, nil
|
|
case 'n':
|
|
r.token.byteValue = append(r.token.byteValue, '\n')
|
|
return 2, nil
|
|
case 'r':
|
|
r.token.byteValue = append(r.token.byteValue, '\r')
|
|
return 2, nil
|
|
case 't':
|
|
r.token.byteValue = append(r.token.byteValue, '\t')
|
|
return 2, nil
|
|
case 'u':
|
|
rr := getu4(data)
|
|
if rr < 0 {
|
|
return 0, errors.New("syntax error")
|
|
}
|
|
|
|
read := 6
|
|
if utf16.IsSurrogate(rr) {
|
|
rr1 := getu4(data[read:])
|
|
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
|
|
read += 6
|
|
rr = dec
|
|
} else {
|
|
rr = unicode.ReplacementChar
|
|
}
|
|
}
|
|
var d [4]byte
|
|
s := utf8.EncodeRune(d[:], rr)
|
|
r.token.byteValue = append(r.token.byteValue, d[:s]...)
|
|
return read, nil
|
|
}
|
|
|
|
return 0, errors.New("syntax error")
|
|
}
|
|
|
|
// fetchString scans a string literal token.
|
|
func (r *Lexer) fetchString() {
|
|
r.pos++
|
|
data := r.Data[r.pos:]
|
|
|
|
isValid, hasEscapes, length := findStringLen(data)
|
|
if !isValid {
|
|
r.pos += length
|
|
r.errParse("unterminated string literal")
|
|
return
|
|
}
|
|
if !hasEscapes {
|
|
r.token.byteValue = data[:length]
|
|
r.pos += length + 1
|
|
return
|
|
}
|
|
|
|
r.token.byteValue = make([]byte, 0, length)
|
|
p := 0
|
|
for i := 0; i < len(data); {
|
|
switch data[i] {
|
|
case '"':
|
|
r.pos += i + 1
|
|
r.token.byteValue = append(r.token.byteValue, data[p:i]...)
|
|
i++
|
|
return
|
|
|
|
case '\\':
|
|
r.token.byteValue = append(r.token.byteValue, data[p:i]...)
|
|
off, err := r.processEscape(data[i:])
|
|
if err != nil {
|
|
r.errParse(err.Error())
|
|
return
|
|
}
|
|
i += off
|
|
p = i
|
|
|
|
default:
|
|
i++
|
|
}
|
|
}
|
|
r.errParse("unterminated string literal")
|
|
}
|
|
|
|
// scanToken scans the next token if no token is currently available in the lexer.
|
|
func (r *Lexer) scanToken() {
|
|
if r.token.kind != tokenUndef || r.fatalError != nil {
|
|
return
|
|
}
|
|
|
|
r.FetchToken()
|
|
}
|
|
|
|
// consume resets the current token to allow scanning the next one.
|
|
func (r *Lexer) consume() {
|
|
r.token.kind = tokenUndef
|
|
r.token.delimValue = 0
|
|
}
|
|
|
|
// Ok returns true if no error (including io.EOF) was encountered during scanning.
|
|
func (r *Lexer) Ok() bool {
|
|
return r.fatalError == nil
|
|
}
|
|
|
|
const maxErrorContextLen = 13
|
|
|
|
func (r *Lexer) errParse(what string) {
|
|
if r.fatalError == nil {
|
|
var str string
|
|
if len(r.Data)-r.pos <= maxErrorContextLen {
|
|
str = string(r.Data)
|
|
} else {
|
|
str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..."
|
|
}
|
|
r.fatalError = &LexerError{
|
|
Reason: what,
|
|
Offset: r.pos,
|
|
Data: str,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Lexer) errSyntax() {
|
|
r.errParse("syntax error")
|
|
}
|
|
|
|
func (r *Lexer) errInvalidToken(expected string) {
|
|
if r.fatalError != nil {
|
|
return
|
|
}
|
|
if r.UseMultipleErrors {
|
|
r.pos = r.start
|
|
r.consume()
|
|
r.SkipRecursive()
|
|
switch expected {
|
|
case "[":
|
|
r.token.delimValue = ']'
|
|
r.token.kind = tokenDelim
|
|
case "{":
|
|
r.token.delimValue = '}'
|
|
r.token.kind = tokenDelim
|
|
}
|
|
r.addNonfatalError(&LexerError{
|
|
Reason: fmt.Sprintf("expected %s", expected),
|
|
Offset: r.start,
|
|
Data: string(r.Data[r.start:r.pos]),
|
|
})
|
|
return
|
|
}
|
|
|
|
var str string
|
|
if len(r.token.byteValue) <= maxErrorContextLen {
|
|
str = string(r.token.byteValue)
|
|
} else {
|
|
str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..."
|
|
}
|
|
r.fatalError = &LexerError{
|
|
Reason: fmt.Sprintf("expected %s", expected),
|
|
Offset: r.pos,
|
|
Data: str,
|
|
}
|
|
}
|
|
|
|
func (r *Lexer) GetPos() int {
|
|
return r.pos
|
|
}
|
|
|
|
// Delim consumes a token and verifies that it is the given delimiter.
|
|
func (r *Lexer) Delim(c byte) {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
|
|
if !r.Ok() || r.token.delimValue != c {
|
|
r.consume() // errInvalidToken can change token if UseMultipleErrors is enabled.
|
|
r.errInvalidToken(string([]byte{c}))
|
|
} else {
|
|
r.consume()
|
|
}
|
|
}
|
|
|
|
// IsDelim returns true if there was no scanning error and next token is the given delimiter.
|
|
func (r *Lexer) IsDelim(c byte) bool {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
return !r.Ok() || r.token.delimValue == c
|
|
}
|
|
|
|
// Null verifies that the next token is null and consumes it.
|
|
func (r *Lexer) Null() {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenNull {
|
|
r.errInvalidToken("null")
|
|
}
|
|
r.consume()
|
|
}
|
|
|
|
// IsNull returns true if the next token is a null keyword.
|
|
func (r *Lexer) IsNull() bool {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
return r.Ok() && r.token.kind == tokenNull
|
|
}
|
|
|
|
// Skip skips a single token.
|
|
func (r *Lexer) Skip() {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
r.consume()
|
|
}
|
|
|
|
// SkipRecursive skips next array or object completely, or just skips a single token if not
|
|
// an array/object.
|
|
//
|
|
// Note: no syntax validation is performed on the skipped data.
|
|
func (r *Lexer) SkipRecursive() {
|
|
r.scanToken()
|
|
var start, end byte
|
|
|
|
if r.token.delimValue == '{' {
|
|
start, end = '{', '}'
|
|
} else if r.token.delimValue == '[' {
|
|
start, end = '[', ']'
|
|
} else {
|
|
r.consume()
|
|
return
|
|
}
|
|
|
|
r.consume()
|
|
|
|
level := 1
|
|
inQuotes := false
|
|
wasEscape := false
|
|
|
|
for i, c := range r.Data[r.pos:] {
|
|
switch {
|
|
case c == start && !inQuotes:
|
|
level++
|
|
case c == end && !inQuotes:
|
|
level--
|
|
if level == 0 {
|
|
r.pos += i + 1
|
|
return
|
|
}
|
|
case c == '\\' && inQuotes:
|
|
wasEscape = !wasEscape
|
|
continue
|
|
case c == '"' && inQuotes:
|
|
inQuotes = wasEscape
|
|
case c == '"':
|
|
inQuotes = true
|
|
}
|
|
wasEscape = false
|
|
}
|
|
r.pos = len(r.Data)
|
|
r.fatalError = &LexerError{
|
|
Reason: "EOF reached while skipping array/object or token",
|
|
Offset: r.pos,
|
|
Data: string(r.Data[r.pos:]),
|
|
}
|
|
}
|
|
|
|
// Raw fetches the next item recursively as a data slice
|
|
func (r *Lexer) Raw() []byte {
|
|
r.SkipRecursive()
|
|
if !r.Ok() {
|
|
return nil
|
|
}
|
|
return r.Data[r.start:r.pos]
|
|
}
|
|
|
|
// IsStart returns whether the lexer is positioned at the start
|
|
// of an input string.
|
|
func (r *Lexer) IsStart() bool {
|
|
return r.pos == 0
|
|
}
|
|
|
|
// Consumed reads all remaining bytes from the input, publishing an error if
|
|
// there is anything but whitespace remaining.
|
|
func (r *Lexer) Consumed() {
|
|
if r.pos > len(r.Data) || !r.Ok() {
|
|
return
|
|
}
|
|
|
|
for _, c := range r.Data[r.pos:] {
|
|
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
|
r.AddError(&LexerError{
|
|
Reason: "invalid character '" + string(c) + "' after top-level value",
|
|
Offset: r.pos,
|
|
Data: string(r.Data[r.pos:]),
|
|
})
|
|
return
|
|
}
|
|
|
|
r.pos++
|
|
r.start++
|
|
}
|
|
}
|
|
|
|
func (r *Lexer) unsafeString() (string, []byte) {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenString {
|
|
r.errInvalidToken("string")
|
|
return "", nil
|
|
}
|
|
bytes := r.token.byteValue
|
|
ret := bytesToStr(r.token.byteValue)
|
|
r.consume()
|
|
return ret, bytes
|
|
}
|
|
|
|
// UnsafeString returns the string value if the token is a string literal.
|
|
//
|
|
// Warning: returned string may point to the input buffer, so the string should not outlive
|
|
// the input buffer. Intended pattern of usage is as an argument to a switch statement.
|
|
func (r *Lexer) UnsafeString() string {
|
|
ret, _ := r.unsafeString()
|
|
return ret
|
|
}
|
|
|
|
// UnsafeBytes returns the byte slice if the token is a string literal.
|
|
func (r *Lexer) UnsafeBytes() []byte {
|
|
_, ret := r.unsafeString()
|
|
return ret
|
|
}
|
|
|
|
// String reads a string literal.
|
|
func (r *Lexer) String() string {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenString {
|
|
r.errInvalidToken("string")
|
|
return ""
|
|
}
|
|
ret := string(r.token.byteValue)
|
|
r.consume()
|
|
return ret
|
|
}
|
|
|
|
// Bytes reads a string literal and base64 decodes it into a byte slice.
|
|
func (r *Lexer) Bytes() []byte {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenString {
|
|
r.errInvalidToken("string")
|
|
return nil
|
|
}
|
|
ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue)))
|
|
n, err := base64.StdEncoding.Decode(ret, r.token.byteValue)
|
|
if err != nil {
|
|
r.fatalError = &LexerError{
|
|
Reason: err.Error(),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
r.consume()
|
|
return ret[:n]
|
|
}
|
|
|
|
// Bool reads a true or false boolean keyword.
|
|
func (r *Lexer) Bool() bool {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenBool {
|
|
r.errInvalidToken("bool")
|
|
return false
|
|
}
|
|
ret := r.token.boolValue
|
|
r.consume()
|
|
return ret
|
|
}
|
|
|
|
func (r *Lexer) number() string {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() || r.token.kind != tokenNumber {
|
|
r.errInvalidToken("number")
|
|
return ""
|
|
}
|
|
ret := bytesToStr(r.token.byteValue)
|
|
r.consume()
|
|
return ret
|
|
}
|
|
|
|
func (r *Lexer) Uint8() uint8 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 8)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return uint8(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint16() uint16 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 16)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return uint16(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint32() uint32 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return uint32(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint64() uint64 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) Uint() uint {
|
|
return uint(r.Uint64())
|
|
}
|
|
|
|
func (r *Lexer) Int8() int8 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 8)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return int8(n)
|
|
}
|
|
|
|
func (r *Lexer) Int16() int16 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 16)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return int16(n)
|
|
}
|
|
|
|
func (r *Lexer) Int32() int32 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return int32(n)
|
|
}
|
|
|
|
func (r *Lexer) Int64() int64 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) Int() int {
|
|
return int(r.Int64())
|
|
}
|
|
|
|
func (r *Lexer) Uint8Str() uint8 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 8)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return uint8(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint16Str() uint16 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 16)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return uint16(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint32Str() uint32 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return uint32(n)
|
|
}
|
|
|
|
func (r *Lexer) Uint64Str() uint64 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseUint(s, 10, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) UintStr() uint {
|
|
return uint(r.Uint64Str())
|
|
}
|
|
|
|
func (r *Lexer) UintptrStr() uintptr {
|
|
return uintptr(r.Uint64Str())
|
|
}
|
|
|
|
func (r *Lexer) Int8Str() int8 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 8)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return int8(n)
|
|
}
|
|
|
|
func (r *Lexer) Int16Str() int16 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 16)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return int16(n)
|
|
}
|
|
|
|
func (r *Lexer) Int32Str() int32 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return int32(n)
|
|
}
|
|
|
|
func (r *Lexer) Int64Str() int64 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) IntStr() int {
|
|
return int(r.Int64Str())
|
|
}
|
|
|
|
func (r *Lexer) Float32() float32 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseFloat(s, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return float32(n)
|
|
}
|
|
|
|
func (r *Lexer) Float32Str() float32 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
n, err := strconv.ParseFloat(s, 32)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return float32(n)
|
|
}
|
|
|
|
func (r *Lexer) Float64() float64 {
|
|
s := r.number()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
|
|
n, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: s,
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) Float64Str() float64 {
|
|
s, b := r.unsafeString()
|
|
if !r.Ok() {
|
|
return 0
|
|
}
|
|
n, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Reason: err.Error(),
|
|
Data: string(b),
|
|
})
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (r *Lexer) Error() error {
|
|
return r.fatalError
|
|
}
|
|
|
|
func (r *Lexer) AddError(e error) {
|
|
if r.fatalError == nil {
|
|
r.fatalError = e
|
|
}
|
|
}
|
|
|
|
func (r *Lexer) AddNonFatalError(e error) {
|
|
r.addNonfatalError(&LexerError{
|
|
Offset: r.start,
|
|
Data: string(r.Data[r.start:r.pos]),
|
|
Reason: e.Error(),
|
|
})
|
|
}
|
|
|
|
func (r *Lexer) addNonfatalError(err *LexerError) {
|
|
if r.UseMultipleErrors {
|
|
// We don't want to add errors with the same offset.
|
|
if len(r.multipleErrors) != 0 && r.multipleErrors[len(r.multipleErrors)-1].Offset == err.Offset {
|
|
return
|
|
}
|
|
r.multipleErrors = append(r.multipleErrors, err)
|
|
return
|
|
}
|
|
r.fatalError = err
|
|
}
|
|
|
|
func (r *Lexer) GetNonFatalErrors() []*LexerError {
|
|
return r.multipleErrors
|
|
}
|
|
|
|
// JsonNumber fetches and json.Number from 'encoding/json' package.
|
|
// Both int, float or string, contains them are valid values
|
|
func (r *Lexer) JsonNumber() json.Number {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
if !r.Ok() {
|
|
r.errInvalidToken("json.Number")
|
|
return json.Number("")
|
|
}
|
|
|
|
switch r.token.kind {
|
|
case tokenString:
|
|
return json.Number(r.String())
|
|
case tokenNumber:
|
|
return json.Number(r.Raw())
|
|
case tokenNull:
|
|
r.Null()
|
|
return json.Number("")
|
|
default:
|
|
r.errSyntax()
|
|
return json.Number("")
|
|
}
|
|
}
|
|
|
|
// Interface fetches an interface{} analogous to the 'encoding/json' package.
|
|
func (r *Lexer) Interface() interface{} {
|
|
if r.token.kind == tokenUndef && r.Ok() {
|
|
r.FetchToken()
|
|
}
|
|
|
|
if !r.Ok() {
|
|
return nil
|
|
}
|
|
switch r.token.kind {
|
|
case tokenString:
|
|
return r.String()
|
|
case tokenNumber:
|
|
return r.Float64()
|
|
case tokenBool:
|
|
return r.Bool()
|
|
case tokenNull:
|
|
r.Null()
|
|
return nil
|
|
}
|
|
|
|
if r.token.delimValue == '{' {
|
|
r.consume()
|
|
|
|
ret := map[string]interface{}{}
|
|
for !r.IsDelim('}') {
|
|
key := r.String()
|
|
r.WantColon()
|
|
ret[key] = r.Interface()
|
|
r.WantComma()
|
|
}
|
|
r.Delim('}')
|
|
|
|
if r.Ok() {
|
|
return ret
|
|
} else {
|
|
return nil
|
|
}
|
|
} else if r.token.delimValue == '[' {
|
|
r.consume()
|
|
|
|
var ret []interface{}
|
|
for !r.IsDelim(']') {
|
|
ret = append(ret, r.Interface())
|
|
r.WantComma()
|
|
}
|
|
r.Delim(']')
|
|
|
|
if r.Ok() {
|
|
return ret
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
r.errSyntax()
|
|
return nil
|
|
}
|
|
|
|
// WantComma requires a comma to be present before fetching next token.
|
|
func (r *Lexer) WantComma() {
|
|
r.wantSep = ','
|
|
r.firstElement = false
|
|
}
|
|
|
|
// WantColon requires a colon to be present before fetching next token.
|
|
func (r *Lexer) WantColon() {
|
|
r.wantSep = ':'
|
|
r.firstElement = false
|
|
}
|