File: parse.go

package info (click to toggle)
snapd 2.71-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 79,536 kB
  • sloc: ansic: 16,114; sh: 16,105; python: 9,941; makefile: 1,890; exp: 190; awk: 40; xml: 22
file content (132 lines) | stat: -rw-r--r-- 2,651 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2024 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package patterns

import (
	"errors"
	"fmt"
)

func parse(tokens []token) (renderNode, error) {
	tr := tokenReader{
		tokens: tokens,
	}
	return parseSeq(&tr)
}

func parseSeq(tr *tokenReader) (renderNode, error) {
	var s seq
seqLoop:
	for {
		t := tr.peek()

		switch t.tType {
		case tokEOF:
			break seqLoop
		case tokBraceOpen:
			inner, err := parseAlt(tr)
			if err != nil {
				return nil, err
			}

			s = append(s, inner)
		case tokBraceClose:
			if tr.depth > 0 {
				break seqLoop
			}
			tr.token()
			return nil, errors.New("unmatched '}' character")
		case tokText:
			tr.token()
			s = append(s, literal(t.text))
		case tokComma:
			if tr.depth > 0 {
				break seqLoop
			}
			tr.token() // discard, we get called in a loop
			s = append(s, literal(","))
		}
	}

	return s.optimize()
}

func parseAlt(tr *tokenReader) (renderNode, error) {
	var a alt

	if t := tr.token(); t.tType != tokBraceOpen {
		// Should not occur, caller should call parseAlt on peeking '{'
		return nil, fmt.Errorf("internal error: expected '{' at start of alt, but got %v", t)
	}

	tr.depth++
	defer func() {
		tr.depth--
	}()
	if tr.depth >= maxExpandedPatterns {
		return nil, fmt.Errorf("nested group depth exceeded maximum number of expanded path patterns (%d)", maxExpandedPatterns)
	}

altLoop:
	for {
		item, err := parseSeq(tr)
		if err != nil {
			return nil, err
		}

		a = append(a, item)

		switch t := tr.token(); t.tType {
		case tokBraceClose:
			break altLoop
		case tokComma:
			continue
		case tokEOF:
			return nil, errors.New("unmatched '{' character")
		default:
			return nil, fmt.Errorf("unexpected token %v when parsing alt", t)
		}
	}

	return a.optimize()
}

type tokenReader struct {
	tokens []token
	depth  int
}

func (tr tokenReader) peek() token {
	if len(tr.tokens) == 0 {
		return token{tType: tokEOF}
	}

	return tr.tokens[0]
}

func (tr *tokenReader) token() token {
	t := tr.peek()

	if t.tType != tokEOF {
		tr.tokens = tr.tokens[1:]
	}

	return t
}