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  }