| package abigen |
| |
| import ( |
| "fmt" |
| "strings" |
| "unicode/utf8" |
| ) |
| |
| // Parser is a type that is able to parse a syscalls.abigen file. |
| type Parser struct { |
| buf []byte |
| off int |
| line, col int |
| name string |
| |
| r rune |
| } |
| |
| type token rune |
| |
| const ( |
| tokenEOF token = iota |
| tokenBad |
| tokenIdent |
| tokenNumber |
| ) |
| |
| // String implements fmt.Stringer on the token type. |
| func (t token) String() string { |
| switch t { |
| case tokenEOF: |
| return "token(EOF)" |
| case tokenBad: |
| return "token(BAD)" |
| case tokenIdent: |
| return "token(Ident)" |
| case tokenNumber: |
| return "token(Number)" |
| default: |
| return "token(" + string(t) + ")" |
| } |
| } |
| |
| func NewParser(buf []byte, fn string) *Parser { |
| return &Parser{ |
| buf: buf, |
| line: 1, |
| name: fn, |
| } |
| } |
| |
| func (p *Parser) Parse() (defs []SysDef, err error) { |
| if len(p.buf) == 0 { |
| return nil, nil |
| } |
| |
| p.nextRune() |
| for { |
| t, lit := p.nextToken() |
| if t == tokenEOF { |
| return defs, nil |
| } else if t == tokenBad { |
| return nil, p.errorf("bad token: %s", lit) |
| } |
| |
| if lit == "syscall" { |
| def, err := p.parseSyscall() |
| if err != nil { |
| return nil, err |
| } |
| |
| // The PCI syscalls are DDK-only and temporary. The hypervisor syscalls |
| // have complex parameter and return types. Exclude all of these. |
| // helpers for them. |
| if strings.Contains(def.Name, "_test") || strings.HasPrefix(def.Name, "pci_") || strings.HasPrefix(def.Name, "guest_") || strings.HasPrefix(def.Name, "vcpu_") { |
| continue |
| } |
| |
| defs = append(defs, def) |
| } else { |
| return nil, p.errorf("unexpected token: %q (literal %q)", t, lit) |
| } |
| } |
| |
| return defs, nil |
| } |
| |
| func (p *Parser) expect(want token) { |
| t, lit := p.nextToken() |
| if t != want { |
| p.panicf("expected %q but got %q (literal %q)", want, t, lit) |
| } |
| } |
| |
| func (p *Parser) parseAttributes() (attr []string, err error) { |
| for { |
| p.skipWhitespace() |
| if p.r == '(' { |
| return |
| } |
| attr = append(attr, p.nextIdent()) |
| p.skipWhitespace() |
| if p.r == ',' { |
| p.nextRune() |
| } |
| } |
| } |
| |
| func (p *Parser) parseArg() (arg SysArg, err error) { |
| if p.r == ')' { |
| return |
| } |
| arg.Name = p.nextIdent() |
| p.expect(':') |
| arg.Type, err = p.parseType() |
| if err != nil { |
| return SysArg{}, err |
| } |
| p.skipWhitespace() |
| if isLetter(p.r) { |
| arg.Constraint = p.nextIdent() |
| p.skipWhitespace() |
| } |
| return |
| } |
| |
| func (p *Parser) parseSyscall() (d SysDef, err error) { |
| d.Name = p.nextIdent() |
| d.Attrs, err = p.parseAttributes() |
| if err != nil { |
| return SysDef{}, err |
| } |
| for _, a := range d.Attrs { |
| if a == "vdsocall" { |
| d.VDSOCall = true |
| } |
| } |
| p.expect('(') |
| for { |
| p.skipWhitespace() |
| if p.r == ')' { |
| p.nextRune() |
| break |
| } |
| arg, err := p.parseArg() |
| if err != nil { |
| return SysDef{}, err |
| } |
| if arg.Name == "" { |
| break |
| } |
| d.Args = append(d.Args, arg) |
| if p.r == ',' { |
| p.nextRune() |
| } |
| } |
| p.skipWhitespace() |
| if p.r != ';' { |
| kw := p.nextIdent() |
| if kw != "returns" { |
| p.panicf("expected \"returns\" but got \"%s\"", kw) |
| } |
| p.expect('(') |
| first := true |
| for { |
| p.skipWhitespace() |
| if p.r == ')' { |
| p.nextRune() |
| break |
| } |
| if first { |
| first = false |
| rtype, err := p.parseType() |
| if err != nil { |
| return SysDef{}, err |
| } |
| d.Ret = rtype |
| } else { |
| arg, err := p.parseArg() |
| if err != nil { |
| return SysDef{}, err |
| } |
| // Extra return types are returned as pointers. |
| arg.Type.Array = true |
| arg.Type.ArraySize = "1" |
| d.Args = append(d.Args, arg) |
| } |
| if p.r == ',' { |
| p.nextRune() |
| } |
| } |
| } |
| p.skipWhitespace() |
| p.expect(';') |
| return d, nil |
| } |
| |
| func (p *Parser) parseType() (t SysType, err error) { |
| ident := p.nextIdent() |
| n, ok := sysTypeNames[ident] |
| if !ok { |
| return t, p.errorf("unknown type name: %s", ident) |
| } |
| t.Name = n |
| p.skipWhitespace() |
| if p.r == '[' { |
| p.nextRune() |
| t.Array = true |
| t.ArraySize = p.nextIdent() |
| p.skipWhitespace() |
| for p.r == '*' { |
| p.nextRune() |
| t.ArraySize += " * " + p.nextIdent() |
| } |
| p.expect(']') |
| } |
| return t, nil |
| } |
| |
| func (p *Parser) nextToken() (t token, lit string) { |
| p.skipWhitespace() |
| |
| if isLetter(p.r) { |
| lit = p.nextIdent() |
| return tokenIdent, lit |
| } |
| if isNumber(p.r) { |
| lit = p.nextNumber() |
| return tokenNumber, lit |
| } |
| |
| r := p.r |
| p.nextRune() |
| switch r { |
| case -1: |
| return tokenEOF, "" |
| case '(', ')', '*', ',', ';', ':', '[', ']': |
| return token(r), "" |
| case '#': |
| p.skipComment() |
| return p.nextToken() |
| } |
| return tokenBad, fmt.Sprintf("%q", r) |
| } |
| |
| func (p *Parser) nextIdent() string { |
| p.skipWhitespace() |
| if !isLetter(p.r) && !isNumber(p.r) { |
| p.panicf("expected ident but got %q", p.r) |
| } |
| off := p.off - 1 |
| for isLetter(p.r) || isNumber(p.r) { |
| p.nextRune() |
| } |
| r := string(p.buf[off : p.off-1]) |
| if r == "type" { |
| // "type" is a reserved word in Go. |
| return "typ" |
| } |
| return r |
| } |
| |
| func (p *Parser) nextNumber() string { |
| p.skipWhitespace() |
| if !isNumber(p.r) { |
| p.panicf("expected number but got %q", p.r) |
| } |
| off := p.off - 1 |
| for isNumber(p.r) { |
| p.nextRune() |
| } |
| return string(p.buf[off : p.off-1]) |
| } |
| |
| func (p *Parser) skipWhitespace() { |
| for p.r == ' ' || p.r == '\t' || p.r == '\n' { |
| p.nextRune() |
| } |
| } |
| |
| func (p *Parser) skipComment() { |
| // skip until end of line |
| p.nextRune() |
| for p.r != '\n' && p.r >= 0 { |
| p.nextRune() |
| } |
| } |
| |
| func (p *Parser) nextRune() { |
| if p.off >= len(p.buf) { |
| p.r = -1 |
| return |
| } |
| if p.r == '\n' { |
| p.line++ |
| p.col = 0 |
| } else { |
| p.col += utf8.RuneLen(p.r) |
| } |
| |
| r, w := utf8.DecodeRune(p.buf[p.off:]) |
| if r == utf8.RuneError && w == 1 { |
| p.r = -1 |
| p.panicf("input is not UTF-8") |
| return |
| } |
| p.r = r |
| p.off += w |
| } |
| |
| func (p *Parser) panicf(format string, args ...interface{}) { |
| panic(parsePanic{err: p.errorf(format, args...)}) |
| } |
| |
| func (p *Parser) errorf(format string, args ...interface{}) error { |
| return fmt.Errorf("%s:%d:%d: %s", p.name, p.line, p.col, fmt.Sprintf(format, args...)) |
| } |
| |
| func isLetter(r rune) bool { return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_' } |
| func isNumber(r rune) bool { return '0' <= r && r <= '9' } |
| |
| type parsePanic struct { |
| err error |
| } |
| |
| func (pp parsePanic) String() string { return pp.err.Error() } |