podman

Форк
0
395 строк · 8.6 Кб
1
// Copyright 2013-2022 Frank Schroeder. 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
// Parts of the lexer are from the template/text/parser package
6
// For these parts the following applies:
7
//
8
// Copyright 2011 The Go Authors. All rights reserved.
9
// Use of this source code is governed by a BSD-style
10
// license that can be found in the LICENSE file of the go 1.2
11
// distribution.
12

13
package properties
14

15
import (
16
	"fmt"
17
	"strconv"
18
	"strings"
19
	"unicode/utf8"
20
)
21

22
// item represents a token or text string returned from the scanner.
23
type item struct {
24
	typ itemType // The type of this item.
25
	pos int      // The starting position, in bytes, of this item in the input string.
26
	val string   // The value of this item.
27
}
28

29
func (i item) String() string {
30
	switch {
31
	case i.typ == itemEOF:
32
		return "EOF"
33
	case i.typ == itemError:
34
		return i.val
35
	case len(i.val) > 10:
36
		return fmt.Sprintf("%.10q...", i.val)
37
	}
38
	return fmt.Sprintf("%q", i.val)
39
}
40

41
// itemType identifies the type of lex items.
42
type itemType int
43

44
const (
45
	itemError itemType = iota // error occurred; value is text of error
46
	itemEOF
47
	itemKey     // a key
48
	itemValue   // a value
49
	itemComment // a comment
50
)
51

52
// defines a constant for EOF
53
const eof = -1
54

55
// permitted whitespace characters space, FF and TAB
56
const whitespace = " \f\t"
57

58
// stateFn represents the state of the scanner as a function that returns the next state.
59
type stateFn func(*lexer) stateFn
60

61
// lexer holds the state of the scanner.
62
type lexer struct {
63
	input   string    // the string being scanned
64
	state   stateFn   // the next lexing function to enter
65
	pos     int       // current position in the input
66
	start   int       // start position of this item
67
	width   int       // width of last rune read from input
68
	lastPos int       // position of most recent item returned by nextItem
69
	runes   []rune    // scanned runes for this item
70
	items   chan item // channel of scanned items
71
}
72

73
// next returns the next rune in the input.
74
func (l *lexer) next() rune {
75
	if l.pos >= len(l.input) {
76
		l.width = 0
77
		return eof
78
	}
79
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
80
	l.width = w
81
	l.pos += l.width
82
	return r
83
}
84

85
// peek returns but does not consume the next rune in the input.
86
func (l *lexer) peek() rune {
87
	r := l.next()
88
	l.backup()
89
	return r
90
}
91

92
// backup steps back one rune. Can only be called once per call of next.
93
func (l *lexer) backup() {
94
	l.pos -= l.width
95
}
96

97
// emit passes an item back to the client.
98
func (l *lexer) emit(t itemType) {
99
	i := item{t, l.start, string(l.runes)}
100
	l.items <- i
101
	l.start = l.pos
102
	l.runes = l.runes[:0]
103
}
104

105
// ignore skips over the pending input before this point.
106
func (l *lexer) ignore() {
107
	l.start = l.pos
108
}
109

110
// appends the rune to the current value
111
func (l *lexer) appendRune(r rune) {
112
	l.runes = append(l.runes, r)
113
}
114

115
// accept consumes the next rune if it's from the valid set.
116
func (l *lexer) accept(valid string) bool {
117
	if strings.ContainsRune(valid, l.next()) {
118
		return true
119
	}
120
	l.backup()
121
	return false
122
}
123

124
// acceptRun consumes a run of runes from the valid set.
125
func (l *lexer) acceptRun(valid string) {
126
	for strings.ContainsRune(valid, l.next()) {
127
	}
128
	l.backup()
129
}
130

131
// lineNumber reports which line we're on, based on the position of
132
// the previous item returned by nextItem. Doing it this way
133
// means we don't have to worry about peek double counting.
134
func (l *lexer) lineNumber() int {
135
	return 1 + strings.Count(l.input[:l.lastPos], "\n")
136
}
137

138
// errorf returns an error token and terminates the scan by passing
139
// back a nil pointer that will be the next state, terminating l.nextItem.
140
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
141
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
142
	return nil
143
}
144

145
// nextItem returns the next item from the input.
146
func (l *lexer) nextItem() item {
147
	i := <-l.items
148
	l.lastPos = i.pos
149
	return i
150
}
151

152
// lex creates a new scanner for the input string.
153
func lex(input string) *lexer {
154
	l := &lexer{
155
		input: input,
156
		items: make(chan item),
157
		runes: make([]rune, 0, 32),
158
	}
159
	go l.run()
160
	return l
161
}
162

163
// run runs the state machine for the lexer.
164
func (l *lexer) run() {
165
	for l.state = lexBeforeKey(l); l.state != nil; {
166
		l.state = l.state(l)
167
	}
168
}
169

170
// state functions
171

172
// lexBeforeKey scans until a key begins.
173
func lexBeforeKey(l *lexer) stateFn {
174
	switch r := l.next(); {
175
	case isEOF(r):
176
		l.emit(itemEOF)
177
		return nil
178

179
	case isEOL(r):
180
		l.ignore()
181
		return lexBeforeKey
182

183
	case isComment(r):
184
		return lexComment
185

186
	case isWhitespace(r):
187
		l.ignore()
188
		return lexBeforeKey
189

190
	default:
191
		l.backup()
192
		return lexKey
193
	}
194
}
195

196
// lexComment scans a comment line. The comment character has already been scanned.
197
func lexComment(l *lexer) stateFn {
198
	l.acceptRun(whitespace)
199
	l.ignore()
200
	for {
201
		switch r := l.next(); {
202
		case isEOF(r):
203
			l.ignore()
204
			l.emit(itemEOF)
205
			return nil
206
		case isEOL(r):
207
			l.emit(itemComment)
208
			return lexBeforeKey
209
		default:
210
			l.appendRune(r)
211
		}
212
	}
213
}
214

215
// lexKey scans the key up to a delimiter
216
func lexKey(l *lexer) stateFn {
217
	var r rune
218

219
Loop:
220
	for {
221
		switch r = l.next(); {
222

223
		case isEscape(r):
224
			err := l.scanEscapeSequence()
225
			if err != nil {
226
				return l.errorf(err.Error())
227
			}
228

229
		case isEndOfKey(r):
230
			l.backup()
231
			break Loop
232

233
		case isEOF(r):
234
			break Loop
235

236
		default:
237
			l.appendRune(r)
238
		}
239
	}
240

241
	if len(l.runes) > 0 {
242
		l.emit(itemKey)
243
	}
244

245
	if isEOF(r) {
246
		l.emit(itemEOF)
247
		return nil
248
	}
249

250
	return lexBeforeValue
251
}
252

253
// lexBeforeValue scans the delimiter between key and value.
254
// Leading and trailing whitespace is ignored.
255
// We expect to be just after the key.
256
func lexBeforeValue(l *lexer) stateFn {
257
	l.acceptRun(whitespace)
258
	l.accept(":=")
259
	l.acceptRun(whitespace)
260
	l.ignore()
261
	return lexValue
262
}
263

264
// lexValue scans text until the end of the line. We expect to be just after the delimiter.
265
func lexValue(l *lexer) stateFn {
266
	for {
267
		switch r := l.next(); {
268
		case isEscape(r):
269
			if isEOL(l.peek()) {
270
				l.next()
271
				l.acceptRun(whitespace)
272
			} else {
273
				err := l.scanEscapeSequence()
274
				if err != nil {
275
					return l.errorf(err.Error())
276
				}
277
			}
278

279
		case isEOL(r):
280
			l.emit(itemValue)
281
			l.ignore()
282
			return lexBeforeKey
283

284
		case isEOF(r):
285
			l.emit(itemValue)
286
			l.emit(itemEOF)
287
			return nil
288

289
		default:
290
			l.appendRune(r)
291
		}
292
	}
293
}
294

295
// scanEscapeSequence scans either one of the escaped characters
296
// or a unicode literal. We expect to be after the escape character.
297
func (l *lexer) scanEscapeSequence() error {
298
	switch r := l.next(); {
299

300
	case isEscapedCharacter(r):
301
		l.appendRune(decodeEscapedCharacter(r))
302
		return nil
303

304
	case atUnicodeLiteral(r):
305
		return l.scanUnicodeLiteral()
306

307
	case isEOF(r):
308
		return fmt.Errorf("premature EOF")
309

310
	// silently drop the escape character and append the rune as is
311
	default:
312
		l.appendRune(r)
313
		return nil
314
	}
315
}
316

317
// scans a unicode literal in the form \uXXXX. We expect to be after the \u.
318
func (l *lexer) scanUnicodeLiteral() error {
319
	// scan the digits
320
	d := make([]rune, 4)
321
	for i := 0; i < 4; i++ {
322
		d[i] = l.next()
323
		if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
324
			return fmt.Errorf("invalid unicode literal")
325
		}
326
	}
327

328
	// decode the digits into a rune
329
	r, err := strconv.ParseInt(string(d), 16, 0)
330
	if err != nil {
331
		return err
332
	}
333

334
	l.appendRune(rune(r))
335
	return nil
336
}
337

338
// decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
339
func decodeEscapedCharacter(r rune) rune {
340
	switch r {
341
	case 'f':
342
		return '\f'
343
	case 'n':
344
		return '\n'
345
	case 'r':
346
		return '\r'
347
	case 't':
348
		return '\t'
349
	default:
350
		return r
351
	}
352
}
353

354
// atUnicodeLiteral reports whether we are at a unicode literal.
355
// The escape character has already been consumed.
356
func atUnicodeLiteral(r rune) bool {
357
	return r == 'u'
358
}
359

360
// isComment reports whether we are at the start of a comment.
361
func isComment(r rune) bool {
362
	return r == '#' || r == '!'
363
}
364

365
// isEndOfKey reports whether the rune terminates the current key.
366
func isEndOfKey(r rune) bool {
367
	return strings.ContainsRune(" \f\t\r\n:=", r)
368
}
369

370
// isEOF reports whether we are at EOF.
371
func isEOF(r rune) bool {
372
	return r == eof
373
}
374

375
// isEOL reports whether we are at a new line character.
376
func isEOL(r rune) bool {
377
	return r == '\n' || r == '\r'
378
}
379

380
// isEscape reports whether the rune is the escape character which
381
// prefixes unicode literals and other escaped characters.
382
func isEscape(r rune) bool {
383
	return r == '\\'
384
}
385

386
// isEscapedCharacter reports whether we are at one of the characters that need escaping.
387
// The escape character has already been consumed.
388
func isEscapedCharacter(r rune) bool {
389
	return strings.ContainsRune(" :=fnrt", r)
390
}
391

392
// isWhitespace reports whether the rune is a whitespace character.
393
func isWhitespace(r rune) bool {
394
	return strings.ContainsRune(whitespace, r)
395
}
396

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.