1// Copyright 2015 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package prog 5 6import ( 7 "bufio" 8 "bytes" 9 "encoding/hex" 10 "fmt" 11 "strconv" 12 "strings" 13) 14 15// String generates a very compact program description (mostly for debug output). 16func (p *Prog) String() string { 17 buf := new(bytes.Buffer) 18 for i, c := range p.Calls { 19 if i != 0 { 20 fmt.Fprintf(buf, "-") 21 } 22 fmt.Fprintf(buf, "%v", c.Meta.Name) 23 } 24 return buf.String() 25} 26 27func (p *Prog) Serialize() []byte { 28 p.debugValidate() 29 ctx := &serializer{ 30 target: p.Target, 31 buf: new(bytes.Buffer), 32 vars: make(map[*ResultArg]int), 33 } 34 for _, c := range p.Calls { 35 ctx.call(c) 36 } 37 return ctx.buf.Bytes() 38} 39 40type serializer struct { 41 target *Target 42 buf *bytes.Buffer 43 vars map[*ResultArg]int 44 varSeq int 45} 46 47func (ctx *serializer) printf(text string, args ...interface{}) { 48 fmt.Fprintf(ctx.buf, text, args...) 49} 50 51func (ctx *serializer) allocVarID(arg *ResultArg) int { 52 id := ctx.varSeq 53 ctx.varSeq++ 54 ctx.vars[arg] = id 55 return id 56} 57 58func (ctx *serializer) call(c *Call) { 59 if c.Ret != nil && len(c.Ret.uses) != 0 { 60 ctx.printf("r%v = ", ctx.allocVarID(c.Ret)) 61 } 62 ctx.printf("%v(", c.Meta.Name) 63 for i, a := range c.Args { 64 if IsPad(a.Type()) { 65 continue 66 } 67 if i != 0 { 68 ctx.printf(", ") 69 } 70 ctx.arg(a) 71 } 72 ctx.printf(")\n") 73} 74 75func (ctx *serializer) arg(arg Arg) { 76 if arg == nil { 77 ctx.printf("nil") 78 return 79 } 80 arg.serialize(ctx) 81} 82 83func (a *ConstArg) serialize(ctx *serializer) { 84 ctx.printf("0x%x", a.Val) 85} 86 87func (a *PointerArg) serialize(ctx *serializer) { 88 if a.IsNull() { 89 ctx.printf("0x0") 90 return 91 } 92 target := ctx.target 93 ctx.printf("&%v", target.serializeAddr(a)) 94 if a.Res != nil && isDefault(a.Res) && !target.isAnyPtr(a.Type()) { 95 return 96 } 97 ctx.printf("=") 98 if target.isAnyPtr(a.Type()) { 99 ctx.printf("ANY=") 100 } 101 ctx.arg(a.Res) 102} 103 104func (a *DataArg) serialize(ctx *serializer) { 105 if a.Type().Dir() == DirOut { 106 ctx.printf("\"\"/%v", a.Size()) 107 return 108 } 109 data := a.Data() 110 if !a.Type().Varlen() { 111 // Statically typed data will be padded with 0s during 112 // deserialization, so we can strip them here for readability. 113 for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 { 114 data = data[:len(data)-1] 115 } 116 } 117 serializeData(ctx.buf, data) 118} 119 120func (a *GroupArg) serialize(ctx *serializer) { 121 var delims []byte 122 switch a.Type().(type) { 123 case *StructType: 124 delims = []byte{'{', '}'} 125 case *ArrayType: 126 delims = []byte{'[', ']'} 127 default: 128 panic("unknown group type") 129 } 130 ctx.buf.WriteByte(delims[0]) 131 lastNonDefault := len(a.Inner) - 1 132 if a.fixedInnerSize() { 133 for ; lastNonDefault >= 0; lastNonDefault-- { 134 if !isDefault(a.Inner[lastNonDefault]) { 135 break 136 } 137 } 138 } 139 for i := 0; i <= lastNonDefault; i++ { 140 arg1 := a.Inner[i] 141 if arg1 != nil && IsPad(arg1.Type()) { 142 continue 143 } 144 if i != 0 { 145 ctx.printf(", ") 146 } 147 ctx.arg(arg1) 148 } 149 ctx.buf.WriteByte(delims[1]) 150} 151 152func (a *UnionArg) serialize(ctx *serializer) { 153 ctx.printf("@%v", a.Option.Type().FieldName()) 154 if isDefault(a.Option) { 155 return 156 } 157 ctx.printf("=") 158 ctx.arg(a.Option) 159} 160 161func (a *ResultArg) serialize(ctx *serializer) { 162 if len(a.uses) != 0 { 163 ctx.printf("<r%v=>", ctx.allocVarID(a)) 164 } 165 if a.Res == nil { 166 ctx.printf("0x%x", a.Val) 167 return 168 } 169 id, ok := ctx.vars[a.Res] 170 if !ok { 171 panic("no result") 172 } 173 ctx.printf("r%v", id) 174 if a.OpDiv != 0 { 175 ctx.printf("/%v", a.OpDiv) 176 } 177 if a.OpAdd != 0 { 178 ctx.printf("+%v", a.OpAdd) 179 } 180} 181 182func (target *Target) Deserialize(data []byte) (prog *Prog, err error) { 183 prog = &Prog{ 184 Target: target, 185 } 186 p := newParser(data) 187 vars := make(map[string]*ResultArg) 188 comment := "" 189 for p.Scan() { 190 if p.EOF() { 191 if comment != "" { 192 prog.Comments = append(prog.Comments, comment) 193 comment = "" 194 } 195 continue 196 } 197 if p.Char() == '#' { 198 if comment != "" { 199 prog.Comments = append(prog.Comments, comment) 200 } 201 comment = strings.TrimSpace(p.s[p.i+1:]) 202 continue 203 } 204 name := p.Ident() 205 r := "" 206 if p.Char() == '=' { 207 r = name 208 p.Parse('=') 209 name = p.Ident() 210 211 } 212 meta := target.SyscallMap[name] 213 if meta == nil { 214 return nil, fmt.Errorf("unknown syscall %v", name) 215 } 216 c := &Call{ 217 Meta: meta, 218 Ret: MakeReturnArg(meta.Ret), 219 Comment: comment, 220 } 221 prog.Calls = append(prog.Calls, c) 222 p.Parse('(') 223 for i := 0; p.Char() != ')'; i++ { 224 if i >= len(meta.Args) { 225 eatExcessive(p, false) 226 break 227 } 228 typ := meta.Args[i] 229 if IsPad(typ) { 230 return nil, fmt.Errorf("padding in syscall %v arguments", name) 231 } 232 arg, err := target.parseArg(typ, p, vars) 233 if err != nil { 234 return nil, err 235 } 236 c.Args = append(c.Args, arg) 237 if p.Char() != ')' { 238 p.Parse(',') 239 } 240 } 241 p.Parse(')') 242 p.SkipWs() 243 if !p.EOF() { 244 if p.Char() != '#' { 245 return nil, fmt.Errorf("tailing data (line #%v)", p.l) 246 } 247 if c.Comment != "" { 248 prog.Comments = append(prog.Comments, c.Comment) 249 } 250 c.Comment = strings.TrimSpace(p.s[p.i+1:]) 251 } 252 for i := len(c.Args); i < len(meta.Args); i++ { 253 c.Args = append(c.Args, meta.Args[i].makeDefaultArg()) 254 } 255 if len(c.Args) != len(meta.Args) { 256 return nil, fmt.Errorf("wrong call arg count: %v, want %v", len(c.Args), len(meta.Args)) 257 } 258 if r != "" && c.Ret != nil { 259 vars[r] = c.Ret 260 } 261 comment = "" 262 } 263 if comment != "" { 264 prog.Comments = append(prog.Comments, comment) 265 } 266 if err := p.Err(); err != nil { 267 return nil, err 268 } 269 // This validation is done even in non-debug mode because deserialization 270 // procedure does not catch all bugs (e.g. mismatched types). 271 // And we can receive bad programs from corpus and hub. 272 if err := prog.validate(); err != nil { 273 return nil, err 274 } 275 for _, c := range prog.Calls { 276 target.SanitizeCall(c) 277 } 278 return 279} 280 281func (target *Target) parseArg(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 282 r := "" 283 if p.Char() == '<' { 284 p.Parse('<') 285 r = p.Ident() 286 p.Parse('=') 287 p.Parse('>') 288 } 289 arg, err := target.parseArgImpl(typ, p, vars) 290 if err != nil { 291 return nil, err 292 } 293 if arg == nil { 294 if typ != nil { 295 arg = typ.makeDefaultArg() 296 } else if r != "" { 297 return nil, fmt.Errorf("named nil argument") 298 } 299 } 300 if r != "" { 301 if res, ok := arg.(*ResultArg); ok { 302 vars[r] = res 303 } 304 } 305 return arg, nil 306} 307 308func (target *Target) parseArgImpl(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 309 switch p.Char() { 310 case '0': 311 return target.parseArgInt(typ, p) 312 case 'r': 313 return target.parseArgRes(typ, p, vars) 314 case '&': 315 return target.parseArgAddr(typ, p, vars) 316 case '"', '\'': 317 return target.parseArgString(typ, p) 318 case '{': 319 return target.parseArgStruct(typ, p, vars) 320 case '[': 321 return target.parseArgArray(typ, p, vars) 322 case '@': 323 return target.parseArgUnion(typ, p, vars) 324 case 'n': 325 p.Parse('n') 326 p.Parse('i') 327 p.Parse('l') 328 return nil, nil 329 330 default: 331 return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)", 332 int(p.Char()), p.l, p.i, p.s) 333 } 334} 335 336func (target *Target) parseArgInt(typ Type, p *parser) (Arg, error) { 337 val := p.Ident() 338 v, err := strconv.ParseUint(val, 0, 64) 339 if err != nil { 340 return nil, fmt.Errorf("wrong arg value '%v': %v", val, err) 341 } 342 switch typ.(type) { 343 case *ConstType, *IntType, *FlagsType, *ProcType, *LenType, *CsumType: 344 return MakeConstArg(typ, v), nil 345 case *ResourceType: 346 return MakeResultArg(typ, nil, v), nil 347 case *PtrType, *VmaType: 348 if typ.Optional() { 349 return MakeNullPointerArg(typ), nil 350 } 351 return typ.makeDefaultArg(), nil 352 default: 353 eatExcessive(p, true) 354 return typ.makeDefaultArg(), nil 355 } 356} 357 358func (target *Target) parseArgRes(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 359 id := p.Ident() 360 var div, add uint64 361 if p.Char() == '/' { 362 p.Parse('/') 363 op := p.Ident() 364 v, err := strconv.ParseUint(op, 0, 64) 365 if err != nil { 366 return nil, fmt.Errorf("wrong result div op: '%v'", op) 367 } 368 div = v 369 } 370 if p.Char() == '+' { 371 p.Parse('+') 372 op := p.Ident() 373 v, err := strconv.ParseUint(op, 0, 64) 374 if err != nil { 375 return nil, fmt.Errorf("wrong result add op: '%v'", op) 376 } 377 add = v 378 } 379 v := vars[id] 380 if v == nil { 381 return typ.makeDefaultArg(), nil 382 } 383 arg := MakeResultArg(typ, v, 0) 384 arg.OpDiv = div 385 arg.OpAdd = add 386 return arg, nil 387} 388 389func (target *Target) parseArgAddr(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 390 var typ1 Type 391 switch t1 := typ.(type) { 392 case *PtrType: 393 typ1 = t1.Type 394 case *VmaType: 395 default: 396 eatExcessive(p, true) 397 return typ.makeDefaultArg(), nil 398 } 399 p.Parse('&') 400 addr, vmaSize, err := target.parseAddr(p) 401 if err != nil { 402 return nil, err 403 } 404 var inner Arg 405 if p.Char() == '=' { 406 p.Parse('=') 407 if p.Char() == 'A' { 408 p.Parse('A') 409 p.Parse('N') 410 p.Parse('Y') 411 p.Parse('=') 412 typ = target.makeAnyPtrType(typ.Size(), typ.FieldName()) 413 typ1 = target.any.array 414 } 415 inner, err = target.parseArg(typ1, p, vars) 416 if err != nil { 417 return nil, err 418 } 419 } 420 if typ1 == nil { 421 return MakeVmaPointerArg(typ, addr, vmaSize), nil 422 } 423 if inner == nil { 424 inner = typ1.makeDefaultArg() 425 } 426 return MakePointerArg(typ, addr, inner), nil 427} 428 429func (target *Target) parseArgString(typ Type, p *parser) (Arg, error) { 430 if _, ok := typ.(*BufferType); !ok { 431 eatExcessive(p, true) 432 return typ.makeDefaultArg(), nil 433 } 434 data, err := deserializeData(p) 435 if err != nil { 436 return nil, err 437 } 438 size := ^uint64(0) 439 if p.Char() == '/' { 440 p.Parse('/') 441 sizeStr := p.Ident() 442 size, err = strconv.ParseUint(sizeStr, 0, 64) 443 if err != nil { 444 return nil, fmt.Errorf("failed to parse buffer size: %q", sizeStr) 445 } 446 } 447 if !typ.Varlen() { 448 size = typ.Size() 449 } else if size == ^uint64(0) { 450 size = uint64(len(data)) 451 } 452 if typ.Dir() == DirOut { 453 return MakeOutDataArg(typ, size), nil 454 } 455 if diff := int(size) - len(data); diff > 0 { 456 data = append(data, make([]byte, diff)...) 457 } 458 data = data[:size] 459 return MakeDataArg(typ, data), nil 460} 461 462func (target *Target) parseArgStruct(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 463 p.Parse('{') 464 t1, ok := typ.(*StructType) 465 if !ok { 466 eatExcessive(p, false) 467 p.Parse('}') 468 return typ.makeDefaultArg(), nil 469 } 470 var inner []Arg 471 for i := 0; p.Char() != '}'; i++ { 472 if i >= len(t1.Fields) { 473 eatExcessive(p, false) 474 break 475 } 476 fld := t1.Fields[i] 477 if IsPad(fld) { 478 inner = append(inner, MakeConstArg(fld, 0)) 479 } else { 480 arg, err := target.parseArg(fld, p, vars) 481 if err != nil { 482 return nil, err 483 } 484 inner = append(inner, arg) 485 if p.Char() != '}' { 486 p.Parse(',') 487 } 488 } 489 } 490 p.Parse('}') 491 for len(inner) < len(t1.Fields) { 492 inner = append(inner, t1.Fields[len(inner)].makeDefaultArg()) 493 } 494 return MakeGroupArg(typ, inner), nil 495} 496 497func (target *Target) parseArgArray(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 498 p.Parse('[') 499 t1, ok := typ.(*ArrayType) 500 if !ok { 501 eatExcessive(p, false) 502 p.Parse(']') 503 return typ.makeDefaultArg(), nil 504 } 505 var inner []Arg 506 for i := 0; p.Char() != ']'; i++ { 507 arg, err := target.parseArg(t1.Type, p, vars) 508 if err != nil { 509 return nil, err 510 } 511 inner = append(inner, arg) 512 if p.Char() != ']' { 513 p.Parse(',') 514 } 515 } 516 p.Parse(']') 517 if t1.Kind == ArrayRangeLen && t1.RangeBegin == t1.RangeEnd { 518 for uint64(len(inner)) < t1.RangeBegin { 519 inner = append(inner, t1.Type.makeDefaultArg()) 520 } 521 inner = inner[:t1.RangeBegin] 522 } 523 return MakeGroupArg(typ, inner), nil 524} 525 526func (target *Target) parseArgUnion(typ Type, p *parser, vars map[string]*ResultArg) (Arg, error) { 527 t1, ok := typ.(*UnionType) 528 if !ok { 529 eatExcessive(p, true) 530 return typ.makeDefaultArg(), nil 531 } 532 p.Parse('@') 533 name := p.Ident() 534 var optType Type 535 for _, t2 := range t1.Fields { 536 if name == t2.FieldName() { 537 optType = t2 538 break 539 } 540 } 541 if optType == nil { 542 eatExcessive(p, true) 543 return typ.makeDefaultArg(), nil 544 } 545 var opt Arg 546 if p.Char() == '=' { 547 p.Parse('=') 548 var err error 549 opt, err = target.parseArg(optType, p, vars) 550 if err != nil { 551 return nil, err 552 } 553 } else { 554 opt = optType.makeDefaultArg() 555 } 556 return MakeUnionArg(typ, opt), nil 557} 558 559// Eats excessive call arguments and struct fields to recover after description changes. 560func eatExcessive(p *parser, stopAtComma bool) { 561 paren, brack, brace := 0, 0, 0 562 for !p.EOF() && p.e == nil { 563 ch := p.Char() 564 switch ch { 565 case '(': 566 paren++ 567 case ')': 568 if paren == 0 { 569 return 570 } 571 paren-- 572 case '[': 573 brack++ 574 case ']': 575 if brack == 0 { 576 return 577 } 578 brack-- 579 case '{': 580 brace++ 581 case '}': 582 if brace == 0 { 583 return 584 } 585 brace-- 586 case ',': 587 if stopAtComma && paren == 0 && brack == 0 && brace == 0 { 588 return 589 } 590 case '\'', '"': 591 p.Parse(ch) 592 for !p.EOF() && p.Char() != ch { 593 p.Parse(p.Char()) 594 } 595 if p.EOF() { 596 return 597 } 598 } 599 p.Parse(ch) 600 } 601} 602 603const ( 604 encodingAddrBase = 0x7f0000000000 605 maxLineLen = 1 << 20 606) 607 608func (target *Target) serializeAddr(arg *PointerArg) string { 609 ssize := "" 610 if arg.VmaSize != 0 { 611 ssize = fmt.Sprintf("/0x%x", arg.VmaSize) 612 } 613 return fmt.Sprintf("(0x%x%v)", encodingAddrBase+arg.Address, ssize) 614} 615 616func (target *Target) parseAddr(p *parser) (uint64, uint64, error) { 617 p.Parse('(') 618 pstr := p.Ident() 619 addr, err := strconv.ParseUint(pstr, 0, 64) 620 if err != nil { 621 return 0, 0, fmt.Errorf("failed to parse addr: %q", pstr) 622 } 623 if addr < encodingAddrBase { 624 return 0, 0, fmt.Errorf("address without base offset: %q", pstr) 625 } 626 addr -= encodingAddrBase 627 // This is not used anymore, but left here to parse old programs. 628 if p.Char() == '+' || p.Char() == '-' { 629 minus := false 630 if p.Char() == '-' { 631 minus = true 632 p.Parse('-') 633 } else { 634 p.Parse('+') 635 } 636 ostr := p.Ident() 637 off, err := strconv.ParseUint(ostr, 0, 64) 638 if err != nil { 639 return 0, 0, fmt.Errorf("failed to parse addr offset: %q", ostr) 640 } 641 if minus { 642 off = -off 643 } 644 addr += off 645 } 646 maxMem := target.NumPages * target.PageSize 647 var vmaSize uint64 648 if p.Char() == '/' { 649 p.Parse('/') 650 pstr := p.Ident() 651 size, err := strconv.ParseUint(pstr, 0, 64) 652 if err != nil { 653 return 0, 0, fmt.Errorf("failed to parse addr size: %q", pstr) 654 } 655 addr = addr & ^(target.PageSize - 1) 656 vmaSize = (size + target.PageSize - 1) & ^(target.PageSize - 1) 657 if vmaSize == 0 { 658 vmaSize = target.PageSize 659 } 660 if vmaSize > maxMem { 661 vmaSize = maxMem 662 } 663 if addr > maxMem-vmaSize { 664 addr = maxMem - vmaSize 665 } 666 } 667 p.Parse(')') 668 return addr, vmaSize, nil 669} 670 671func serializeData(buf *bytes.Buffer, data []byte) { 672 readable := true 673 for _, v := range data { 674 if v >= 0x20 && v < 0x7f { 675 continue 676 } 677 switch v { 678 case 0, '\a', '\b', '\f', '\n', '\r', '\t', '\v': 679 continue 680 } 681 readable = false 682 break 683 } 684 if !readable || len(data) == 0 { 685 fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(data)) 686 return 687 } 688 buf.WriteByte('\'') 689 for _, v := range data { 690 switch v { 691 case 0: 692 buf.Write([]byte{'\\', 'x', '0', '0'}) 693 case '\a': 694 buf.Write([]byte{'\\', 'a'}) 695 case '\b': 696 buf.Write([]byte{'\\', 'b'}) 697 case '\f': 698 buf.Write([]byte{'\\', 'f'}) 699 case '\n': 700 buf.Write([]byte{'\\', 'n'}) 701 case '\r': 702 buf.Write([]byte{'\\', 'r'}) 703 case '\t': 704 buf.Write([]byte{'\\', 't'}) 705 case '\v': 706 buf.Write([]byte{'\\', 'v'}) 707 case '\'': 708 buf.Write([]byte{'\\', '\''}) 709 case '\\': 710 buf.Write([]byte{'\\', '\\'}) 711 default: 712 buf.WriteByte(v) 713 } 714 } 715 buf.WriteByte('\'') 716} 717 718func deserializeData(p *parser) ([]byte, error) { 719 var data []byte 720 if p.Char() == '"' { 721 p.Parse('"') 722 val := "" 723 if p.Char() != '"' { 724 val = p.Ident() 725 } 726 p.Parse('"') 727 var err error 728 data, err = hex.DecodeString(val) 729 if err != nil { 730 return nil, fmt.Errorf("data arg has bad value %q", val) 731 } 732 } else { 733 if p.consume() != '\'' { 734 return nil, fmt.Errorf("data arg does not start with \" nor with '") 735 } 736 for p.Char() != '\'' && p.Char() != 0 { 737 v := p.consume() 738 if v != '\\' { 739 data = append(data, v) 740 continue 741 } 742 v = p.consume() 743 switch v { 744 case 'x': 745 hi := p.consume() 746 lo := p.consume() 747 if lo != '0' || hi != '0' { 748 return nil, fmt.Errorf( 749 "invalid \\x%c%c escape sequence in data arg", hi, lo) 750 } 751 data = append(data, 0) 752 case 'a': 753 data = append(data, '\a') 754 case 'b': 755 data = append(data, '\b') 756 case 'f': 757 data = append(data, '\f') 758 case 'n': 759 data = append(data, '\n') 760 case 'r': 761 data = append(data, '\r') 762 case 't': 763 data = append(data, '\t') 764 case 'v': 765 data = append(data, '\v') 766 case '\'': 767 data = append(data, '\'') 768 case '\\': 769 data = append(data, '\\') 770 default: 771 return nil, fmt.Errorf("invalid \\%c escape sequence in data arg", v) 772 } 773 } 774 p.Parse('\'') 775 } 776 return data, nil 777} 778 779type parser struct { 780 r *bufio.Scanner 781 s string 782 i int 783 l int 784 e error 785} 786 787func newParser(data []byte) *parser { 788 p := &parser{r: bufio.NewScanner(bytes.NewReader(data))} 789 p.r.Buffer(nil, maxLineLen) 790 return p 791} 792 793func (p *parser) Scan() bool { 794 if p.e != nil { 795 return false 796 } 797 if !p.r.Scan() { 798 p.e = p.r.Err() 799 return false 800 } 801 p.s = p.r.Text() 802 p.i = 0 803 p.l++ 804 return true 805} 806 807func (p *parser) Err() error { 808 return p.e 809} 810 811func (p *parser) Str() string { 812 return p.s 813} 814 815func (p *parser) EOF() bool { 816 return p.i == len(p.s) 817} 818 819func (p *parser) Char() byte { 820 if p.e != nil { 821 return 0 822 } 823 if p.EOF() { 824 p.failf("unexpected eof") 825 return 0 826 } 827 return p.s[p.i] 828} 829 830func (p *parser) Parse(ch byte) { 831 if p.e != nil { 832 return 833 } 834 if p.EOF() { 835 p.failf("want %s, got EOF", string(ch)) 836 return 837 } 838 if p.s[p.i] != ch { 839 p.failf("want '%v', got '%v'", string(ch), string(p.s[p.i])) 840 return 841 } 842 p.i++ 843 p.SkipWs() 844} 845 846func (p *parser) consume() byte { 847 if p.e != nil { 848 return 0 849 } 850 if p.EOF() { 851 p.failf("unexpected eof") 852 return 0 853 } 854 v := p.s[p.i] 855 p.i++ 856 return v 857} 858 859func (p *parser) SkipWs() { 860 for p.i < len(p.s) && (p.s[p.i] == ' ' || p.s[p.i] == '\t') { 861 p.i++ 862 } 863} 864 865func (p *parser) Ident() string { 866 i := p.i 867 for p.i < len(p.s) && 868 (p.s[p.i] >= 'a' && p.s[p.i] <= 'z' || 869 p.s[p.i] >= 'A' && p.s[p.i] <= 'Z' || 870 p.s[p.i] >= '0' && p.s[p.i] <= '9' || 871 p.s[p.i] == '_' || p.s[p.i] == '$') { 872 p.i++ 873 } 874 if i == p.i { 875 p.failf("failed to parse identifier at pos %v", i) 876 return "" 877 } 878 s := p.s[i:p.i] 879 p.SkipWs() 880 return s 881} 882 883func (p *parser) failf(msg string, args ...interface{}) { 884 p.e = fmt.Errorf("%v\nline #%v: %v", fmt.Sprintf(msg, args...), p.l, p.s) 885} 886 887// CallSet returns a set of all calls in the program. 888// It does very conservative parsing and is intended to parse paste/future serialization formats. 889func CallSet(data []byte) (map[string]struct{}, error) { 890 calls := make(map[string]struct{}) 891 s := bufio.NewScanner(bytes.NewReader(data)) 892 s.Buffer(nil, maxLineLen) 893 for s.Scan() { 894 ln := s.Bytes() 895 if len(ln) == 0 || ln[0] == '#' { 896 continue 897 } 898 bracket := bytes.IndexByte(ln, '(') 899 if bracket == -1 { 900 return nil, fmt.Errorf("line does not contain opening bracket") 901 } 902 call := ln[:bracket] 903 if eq := bytes.IndexByte(call, '='); eq != -1 { 904 eq++ 905 for eq < len(call) && call[eq] == ' ' { 906 eq++ 907 } 908 call = call[eq:] 909 } 910 if len(call) == 0 { 911 return nil, fmt.Errorf("call name is empty") 912 } 913 calls[string(call)] = struct{}{} 914 } 915 if err := s.Err(); err != nil { 916 return nil, err 917 } 918 if len(calls) == 0 { 919 return nil, fmt.Errorf("program does not contain any calls") 920 } 921 return calls, nil 922} 923