Source file src/text/scanner/scanner.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package scanner provides a scanner and tokenizer for UTF-8-encoded text.
     6  // It takes an io.Reader providing the source, which then can be tokenized
     7  // through repeated calls to the Scan function. For compatibility with
     8  // existing tools, the NUL character is not allowed. If the first character
     9  // in the source is a UTF-8 encoded byte order mark (BOM), it is discarded.
    10  //
    11  // By default, a [Scanner] skips white space and Go comments and recognizes all
    12  // literals as defined by the Go language specification. It may be
    13  // customized to recognize only a subset of those literals and to recognize
    14  // different identifier and white space characters.
    15  package scanner
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"unicode"
    23  	"unicode/utf8"
    24  )
    25  
    26  // Position is a value that represents a source position.
    27  // A position is valid if Line > 0.
    28  type Position struct {
    29  	Filename string // filename, if any
    30  	Offset   int    // byte offset, starting at 0
    31  	Line     int    // line number, starting at 1
    32  	Column   int    // column number, starting at 1 (character count per line)
    33  }
    34  
    35  // IsValid reports whether the position is valid.
    36  func (pos *Position) IsValid() bool { return pos.Line > 0 }
    37  
    38  func (pos Position) String() string {
    39  	s := pos.Filename
    40  	if s == "" {
    41  		s = "<input>"
    42  	}
    43  	if pos.IsValid() {
    44  		s += fmt.Sprintf(":%d:%d", pos.Line, pos.Column)
    45  	}
    46  	return s
    47  }
    48  
    49  // Predefined mode bits to control recognition of tokens. For instance,
    50  // to configure a [Scanner] such that it only recognizes (Go) identifiers,
    51  // integers, and skips comments, set the Scanner's Mode field to:
    52  //
    53  //	ScanIdents | ScanInts | SkipComments
    54  //
    55  // With the exceptions of comments, which are skipped if SkipComments is
    56  // set, unrecognized tokens are not ignored. Instead, the scanner simply
    57  // returns the respective individual characters (or possibly sub-tokens).
    58  // For instance, if the mode is ScanIdents (not ScanStrings), the string
    59  // "foo" is scanned as the token sequence '"' [Ident] '"'.
    60  //
    61  // Use GoTokens to configure the Scanner such that it accepts all Go
    62  // literal tokens including Go identifiers. Comments will be skipped.
    63  const (
    64  	ScanIdents     = 1 << -Ident
    65  	ScanInts       = 1 << -Int
    66  	ScanFloats     = 1 << -Float // includes Ints and hexadecimal floats
    67  	ScanChars      = 1 << -Char
    68  	ScanStrings    = 1 << -String
    69  	ScanRawStrings = 1 << -RawString
    70  	ScanComments   = 1 << -Comment
    71  	SkipComments   = 1 << -skipComment // if set with ScanComments, comments become white space
    72  	GoTokens       = ScanIdents | ScanFloats | ScanChars | ScanStrings | ScanRawStrings | ScanComments | SkipComments
    73  )
    74  
    75  // The result of Scan is one of these tokens or a Unicode character.
    76  const (
    77  	EOF = -(iota + 1)
    78  	Ident
    79  	Int
    80  	Float
    81  	Char
    82  	String
    83  	RawString
    84  	Comment
    85  
    86  	// internal use only
    87  	skipComment
    88  )
    89  
    90  var tokenString = map[rune]string{
    91  	EOF:       "EOF",
    92  	Ident:     "Ident",
    93  	Int:       "Int",
    94  	Float:     "Float",
    95  	Char:      "Char",
    96  	String:    "String",
    97  	RawString: "RawString",
    98  	Comment:   "Comment",
    99  }
   100  
   101  // TokenString returns a printable string for a token or Unicode character.
   102  func TokenString(tok rune) string {
   103  	if s, found := tokenString[tok]; found {
   104  		return s
   105  	}
   106  	return fmt.Sprintf("%q", string(tok))
   107  }
   108  
   109  // GoWhitespace is the default value for the [Scanner]'s Whitespace field.
   110  // Its value selects Go's white space characters.
   111  const GoWhitespace = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '
   112  
   113  const bufLen = 1024 // at least utf8.UTFMax
   114  
   115  // A Scanner implements reading of Unicode characters and tokens from an [io.Reader].
   116  type Scanner struct {
   117  	// Input
   118  	src io.Reader
   119  
   120  	// Source buffer
   121  	srcBuf [bufLen + 1]byte // +1 for sentinel for common case of s.next()
   122  	srcPos int              // reading position (srcBuf index)
   123  	srcEnd int              // source end (srcBuf index)
   124  
   125  	// Source position
   126  	srcBufOffset int // byte offset of srcBuf[0] in source
   127  	line         int // line count
   128  	column       int // character count
   129  	lastLineLen  int // length of last line in characters (for correct column reporting)
   130  	lastCharLen  int // length of last character in bytes
   131  
   132  	// Token text buffer
   133  	// Typically, token text is stored completely in srcBuf, but in general
   134  	// the token text's head may be buffered in tokBuf while the token text's
   135  	// tail is stored in srcBuf.
   136  	tokBuf bytes.Buffer // token text head that is not in srcBuf anymore
   137  	tokPos int          // token text tail position (srcBuf index); valid if >= 0
   138  	tokEnd int          // token text tail end (srcBuf index)
   139  
   140  	// One character look-ahead
   141  	ch rune // character before current srcPos
   142  
   143  	// Error is called for each error encountered. If no Error
   144  	// function is set, the error is reported to os.Stderr.
   145  	Error func(s *Scanner, msg string)
   146  
   147  	// ErrorCount is incremented by one for each error encountered.
   148  	ErrorCount int
   149  
   150  	// The Mode field controls which tokens are recognized. For instance,
   151  	// to recognize Ints, set the ScanInts bit in Mode. The field may be
   152  	// changed at any time.
   153  	Mode uint
   154  
   155  	// The Whitespace field controls which characters are recognized
   156  	// as white space. To recognize a character ch <= ' ' as white space,
   157  	// set the ch'th bit in Whitespace (the Scanner's behavior is undefined
   158  	// for values ch > ' '). The field may be changed at any time.
   159  	Whitespace uint64
   160  
   161  	// IsIdentRune is a predicate controlling the characters accepted
   162  	// as the ith rune in an identifier. The set of valid characters
   163  	// must not intersect with the set of white space characters.
   164  	// If no IsIdentRune function is set, regular Go identifiers are
   165  	// accepted instead. The field may be changed at any time.
   166  	IsIdentRune func(ch rune, i int) bool
   167  
   168  	// Start position of most recently scanned token; set by Scan.
   169  	// Calling Init or Next invalidates the position (Line == 0).
   170  	// The Filename field is always left untouched by the Scanner.
   171  	// If an error is reported (via Error) and Position is invalid,
   172  	// the scanner is not inside a token. Call Pos to obtain an error
   173  	// position in that case, or to obtain the position immediately
   174  	// after the most recently scanned token.
   175  	Position
   176  }
   177  
   178  // Init initializes a [Scanner] with a new source and returns s.
   179  // [Scanner.Error] is set to nil, [Scanner.ErrorCount] is set to 0, [Scanner.Mode] is set to [GoTokens],
   180  // and [Scanner.Whitespace] is set to [GoWhitespace].
   181  func (s *Scanner) Init(src io.Reader) *Scanner {
   182  	s.src = src
   183  
   184  	// initialize source buffer
   185  	// (the first call to next() will fill it by calling src.Read)
   186  	s.srcBuf[0] = utf8.RuneSelf // sentinel
   187  	s.srcPos = 0
   188  	s.srcEnd = 0
   189  
   190  	// initialize source position
   191  	s.srcBufOffset = 0
   192  	s.line = 1
   193  	s.column = 0
   194  	s.lastLineLen = 0
   195  	s.lastCharLen = 0
   196  
   197  	// initialize token text buffer
   198  	// (required for first call to next()).
   199  	s.tokPos = -1
   200  
   201  	// initialize one character look-ahead
   202  	s.ch = -2 // no char read yet, not EOF
   203  
   204  	// initialize public fields
   205  	s.Error = nil
   206  	s.ErrorCount = 0
   207  	s.Mode = GoTokens
   208  	s.Whitespace = GoWhitespace
   209  	s.Line = 0 // invalidate token position
   210  
   211  	return s
   212  }
   213  
   214  // next reads and returns the next Unicode character. It is designed such
   215  // that only a minimal amount of work needs to be done in the common ASCII
   216  // case (one test to check for both ASCII and end-of-buffer, and one test
   217  // to check for newlines).
   218  func (s *Scanner) next() rune {
   219  	ch, width := rune(s.srcBuf[s.srcPos]), 1
   220  
   221  	if ch >= utf8.RuneSelf {
   222  		// uncommon case: not ASCII or not enough bytes
   223  		for s.srcPos+utf8.UTFMax > s.srcEnd && !utf8.FullRune(s.srcBuf[s.srcPos:s.srcEnd]) {
   224  			// not enough bytes: read some more, but first
   225  			// save away token text if any
   226  			if s.tokPos >= 0 {
   227  				s.tokBuf.Write(s.srcBuf[s.tokPos:s.srcPos])
   228  				s.tokPos = 0
   229  				// s.tokEnd is set by Scan()
   230  			}
   231  			// move unread bytes to beginning of buffer
   232  			copy(s.srcBuf[0:], s.srcBuf[s.srcPos:s.srcEnd])
   233  			s.srcBufOffset += s.srcPos
   234  			// read more bytes
   235  			// (an io.Reader must return io.EOF when it reaches
   236  			// the end of what it is reading - simply returning
   237  			// n == 0 will make this loop retry forever; but the
   238  			// error is in the reader implementation in that case)
   239  			i := s.srcEnd - s.srcPos
   240  			n, err := s.src.Read(s.srcBuf[i:bufLen])
   241  			s.srcPos = 0
   242  			s.srcEnd = i + n
   243  			s.srcBuf[s.srcEnd] = utf8.RuneSelf // sentinel
   244  			if err != nil {
   245  				if err != io.EOF {
   246  					s.error(err.Error())
   247  				}
   248  				if s.srcEnd == 0 {
   249  					if s.lastCharLen > 0 {
   250  						// previous character was not EOF
   251  						s.column++
   252  					}
   253  					s.lastCharLen = 0
   254  					return EOF
   255  				}
   256  				// If err == EOF, we won't be getting more
   257  				// bytes; break to avoid infinite loop. If
   258  				// err is something else, we don't know if
   259  				// we can get more bytes; thus also break.
   260  				break
   261  			}
   262  		}
   263  		// at least one byte
   264  		ch = rune(s.srcBuf[s.srcPos])
   265  		if ch >= utf8.RuneSelf {
   266  			// uncommon case: not ASCII
   267  			ch, width = utf8.DecodeRune(s.srcBuf[s.srcPos:s.srcEnd])
   268  			if ch == utf8.RuneError && width == 1 {
   269  				// advance for correct error position
   270  				s.srcPos += width
   271  				s.lastCharLen = width
   272  				s.column++
   273  				s.error("invalid UTF-8 encoding")
   274  				return ch
   275  			}
   276  		}
   277  	}
   278  
   279  	// advance
   280  	s.srcPos += width
   281  	s.lastCharLen = width
   282  	s.column++
   283  
   284  	// special situations
   285  	switch ch {
   286  	case 0:
   287  		// for compatibility with other tools
   288  		s.error("invalid character NUL")
   289  	case '\n':
   290  		s.line++
   291  		s.lastLineLen = s.column
   292  		s.column = 0
   293  	}
   294  
   295  	return ch
   296  }
   297  
   298  // Next reads and returns the next Unicode character.
   299  // It returns [EOF] at the end of the source. It reports
   300  // a read error by calling s.Error, if not nil; otherwise
   301  // it prints an error message to [os.Stderr]. Next does not
   302  // update the [Scanner.Position] field; use [Scanner.Pos]() to
   303  // get the current position.
   304  func (s *Scanner) Next() rune {
   305  	s.tokPos = -1 // don't collect token text
   306  	s.Line = 0    // invalidate token position
   307  	ch := s.Peek()
   308  	if ch != EOF {
   309  		s.ch = s.next()
   310  	}
   311  	return ch
   312  }
   313  
   314  // Peek returns the next Unicode character in the source without advancing
   315  // the scanner. It returns [EOF] if the scanner's position is at the last
   316  // character of the source.
   317  func (s *Scanner) Peek() rune {
   318  	if s.ch == -2 {
   319  		// this code is only run for the very first character
   320  		s.ch = s.next()
   321  		if s.ch == '\uFEFF' {
   322  			s.ch = s.next() // ignore BOM
   323  		}
   324  	}
   325  	return s.ch
   326  }
   327  
   328  func (s *Scanner) error(msg string) {
   329  	s.tokEnd = s.srcPos - s.lastCharLen // make sure token text is terminated
   330  	s.ErrorCount++
   331  	if s.Error != nil {
   332  		s.Error(s, msg)
   333  		return
   334  	}
   335  	pos := s.Position
   336  	if !pos.IsValid() {
   337  		pos = s.Pos()
   338  	}
   339  	fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg)
   340  }
   341  
   342  func (s *Scanner) errorf(format string, args ...any) {
   343  	s.error(fmt.Sprintf(format, args...))
   344  }
   345  
   346  func (s *Scanner) isIdentRune(ch rune, i int) bool {
   347  	if s.IsIdentRune != nil {
   348  		return ch != EOF && s.IsIdentRune(ch, i)
   349  	}
   350  	return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0
   351  }
   352  
   353  func (s *Scanner) scanIdentifier() rune {
   354  	// we know the zero'th rune is OK; start scanning at the next one
   355  	ch := s.next()
   356  	for i := 1; s.isIdentRune(ch, i); i++ {
   357  		ch = s.next()
   358  	}
   359  	return ch
   360  }
   361  
   362  func lower(ch rune) rune     { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
   363  func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
   364  func isHex(ch rune) bool     { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }
   365  
   366  // digits accepts the sequence { digit | '_' } starting with ch0.
   367  // If base <= 10, digits accepts any decimal digit but records
   368  // the first invalid digit >= base in *invalid if *invalid == 0.
   369  // digits returns the first rune that is not part of the sequence
   370  // anymore, and a bitset describing whether the sequence contained
   371  // digits (bit 0 is set), or separators '_' (bit 1 is set).
   372  func (s *Scanner) digits(ch0 rune, base int, invalid *rune) (ch rune, digsep int) {
   373  	ch = ch0
   374  	if base <= 10 {
   375  		max := rune('0' + base)
   376  		for isDecimal(ch) || ch == '_' {
   377  			ds := 1
   378  			if ch == '_' {
   379  				ds = 2
   380  			} else if ch >= max && *invalid == 0 {
   381  				*invalid = ch
   382  			}
   383  			digsep |= ds
   384  			ch = s.next()
   385  		}
   386  	} else {
   387  		for isHex(ch) || ch == '_' {
   388  			ds := 1
   389  			if ch == '_' {
   390  				ds = 2
   391  			}
   392  			digsep |= ds
   393  			ch = s.next()
   394  		}
   395  	}
   396  	return
   397  }
   398  
   399  func (s *Scanner) scanNumber(ch rune, seenDot bool) (rune, rune) {
   400  	base := 10         // number base
   401  	prefix := rune(0)  // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
   402  	digsep := 0        // bit 0: digit present, bit 1: '_' present
   403  	invalid := rune(0) // invalid digit in literal, or 0
   404  
   405  	// integer part
   406  	var tok rune
   407  	var ds int
   408  	if !seenDot {
   409  		tok = Int
   410  		if ch == '0' {
   411  			ch = s.next()
   412  			switch lower(ch) {
   413  			case 'x':
   414  				ch = s.next()
   415  				base, prefix = 16, 'x'
   416  			case 'o':
   417  				ch = s.next()
   418  				base, prefix = 8, 'o'
   419  			case 'b':
   420  				ch = s.next()
   421  				base, prefix = 2, 'b'
   422  			default:
   423  				base, prefix = 8, '0'
   424  				digsep = 1 // leading 0
   425  			}
   426  		}
   427  		ch, ds = s.digits(ch, base, &invalid)
   428  		digsep |= ds
   429  		if ch == '.' && s.Mode&ScanFloats != 0 {
   430  			ch = s.next()
   431  			seenDot = true
   432  		}
   433  	}
   434  
   435  	// fractional part
   436  	if seenDot {
   437  		tok = Float
   438  		if prefix == 'o' || prefix == 'b' {
   439  			s.error("invalid radix point in " + litname(prefix))
   440  		}
   441  		ch, ds = s.digits(ch, base, &invalid)
   442  		digsep |= ds
   443  	}
   444  
   445  	if digsep&1 == 0 {
   446  		s.error(litname(prefix) + " has no digits")
   447  	}
   448  
   449  	// exponent
   450  	if e := lower(ch); (e == 'e' || e == 'p') && s.Mode&ScanFloats != 0 {
   451  		switch {
   452  		case e == 'e' && prefix != 0 && prefix != '0':
   453  			s.errorf("%q exponent requires decimal mantissa", ch)
   454  		case e == 'p' && prefix != 'x':
   455  			s.errorf("%q exponent requires hexadecimal mantissa", ch)
   456  		}
   457  		ch = s.next()
   458  		tok = Float
   459  		if ch == '+' || ch == '-' {
   460  			ch = s.next()
   461  		}
   462  		ch, ds = s.digits(ch, 10, nil)
   463  		digsep |= ds
   464  		if ds&1 == 0 {
   465  			s.error("exponent has no digits")
   466  		}
   467  	} else if prefix == 'x' && tok == Float {
   468  		s.error("hexadecimal mantissa requires a 'p' exponent")
   469  	}
   470  
   471  	if tok == Int && invalid != 0 {
   472  		s.errorf("invalid digit %q in %s", invalid, litname(prefix))
   473  	}
   474  
   475  	if digsep&2 != 0 {
   476  		s.tokEnd = s.srcPos - s.lastCharLen // make sure token text is terminated
   477  		if i := invalidSep(s.TokenText()); i >= 0 {
   478  			s.error("'_' must separate successive digits")
   479  		}
   480  	}
   481  
   482  	return tok, ch
   483  }
   484  
   485  func litname(prefix rune) string {
   486  	switch prefix {
   487  	default:
   488  		return "decimal literal"
   489  	case 'x':
   490  		return "hexadecimal literal"
   491  	case 'o', '0':
   492  		return "octal literal"
   493  	case 'b':
   494  		return "binary literal"
   495  	}
   496  }
   497  
   498  // invalidSep returns the index of the first invalid separator in x, or -1.
   499  func invalidSep(x string) int {
   500  	x1 := ' ' // prefix char, we only care if it's 'x'
   501  	d := '.'  // digit, one of '_', '0' (a digit), or '.' (anything else)
   502  	i := 0
   503  
   504  	// a prefix counts as a digit
   505  	if len(x) >= 2 && x[0] == '0' {
   506  		x1 = lower(rune(x[1]))
   507  		if x1 == 'x' || x1 == 'o' || x1 == 'b' {
   508  			d = '0'
   509  			i = 2
   510  		}
   511  	}
   512  
   513  	// mantissa and exponent
   514  	for ; i < len(x); i++ {
   515  		p := d // previous digit
   516  		d = rune(x[i])
   517  		switch {
   518  		case d == '_':
   519  			if p != '0' {
   520  				return i
   521  			}
   522  		case isDecimal(d) || x1 == 'x' && isHex(d):
   523  			d = '0'
   524  		default:
   525  			if p == '_' {
   526  				return i - 1
   527  			}
   528  			d = '.'
   529  		}
   530  	}
   531  	if d == '_' {
   532  		return len(x) - 1
   533  	}
   534  
   535  	return -1
   536  }
   537  
   538  func digitVal(ch rune) int {
   539  	switch {
   540  	case '0' <= ch && ch <= '9':
   541  		return int(ch - '0')
   542  	case 'a' <= lower(ch) && lower(ch) <= 'f':
   543  		return int(lower(ch) - 'a' + 10)
   544  	}
   545  	return 16 // larger than any legal digit val
   546  }
   547  
   548  func (s *Scanner) scanDigits(ch rune, base, n int) rune {
   549  	for n > 0 && digitVal(ch) < base {
   550  		ch = s.next()
   551  		n--
   552  	}
   553  	if n > 0 {
   554  		s.error("invalid char escape")
   555  	}
   556  	return ch
   557  }
   558  
   559  func (s *Scanner) scanEscape(quote rune) rune {
   560  	ch := s.next() // read character after '/'
   561  	switch ch {
   562  	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
   563  		// nothing to do
   564  		ch = s.next()
   565  	case '0', '1', '2', '3', '4', '5', '6', '7':
   566  		ch = s.scanDigits(ch, 8, 3)
   567  	case 'x':
   568  		ch = s.scanDigits(s.next(), 16, 2)
   569  	case 'u':
   570  		ch = s.scanDigits(s.next(), 16, 4)
   571  	case 'U':
   572  		ch = s.scanDigits(s.next(), 16, 8)
   573  	default:
   574  		s.error("invalid char escape")
   575  	}
   576  	return ch
   577  }
   578  
   579  func (s *Scanner) scanString(quote rune) (n int) {
   580  	ch := s.next() // read character after quote
   581  	for ch != quote {
   582  		if ch == '\n' || ch < 0 {
   583  			s.error("literal not terminated")
   584  			return
   585  		}
   586  		if ch == '\\' {
   587  			ch = s.scanEscape(quote)
   588  		} else {
   589  			ch = s.next()
   590  		}
   591  		n++
   592  	}
   593  	return
   594  }
   595  
   596  func (s *Scanner) scanRawString() {
   597  	ch := s.next() // read character after '`'
   598  	for ch != '`' {
   599  		if ch < 0 {
   600  			s.error("literal not terminated")
   601  			return
   602  		}
   603  		ch = s.next()
   604  	}
   605  }
   606  
   607  func (s *Scanner) scanChar() {
   608  	if s.scanString('\'') != 1 {
   609  		s.error("invalid char literal")
   610  	}
   611  }
   612  
   613  func (s *Scanner) scanComment(ch rune) rune {
   614  	// ch == '/' || ch == '*'
   615  	if ch == '/' {
   616  		// line comment
   617  		ch = s.next() // read character after "//"
   618  		for ch != '\n' && ch >= 0 {
   619  			ch = s.next()
   620  		}
   621  		return ch
   622  	}
   623  
   624  	// general comment
   625  	ch = s.next() // read character after "/*"
   626  	for {
   627  		if ch < 0 {
   628  			s.error("comment not terminated")
   629  			break
   630  		}
   631  		ch0 := ch
   632  		ch = s.next()
   633  		if ch0 == '*' && ch == '/' {
   634  			ch = s.next()
   635  			break
   636  		}
   637  	}
   638  	return ch
   639  }
   640  
   641  // Scan reads the next token or Unicode character from source and returns it.
   642  // It only recognizes tokens t for which the respective [Scanner.Mode] bit (1<<-t) is set.
   643  // It returns [EOF] at the end of the source. It reports scanner errors (read and
   644  // token errors) by calling s.Error, if not nil; otherwise it prints an error
   645  // message to [os.Stderr].
   646  func (s *Scanner) Scan() rune {
   647  	ch := s.Peek()
   648  
   649  	// reset token text position
   650  	s.tokPos = -1
   651  	s.Line = 0
   652  
   653  redo:
   654  	// skip white space
   655  	for s.Whitespace&(1<<uint(ch)) != 0 {
   656  		ch = s.next()
   657  	}
   658  
   659  	// start collecting token text
   660  	s.tokBuf.Reset()
   661  	s.tokPos = s.srcPos - s.lastCharLen
   662  
   663  	// set token position
   664  	// (this is a slightly optimized version of the code in Pos())
   665  	s.Offset = s.srcBufOffset + s.tokPos
   666  	if s.column > 0 {
   667  		// common case: last character was not a '\n'
   668  		s.Line = s.line
   669  		s.Column = s.column
   670  	} else {
   671  		// last character was a '\n'
   672  		// (we cannot be at the beginning of the source
   673  		// since we have called next() at least once)
   674  		s.Line = s.line - 1
   675  		s.Column = s.lastLineLen
   676  	}
   677  
   678  	// determine token value
   679  	tok := ch
   680  	switch {
   681  	case s.isIdentRune(ch, 0):
   682  		if s.Mode&ScanIdents != 0 {
   683  			tok = Ident
   684  			ch = s.scanIdentifier()
   685  		} else {
   686  			ch = s.next()
   687  		}
   688  	case isDecimal(ch):
   689  		if s.Mode&(ScanInts|ScanFloats) != 0 {
   690  			tok, ch = s.scanNumber(ch, false)
   691  		} else {
   692  			ch = s.next()
   693  		}
   694  	default:
   695  		switch ch {
   696  		case EOF:
   697  			break
   698  		case '"':
   699  			if s.Mode&ScanStrings != 0 {
   700  				s.scanString('"')
   701  				tok = String
   702  			}
   703  			ch = s.next()
   704  		case '\'':
   705  			if s.Mode&ScanChars != 0 {
   706  				s.scanChar()
   707  				tok = Char
   708  			}
   709  			ch = s.next()
   710  		case '.':
   711  			ch = s.next()
   712  			if isDecimal(ch) && s.Mode&ScanFloats != 0 {
   713  				tok, ch = s.scanNumber(ch, true)
   714  			}
   715  		case '/':
   716  			ch = s.next()
   717  			if (ch == '/' || ch == '*') && s.Mode&ScanComments != 0 {
   718  				if s.Mode&SkipComments != 0 {
   719  					s.tokPos = -1 // don't collect token text
   720  					ch = s.scanComment(ch)
   721  					goto redo
   722  				}
   723  				ch = s.scanComment(ch)
   724  				tok = Comment
   725  			}
   726  		case '`':
   727  			if s.Mode&ScanRawStrings != 0 {
   728  				s.scanRawString()
   729  				tok = RawString
   730  			}
   731  			ch = s.next()
   732  		default:
   733  			ch = s.next()
   734  		}
   735  	}
   736  
   737  	// end of token text
   738  	s.tokEnd = s.srcPos - s.lastCharLen
   739  
   740  	s.ch = ch
   741  	return tok
   742  }
   743  
   744  // Pos returns the position of the character immediately after
   745  // the character or token returned by the last call to [Scanner.Next] or [Scanner.Scan].
   746  // Use the [Scanner.Position] field for the start position of the most
   747  // recently scanned token.
   748  func (s *Scanner) Pos() (pos Position) {
   749  	pos.Filename = s.Filename
   750  	pos.Offset = s.srcBufOffset + s.srcPos - s.lastCharLen
   751  	switch {
   752  	case s.column > 0:
   753  		// common case: last character was not a '\n'
   754  		pos.Line = s.line
   755  		pos.Column = s.column
   756  	case s.lastLineLen > 0:
   757  		// last character was a '\n'
   758  		pos.Line = s.line - 1
   759  		pos.Column = s.lastLineLen
   760  	default:
   761  		// at the beginning of the source
   762  		pos.Line = 1
   763  		pos.Column = 1
   764  	}
   765  	return
   766  }
   767  
   768  // TokenText returns the string corresponding to the most recently scanned token.
   769  // Valid after calling [Scanner.Scan] and in calls of [Scanner.Error].
   770  func (s *Scanner) TokenText() string {
   771  	if s.tokPos < 0 {
   772  		// no token text
   773  		return ""
   774  	}
   775  
   776  	if s.tokEnd < s.tokPos {
   777  		// if EOF was reached, s.tokEnd is set to -1 (s.srcPos == 0)
   778  		s.tokEnd = s.tokPos
   779  	}
   780  	// s.tokEnd >= s.tokPos
   781  
   782  	if s.tokBuf.Len() == 0 {
   783  		// common case: the entire token text is still in srcBuf
   784  		return string(s.srcBuf[s.tokPos:s.tokEnd])
   785  	}
   786  
   787  	// part of the token text was saved in tokBuf: save the rest in
   788  	// tokBuf as well and return its content
   789  	s.tokBuf.Write(s.srcBuf[s.tokPos:s.tokEnd])
   790  	s.tokPos = s.tokEnd // ensure idempotency of TokenText() call
   791  	return s.tokBuf.String()
   792  }
   793  

View as plain text