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

     1  // Copyright 2018 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  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"iter"
    12  	"regexp"
    13  	"strings"
    14  )
    15  
    16  // Reader reads enc/dec-instruction objects from XED datafile.
    17  type Reader struct {
    18  	r io.Reader
    19  
    20  	// Initialized on first call to Read
    21  	next func() (*Object, error, bool)
    22  	stop func()
    23  	err  error
    24  }
    25  
    26  // NewReader returns a new Reader that reads from r.
    27  func NewReader(r io.Reader) *Reader {
    28  	return &Reader{r: r}
    29  }
    30  
    31  // Read reads single XED instruction object from
    32  // the stream backed by reader.
    33  //
    34  // If there is no data left to be read,
    35  // returned error is io.EOF.
    36  func (r *Reader) Read() (*Object, error) {
    37  	if r.err != nil {
    38  		return nil, r.err
    39  	}
    40  	if r.next == nil {
    41  		r.next, r.stop = iter.Pull2(readObjects(r.r))
    42  	}
    43  	obj, err, end := r.next()
    44  	if end {
    45  		err = io.EOF
    46  	}
    47  	if err != nil {
    48  		r.stop()
    49  		r.err, r.next, r.stop = err, nil, nil
    50  		return nil, err
    51  	}
    52  	return obj, nil
    53  }
    54  
    55  // ReadAll reads all the remaining objects from r.
    56  // A successful call returns err == nil, not err == io.EOF,
    57  // just like csv.Reader.ReadAll().
    58  func (r *Reader) ReadAll() ([]*Object, error) {
    59  	var objects []*Object
    60  	for obj, err := range readObjects(r.r) {
    61  		if err != nil {
    62  			return objects, err
    63  		}
    64  		objects = append(objects, obj)
    65  	}
    66  	return objects, nil
    67  }
    68  
    69  // readObjects yields all of the objects from r.
    70  func readObjects(r io.Reader) iter.Seq2[*Object, error] {
    71  	iterLines := readLines(r)
    72  	return func(yield func(*Object, error) bool) {
    73  		var blockPos Pos
    74  		var block []string // Reused on each iteration
    75  		var linePos []Pos
    76  		inBlock := false
    77  		for line, err := range iterLines {
    78  			if err != nil {
    79  				yield(nil, err)
    80  				return
    81  			}
    82  			if !inBlock {
    83  				inBlock = line.data[0] == '{'
    84  				blockPos = line.Pos
    85  			} else if line.data[0] == '}' {
    86  				inBlock = false
    87  				obj, err := parseObjectLines(blockPos, block, linePos)
    88  				if !yield(obj, err) {
    89  					return
    90  				}
    91  				block, linePos = block[:0], linePos[:0]
    92  			} else {
    93  				block = append(block, string(line.data))
    94  				linePos = append(linePos, line.Pos)
    95  			}
    96  		}
    97  		if inBlock {
    98  			yield(nil, errors.New("no matching '}' found"))
    99  		}
   100  	}
   101  }
   102  
   103  // instLineRE matches valid XED object/inst line.
   104  // It expects lines that are joined by '\' to be concatenated.
   105  //
   106  // The format can be described as:
   107  //
   108  //	unquoted field name "[A-Z_]+" (captured)
   109  //	field value delimiter ":"
   110  //	field value string (captured)
   111  var instLineRE = regexp.MustCompile(`^([A-Z_]+)\s*:\s*(.*)`)
   112  
   113  // parseLines turns collected object lines into Object.
   114  func parseObjectLines(blockPos Pos, lines []string, linePos []Pos) (*Object, error) {
   115  	o := &Object{}
   116  	o.Pos = blockPos
   117  
   118  	// Repeatable tokens.
   119  	// We can not assign them eagerly, because these fields
   120  	// are not guaranteed to follow strict order.
   121  	var (
   122  		operands []string
   123  		iforms   []string
   124  		patterns []string
   125  		poses    []Pos
   126  	)
   127  
   128  	for i, l := range lines {
   129  		l = strings.TrimLeft(l, " ")
   130  		if l[0] == '#' { // Skip comment lines.
   131  			continue
   132  		}
   133  		m := instLineRE.FindStringSubmatch(l)
   134  		if len(m) == 0 {
   135  			return nil, fmt.Errorf("malformed line: %s", l)
   136  		}
   137  		key, val := m[1], m[2]
   138  		val = strings.TrimSpace(val)
   139  
   140  		switch key {
   141  		case "ICLASS":
   142  			o.Iclass = val
   143  		case "DISASM":
   144  			o.Disasm = val
   145  		case "DISASM_INTEL":
   146  			o.DisasmIntel = val
   147  		case "DISASM_ATTSV":
   148  			o.DisasmATTSV = val
   149  		case "ATTRIBUTES":
   150  			o.Attributes = val
   151  		case "UNAME":
   152  			o.Uname = val
   153  		case "CPL":
   154  			o.CPL = val
   155  		case "CATEGORY":
   156  			o.Category = val
   157  		case "EXTENSION":
   158  			o.Extension = val
   159  		case "EXCEPTIONS":
   160  			o.Exceptions = val
   161  		case "ISA_SET":
   162  			o.ISASet = val
   163  		case "FLAGS":
   164  			o.Flags = val
   165  		case "COMMENT":
   166  			o.Comment = val
   167  		case "VERSION":
   168  			o.Version = val
   169  		case "REAL_OPCODE":
   170  			o.RealOpcode = val
   171  
   172  		case "OPERANDS":
   173  			operands = append(operands, val)
   174  		case "PATTERN":
   175  			patterns = append(patterns, val)
   176  			poses = append(poses, linePos[i])
   177  		case "IFORM":
   178  			iforms = append(iforms, val)
   179  
   180  		default:
   181  			// Being strict about unknown field names gives a nice
   182  			// XED file validation diagnostics.
   183  			// Also defends against typos in test files.
   184  			return nil, fmt.Errorf("unknown key token: %s", key)
   185  		}
   186  	}
   187  
   188  	if len(operands) != len(patterns) {
   189  		return nil, fmt.Errorf("%s: OPERANDS and PATTERN lines mismatch", o.Opcode())
   190  	}
   191  
   192  	insts := make([]*Inst, len(operands))
   193  	for i := range operands {
   194  		insts[i] = &Inst{
   195  			Object:   o,
   196  			Index:    i,
   197  			Pattern:  patterns[i],
   198  			Pos:      poses[i],
   199  			Operands: operands[i],
   200  		}
   201  		// There can be less IFORMs than insts.
   202  		if i < len(iforms) {
   203  			insts[i].Iform = iforms[i]
   204  		}
   205  	}
   206  	o.Insts = insts
   207  
   208  	return o, nil
   209  }