...

Source file src/golang.org/x/arch/x86/xeddata/reader.go

Documentation: golang.org/x/arch/x86/xeddata

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

View as plain text