go-tg-screenshot-bot

Форк
0
284 строки · 4.7 Кб
1
package dbus
2

3
import (
4
	"fmt"
5
	"strings"
6
	"unicode"
7
	"unicode/utf8"
8
)
9

10
// Heavily inspired by the lexer from text/template.
11

12
type varToken struct {
13
	typ varTokenType
14
	val string
15
}
16

17
type varTokenType byte
18

19
const (
20
	tokEOF varTokenType = iota
21
	tokError
22
	tokNumber
23
	tokString
24
	tokBool
25
	tokArrayStart
26
	tokArrayEnd
27
	tokDictStart
28
	tokDictEnd
29
	tokVariantStart
30
	tokVariantEnd
31
	tokComma
32
	tokColon
33
	tokType
34
	tokByteString
35
)
36

37
type varLexer struct {
38
	input  string
39
	start  int
40
	pos    int
41
	width  int
42
	tokens []varToken
43
}
44

45
type lexState func(*varLexer) lexState
46

47
func varLex(s string) []varToken {
48
	l := &varLexer{input: s}
49
	l.run()
50
	return l.tokens
51
}
52

53
func (l *varLexer) accept(valid string) bool {
54
	if strings.ContainsRune(valid, l.next()) {
55
		return true
56
	}
57
	l.backup()
58
	return false
59
}
60

61
func (l *varLexer) backup() {
62
	l.pos -= l.width
63
}
64

65
func (l *varLexer) emit(t varTokenType) {
66
	l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
67
	l.start = l.pos
68
}
69

70
func (l *varLexer) errorf(format string, v ...interface{}) lexState {
71
	l.tokens = append(l.tokens, varToken{
72
		tokError,
73
		fmt.Sprintf(format, v...),
74
	})
75
	return nil
76
}
77

78
func (l *varLexer) ignore() {
79
	l.start = l.pos
80
}
81

82
func (l *varLexer) next() rune {
83
	var r rune
84

85
	if l.pos >= len(l.input) {
86
		l.width = 0
87
		return -1
88
	}
89
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
90
	l.pos += l.width
91
	return r
92
}
93

94
func (l *varLexer) run() {
95
	for state := varLexNormal; state != nil; {
96
		state = state(l)
97
	}
98
}
99

100
func (l *varLexer) peek() rune {
101
	r := l.next()
102
	l.backup()
103
	return r
104
}
105

106
func varLexNormal(l *varLexer) lexState {
107
	for {
108
		r := l.next()
109
		switch {
110
		case r == -1:
111
			l.emit(tokEOF)
112
			return nil
113
		case r == '[':
114
			l.emit(tokArrayStart)
115
		case r == ']':
116
			l.emit(tokArrayEnd)
117
		case r == '{':
118
			l.emit(tokDictStart)
119
		case r == '}':
120
			l.emit(tokDictEnd)
121
		case r == '<':
122
			l.emit(tokVariantStart)
123
		case r == '>':
124
			l.emit(tokVariantEnd)
125
		case r == ':':
126
			l.emit(tokColon)
127
		case r == ',':
128
			l.emit(tokComma)
129
		case r == '\'' || r == '"':
130
			l.backup()
131
			return varLexString
132
		case r == '@':
133
			l.backup()
134
			return varLexType
135
		case unicode.IsSpace(r):
136
			l.ignore()
137
		case unicode.IsNumber(r) || r == '+' || r == '-':
138
			l.backup()
139
			return varLexNumber
140
		case r == 'b':
141
			pos := l.start
142
			if n := l.peek(); n == '"' || n == '\'' {
143
				return varLexByteString
144
			}
145
			// not a byte string; try to parse it as a type or bool below
146
			l.pos = pos + 1
147
			l.width = 1
148
			fallthrough
149
		default:
150
			// either a bool or a type. Try bools first.
151
			l.backup()
152
			if l.pos+4 <= len(l.input) {
153
				if l.input[l.pos:l.pos+4] == "true" {
154
					l.pos += 4
155
					l.emit(tokBool)
156
					continue
157
				}
158
			}
159
			if l.pos+5 <= len(l.input) {
160
				if l.input[l.pos:l.pos+5] == "false" {
161
					l.pos += 5
162
					l.emit(tokBool)
163
					continue
164
				}
165
			}
166
			// must be a type.
167
			return varLexType
168
		}
169
	}
170
}
171

172
var varTypeMap = map[string]string{
173
	"boolean":    "b",
174
	"byte":       "y",
175
	"int16":      "n",
176
	"uint16":     "q",
177
	"int32":      "i",
178
	"uint32":     "u",
179
	"int64":      "x",
180
	"uint64":     "t",
181
	"double":     "f",
182
	"string":     "s",
183
	"objectpath": "o",
184
	"signature":  "g",
185
}
186

187
func varLexByteString(l *varLexer) lexState {
188
	q := l.next()
189
Loop:
190
	for {
191
		switch l.next() {
192
		case '\\':
193
			if r := l.next(); r != -1 {
194
				break
195
			}
196
			fallthrough
197
		case -1:
198
			return l.errorf("unterminated bytestring")
199
		case q:
200
			break Loop
201
		}
202
	}
203
	l.emit(tokByteString)
204
	return varLexNormal
205
}
206

207
func varLexNumber(l *varLexer) lexState {
208
	l.accept("+-")
209
	digits := "0123456789"
210
	if l.accept("0") {
211
		if l.accept("x") {
212
			digits = "0123456789abcdefABCDEF"
213
		} else {
214
			digits = "01234567"
215
		}
216
	}
217
	for strings.ContainsRune(digits, l.next()) {
218
	}
219
	l.backup()
220
	if l.accept(".") {
221
		for strings.ContainsRune(digits, l.next()) {
222
		}
223
		l.backup()
224
	}
225
	if l.accept("eE") {
226
		l.accept("+-")
227
		for strings.ContainsRune("0123456789", l.next()) {
228
		}
229
		l.backup()
230
	}
231
	if r := l.peek(); unicode.IsLetter(r) {
232
		l.next()
233
		return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
234
	}
235
	l.emit(tokNumber)
236
	return varLexNormal
237
}
238

239
func varLexString(l *varLexer) lexState {
240
	q := l.next()
241
Loop:
242
	for {
243
		switch l.next() {
244
		case '\\':
245
			if r := l.next(); r != -1 {
246
				break
247
			}
248
			fallthrough
249
		case -1:
250
			return l.errorf("unterminated string")
251
		case q:
252
			break Loop
253
		}
254
	}
255
	l.emit(tokString)
256
	return varLexNormal
257
}
258

259
func varLexType(l *varLexer) lexState {
260
	at := l.accept("@")
261
	for {
262
		r := l.next()
263
		if r == -1 {
264
			break
265
		}
266
		if unicode.IsSpace(r) {
267
			l.backup()
268
			break
269
		}
270
	}
271
	if at {
272
		if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
273
			return l.errorf("%s", err)
274
		}
275
	} else {
276
		if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
277
			l.emit(tokType)
278
			return varLexNormal
279
		}
280
		return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
281
	}
282
	l.emit(tokType)
283
	return varLexNormal
284
}
285

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

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

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

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