package scanner

import "text/scanner"

Package scanner 提供 UTF-8 编码文本的扫描器和分词器。 它接受一个 io.Reader 作为输入源,然后可以通过反复调用 Scan 函数对其进行分词。 为了与现有工具兼容,不允许使用 NUL 字符。如果源代码的第一个字符是 UTF-8 编码的 字节顺序标记(BOM),它将被丢弃。

默认情况下,Scanner 会跳过空白字符和 Go 注释,并识别 Go 语言规范中定义的所有字面量。 可以对其进行自定义,以仅识别这些字面量的子集,以及识别不同的标识符和空白字符。

Example
package main

import (
	"fmt"
	"strings"
	"text/scanner"
)

func main() {
	const src = `
// This is scanned code.
if a > 10 {
	someParsable = text
}`

	var s scanner.Scanner
	s.Init(strings.NewReader(src))
	s.Filename = "example"
	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		fmt.Printf("%s: %s\n", s.Position, s.TokenText())
	}

}

Output:

example:3:1: if
example:3:4: a
example:3:6: >
example:3:8: 10
example:3:11: {
example:4:2: someParsable
example:4:15: =
example:4:17: text
example:5:1: }
Example (IsIdentRune)
package main

import (
	"fmt"
	"strings"
	"text/scanner"
	"unicode"
)

func main() {
	const src = "%var1 var2%"

	var s scanner.Scanner
	s.Init(strings.NewReader(src))
	s.Filename = "default"

	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		fmt.Printf("%s: %s\n", s.Position, s.TokenText())
	}

	fmt.Println()
	s.Init(strings.NewReader(src))
	s.Filename = "percent"

	// treat leading '%' as part of an identifier
	s.IsIdentRune = func(ch rune, i int) bool {
		return ch == '%' && i == 0 || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0
	}

	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		fmt.Printf("%s: %s\n", s.Position, s.TokenText())
	}

}

Output:

default:1:1: %
default:1:2: var1
default:1:7: var2
default:1:11: %

percent:1:1: %var1
percent:1:7: var2
percent:1:11: %
Example (Mode)
package main

import (
	"fmt"
	"strings"
	"text/scanner"
)

func main() {
	const src = `
    // Comment begins at column 5.

This line should not be included in the output.

/*
This multiline comment
should be extracted in
its entirety.
*/
`

	var s scanner.Scanner
	s.Init(strings.NewReader(src))
	s.Filename = "comments"
	s.Mode ^= scanner.SkipComments // don't skip comments

	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		txt := s.TokenText()
		if strings.HasPrefix(txt, "//") || strings.HasPrefix(txt, "/*") {
			fmt.Printf("%s: %s\n", s.Position, txt)
		}
	}

}

Output:

comments:2:5: // Comment begins at column 5.
comments:6:1: /*
This multiline comment
should be extracted in
its entirety.
*/
Example (Whitespace)
package main

import (
	"fmt"
	"strings"
	"text/scanner"
)

func main() {
	// tab-separated values
	const src = `aa	ab	ac	ad
ba	bb	bc	bd
ca	cb	cc	cd
da	db	dc	dd`

	var (
		col, row int
		s        scanner.Scanner
		tsv      [4][4]string // large enough for example above
	)
	s.Init(strings.NewReader(src))
	s.Whitespace ^= 1<<'\t' | 1<<'\n' // don't skip tabs and new lines

	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
		switch tok {
		case '\n':
			row++
			col = 0
		case '\t':
			col++
		default:
			tsv[row][col] = s.TokenText()
		}
	}

	fmt.Print(tsv)

}

Output:

[[aa ab ac ad] [ba bb bc bd] [ca cb cc cd] [da db dc dd]]

Index

Examples

Constants

const (
	ScanIdents     = 1 << -Ident
	ScanInts       = 1 << -Int
	ScanFloats     = 1 << -Float // includes Ints and hexadecimal floats
	ScanChars      = 1 << -Char
	ScanStrings    = 1 << -String
	ScanRawStrings = 1 << -RawString
	ScanComments   = 1 << -Comment
	SkipComments   = 1 << -skipComment // if set with ScanComments, comments become white space
	GoTokens       = ScanIdents | ScanFloats | ScanChars | ScanStrings | ScanRawStrings | ScanComments | SkipComments
)

预定义模式位,用于控制词法记号的识别。例如,要配置 Scanner 仅识别 Go 标识符、 整数并跳过注释,请将 Scanner 的 Mode 字段设置为:

ScanIdents | ScanInts | ScanComments | SkipComments

除了注释(在设置 SkipComments 时会被跳过)之外,未识别的词法记号不会被忽略。 相反,扫描器会简单地返回各个单独的字符(或者可能是子记号)。例如,如果模式为 ScanIdents(非 ScanStrings),则字符串 "foo" 会被扫描为词法记号序列 '"' Ident '"'。

使用 GoTokens 配置 Scanner,使其接受所有 Go 字面量词法记号,包括 Go 标识符。 注释将被跳过。

const (
	EOF = -(iota + 1)
	Ident
	Int
	Float
	Char
	String
	RawString
	Comment
)

Scan 的结果是以下词法记号之一或一个 Unicode 字符。

const GoWhitespace = 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' '

GoWhitespace 是 Scanner 的 Whitespace 字段的默认值。 它的值选择 Go 的空白字符。

Functions

func TokenString

func TokenString(tok rune) string

TokenString 返回词法记号或 Unicode 字符的可打印字符串。

Types

type Position

type Position struct {
	Filename string // filename, if any
	Offset   int    // byte offset, starting at 0
	Line     int    // line number, starting at 1
	Column   int    // column number, starting at 1 (character count per line)
}

Position 表示源代码位置的值。 当 Line > 0 时,位置有效。

func (*Position) IsValid

func (pos *Position) IsValid() bool

IsValid 报告位置是否有效。

func (Position) String

func (pos Position) String() string

type Scanner

type Scanner struct {

	// Error 在遇到每个错误时被调用。如果没有设置 Error 函数,
	// 则错误会被报告到 os.Stderr。
	Error func(s *Scanner, msg string)

	// ErrorCount 在遇到每个错误时递增一。
	ErrorCount int

	// Mode 字段控制识别哪些词法记号。例如,要识别整数,
	// 请在 Mode 中设置 ScanInts 位。该字段可随时更改。
	Mode uint

	// Whitespace 字段控制哪些字符被识别为空白字符。要将字符 ch <= ' ' 识别为空白字符,
	// 请在 Whitespace 中设置第 ch 位(Scanner 对于 ch > ' ' 的值的行为是未定义的)。
	// 该字段可随时更改。
	Whitespace uint64

	// IsIdentRune 是一个谓词,控制哪些字符被接受为标识符中第 i 个字符。
	// 有效字符集不得与空白字符集相交。如果未设置 IsIdentRune 函数,
	// 则会接受常规的 Go 标识符。该字段可随时更改。
	IsIdentRune func(ch rune, i int) bool

	// 最近扫描的词法记号的起始位置;由 Scan 设置。
	// 调用 Init 或 Next 会使该位置失效(Line == 0)。
	// Scanner 始终不会修改 Filename 字段。
	// 如果报告了错误(通过 Error)且 Position 无效,则扫描器不在词法记号内。
	// 在这种情况下,调用 Pos 获取错误位置,或者获取最近扫描的词法记号之后的位置。
	Position
	// contains filtered or unexported fields
}

Scanner 实现了从 io.Reader 读取 Unicode 字符和词法记号。

func (*Scanner) Init

func (s *Scanner) Init(src io.Reader) *Scanner

Init 使用新的源初始化 Scanner 并返回 s。 Scanner.Error 被设置为 nil,Scanner.ErrorCount 被设置为 0, Scanner.Mode 被设置为 GoTokensScanner.Whitespace 被设置为 GoWhitespace

func (*Scanner) Next

func (s *Scanner) Next() rune

Next 读取并返回下一个 Unicode 字符。 在源结束时返回 EOF。如果 s.Error 不为 nil,它会通过调用 s.Error 报告读取错误; 否则打印错误消息到 os.Stderr。Next 不更新 [Scanner.Position] 字段; 使用 Scanner.Pos() 获取当前位置。

func (*Scanner) Peek

func (s *Scanner) Peek() rune

Peek 返回源中的下一个 Unicode 字符,但不推进扫描器。 如果扫描器位置在源的最后一个字符处,则返回 EOF

func (*Scanner) Pos

func (s *Scanner) Pos() (pos Position)

Pos 返回上一次调用 Scanner.NextScanner.Scan 所返回的字符或词法记号 之后那个字符的位置。使用 [Scanner.Position] 字段获取最近扫描的词法记号的起始位置。

func (*Scanner) Scan

func (s *Scanner) Scan() rune

Scan 从源中读取下一个词法记号或 Unicode 字符并返回它。 它只识别设置了相应 Scanner.Mode 位 (1<<-t) 的词法记号 t。 在源结束时返回 EOF。如果 s.Error 不为 nil,它通过调用 s.Error 报告 扫描器错误(读取错误和词法记号错误);否则打印错误消息到 os.Stderr

func (*Scanner) TokenText

func (s *Scanner) TokenText() string

TokenText 返回最近扫描的词法记号对应的字符串。 在调用 Scanner.Scan 之后以及在 Scanner.Error 调用中有效。