github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/parse/parser.go (about)

     1  package parse
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"strings"
    10  	"unicode/utf8"
    11  
    12  	"github.com/markusbkk/elvish/pkg/diag"
    13  )
    14  
    15  // parser maintains some mutable states of parsing.
    16  //
    17  // NOTE: The str member is assumed to be valid UF-8.
    18  type parser struct {
    19  	srcName string
    20  	src     string
    21  	pos     int
    22  	overEOF int
    23  	errors  Error
    24  	warn    io.Writer
    25  }
    26  
    27  func (ps *parser) parse(n Node) parsed {
    28  	begin := ps.pos
    29  	n.n().From = begin
    30  	n.parse(ps)
    31  	n.n().To = ps.pos
    32  	n.n().sourceText = ps.src[begin:ps.pos]
    33  	return parsed{n}
    34  }
    35  
    36  type parserState struct {
    37  	pos     int
    38  	overEOF int
    39  	errors  Error
    40  }
    41  
    42  func (ps *parser) save() parserState {
    43  	return parserState{ps.pos, ps.overEOF, ps.errors}
    44  }
    45  
    46  func (ps *parser) restore(s parserState) {
    47  	ps.pos, ps.overEOF, ps.errors = s.pos, s.overEOF, s.errors
    48  }
    49  
    50  var nodeType = reflect.TypeOf((*Node)(nil)).Elem()
    51  
    52  type parsed struct {
    53  	n Node
    54  }
    55  
    56  func (p parsed) addAs(ptr interface{}, parent Node) {
    57  	dst := reflect.ValueOf(ptr).Elem()
    58  	dst.Set(reflect.ValueOf(p.n)) // *ptr = p.n
    59  	addChild(parent, p.n)
    60  }
    61  
    62  func (p parsed) addTo(ptr interface{}, parent Node) {
    63  	dst := reflect.ValueOf(ptr).Elem()
    64  	dst.Set(reflect.Append(dst, reflect.ValueOf(p.n))) // *ptr = append(*ptr, n)
    65  	addChild(parent, p.n)
    66  }
    67  
    68  // Tells the parser that parsing is done.
    69  func (ps *parser) done() {
    70  	if ps.pos != len(ps.src) {
    71  		r, _ := utf8.DecodeRuneInString(ps.src[ps.pos:])
    72  		ps.error(fmt.Errorf("unexpected rune %q", r))
    73  	}
    74  }
    75  
    76  // Assembles all parsing errors as one, or returns nil if there were no errors.
    77  func (ps *parser) assembleError() error {
    78  	if len(ps.errors.Entries) > 0 {
    79  		return &ps.errors
    80  	}
    81  	return nil
    82  }
    83  
    84  const eof rune = -1
    85  
    86  func (ps *parser) peek() rune {
    87  	if ps.pos == len(ps.src) {
    88  		return eof
    89  	}
    90  	r, _ := utf8.DecodeRuneInString(ps.src[ps.pos:])
    91  	return r
    92  }
    93  
    94  func (ps *parser) hasPrefix(prefix string) bool {
    95  	return strings.HasPrefix(ps.src[ps.pos:], prefix)
    96  }
    97  
    98  func (ps *parser) next() rune {
    99  	if ps.pos == len(ps.src) {
   100  		ps.overEOF++
   101  		return eof
   102  	}
   103  	r, s := utf8.DecodeRuneInString(ps.src[ps.pos:])
   104  	ps.pos += s
   105  	return r
   106  }
   107  
   108  func (ps *parser) backup() {
   109  	if ps.overEOF > 0 {
   110  		ps.overEOF--
   111  		return
   112  	}
   113  	_, s := utf8.DecodeLastRuneInString(ps.src[:ps.pos])
   114  	ps.pos -= s
   115  }
   116  
   117  func (ps *parser) errorp(r diag.Ranger, e error) {
   118  	ps.errors.add(e.Error(), diag.NewContext(ps.srcName, ps.src, r))
   119  }
   120  
   121  func (ps *parser) error(e error) {
   122  	end := ps.pos
   123  	if end < len(ps.src) {
   124  		end++
   125  	}
   126  	ps.errorp(diag.Ranging{From: ps.pos, To: end}, e)
   127  }
   128  
   129  func newError(text string, shouldbe ...string) error {
   130  	if len(shouldbe) == 0 {
   131  		return errors.New(text)
   132  	}
   133  	var buf bytes.Buffer
   134  	if len(text) > 0 {
   135  		buf.WriteString(text + ", ")
   136  	}
   137  	buf.WriteString("should be " + shouldbe[0])
   138  	for i, opt := range shouldbe[1:] {
   139  		if i == len(shouldbe)-2 {
   140  			buf.WriteString(" or ")
   141  		} else {
   142  			buf.WriteString(", ")
   143  		}
   144  		buf.WriteString(opt)
   145  	}
   146  	return errors.New(buf.String())
   147  }