--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bufio"
+ "io"
+)
+
+// NamedScanner is a simple struct to pair a name with a Scanner.
+type NamedScanner struct {
+ Name string
+ Scanner *bufio.Scanner
+}
+
+// NamedReader is a simple struct to pair a name with a Reader,
+// which will be converted to a Scanner using bufio.NewScanner.
+type NamedReader struct {
+ Name string
+ Reader io.Reader
+}
+
+// MultiScanner scans over multiple bufio.Scanners as if they were a single stream.
+// It also keeps track of the name of the current scanner and the line number.
+type MultiScanner struct {
+ scanners []NamedScanner
+ scannerIdx int
+ line int
+ totalLine int
+ err error
+}
+
+// NewMultiScanner creates a new MultiScanner from slice of NamedScanners.
+func NewMultiScanner(scanners []NamedScanner) *MultiScanner {
+ return &MultiScanner{
+ scanners: scanners,
+ scannerIdx: -1, // Start before the first scanner
+ }
+}
+
+// MultiScannerFromReaders creates a new MultiScanner from a slice of NamedReaders.
+func MultiScannerFromReaders(readers []NamedReader) *MultiScanner {
+ var scanners []NamedScanner
+ for _, r := range readers {
+ scanners = append(scanners, NamedScanner{
+ Name: r.Name,
+ Scanner: bufio.NewScanner(r.Reader),
+ })
+ }
+ return NewMultiScanner(scanners)
+}
+
+// Scan advances the scanner to the next token, which will then be
+// available through the Text method. It returns false when the scan stops,
+// either by reaching the end of the input or an error.
+// After Scan returns false, the Err method will return any error that
+// occurred during scanning, except that if it was io.EOF, Err
+// will return nil.
+func (ms *MultiScanner) Scan() bool {
+ if ms.scannerIdx == -1 {
+ ms.scannerIdx = 0
+ }
+
+ for ms.scannerIdx < len(ms.scanners) {
+ current := ms.scanners[ms.scannerIdx]
+ if current.Scanner.Scan() {
+ ms.line++
+ ms.totalLine++
+ return true
+ }
+ if err := current.Scanner.Err(); err != nil {
+ ms.err = err
+ return false
+ }
+ // Move to the next scanner
+ ms.scannerIdx++
+ ms.line = 0
+ }
+
+ return false
+}
+
+// Text returns the most recent token generated by a call to Scan.
+func (ms *MultiScanner) Text() string {
+ if ms.scannerIdx < 0 || ms.scannerIdx >= len(ms.scanners) {
+ return ""
+ }
+ return ms.scanners[ms.scannerIdx].Scanner.Text()
+}
+
+// Err returns the first non-EOF error that was encountered by the MultiScanner.
+func (ms *MultiScanner) Err() error {
+ return ms.err
+}
+
+// Name returns the name of the current scanner.
+func (ms *MultiScanner) Name() string {
+ if ms.scannerIdx < 0 {
+ return "<before first>"
+ }
+ if ms.scannerIdx >= len(ms.scanners) {
+ return "<after last>"
+ }
+ return ms.scanners[ms.scannerIdx].Name
+}
+
+// Line returns the current line number within the current scanner.
+func (ms *MultiScanner) Line() int {
+ return ms.line
+}
+
+// TotalLine returns the total number of lines scanned across all scanners.
+func (ms *MultiScanner) TotalLine() int {
+ return ms.totalLine
+}
func genLateLowerRules(arch arch) { genRulesSuffix(arch, "latelower") }
func genRulesSuffix(arch arch, suff string) {
+ var readers []NamedReader
// Open input file.
var text io.Reader
- text, err := os.Open(arch.name + suff + ".rules")
+ name := arch.name + suff + ".rules"
+ text, err := os.Open(name)
if err != nil {
if suff == "" {
// All architectures must have a plain rules file.
// Some architectures have bonus rules files that others don't share. That's fine.
return
}
+ readers = append(readers, NamedReader{name, text})
// Check for file of SIMD rules to add
if suff == "" {
- simdtext, err := os.Open("simd" + arch.name + ".rules")
+ simdname := "simd" + arch.name + ".rules"
+ simdtext, err := os.Open(simdname)
if err == nil {
- text = io.MultiReader(text, simdtext)
+ readers = append(readers, NamedReader{simdname, simdtext})
}
}
oprules := map[string][]Rule{}
// read rule file
- scanner := bufio.NewScanner(text)
+ scanner := MultiScannerFromReaders(readers)
rule := ""
var lineno int
var ruleLineno int // line number of "=>"
for scanner.Scan() {
- lineno++
+ lineno = scanner.Line()
line := scanner.Text()
if i := strings.Index(line, "//"); i >= 0 {
// Remove comments. Note that this isn't string safe, so
break // continuing the line can't help, and it will only make errors worse
}
- loc := fmt.Sprintf("%s%s.rules:%d", arch.name, suff, ruleLineno)
+ loc := fmt.Sprintf("%s:%d", scanner.Name(), ruleLineno)
for _, rule2 := range expandOr(rule) {
r := Rule{Rule: rule2, Loc: loc}
if rawop := strings.Split(rule2, " ")[0][1:]; isBlock(rawop, arch) {
log.Fatalf("scanner failed: %v\n", err)
}
if balance(rule) != 0 {
- log.Fatalf("%s.rules:%d: unbalanced rule: %v\n", arch.name, lineno, rule)
+ log.Fatalf("%s:%d: unbalanced rule: %v\n", scanner.Name(), lineno, rule)
}
// Order all the ops.