bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/conf/rule/parse/parse.go (about) 1 // Copyright 2011 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 parse builds parse trees for configurations as defined by conf. 6 // Clients should use that package to construct configurations rather than this 7 // one, which provides shared internal data structures not intended for general 8 // use. 9 package parse // import "bosun.org/cmd/bosun/conf/rule/parse" 10 11 import ( 12 "fmt" 13 "runtime" 14 "strconv" 15 "strings" 16 ) 17 18 // Tree is the representation of a single parsed configuration. 19 type Tree struct { 20 Name string // name of the template represented by the tree. 21 Root *ListNode // top-level root of the tree. 22 text string // text parsed to create the template (or its parent) 23 // Parsing only; cleared after parse. 24 lex *lexer 25 token [2]item // two-token lookahead for parser. 26 peekCount int 27 } 28 29 // Parse returns a Tree, created by parsing the configuration described in the 30 // argument string. If an error is encountered, parsing stops and an empty Tree 31 // is returned with the error. 32 func Parse(name, text string) (t *Tree, err error) { 33 t = New(name) 34 t.text = text 35 err = t.Parse(text) 36 return 37 } 38 39 // next returns the next token. 40 func (t *Tree) next() item { 41 if t.peekCount > 0 { 42 t.peekCount-- 43 } else { 44 t.token[0] = t.lex.nextItem() 45 } 46 return t.token[t.peekCount] 47 } 48 49 // backup backs the input stream up one token. 50 func (t *Tree) backup() { 51 t.peekCount++ 52 } 53 54 // backup2 backs the input stream up two tokens. 55 // The zeroth token is already there. 56 func (t *Tree) backup2(t1 item) { 57 t.token[1] = t1 58 t.peekCount = 2 59 } 60 61 // peek returns but does not consume the next token. 62 func (t *Tree) peek() item { 63 if t.peekCount > 0 { 64 return t.token[t.peekCount-1] 65 } 66 t.peekCount = 1 67 t.token[0] = t.lex.nextItem() 68 return t.token[0] 69 } 70 71 // Parsing. 72 73 // New allocates a new parse tree with the given name. 74 func New(name string) *Tree { 75 return &Tree{ 76 Name: name, 77 } 78 } 79 80 // ErrorContext returns a textual representation of the location of the node in the input text. 81 func (t *Tree) ErrorContext(n Node) (location, context string) { 82 pos := int(n.Position()) 83 text := t.text[:pos] 84 byteNum := strings.LastIndex(text, "\n") 85 if byteNum == -1 { 86 byteNum = pos // On first line. 87 } else { 88 byteNum++ // After the newline. 89 byteNum = pos - byteNum 90 } 91 lineNum := 1 + strings.Count(text, "\n") 92 context = n.String() 93 context = strings.TrimSpace(context) 94 context = strings.Replace(context, "\n", "\\n", -1) 95 if len(context) > 20 { 96 context = fmt.Sprintf("%.20s...", context) 97 } 98 return fmt.Sprintf("%s:%d:%d", t.Name, lineNum, byteNum), context 99 } 100 101 // errorf formats the error and terminates processing. 102 func (t *Tree) errorf(format string, args ...interface{}) { 103 t.Root = nil 104 format = fmt.Sprintf("parse: %s:%d: %s", t.Name, t.lex.lineNumber(), format) 105 panic(fmt.Errorf(format, args...)) 106 } 107 108 // error terminates processing. 109 func (t *Tree) error(err error) { 110 t.errorf("%s", err) 111 } 112 113 // expect consumes the next token and guarantees it has the required type. 114 func (t *Tree) expect(expected itemType, context string) item { 115 token := t.next() 116 if token.typ != expected { 117 t.unexpected(token, context) 118 } 119 return token 120 } 121 122 // expectOneOf consumes the next token and guarantees it has one of the required types. 123 func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { 124 token := t.next() 125 if token.typ != expected1 && token.typ != expected2 { 126 t.unexpected(token, context) 127 } 128 return token 129 } 130 131 // unexpected complains about the token and terminates processing. 132 func (t *Tree) unexpected(token item, context string) { 133 t.errorf("unexpected %s in %s", token, context) 134 } 135 136 // recover is the handler that turns panics into returns from the top level of Parse. 137 func (t *Tree) recover(errp *error) { 138 e := recover() 139 if e != nil { 140 if _, ok := e.(runtime.Error); ok { 141 panic(e) 142 } 143 if t != nil { 144 t.stopParse() 145 } 146 *errp = e.(error) 147 } 148 return 149 } 150 151 // startParse initializes the parser, using the lexer. 152 func (t *Tree) startParse(lex *lexer) { 153 t.Root = nil 154 t.lex = lex 155 } 156 157 // stopParse terminates parsing. 158 func (t *Tree) stopParse() { 159 t.lex = nil 160 } 161 162 // Parse parses the template definition string to construct a representation of 163 // the template for execution. If either action delimiter string is empty, the 164 // default ("{{" or "}}") is used. Embedded template definitions are added to 165 // the treeSet map. 166 func (t *Tree) Parse(text string) (err error) { 167 defer t.recover(&err) 168 t.startParse(lex(t.Name, text)) 169 t.text = text 170 t.Root = newList(t.peek().pos) 171 t.parse(t.Root) 172 t.stopParse() 173 return nil 174 } 175 176 // parse is the top-level parser for a conf. 177 // It runs to EOF. 178 func (t *Tree) parse(root *ListNode) item { 179 var n Node 180 for { 181 switch token := t.next(); token.typ { 182 case itemIdentifier: 183 switch token2 := t.next(); token2.typ { 184 case itemEqual: 185 t.backup2(token) 186 n = t.parsePair() 187 case itemIdentifier, itemSubsectionIdentifier: 188 t.backup2(token) 189 n = t.parseSection() 190 default: 191 t.unexpected(token, "input") 192 } 193 case itemEOF: 194 if root != t.Root { 195 t.unexpected(token, "input") 196 } 197 return token 198 case itemRightDelim: 199 if root == t.Root { 200 t.unexpected(token, "input") 201 } 202 return token 203 default: 204 t.unexpected(token, "input") 205 } 206 root.append(n) 207 } 208 } 209 210 func (t *Tree) parsePair() *PairNode { 211 const context = "key=value declaration" 212 token := t.expect(itemIdentifier, context) 213 p := newPair(token.pos) 214 p.Key = newString(token.pos, token.val, token.val) 215 t.expect(itemEqual, context) 216 token = t.expectOneOf(itemString, itemRawString, context) 217 switch token.typ { 218 case itemString: 219 p.Val = newString(token.pos, token.val, token.val) 220 case itemRawString: 221 s, err := strconv.Unquote(token.val) 222 if err != nil { 223 t.error(err) 224 } 225 p.Val = newString(token.pos, token.val, s) 226 default: 227 t.unexpected(token, context) 228 } 229 return p 230 } 231 232 func (t *Tree) parseSection() *SectionNode { 233 const context = "section declaration" 234 token := t.expect(itemIdentifier, context) 235 s := newSection(token.pos) 236 start := token.pos 237 s.SectionType = newString(token.pos, token.val, token.val) 238 token = t.expectOneOf(itemIdentifier, itemSubsectionIdentifier, context) 239 s.Name = newString(token.pos, token.val, token.val) 240 t.expect(itemLeftDelim, context) 241 token = t.parse(s.Nodes) 242 s.RawText = t.text[start : token.pos+1] 243 return s 244 }