podman
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
13package properties14
15import (16"fmt"17"strconv"18"strings"19"unicode/utf8"20)
21
22// item represents a token or text string returned from the scanner.
23type item struct {24typ itemType // The type of this item.25pos int // The starting position, in bytes, of this item in the input string.26val string // The value of this item.27}
28
29func (i item) String() string {30switch {31case i.typ == itemEOF:32return "EOF"33case i.typ == itemError:34return i.val35case len(i.val) > 10:36return fmt.Sprintf("%.10q...", i.val)37}38return fmt.Sprintf("%q", i.val)39}
40
41// itemType identifies the type of lex items.
42type itemType int43
44const (45itemError itemType = iota // error occurred; value is text of error46itemEOF
47itemKey // a key48itemValue // a value49itemComment // a comment50)
51
52// defines a constant for EOF
53const eof = -154
55// permitted whitespace characters space, FF and TAB
56const whitespace = " \f\t"57
58// stateFn represents the state of the scanner as a function that returns the next state.
59type stateFn func(*lexer) stateFn60
61// lexer holds the state of the scanner.
62type lexer struct {63input string // the string being scanned64state stateFn // the next lexing function to enter65pos int // current position in the input66start int // start position of this item67width int // width of last rune read from input68lastPos int // position of most recent item returned by nextItem69runes []rune // scanned runes for this item70items chan item // channel of scanned items71}
72
73// next returns the next rune in the input.
74func (l *lexer) next() rune {75if l.pos >= len(l.input) {76l.width = 077return eof78}79r, w := utf8.DecodeRuneInString(l.input[l.pos:])80l.width = w81l.pos += l.width82return r83}
84
85// peek returns but does not consume the next rune in the input.
86func (l *lexer) peek() rune {87r := l.next()88l.backup()89return r90}
91
92// backup steps back one rune. Can only be called once per call of next.
93func (l *lexer) backup() {94l.pos -= l.width95}
96
97// emit passes an item back to the client.
98func (l *lexer) emit(t itemType) {99i := item{t, l.start, string(l.runes)}100l.items <- i101l.start = l.pos102l.runes = l.runes[:0]103}
104
105// ignore skips over the pending input before this point.
106func (l *lexer) ignore() {107l.start = l.pos108}
109
110// appends the rune to the current value
111func (l *lexer) appendRune(r rune) {112l.runes = append(l.runes, r)113}
114
115// accept consumes the next rune if it's from the valid set.
116func (l *lexer) accept(valid string) bool {117if strings.ContainsRune(valid, l.next()) {118return true119}120l.backup()121return false122}
123
124// acceptRun consumes a run of runes from the valid set.
125func (l *lexer) acceptRun(valid string) {126for strings.ContainsRune(valid, l.next()) {127}128l.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.
134func (l *lexer) lineNumber() int {135return 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.
140func (l *lexer) errorf(format string, args ...interface{}) stateFn {141l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}142return nil143}
144
145// nextItem returns the next item from the input.
146func (l *lexer) nextItem() item {147i := <-l.items148l.lastPos = i.pos149return i150}
151
152// lex creates a new scanner for the input string.
153func lex(input string) *lexer {154l := &lexer{155input: input,156items: make(chan item),157runes: make([]rune, 0, 32),158}159go l.run()160return l161}
162
163// run runs the state machine for the lexer.
164func (l *lexer) run() {165for l.state = lexBeforeKey(l); l.state != nil; {166l.state = l.state(l)167}168}
169
170// state functions
171
172// lexBeforeKey scans until a key begins.
173func lexBeforeKey(l *lexer) stateFn {174switch r := l.next(); {175case isEOF(r):176l.emit(itemEOF)177return nil178
179case isEOL(r):180l.ignore()181return lexBeforeKey182
183case isComment(r):184return lexComment185
186case isWhitespace(r):187l.ignore()188return lexBeforeKey189
190default:191l.backup()192return lexKey193}194}
195
196// lexComment scans a comment line. The comment character has already been scanned.
197func lexComment(l *lexer) stateFn {198l.acceptRun(whitespace)199l.ignore()200for {201switch r := l.next(); {202case isEOF(r):203l.ignore()204l.emit(itemEOF)205return nil206case isEOL(r):207l.emit(itemComment)208return lexBeforeKey209default:210l.appendRune(r)211}212}213}
214
215// lexKey scans the key up to a delimiter
216func lexKey(l *lexer) stateFn {217var r rune218
219Loop:220for {221switch r = l.next(); {222
223case isEscape(r):224err := l.scanEscapeSequence()225if err != nil {226return l.errorf(err.Error())227}228
229case isEndOfKey(r):230l.backup()231break Loop232
233case isEOF(r):234break Loop235
236default:237l.appendRune(r)238}239}240
241if len(l.runes) > 0 {242l.emit(itemKey)243}244
245if isEOF(r) {246l.emit(itemEOF)247return nil248}249
250return lexBeforeValue251}
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.
256func lexBeforeValue(l *lexer) stateFn {257l.acceptRun(whitespace)258l.accept(":=")259l.acceptRun(whitespace)260l.ignore()261return lexValue262}
263
264// lexValue scans text until the end of the line. We expect to be just after the delimiter.
265func lexValue(l *lexer) stateFn {266for {267switch r := l.next(); {268case isEscape(r):269if isEOL(l.peek()) {270l.next()271l.acceptRun(whitespace)272} else {273err := l.scanEscapeSequence()274if err != nil {275return l.errorf(err.Error())276}277}278
279case isEOL(r):280l.emit(itemValue)281l.ignore()282return lexBeforeKey283
284case isEOF(r):285l.emit(itemValue)286l.emit(itemEOF)287return nil288
289default:290l.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.
297func (l *lexer) scanEscapeSequence() error {298switch r := l.next(); {299
300case isEscapedCharacter(r):301l.appendRune(decodeEscapedCharacter(r))302return nil303
304case atUnicodeLiteral(r):305return l.scanUnicodeLiteral()306
307case isEOF(r):308return fmt.Errorf("premature EOF")309
310// silently drop the escape character and append the rune as is311default:312l.appendRune(r)313return nil314}315}
316
317// scans a unicode literal in the form \uXXXX. We expect to be after the \u.
318func (l *lexer) scanUnicodeLiteral() error {319// scan the digits320d := make([]rune, 4)321for i := 0; i < 4; i++ {322d[i] = l.next()323if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {324return fmt.Errorf("invalid unicode literal")325}326}327
328// decode the digits into a rune329r, err := strconv.ParseInt(string(d), 16, 0)330if err != nil {331return err332}333
334l.appendRune(rune(r))335return nil336}
337
338// decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
339func decodeEscapedCharacter(r rune) rune {340switch r {341case 'f':342return '\f'343case 'n':344return '\n'345case 'r':346return '\r'347case 't':348return '\t'349default:350return r351}352}
353
354// atUnicodeLiteral reports whether we are at a unicode literal.
355// The escape character has already been consumed.
356func atUnicodeLiteral(r rune) bool {357return r == 'u'358}
359
360// isComment reports whether we are at the start of a comment.
361func isComment(r rune) bool {362return r == '#' || r == '!'363}
364
365// isEndOfKey reports whether the rune terminates the current key.
366func isEndOfKey(r rune) bool {367return strings.ContainsRune(" \f\t\r\n:=", r)368}
369
370// isEOF reports whether we are at EOF.
371func isEOF(r rune) bool {372return r == eof373}
374
375// isEOL reports whether we are at a new line character.
376func isEOL(r rune) bool {377return 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.
382func isEscape(r rune) bool {383return 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.
388func isEscapedCharacter(r rune) bool {389return strings.ContainsRune(" :=fnrt", r)390}
391
392// isWhitespace reports whether the rune is a whitespace character.
393func isWhitespace(r rune) bool {394return strings.ContainsRune(whitespace, r)395}
396