Path: blob/main/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
2893 views
package unstable12import (3"bytes"4"fmt"5"unicode"67"github.com/pelletier/go-toml/v2/internal/characters"8"github.com/pelletier/go-toml/v2/internal/danger"9)1011// ParserError describes an error relative to the content of the document.12//13// It cannot outlive the instance of Parser it refers to, and may cause panics14// if the parser is reset.15type ParserError struct {16Highlight []byte17Message string18Key []string // optional19}2021// Error is the implementation of the error interface.22func (e *ParserError) Error() string {23return e.Message24}2526// NewParserError is a convenience function to create a ParserError27//28// Warning: Highlight needs to be a subslice of Parser.data, so only slices29// returned by Parser.Raw are valid candidates.30func NewParserError(highlight []byte, format string, args ...interface{}) error {31return &ParserError{32Highlight: highlight,33Message: fmt.Errorf(format, args...).Error(),34}35}3637// Parser scans over a TOML-encoded document and generates an iterative AST.38//39// To prime the Parser, first reset it with the contents of a TOML document.40// Then, process all top-level expressions sequentially. See Example.41//42// Don't forget to check Error() after you're done parsing.43//44// Each top-level expression needs to be fully processed before calling45// NextExpression() again. Otherwise, calls to various Node methods may panic if46// the parser has moved on the next expression.47//48// For performance reasons, go-toml doesn't make a copy of the input bytes to49// the parser. Make sure to copy all the bytes you need to outlive the slice50// given to the parser.51type Parser struct {52data []byte53builder builder54ref reference55left []byte56err error57first bool5859KeepComments bool60}6162// Data returns the slice provided to the last call to Reset.63func (p *Parser) Data() []byte {64return p.data65}6667// Range returns a range description that corresponds to a given slice of the68// input. If the argument is not a subslice of the parser input, this function69// panics.70func (p *Parser) Range(b []byte) Range {71return Range{72Offset: uint32(danger.SubsliceOffset(p.data, b)),73Length: uint32(len(b)),74}75}7677// Raw returns the slice corresponding to the bytes in the given range.78func (p *Parser) Raw(raw Range) []byte {79return p.data[raw.Offset : raw.Offset+raw.Length]80}8182// Reset brings the parser to its initial state for a given input. It wipes an83// reuses internal storage to reduce allocation.84func (p *Parser) Reset(b []byte) {85p.builder.Reset()86p.ref = invalidReference87p.data = b88p.left = b89p.err = nil90p.first = true91}9293// NextExpression parses the next top-level expression. If an expression was94// successfully parsed, it returns true. If the parser is at the end of the95// document or an error occurred, it returns false.96//97// Retrieve the parsed expression with Expression().98func (p *Parser) NextExpression() bool {99if len(p.left) == 0 || p.err != nil {100return false101}102103p.builder.Reset()104p.ref = invalidReference105106for {107if len(p.left) == 0 || p.err != nil {108return false109}110111if !p.first {112p.left, p.err = p.parseNewline(p.left)113}114115if len(p.left) == 0 || p.err != nil {116return false117}118119p.ref, p.left, p.err = p.parseExpression(p.left)120121if p.err != nil {122return false123}124125p.first = false126127if p.ref.Valid() {128return true129}130}131}132133// Expression returns a pointer to the node representing the last successfully134// parsed expression.135func (p *Parser) Expression() *Node {136return p.builder.NodeAt(p.ref)137}138139// Error returns any error that has occurred during parsing.140func (p *Parser) Error() error {141return p.err142}143144// Position describes a position in the input.145type Position struct {146// Number of bytes from the beginning of the input.147Offset int148// Line number, starting at 1.149Line int150// Column number, starting at 1.151Column int152}153154// Shape describes the position of a range in the input.155type Shape struct {156Start Position157End Position158}159160func (p *Parser) position(b []byte) Position {161offset := danger.SubsliceOffset(p.data, b)162163lead := p.data[:offset]164165return Position{166Offset: offset,167Line: bytes.Count(lead, []byte{'\n'}) + 1,168Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}),169}170}171172// Shape returns the shape of the given range in the input. Will173// panic if the range is not a subslice of the input.174func (p *Parser) Shape(r Range) Shape {175raw := p.Raw(r)176return Shape{177Start: p.position(raw),178End: p.position(raw[r.Length:]),179}180}181182func (p *Parser) parseNewline(b []byte) ([]byte, error) {183if b[0] == '\n' {184return b[1:], nil185}186187if b[0] == '\r' {188_, rest, err := scanWindowsNewline(b)189return rest, err190}191192return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0])193}194195func (p *Parser) parseComment(b []byte) (reference, []byte, error) {196ref := invalidReference197data, rest, err := scanComment(b)198if p.KeepComments && err == nil {199ref = p.builder.Push(Node{200Kind: Comment,201Raw: p.Range(data),202Data: data,203})204}205return ref, rest, err206}207208func (p *Parser) parseExpression(b []byte) (reference, []byte, error) {209// expression = ws [ comment ]210// expression =/ ws keyval ws [ comment ]211// expression =/ ws table ws [ comment ]212ref := invalidReference213214b = p.parseWhitespace(b)215216if len(b) == 0 {217return ref, b, nil218}219220if b[0] == '#' {221ref, rest, err := p.parseComment(b)222return ref, rest, err223}224225if b[0] == '\n' || b[0] == '\r' {226return ref, b, nil227}228229var err error230if b[0] == '[' {231ref, b, err = p.parseTable(b)232} else {233ref, b, err = p.parseKeyval(b)234}235236if err != nil {237return ref, nil, err238}239240b = p.parseWhitespace(b)241242if len(b) > 0 && b[0] == '#' {243cref, rest, err := p.parseComment(b)244if cref != invalidReference {245p.builder.Chain(ref, cref)246}247return ref, rest, err248}249250return ref, b, nil251}252253func (p *Parser) parseTable(b []byte) (reference, []byte, error) {254// table = std-table / array-table255if len(b) > 1 && b[1] == '[' {256return p.parseArrayTable(b)257}258259return p.parseStdTable(b)260}261262func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) {263// array-table = array-table-open key array-table-close264// array-table-open = %x5B.5B ws ; [[ Double left square bracket265// array-table-close = ws %x5D.5D ; ]] Double right square bracket266ref := p.builder.Push(Node{267Kind: ArrayTable,268})269270b = b[2:]271b = p.parseWhitespace(b)272273k, b, err := p.parseKey(b)274if err != nil {275return ref, nil, err276}277278p.builder.AttachChild(ref, k)279b = p.parseWhitespace(b)280281b, err = expect(']', b)282if err != nil {283return ref, nil, err284}285286b, err = expect(']', b)287288return ref, b, err289}290291func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) {292// std-table = std-table-open key std-table-close293// std-table-open = %x5B ws ; [ Left square bracket294// std-table-close = ws %x5D ; ] Right square bracket295ref := p.builder.Push(Node{296Kind: Table,297})298299b = b[1:]300b = p.parseWhitespace(b)301302key, b, err := p.parseKey(b)303if err != nil {304return ref, nil, err305}306307p.builder.AttachChild(ref, key)308309b = p.parseWhitespace(b)310311b, err = expect(']', b)312313return ref, b, err314}315316func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) {317// keyval = key keyval-sep val318ref := p.builder.Push(Node{319Kind: KeyValue,320})321322key, b, err := p.parseKey(b)323if err != nil {324return invalidReference, nil, err325}326327// keyval-sep = ws %x3D ws ; =328329b = p.parseWhitespace(b)330331if len(b) == 0 {332return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there")333}334335b, err = expect('=', b)336if err != nil {337return invalidReference, nil, err338}339340b = p.parseWhitespace(b)341342valRef, b, err := p.parseVal(b)343if err != nil {344return ref, b, err345}346347p.builder.Chain(valRef, key)348p.builder.AttachChild(ref, valRef)349350return ref, b, err351}352353//nolint:cyclop,funlen354func (p *Parser) parseVal(b []byte) (reference, []byte, error) {355// val = string / boolean / array / inline-table / date-time / float / integer356ref := invalidReference357358if len(b) == 0 {359return ref, nil, NewParserError(b, "expected value, not eof")360}361362var err error363c := b[0]364365switch c {366case '"':367var raw []byte368var v []byte369if scanFollowsMultilineBasicStringDelimiter(b) {370raw, v, b, err = p.parseMultilineBasicString(b)371} else {372raw, v, b, err = p.parseBasicString(b)373}374375if err == nil {376ref = p.builder.Push(Node{377Kind: String,378Raw: p.Range(raw),379Data: v,380})381}382383return ref, b, err384case '\'':385var raw []byte386var v []byte387if scanFollowsMultilineLiteralStringDelimiter(b) {388raw, v, b, err = p.parseMultilineLiteralString(b)389} else {390raw, v, b, err = p.parseLiteralString(b)391}392393if err == nil {394ref = p.builder.Push(Node{395Kind: String,396Raw: p.Range(raw),397Data: v,398})399}400401return ref, b, err402case 't':403if !scanFollowsTrue(b) {404return ref, nil, NewParserError(atmost(b, 4), "expected 'true'")405}406407ref = p.builder.Push(Node{408Kind: Bool,409Data: b[:4],410})411412return ref, b[4:], nil413case 'f':414if !scanFollowsFalse(b) {415return ref, nil, NewParserError(atmost(b, 5), "expected 'false'")416}417418ref = p.builder.Push(Node{419Kind: Bool,420Data: b[:5],421})422423return ref, b[5:], nil424case '[':425return p.parseValArray(b)426case '{':427return p.parseInlineTable(b)428default:429return p.parseIntOrFloatOrDateTime(b)430}431}432433func atmost(b []byte, n int) []byte {434if n >= len(b) {435return b436}437438return b[:n]439}440441func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) {442v, rest, err := scanLiteralString(b)443if err != nil {444return nil, nil, nil, err445}446447return v, v[1 : len(v)-1], rest, nil448}449450func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) {451// inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close452// inline-table-open = %x7B ws ; {453// inline-table-close = ws %x7D ; }454// inline-table-sep = ws %x2C ws ; , Comma455// inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]456parent := p.builder.Push(Node{457Kind: InlineTable,458Raw: p.Range(b[:1]),459})460461first := true462463var child reference464465b = b[1:]466467var err error468469for len(b) > 0 {470previousB := b471b = p.parseWhitespace(b)472473if len(b) == 0 {474return parent, nil, NewParserError(previousB[:1], "inline table is incomplete")475}476477if b[0] == '}' {478break479}480481if !first {482b, err = expect(',', b)483if err != nil {484return parent, nil, err485}486b = p.parseWhitespace(b)487}488489var kv reference490491kv, b, err = p.parseKeyval(b)492if err != nil {493return parent, nil, err494}495496if first {497p.builder.AttachChild(parent, kv)498} else {499p.builder.Chain(child, kv)500}501child = kv502503first = false504}505506rest, err := expect('}', b)507508return parent, rest, err509}510511//nolint:funlen,cyclop512func (p *Parser) parseValArray(b []byte) (reference, []byte, error) {513// array = array-open [ array-values ] ws-comment-newline array-close514// array-open = %x5B ; [515// array-close = %x5D ; ]516// array-values = ws-comment-newline val ws-comment-newline array-sep array-values517// array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]518// array-sep = %x2C ; , Comma519// ws-comment-newline = *( wschar / [ comment ] newline )520arrayStart := b521b = b[1:]522523parent := p.builder.Push(Node{524Kind: Array,525})526527// First indicates whether the parser is looking for the first element528// (non-comment) of the array.529first := true530531lastChild := invalidReference532533addChild := func(valueRef reference) {534if lastChild == invalidReference {535p.builder.AttachChild(parent, valueRef)536} else {537p.builder.Chain(lastChild, valueRef)538}539lastChild = valueRef540}541542var err error543for len(b) > 0 {544cref := invalidReference545cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)546if err != nil {547return parent, nil, err548}549550if cref != invalidReference {551addChild(cref)552}553554if len(b) == 0 {555return parent, nil, NewParserError(arrayStart[:1], "array is incomplete")556}557558if b[0] == ']' {559break560}561562if b[0] == ',' {563if first {564return parent, nil, NewParserError(b[0:1], "array cannot start with comma")565}566b = b[1:]567568cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)569if err != nil {570return parent, nil, err571}572if cref != invalidReference {573addChild(cref)574}575} else if !first {576return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas")577}578579// TOML allows trailing commas in arrays.580if len(b) > 0 && b[0] == ']' {581break582}583584var valueRef reference585valueRef, b, err = p.parseVal(b)586if err != nil {587return parent, nil, err588}589590addChild(valueRef)591592cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)593if err != nil {594return parent, nil, err595}596if cref != invalidReference {597addChild(cref)598}599600first = false601}602603rest, err := expect(']', b)604605return parent, rest, err606}607608func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) {609rootCommentRef := invalidReference610latestCommentRef := invalidReference611612addComment := func(ref reference) {613if rootCommentRef == invalidReference {614rootCommentRef = ref615} else if latestCommentRef == invalidReference {616p.builder.AttachChild(rootCommentRef, ref)617latestCommentRef = ref618} else {619p.builder.Chain(latestCommentRef, ref)620latestCommentRef = ref621}622}623624for len(b) > 0 {625var err error626b = p.parseWhitespace(b)627628if len(b) > 0 && b[0] == '#' {629var ref reference630ref, b, err = p.parseComment(b)631if err != nil {632return invalidReference, nil, err633}634if ref != invalidReference {635addComment(ref)636}637}638639if len(b) == 0 {640break641}642643if b[0] == '\n' || b[0] == '\r' {644b, err = p.parseNewline(b)645if err != nil {646return invalidReference, nil, err647}648} else {649break650}651}652653return rootCommentRef, b, nil654}655656func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) {657token, rest, err := scanMultilineLiteralString(b)658if err != nil {659return nil, nil, nil, err660}661662i := 3663664// skip the immediate new line665if token[i] == '\n' {666i++667} else if token[i] == '\r' && token[i+1] == '\n' {668i += 2669}670671return token, token[i : len(token)-3], rest, err672}673674//nolint:funlen,gocognit,cyclop675func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) {676// ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body677// ml-basic-string-delim678// ml-basic-string-delim = 3quotation-mark679// ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]680//681// mlb-content = mlb-char / newline / mlb-escaped-nl682// mlb-char = mlb-unescaped / escaped683// mlb-quotes = 1*2quotation-mark684// mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii685// mlb-escaped-nl = escape ws newline *( wschar / newline )686token, escaped, rest, err := scanMultilineBasicString(b)687if err != nil {688return nil, nil, nil, err689}690691i := 3692693// skip the immediate new line694if token[i] == '\n' {695i++696} else if token[i] == '\r' && token[i+1] == '\n' {697i += 2698}699700// fast path701startIdx := i702endIdx := len(token) - len(`"""`)703704if !escaped {705str := token[startIdx:endIdx]706verr := characters.Utf8TomlValidAlreadyEscaped(str)707if verr.Zero() {708return token, str, rest, nil709}710return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")711}712713var builder bytes.Buffer714715// The scanner ensures that the token starts and ends with quotes and that716// escapes are balanced.717for i < len(token)-3 {718c := token[i]719720//nolint:nestif721if c == '\\' {722// When the last non-whitespace character on a line is an unescaped \,723// it will be trimmed along with all whitespace (including newlines) up724// to the next non-whitespace character or closing delimiter.725726isLastNonWhitespaceOnLine := false727j := 1728findEOLLoop:729for ; j < len(token)-3-i; j++ {730switch token[i+j] {731case ' ', '\t':732continue733case '\r':734if token[i+j+1] == '\n' {735continue736}737case '\n':738isLastNonWhitespaceOnLine = true739}740break findEOLLoop741}742if isLastNonWhitespaceOnLine {743i += j744for ; i < len(token)-3; i++ {745c := token[i]746if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') {747i--748break749}750}751i++752continue753}754755// handle escaping756i++757c = token[i]758759switch c {760case '"', '\\':761builder.WriteByte(c)762case 'b':763builder.WriteByte('\b')764case 'f':765builder.WriteByte('\f')766case 'n':767builder.WriteByte('\n')768case 'r':769builder.WriteByte('\r')770case 't':771builder.WriteByte('\t')772case 'e':773builder.WriteByte(0x1B)774case 'u':775x, err := hexToRune(atmost(token[i+1:], 4), 4)776if err != nil {777return nil, nil, nil, err778}779builder.WriteRune(x)780i += 4781case 'U':782x, err := hexToRune(atmost(token[i+1:], 8), 8)783if err != nil {784return nil, nil, nil, err785}786787builder.WriteRune(x)788i += 8789default:790return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c)791}792i++793} else {794size := characters.Utf8ValidNext(token[i:])795if size == 0 {796return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c)797}798builder.Write(token[i : i+size])799i += size800}801}802803return token, builder.Bytes(), rest, nil804}805806func (p *Parser) parseKey(b []byte) (reference, []byte, error) {807// key = simple-key / dotted-key808// simple-key = quoted-key / unquoted-key809//810// unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _811// quoted-key = basic-string / literal-string812// dotted-key = simple-key 1*( dot-sep simple-key )813//814// dot-sep = ws %x2E ws ; . Period815raw, key, b, err := p.parseSimpleKey(b)816if err != nil {817return invalidReference, nil, err818}819820ref := p.builder.Push(Node{821Kind: Key,822Raw: p.Range(raw),823Data: key,824})825826for {827b = p.parseWhitespace(b)828if len(b) > 0 && b[0] == '.' {829b = p.parseWhitespace(b[1:])830831raw, key, b, err = p.parseSimpleKey(b)832if err != nil {833return ref, nil, err834}835836p.builder.PushAndChain(Node{837Kind: Key,838Raw: p.Range(raw),839Data: key,840})841} else {842break843}844}845846return ref, b, nil847}848849func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) {850if len(b) == 0 {851return nil, nil, nil, NewParserError(b, "expected key but found none")852}853854// simple-key = quoted-key / unquoted-key855// unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _856// quoted-key = basic-string / literal-string857switch {858case b[0] == '\'':859return p.parseLiteralString(b)860case b[0] == '"':861return p.parseBasicString(b)862case isUnquotedKeyChar(b[0]):863key, rest = scanUnquotedKey(b)864return key, key, rest, nil865default:866return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0])867}868}869870//nolint:funlen,cyclop871func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) {872// basic-string = quotation-mark *basic-char quotation-mark873// quotation-mark = %x22 ; "874// basic-char = basic-unescaped / escaped875// basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii876// escaped = escape escape-seq-char877// escape-seq-char = %x22 ; " quotation mark U+0022878// escape-seq-char =/ %x5C ; \ reverse solidus U+005C879// escape-seq-char =/ %x62 ; b backspace U+0008880// escape-seq-char =/ %x66 ; f form feed U+000C881// escape-seq-char =/ %x6E ; n line feed U+000A882// escape-seq-char =/ %x72 ; r carriage return U+000D883// escape-seq-char =/ %x74 ; t tab U+0009884// escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX885// escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX886token, escaped, rest, err := scanBasicString(b)887if err != nil {888return nil, nil, nil, err889}890891startIdx := len(`"`)892endIdx := len(token) - len(`"`)893894// Fast path. If there is no escape sequence, the string should just be895// an UTF-8 encoded string, which is the same as Go. In that case,896// validate the string and return a direct reference to the buffer.897if !escaped {898str := token[startIdx:endIdx]899verr := characters.Utf8TomlValidAlreadyEscaped(str)900if verr.Zero() {901return token, str, rest, nil902}903return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8")904}905906i := startIdx907908var builder bytes.Buffer909910// The scanner ensures that the token starts and ends with quotes and that911// escapes are balanced.912for i < len(token)-1 {913c := token[i]914if c == '\\' {915i++916c = token[i]917918switch c {919case '"', '\\':920builder.WriteByte(c)921case 'b':922builder.WriteByte('\b')923case 'f':924builder.WriteByte('\f')925case 'n':926builder.WriteByte('\n')927case 'r':928builder.WriteByte('\r')929case 't':930builder.WriteByte('\t')931case 'e':932builder.WriteByte(0x1B)933case 'u':934x, err := hexToRune(token[i+1:len(token)-1], 4)935if err != nil {936return nil, nil, nil, err937}938939builder.WriteRune(x)940i += 4941case 'U':942x, err := hexToRune(token[i+1:len(token)-1], 8)943if err != nil {944return nil, nil, nil, err945}946947builder.WriteRune(x)948i += 8949default:950return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c)951}952i++953} else {954size := characters.Utf8ValidNext(token[i:])955if size == 0 {956return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c)957}958builder.Write(token[i : i+size])959i += size960}961}962963return token, builder.Bytes(), rest, nil964}965966func hexToRune(b []byte, length int) (rune, error) {967if len(b) < length {968return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b))969}970b = b[:length]971972var r uint32973for i, c := range b {974d := uint32(0)975switch {976case '0' <= c && c <= '9':977d = uint32(c - '0')978case 'a' <= c && c <= 'f':979d = uint32(c - 'a' + 10)980case 'A' <= c && c <= 'F':981d = uint32(c - 'A' + 10)982default:983return -1, NewParserError(b[i:i+1], "non-hex character")984}985r = r*16 + d986}987988if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 {989return -1, NewParserError(b, "escape sequence is invalid Unicode code point")990}991992return rune(r), nil993}994995func (p *Parser) parseWhitespace(b []byte) []byte {996// ws = *wschar997// wschar = %x20 ; Space998// wschar =/ %x09 ; Horizontal tab999_, rest := scanWhitespace(b)10001001return rest1002}10031004//nolint:cyclop1005func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) {1006switch b[0] {1007case 'i':1008if !scanFollowsInf(b) {1009return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'")1010}10111012return p.builder.Push(Node{1013Kind: Float,1014Data: b[:3],1015Raw: p.Range(b[:3]),1016}), b[3:], nil1017case 'n':1018if !scanFollowsNan(b) {1019return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'")1020}10211022return p.builder.Push(Node{1023Kind: Float,1024Data: b[:3],1025Raw: p.Range(b[:3]),1026}), b[3:], nil1027case '+', '-':1028return p.scanIntOrFloat(b)1029}10301031if len(b) < 3 {1032return p.scanIntOrFloat(b)1033}10341035s := 51036if len(b) < s {1037s = len(b)1038}10391040for idx, c := range b[:s] {1041if isDigit(c) {1042continue1043}10441045if idx == 2 && c == ':' || (idx == 4 && c == '-') {1046return p.scanDateTime(b)1047}10481049break1050}10511052return p.scanIntOrFloat(b)1053}10541055func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) {1056// scans for contiguous characters in [0-9T:Z.+-], and up to one space if1057// followed by a digit.1058hasDate := false1059hasTime := false1060hasTz := false1061seenSpace := false10621063i := 01064byteLoop:1065for ; i < len(b); i++ {1066c := b[i]10671068switch {1069case isDigit(c):1070case c == '-':1071hasDate = true1072const minOffsetOfTz = 81073if i >= minOffsetOfTz {1074hasTz = true1075}1076case c == 'T' || c == 't' || c == ':' || c == '.':1077hasTime = true1078case c == '+' || c == '-' || c == 'Z' || c == 'z':1079hasTz = true1080case c == ' ':1081if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) {1082i += 21083// Avoid reaching past the end of the document in case the time1084// is malformed. See TestIssue585.1085if i >= len(b) {1086i--1087}1088seenSpace = true1089hasTime = true1090} else {1091break byteLoop1092}1093default:1094break byteLoop1095}1096}10971098var kind Kind10991100if hasTime {1101if hasDate {1102if hasTz {1103kind = DateTime1104} else {1105kind = LocalDateTime1106}1107} else {1108kind = LocalTime1109}1110} else {1111kind = LocalDate1112}11131114return p.builder.Push(Node{1115Kind: kind,1116Data: b[:i],1117}), b[i:], nil1118}11191120//nolint:funlen,gocognit,cyclop1121func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {1122i := 011231124if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' {1125var isValidRune validRuneFn11261127switch b[1] {1128case 'x':1129isValidRune = isValidHexRune1130case 'o':1131isValidRune = isValidOctalRune1132case 'b':1133isValidRune = isValidBinaryRune1134default:1135i++1136}11371138if isValidRune != nil {1139i += 21140for ; i < len(b); i++ {1141if !isValidRune(b[i]) {1142break1143}1144}1145}11461147return p.builder.Push(Node{1148Kind: Integer,1149Data: b[:i],1150Raw: p.Range(b[:i]),1151}), b[i:], nil1152}11531154isFloat := false11551156for ; i < len(b); i++ {1157c := b[i]11581159if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' {1160continue1161}11621163if c == '.' || c == 'e' || c == 'E' {1164isFloat = true11651166continue1167}11681169if c == 'i' {1170if scanFollowsInf(b[i:]) {1171return p.builder.Push(Node{1172Kind: Float,1173Data: b[:i+3],1174Raw: p.Range(b[:i+3]),1175}), b[i+3:], nil1176}11771178return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number")1179}11801181if c == 'n' {1182if scanFollowsNan(b[i:]) {1183return p.builder.Push(Node{1184Kind: Float,1185Data: b[:i+3],1186Raw: p.Range(b[:i+3]),1187}), b[i+3:], nil1188}11891190return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number")1191}11921193break1194}11951196if i == 0 {1197return invalidReference, b, NewParserError(b, "incomplete number")1198}11991200kind := Integer12011202if isFloat {1203kind = Float1204}12051206return p.builder.Push(Node{1207Kind: kind,1208Data: b[:i],1209Raw: p.Range(b[:i]),1210}), b[i:], nil1211}12121213func isDigit(r byte) bool {1214return r >= '0' && r <= '9'1215}12161217type validRuneFn func(r byte) bool12181219func isValidHexRune(r byte) bool {1220return r >= 'a' && r <= 'f' ||1221r >= 'A' && r <= 'F' ||1222r >= '0' && r <= '9' ||1223r == '_'1224}12251226func isValidOctalRune(r byte) bool {1227return r >= '0' && r <= '7' || r == '_'1228}12291230func isValidBinaryRune(r byte) bool {1231return r == '0' || r == '1' || r == '_'1232}12331234func expect(x byte, b []byte) ([]byte, error) {1235if len(b) == 0 {1236return nil, NewParserError(b, "expected character %c but the document ended here", x)1237}12381239if b[0] != x {1240return nil, NewParserError(b[0:1], "expected character %c", x)1241}12421243return b[1:], nil1244}124512461247