github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/parse/parser.go (about) 1 package parse 2 3 import ( 4 "bytes" 5 "errors" 6 "strings" 7 "unicode/utf8" 8 9 "github.com/elves/elvish/util" 10 ) 11 12 // Parser maintains some mutable states of parsing. 13 // 14 // NOTE: The str member is assumed to be valid UF-8. 15 type Parser struct { 16 srcName string 17 src string 18 pos int 19 overEOF int 20 cutsets []map[rune]int 21 errors Error 22 } 23 24 // NewParser creates a new parser from a piece of source text and its name. 25 func NewParser(srcname, src string) *Parser { 26 return &Parser{srcname, src, 0, 0, []map[rune]int{{}}, Error{}} 27 } 28 29 // Done tells the parser that parsing has completed. 30 func (ps *Parser) Done() { 31 if ps.pos != len(ps.src) { 32 ps.error(errUnexpectedRune) 33 } 34 } 35 36 // Errors gets the parsing errors after calling one of the parse* functions. If 37 // the return value is not nil, it is always of type Error. 38 func (ps *Parser) Errors() error { 39 if len(ps.errors.Entries) > 0 { 40 return &ps.errors 41 } 42 return nil 43 } 44 45 // Source returns the source code that is being parsed. 46 func (ps *Parser) Source() string { 47 return ps.src 48 } 49 50 const eof rune = -1 51 52 func (ps *Parser) peek() rune { 53 if ps.pos == len(ps.src) { 54 return eof 55 } 56 r, _ := utf8.DecodeRuneInString(ps.src[ps.pos:]) 57 if ps.currentCutset()[r] > 0 { 58 return eof 59 } 60 return r 61 } 62 63 func (ps *Parser) hasPrefix(prefix string) bool { 64 return strings.HasPrefix(ps.src[ps.pos:], prefix) 65 } 66 67 // findWord looks ahead for [a-z]* that is also a valid compound. If the 68 // lookahead fails, it returns an empty string. It is useful for looking for 69 // command leaders. 70 func (ps *Parser) findPossibleLeader() string { 71 rest := ps.src[ps.pos:] 72 i := strings.IndexFunc(rest, func(r rune) bool { 73 return r < 'a' || r > 'z' 74 }) 75 if i == -1 { 76 // The whole rest is just one possible leader. 77 return rest 78 } 79 r, _ := utf8.DecodeRuneInString(rest[i:]) 80 if startsPrimary(r, false) { 81 return "" 82 } 83 return rest[:i] 84 } 85 86 func (ps *Parser) next() rune { 87 if ps.pos == len(ps.src) { 88 ps.overEOF++ 89 return eof 90 } 91 r, s := utf8.DecodeRuneInString(ps.src[ps.pos:]) 92 if ps.currentCutset()[r] > 0 { 93 return eof 94 } 95 ps.pos += s 96 return r 97 } 98 99 func (ps *Parser) backup() { 100 if ps.overEOF > 0 { 101 ps.overEOF-- 102 return 103 } 104 _, s := utf8.DecodeLastRuneInString(ps.src[:ps.pos]) 105 ps.pos -= s 106 } 107 108 func (ps *Parser) advance(c int) { 109 ps.pos += c 110 if ps.pos > len(ps.src) { 111 ps.overEOF = ps.pos - len(ps.src) 112 ps.pos = len(ps.src) 113 } 114 } 115 116 func (ps *Parser) errorp(begin, end int, e error) { 117 ps.errors.Add(e.Error(), util.SourceContext{ps.srcName, ps.src, begin, end, nil}) 118 } 119 120 func (ps *Parser) error(e error) { 121 end := ps.pos 122 if end < len(ps.src) { 123 end++ 124 } 125 ps.errorp(ps.pos, end, e) 126 } 127 128 func (ps *Parser) pushCutset(rs ...rune) { 129 ps.cutsets = append(ps.cutsets, map[rune]int{}) 130 ps.cut(rs...) 131 } 132 133 func (ps *Parser) popCutset() { 134 n := len(ps.cutsets) 135 ps.cutsets[n-1] = nil 136 ps.cutsets = ps.cutsets[:n-1] 137 } 138 139 func (ps *Parser) currentCutset() map[rune]int { 140 return ps.cutsets[len(ps.cutsets)-1] 141 } 142 143 func (ps *Parser) cut(rs ...rune) { 144 cutset := ps.currentCutset() 145 for _, r := range rs { 146 cutset[r]++ 147 } 148 } 149 150 func (ps *Parser) uncut(rs ...rune) { 151 cutset := ps.currentCutset() 152 for _, r := range rs { 153 cutset[r]-- 154 } 155 } 156 157 func newError(text string, shouldbe ...string) error { 158 if len(shouldbe) == 0 { 159 return errors.New(text) 160 } 161 var buf bytes.Buffer 162 if len(text) > 0 { 163 buf.WriteString(text + ", ") 164 } 165 buf.WriteString("should be " + shouldbe[0]) 166 for i, opt := range shouldbe[1:] { 167 if i == len(shouldbe)-2 { 168 buf.WriteString(" or ") 169 } else { 170 buf.WriteString(", ") 171 } 172 buf.WriteString(opt) 173 } 174 return errors.New(buf.String()) 175 }