golang.org/x/arch@v0.17.0/x86/xeddata/readlines.go (about)

     1  // Copyright 2025 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 xeddata
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"iter"
    13  	"path/filepath"
    14  	"strings"
    15  )
    16  
    17  type lineInfo struct {
    18  	Pos
    19  	data []byte
    20  }
    21  
    22  type Pos struct {
    23  	Path string
    24  	Line int
    25  }
    26  
    27  func (p Pos) String() string {
    28  	if p.Line == 0 {
    29  		if p.Path == "" {
    30  			return "?:?"
    31  		}
    32  		return p.Path
    33  	} else if p.Path == "" {
    34  		return fmt.Sprintf("?:%d", p.Line)
    35  	}
    36  	return fmt.Sprintf("%s:%d", p.Path, p.Line)
    37  }
    38  
    39  func (p Pos) ShortString() string {
    40  	p2 := p
    41  	p2.Path = filepath.Base(p.Path)
    42  	return p2.String()
    43  }
    44  
    45  // readLines yields lines from r, with continuation lines folded, comments and
    46  // trailing whitespace removed, and blank lines omitted.
    47  //
    48  // The returned lineInfo.data buffer may be reused between yields.
    49  //
    50  // If r has a Name() string method, this is used to populate lineInfo.Path.
    51  func readLines(r io.Reader) iter.Seq2[lineInfo, error] {
    52  	type Named interface {
    53  		Name() string // Matches os.File
    54  	}
    55  	path := ""
    56  	if f, ok := r.(Named); ok {
    57  		path = f.Name()
    58  	}
    59  
    60  	s := bufio.NewScanner(r)
    61  	return func(yield func(lineInfo, error) bool) {
    62  		var info lineInfo
    63  		info.Path = path
    64  		var lineBuf []byte
    65  		for s.Scan() {
    66  			info.Line++
    67  
    68  			lineBuf = append(lineBuf, s.Bytes()...)
    69  			if len(lineBuf) > 0 && lineBuf[len(lineBuf)-1] == '\\' {
    70  				// Continuation line. Drop the \ and keep reading.
    71  				lineBuf = lineBuf[:len(lineBuf)-1]
    72  				continue
    73  			}
    74  			// Remove comments and trailing whitespace
    75  			if i := strings.IndexByte(string(lineBuf), '#'); i >= 0 {
    76  				lineBuf = lineBuf[:i]
    77  			}
    78  			lineBuf = bytes.TrimRight(lineBuf, " \t")
    79  			// Don't yield blank lines
    80  			if len(lineBuf) == 0 {
    81  				continue
    82  			}
    83  
    84  			info.data = lineBuf
    85  			if !yield(info, nil) {
    86  				return
    87  			}
    88  			lineBuf = lineBuf[:0]
    89  		}
    90  
    91  		if err := s.Err(); err != nil {
    92  			yield(lineInfo{}, err)
    93  			return
    94  		}
    95  		if len(lineBuf) > 0 {
    96  			yield(lineInfo{}, fmt.Errorf("continuation line at EOF"))
    97  		}
    98  	}
    99  }