github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/internal/rsg/yacc/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 licenses/BSD-golang.txt.
     4  
     5  // Portions of this file are additionally subject to the following license
     6  // and copyright.
     7  //
     8  // Copyright 2016 The Cockroach Authors.
     9  //
    10  // Use of this software is governed by the Business Source License
    11  // included in the file licenses/BSL.txt.
    12  //
    13  // As of the Change Date specified in that file, in accordance with
    14  // the Business Source License, use of this software will be governed
    15  // by the Apache License, Version 2.0, included in the file
    16  // licenses/APL.txt.
    17  
    18  // Copied from Go's text/template/parse package and modified for yacc.
    19  
    20  // Package yacc parses .y files.
    21  package yacc
    22  
    23  import (
    24  	"fmt"
    25  	"runtime"
    26  
    27  	"github.com/cockroachdb/errors"
    28  )
    29  
    30  // Tree is the representation of a single parsed file.
    31  type Tree struct {
    32  	Name        string // name of the template represented by the tree.
    33  	Productions []*ProductionNode
    34  	text        string // text parsed to create the template (or its parent)
    35  	// Parsing only; cleared after parse.
    36  	lex       *lexer
    37  	token     [2]item // two-token lookahead for parser.
    38  	peekCount int
    39  }
    40  
    41  // Parse parses the yacc file text with optional name.
    42  func Parse(name, text string) (t *Tree, err error) {
    43  	t = New(name)
    44  	t.text = text
    45  	err = t.Parse(text)
    46  	return
    47  }
    48  
    49  // next returns the next token.
    50  func (t *Tree) next() item {
    51  	if t.peekCount > 0 {
    52  		t.peekCount--
    53  	} else {
    54  		t.token[0] = t.lex.nextItem()
    55  	}
    56  	return t.token[t.peekCount]
    57  }
    58  
    59  // backup backs the input stream up one token.
    60  func (t *Tree) backup() {
    61  	t.peekCount++
    62  }
    63  
    64  // peek returns but does not consume the next token.
    65  func (t *Tree) peek() item {
    66  	if t.peekCount > 0 {
    67  		return t.token[t.peekCount-1]
    68  	}
    69  	t.peekCount = 1
    70  	t.token[0] = t.lex.nextItem()
    71  	return t.token[0]
    72  }
    73  
    74  // Parsing.
    75  
    76  // New allocates a new parse tree with the given name.
    77  func New(name string) *Tree {
    78  	return &Tree{
    79  		Name: name,
    80  	}
    81  }
    82  
    83  // errorf formats the error and terminates processing.
    84  func (t *Tree) errorf(format string, args ...interface{}) {
    85  	err := fmt.Errorf(format, args...)
    86  	err = errors.Wrapf(err, "parse: %s:%d", t.Name, t.lex.lineNumber())
    87  	panic(err)
    88  }
    89  
    90  // expect consumes the next token and guarantees it has the required type.
    91  func (t *Tree) expect(expected itemType, context string) item {
    92  	token := t.next()
    93  	if token.typ != expected {
    94  		t.unexpected(token, context)
    95  	}
    96  	return token
    97  }
    98  
    99  // unexpected complains about the token and terminates processing.
   100  func (t *Tree) unexpected(token item, context string) {
   101  	t.errorf("unexpected %s in %s", token, context)
   102  }
   103  
   104  // recover is the handler that turns panics into returns from the top level of Parse.
   105  func (t *Tree) recover(errp *error) {
   106  	if e := recover(); e != nil {
   107  		if _, ok := e.(runtime.Error); ok {
   108  			panic(e)
   109  		}
   110  		if t != nil {
   111  			t.stopParse()
   112  		}
   113  		*errp = e.(error)
   114  	}
   115  }
   116  
   117  // startParse initializes the parser, using the lexer.
   118  func (t *Tree) startParse(lex *lexer) {
   119  	t.lex = lex
   120  }
   121  
   122  // stopParse terminates parsing.
   123  func (t *Tree) stopParse() {
   124  	t.lex = nil
   125  }
   126  
   127  // Parse parses the yacc string to construct a representation of
   128  // the file for analysis.
   129  func (t *Tree) Parse(text string) (err error) {
   130  	defer t.recover(&err)
   131  	t.startParse(lex(t.Name, text))
   132  	t.text = text
   133  	t.parse()
   134  	t.stopParse()
   135  	return nil
   136  }
   137  
   138  // parse is the top-level parser for a file.
   139  // It runs to EOF.
   140  func (t *Tree) parse() {
   141  	for {
   142  		switch token := t.next(); token.typ {
   143  		case itemIdent:
   144  			p := newProduction(token.pos, token.val)
   145  			t.parseProduction(p)
   146  			t.Productions = append(t.Productions, p)
   147  		case itemEOF:
   148  			return
   149  		}
   150  	}
   151  }
   152  
   153  func (t *Tree) parseProduction(p *ProductionNode) {
   154  	const context = "production"
   155  	t.expect(itemColon, context)
   156  	if t.peek().typ == itemNL {
   157  		t.next()
   158  	}
   159  	expectExpr := true
   160  	for {
   161  		token := t.next()
   162  		switch token.typ {
   163  		case itemComment, itemNL:
   164  			// ignore
   165  		case itemPipe:
   166  			if expectExpr {
   167  				t.unexpected(token, context)
   168  			}
   169  			expectExpr = true
   170  		default:
   171  			t.backup()
   172  			if !expectExpr {
   173  				return
   174  			}
   175  			e := newExpression(token.pos)
   176  			t.parseExpression(e)
   177  			p.Expressions = append(p.Expressions, e)
   178  			expectExpr = false
   179  		}
   180  	}
   181  }
   182  
   183  func (t *Tree) parseExpression(e *ExpressionNode) {
   184  	const context = "expression"
   185  	for {
   186  		switch token := t.next(); token.typ {
   187  		case itemNL:
   188  			peek := t.peek().typ
   189  			if peek == itemPipe || peek == itemNL {
   190  				return
   191  			}
   192  		case itemIdent:
   193  			e.Items = append(e.Items, Item{token.val, TypToken})
   194  		case itemLiteral:
   195  			e.Items = append(e.Items, Item{token.val, TypLiteral})
   196  		case itemExpr:
   197  			e.Command = token.val
   198  			if t.peek().typ == itemNL {
   199  				t.next()
   200  			}
   201  			return
   202  		case itemPct, itemComment:
   203  			// ignore
   204  		default:
   205  			t.unexpected(token, context)
   206  		}
   207  	}
   208  }