variant_lexer.go (4856B)
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 }