github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/schema/ridl/parser.go (about) 1 package ridl 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "log" 8 "reflect" 9 "runtime" 10 "strings" 11 ) 12 13 const ( 14 wordEnum = "enum" 15 wordImport = "import" 16 wordMap = "map" 17 wordMessage = "message" 18 wordName = "name" 19 wordProxy = "proxy" 20 wordService = "service" 21 wordStream = "stream" 22 wordVersion = "version" 23 wordWebRPC = "webrpc" 24 ) 25 26 var ( 27 errUnexpectedToken = errors.New(`unexpected token`) 28 errUnexpectedEOF = errors.New(`unexpected EOF`) 29 errUnexpectedEOL = errors.New(`unexpected EOL`) 30 ) 31 32 var eofToken = &token{tt: tokenEOF} 33 34 type parserState func(*parser) parserState 35 36 type parser struct { 37 tokens []token 38 length int 39 pos int 40 41 words chan interface{} 42 43 root RootNode 44 } 45 46 func newParser(r io.Reader) (*parser, error) { 47 tokens, err := tokenize(r) 48 if err != nil { 49 return nil, err 50 } 51 52 p := &parser{ 53 words: make(chan interface{}), 54 tokens: tokens, 55 length: len(tokens), 56 } 57 return p, nil 58 } 59 60 func (p *parser) emit(message interface{}) { 61 p.words <- message 62 } 63 64 func (p *parser) collect() error { 65 for tok := range p.words { 66 switch word := tok.(type) { 67 68 case error: 69 return word 70 71 case Node: 72 p.root.Push(word) 73 74 case tokenType: 75 if tok == tokenEOF { 76 return nil 77 } 78 79 default: 80 log.Fatalf("unexpected: %v (%T)", word, word) 81 } 82 } 83 84 return nil 85 } 86 87 func (p *parser) run() error { 88 go func() { 89 state := parserDefaultState 90 for state != nil { 91 fname := runtime.FuncForPC(reflect.ValueOf(state).Pointer()).Name() 92 _ = fname 93 //log.Printf("state: %v", fname) 94 state = state(p) 95 } 96 }() 97 98 err := p.collect() 99 if err != nil { 100 return err 101 } 102 103 return nil 104 } 105 106 func (p *parser) continueUntilEOL() error { 107 for { 108 tok := p.cursor() 109 110 switch tok.tt { 111 case tokenNewLine, tokenEOF: 112 return nil 113 } 114 115 p.next() 116 } 117 118 panic("unreachable") 119 } 120 121 func (p *parser) expectOptionalCommentOrEOL() error { 122 for { 123 tok := p.cursor() 124 125 switch tok.tt { 126 127 case tokenWhitespace: 128 p.next() 129 continue 130 131 case tokenEOF, tokenNewLine: 132 // end of line 133 return nil 134 135 case tokenHash: 136 return p.continueUntilEOL() 137 138 default: 139 // another kind of token, stop 140 return errUnexpectedToken 141 } 142 143 panic("unreachable") 144 } 145 146 return nil 147 } 148 149 func (p *parser) stateError(err error) parserState { 150 cur := p.cursor() 151 err = fmt.Errorf("parse error: %q near %q (line: %d, col: %d)", err, cur.val, cur.line, cur.col) 152 p.emit(err) 153 return nil 154 } 155 156 func (p *parser) rewind(n int) bool { 157 if p.pos-n < 0 { 158 return false 159 } 160 p.pos = p.pos - n 161 return true 162 } 163 164 func (p *parser) next() bool { 165 if p.pos >= p.length { 166 return false 167 } 168 p.pos = p.pos + 1 169 return true 170 } 171 172 func (p *parser) cursor() *token { 173 if p.pos >= p.length { 174 return eofToken 175 } 176 return &p.tokens[p.pos] 177 } 178 179 func (p *parser) expectStringValue() (*token, error) { 180 tok := p.cursor() 181 p.next() 182 183 if tok.tt != tokenQuote { 184 return nil, errors.New("strings must start with a quote") 185 } 186 187 tokens := []*token{} 188 189 loop: 190 for { 191 tok := p.cursor() 192 p.next() 193 194 switch tok.tt { 195 196 case tokenNewLine: 197 return nil, errUnexpectedEOL 198 199 case tokenEOF: 200 return nil, errUnexpectedEOF 201 202 case tokenBackslash: 203 // push backlash 204 tokens = append(tokens, tok) 205 206 // push whatever else comes next 207 tok = p.cursor() 208 p.next() 209 210 case tokenQuote: 211 // end of string 212 break loop 213 } 214 215 tokens = append(tokens, tok) 216 } 217 218 return composedValue(tokens) 219 } 220 221 func (p *parser) expectLiteralValue() (*token, error) { 222 tokens := []*token{} 223 224 loop: 225 for { 226 // TODO: should we allow enclosing literal values with quotes? 227 tok := p.cursor() 228 229 switch tok.tt { 230 231 // literal value enclosed by string 232 case tokenQuote: 233 var err error 234 tok, err = p.expectStringValue() 235 if err != nil { 236 return nil, err 237 } 238 return tok, nil 239 240 case tokenWord: 241 tokens = append(tokens, tok) 242 p.next() 243 244 default: 245 break loop 246 } 247 } 248 249 return composedValue(tokens) 250 } 251 252 func (p *parser) match(tokenTypes ...tokenType) ([]*token, error) { 253 matcher := func(p *parser) ([]*token, error) { 254 tokens := make([]*token, 0, len(tokenTypes)) 255 matches := 0 256 257 for _, expecting := range tokenTypes { 258 tok := p.cursor() 259 260 if tok.tt == expecting { 261 tokens = append(tokens, tok) 262 p.next() 263 matches++ 264 continue 265 } 266 267 if expecting == tokenEOL { 268 err := p.expectOptionalCommentOrEOL() 269 if err != nil { 270 return nil, err 271 } 272 tokens = append(tokens, &token{ 273 tt: tokenEOL, 274 line: tok.line, 275 col: tok.col, 276 }) 277 break 278 } 279 280 if expecting != tokenWhitespace { 281 p.rewind(matches) 282 return nil, fmt.Errorf(`expecting %v, got %v`, expecting, tok) 283 } 284 285 tokens = append(tokens, &token{ 286 tt: tokenWhitespace, 287 val: " ", 288 line: tok.line, 289 col: tok.col, 290 }) 291 292 } 293 return tokens, nil 294 } 295 296 return matcher(p) 297 } 298 299 func (p *parser) expectMetadataKey() (*token, error) { 300 matches, err := p.match(tokenWord) 301 if err != nil { 302 return nil, err 303 } 304 305 tokens := []*token{matches[0]} 306 307 loop: 308 for { 309 tok := p.cursor() 310 311 switch tok.tt { 312 case tokenQuestionMark, tokenWhitespace, tokenEqual: 313 break loop 314 case tokenWord: 315 tokens = append(tokens, tok) 316 case tokenDot: 317 matches, err := p.match(tokenDot, tokenWord) 318 if err != nil { 319 return nil, err 320 } 321 322 tokens = append(tokens, matches...) 323 continue 324 default: 325 return nil, errUnexpectedToken 326 } 327 328 p.next() 329 } 330 331 return composedValue(tokens) 332 } 333 334 func (p *parser) expectMetadataValue() (*token, error) { 335 tok := p.cursor() 336 337 if tok.tt == tokenQuote { 338 var err error 339 tok, err = p.expectStringValue() 340 if err != nil { 341 return nil, err 342 } 343 return tok, nil 344 } 345 346 tokens := []*token{} 347 loop: 348 for { 349 tok := p.cursor() 350 351 switch tok.tt { 352 case tokenWhitespace, tokenNewLine, tokenEOF: 353 break loop 354 } 355 356 tokens = append(tokens, tok) 357 p.next() 358 } 359 360 return composedValue(tokens) 361 } 362 363 func parserStateEOL(p *parser) parserState { 364 err := p.expectOptionalCommentOrEOL() 365 if err != nil { 366 return p.stateError(err) 367 } 368 369 return parserStateContinue 370 } 371 372 func parserStateComment(p *parser) parserState { 373 if err := p.continueUntilEOL(); err != nil { 374 return p.stateError(err) 375 } 376 return parserDefaultState 377 } 378 379 func parserStateDeclaration(p *parser) parserState { 380 word := p.cursor() 381 if word.tt != tokenWord { 382 return p.stateError(errUnexpectedToken) 383 } 384 385 switch word.val { 386 case wordWebRPC, wordName, wordVersion: 387 // <word> = <value> # optional comment 388 return parserStateDefinition 389 case wordImport: 390 // import 391 // - <value> [<# comment>] 392 return parserStateImport 393 case wordEnum: 394 // enum <name>: <type> 395 // - <name>[<space>=<space><value>][<#comment>] 396 return parserStateEnum 397 case wordMessage: 398 // message <name> 399 // - <name>: <type> 400 // + <tag.name> = <VALUE> 401 return parserStateMessage 402 case wordService: 403 // service <name> 404 // - <name>([arguments]) [=> ([arguments])] 405 return parserStateService 406 default: 407 return p.stateError(errUnexpectedToken) 408 } 409 410 return parserDefaultState 411 } 412 413 func parserDefaultState(p *parser) parserState { 414 tok := p.cursor() 415 416 switch tok.tt { 417 418 case tokenWhitespace: 419 return parserStateSpace 420 421 case tokenNewLine: 422 return parserStateNewLine 423 424 case tokenWord: 425 return parserStateDeclaration 426 427 case tokenHash: 428 return parserStateComment 429 430 case tokenEOF: 431 return parserStateEOF 432 433 default: 434 return p.stateError(errUnexpectedToken) 435 } 436 437 return nil 438 } 439 440 func parserStateUnexpectedEOF(p *parser) parserState { 441 return p.stateError(errUnexpectedEOF) 442 } 443 444 func parserStateEOF(p *parser) parserState { 445 close(p.words) 446 return nil 447 } 448 449 func parserStateSpace(p *parser) parserState { 450 p.next() 451 452 for p.cursor().tt == tokenWhitespace { 453 // Keep skipping newlines. 454 p.next() 455 } 456 457 return parserDefaultState 458 } 459 460 func parserStateNewLine(p *parser) parserState { 461 p.next() 462 463 for p.cursor().tt == tokenNewLine { 464 // Keep skipping newlines. 465 p.next() 466 } 467 468 return parserDefaultState 469 } 470 471 func parserStateContinue(p *parser) parserState { 472 if !p.next() { 473 return parserStateEOF 474 } 475 return parserDefaultState 476 } 477 478 func composedValue(tokens []*token) (*token, error) { 479 if len(tokens) < 1 { 480 return nil, errors.New("expecting token") 481 } 482 483 baseToken := tokens[0] 484 485 values := []string{} 486 for _, t := range tokens { 487 values = append(values, t.val) 488 } 489 490 stringValue, err := unescapeString(strings.Join(values, "")) 491 if err != nil { 492 return nil, err 493 } 494 495 return &token{ 496 tt: tokenComposed, 497 val: stringValue, 498 line: baseToken.line, 499 col: baseToken.col, 500 }, nil 501 }