diff --git a/README.md b/README.md index 2b94f8b..dfd0f86 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # proto3 [![Build Status](https://travis-ci.org/emicklei/proto3.png)](https://travis-ci.org/emicklei/proto3) +[![GoDoc](https://godoc.org/github.com/emicklei/proto3?status.svg)](https://godoc.org/github.com/emicklei/proto3) Package in Go for parsing Google Protocol Buffers [.proto files version 3] (https://developers.google.com/protocol-buffers/docs/reference/proto3-spec) ### install - go get -u -v github.com/emicklei/proto + go get -u -v github.com/emicklei/proto3 -(c) 2017, ernestmicklei.com. MIT License. Contributions welcome. \ No newline at end of file +© 2017, ernestmicklei.com. MIT License. Contributions welcome. \ No newline at end of file diff --git a/enum.go b/enum.go index b9218af..ab9f7b4 100644 --- a/enum.go +++ b/enum.go @@ -5,6 +5,7 @@ import ( "strconv" ) +// Enum definition consists of a name and an enum body. type Enum struct { Line int Name string @@ -12,6 +13,7 @@ type Enum struct { EnumFields []*EnumField } +// EnumField is part of the body of an Enum. type EnumField struct { Name string Integer int @@ -20,11 +22,11 @@ type EnumField struct { func (f *EnumField) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected identifier", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != EQUALS { + if tok != tEQUALS { return fmt.Errorf("found %q, expected =", lit) } is := p.s.scanIntegerString() @@ -34,7 +36,7 @@ func (f *EnumField) parse(p *Parser) error { } f.Integer = i tok, lit = p.scanIgnoreWhitespace() - if tok == LEFTSQUARE { + if tok == tLEFTSQUARE { o := new(Option) err := o.parse(p) if err != nil { @@ -42,7 +44,7 @@ func (f *EnumField) parse(p *Parser) error { } f.ValueOption = o tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTSQUARE { + if tok != tRIGHTSQUARE { return fmt.Errorf("found %q, expected ]", lit) } } @@ -51,21 +53,21 @@ func (f *EnumField) parse(p *Parser) error { func (e *Enum) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected identifier", lit) } e.Name = lit tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTCURLY { + if tok != tLEFTCURLY { return fmt.Errorf("found %q, expected {", lit) } for { tok, lit = p.scanIgnoreWhitespace() switch tok { - case RIGHTCURLY: + case tRIGHTCURLY: goto done - case SEMICOLON: - case OPTION: + case tSEMICOLON: + case tOPTION: v := new(Option) err := v.parse(p) if err != nil { @@ -83,7 +85,7 @@ func (e *Enum) parse(p *Parser) error { } } done: - if tok != RIGHTCURLY { + if tok != tRIGHTCURLY { return fmt.Errorf("found %q, expected }", lit) } return nil diff --git a/enum_test.go b/enum_test.go index d556c1f..c37d6dd 100644 --- a/enum_test.go +++ b/enum_test.go @@ -24,6 +24,7 @@ func TestEnum(t *testing.T) { func TestEnumWithBody(t *testing.T) { proto := ` +// EnumAllowingAlias is part of TestEnumWithBody enum EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; diff --git a/field.go b/field.go index c8171db..75a5c1a 100644 --- a/field.go +++ b/field.go @@ -19,7 +19,7 @@ func (f *Field) parse(p *Parser) error { for { tok, lit := p.scanIgnoreWhitespace() switch tok { - case IDENT: + case tIDENT: // normal type? if strings.Contains(typeTokens, lit) { f.Type = lit @@ -27,14 +27,14 @@ func (f *Field) parse(p *Parser) error { } //if tok == ONEOF {} //if tok == ONEOFFIELD {} - case MESSAGE: + case tMESSAGE: m := new(Message) err := m.parse(p) if err != nil { return err } f.Messages = append(f.Messages, m) - case REPEATED: + case tREPEATED: f.Repeated = true return f.parse(p) default: @@ -48,12 +48,12 @@ done: // parseNormalField proceeds after reading the type of f. func parseNormalField(f *Field, p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected identifier", lit) } f.Name = lit tok, lit = p.scanIgnoreWhitespace() - if tok != EQUALS { + if tok != tEQUALS { return fmt.Errorf("found %q, expected =", lit) } _, lit = p.scanIgnoreWhitespace() diff --git a/import.go b/import.go index 072a0d3..5895a81 100644 --- a/import.go +++ b/import.go @@ -13,13 +13,13 @@ func (i *Import) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() i.Line = p.s.line switch tok { - case WEAK: + case tWEAK: i.Kind = lit return i.parse(p) - case PUBLIC: + case tPUBLIC: i.Kind = lit return i.parse(p) - case QUOTE: + case tQUOTE: i.Filename = p.s.scanUntil('"') default: return fmt.Errorf("found %q, expected weak|public|quoted identifier", lit) diff --git a/message.go b/message.go index 4677298..1b519ae 100644 --- a/message.go +++ b/message.go @@ -9,26 +9,34 @@ type Message struct { Name string Fields []*Field + Enums []*Enum } func (m *Message) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected identifier", lit) } m.Name = lit tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTCURLY { + if tok != tLEFTCURLY { return fmt.Errorf("found %q, expected {", lit) } for { tok, lit = p.scanIgnoreWhitespace() switch tok { - case COMMENT: + case tCOMMENT: m.Comments = append(m.Comments, p.newComment(lit)) - case RIGHTCURLY: + case tRIGHTCURLY: goto done - case SEMICOLON: + case tSEMICOLON: + case tENUM: + e := new(Enum) + err := e.parse(p) + if err != nil { + return err + } + m.Enums = append(m.Enums, e) default: p.unscan() f := new(Field) @@ -40,7 +48,7 @@ func (m *Message) parse(p *Parser) error { } } done: - if tok != RIGHTCURLY { + if tok != tRIGHTCURLY { return fmt.Errorf("found %q, expected }", lit) } return nil diff --git a/option.go b/option.go index a9523bd..2e986a2 100644 --- a/option.go +++ b/option.go @@ -18,28 +18,28 @@ func (o *Option) accept(v Visitor) { func (o *Option) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() switch tok { - case IDENT: + case tIDENT: o.Line = p.s.line o.Name = lit - case LEFTPAREN: + case tLEFTPAREN: tok, lit = p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected identifier", lit) } o.Name = lit tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTPAREN { + if tok != tRIGHTPAREN { return fmt.Errorf("found %q, expected )", lit) } default: return fmt.Errorf("found %q, expected identifier or (", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != EQUALS { + if tok != tEQUALS { return fmt.Errorf("found %q, expected =", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok == QUOTE { + if tok == tQUOTE { ident := p.s.scanUntil('"') if len(ident) == 0 { return fmt.Errorf("unexpected end of quoted string") // TODO create constant for this @@ -47,7 +47,7 @@ func (o *Option) parse(p *Parser) error { o.String = ident return nil } - if TRUE == tok || FALSE == tok { + if tTRUE == tok || tFALSE == tok { o.Boolean = lit == "true" } else { return fmt.Errorf("found %q, expected true or false", lit) diff --git a/parser.go b/parser.go index d2a146e..c461bf0 100644 --- a/parser.go +++ b/parser.go @@ -45,7 +45,7 @@ func (p *Parser) scan() (tok token, lit string) { // scanIgnoreWhitespace scans the next non-whitespace token. func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) { tok, lit = p.scan() - if tok == WS { + if tok == tWS { tok, lit = p.scan() } return diff --git a/proto.go b/proto.go index 7906781..17b9e24 100644 --- a/proto.go +++ b/proto.go @@ -27,41 +27,40 @@ func (c Comment) IsMultiline() bool { func (proto *Proto) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() switch tok { - case COMMENT: + case tCOMMENT: proto.Comments = append(proto.Comments, p.newComment(lit)) - case SYNTAX: + case tSYNTAX: s := new(Syntax) if err := s.parse(p); err != nil { return err } proto.Syntax = s - case IMPORT: + case tIMPORT: im := new(Import) if err := im.parse(p); err != nil { return err } proto.Imports = append(proto.Imports, im) - case ENUM: + case tENUM: enum := new(Enum) if err := enum.parse(p); err != nil { return err } proto.Enums = append(proto.Enums, enum) - case SERVICE: - // TODO + case tSERVICE: service := new(Service) err := service.parse(p) if err != nil { return err } proto.Services = append(proto.Services, service) - case MESSAGE: + case tMESSAGE: msg := new(Message) if err := msg.parse(p); err != nil { return err } proto.Messages = append(proto.Messages, msg) - case EOF: + case tEOF: return nil } return proto.parse(p) diff --git a/scanner.go b/scanner.go index 008ddce..364cd63 100644 --- a/scanner.go +++ b/scanner.go @@ -40,29 +40,29 @@ func (s *scanner) scan() (tok token, lit string) { // Otherwise read the individual character. switch ch { case eof: - return EOF, "" + return tEOF, "" case ';': - return SEMICOLON, string(ch) + return tSEMICOLON, string(ch) case '=': - return EQUALS, string(ch) + return tEQUALS, string(ch) case '"': - return QUOTE, string(ch) + return tQUOTE, string(ch) case '(': - return LEFTPAREN, string(ch) + return tLEFTPAREN, string(ch) case ')': - return RIGHTPAREN, string(ch) + return tRIGHTPAREN, string(ch) case '{': - return LEFTCURLY, string(ch) + return tLEFTCURLY, string(ch) case '}': - return RIGHTCURLY, string(ch) + return tRIGHTCURLY, string(ch) case '[': - return LEFTSQUARE, string(ch) + return tLEFTSQUARE, string(ch) case ']': - return RIGHTSQUARE, string(ch) + return tRIGHTSQUARE, string(ch) case '/': - return COMMENT, s.scanComment() + return tCOMMENT, s.scanComment() } - return ILLEGAL, string(ch) + return tILLEGAL, string(ch) } // scanWhitespace consumes the current rune and all contiguous whitespace. @@ -84,7 +84,7 @@ func (s *scanner) scanWhitespace() (tok token, lit string) { } } - return WS, buf.String() + return tWS, buf.String() } func (s *scanner) scanIntegerString() string { @@ -131,37 +131,37 @@ func (s *scanner) scanIdent() (tok token, lit string) { ident := buf.String() switch ident { case "syntax": - return SYNTAX, buf.String() + return tSYNTAX, buf.String() case "service": - return SERVICE, buf.String() + return tSERVICE, buf.String() case "message": - return MESSAGE, buf.String() + return tMESSAGE, buf.String() case "rpc": - return RPC, buf.String() + return tRPC, buf.String() case "returns": - return RETURNS, buf.String() + return tRETURNS, buf.String() case "import": - return IMPORT, buf.String() + return tIMPORT, buf.String() case "package": - return PACKAGE, buf.String() + return tPACKAGE, buf.String() case "repeated": - return REPEATED, buf.String() + return tREPEATED, buf.String() case "option": - return OPTION, buf.String() + return tOPTION, buf.String() case "enum": - return ENUM, buf.String() + return tENUM, buf.String() case "true": - return TRUE, buf.String() + return tTRUE, buf.String() case "false": - return FALSE, buf.String() + return tFALSE, buf.String() case "weak": - return WEAK, buf.String() + return tWEAK, buf.String() case "public": - return PUBLIC, buf.String() + return tPUBLIC, buf.String() } // Otherwise return as a regular identifier. - return IDENT, ident + return tIDENT, ident } // read reads the next rune from the bufferred reader. diff --git a/service.go b/service.go index 05c055a..6608756 100644 --- a/service.go +++ b/service.go @@ -16,17 +16,17 @@ func (s *Service) accept(v Visitor) { func (s *Service) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected string", lit) } s.Name = lit tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTCURLY { + if tok != tLEFTCURLY { return fmt.Errorf("found %q, expected {", lit) } for { tok, lit = p.scanIgnoreWhitespace() - if tok == RPC { + if tok == tRPC { rpc := new(RPcall) err := rpc.parse(p) if err != nil { @@ -39,12 +39,13 @@ func (s *Service) parse(p *Parser) error { } } tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTCURLY { + if tok != tRIGHTCURLY { return fmt.Errorf("found %q, expected }", lit) } return nil } +// RPcall represents an rpc entry in a message. type RPcall struct { Method string RequestType string @@ -54,46 +55,46 @@ type RPcall struct { func (r *RPcall) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected method", lit) } r.Method = lit tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTPAREN { + if tok != tLEFTPAREN { return fmt.Errorf("found %q, expected (", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected request type", lit) } r.RequestType = lit tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTPAREN { + if tok != tRIGHTPAREN { return fmt.Errorf("found %q, expected )", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != RETURNS { + if tok != tRETURNS { return fmt.Errorf("found %q, expected returns", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTPAREN { + if tok != tLEFTPAREN { return fmt.Errorf("found %q, expected (", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected returns type", lit) } r.ReturnsType = lit tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTPAREN { + if tok != tRIGHTPAREN { return fmt.Errorf("found %q, expected )", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != LEFTCURLY { + if tok != tLEFTCURLY { return fmt.Errorf("found %q, expected {", lit) } tok, lit = p.scanIgnoreWhitespace() - if tok != RIGHTCURLY { + if tok != tRIGHTCURLY { return fmt.Errorf("found %q, expected }", lit) } return nil diff --git a/syntax.go b/syntax.go index 69fa402..0064487 100644 --- a/syntax.go +++ b/syntax.go @@ -12,17 +12,17 @@ func (s *Syntax) accept(v Visitor) { } func (s *Syntax) parse(p *Parser) error { - if tok, lit := p.scanIgnoreWhitespace(); tok != EQUALS { + if tok, lit := p.scanIgnoreWhitespace(); tok != tEQUALS { return fmt.Errorf("found %q, expected EQUALS", lit) } - if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { + if tok, lit := p.scanIgnoreWhitespace(); tok != tQUOTE { return fmt.Errorf("found %q, expected QUOTE", lit) } tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { + if tok != tIDENT { return fmt.Errorf("found %q, expected string", lit) } - if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { + if tok, lit := p.scanIgnoreWhitespace(); tok != tQUOTE { return fmt.Errorf("found %q, expected QUOTE", lit) } s.Value = lit diff --git a/token.go b/token.go index dbf5fd1..0a37472 100644 --- a/token.go +++ b/token.go @@ -5,51 +5,46 @@ type token int const ( // Special tokens - - ILLEGAL token = iota - EOF - WS + tILLEGAL token = iota + tEOF + tWS // Literals - - IDENT // main - TRUE - FALSE + tIDENT + tTRUE + tFALSE // Misc characters - - SEMICOLON // ; - EQUALS // = - QUOTE // " - LEFTPAREN // ( - RIGHTPAREN // ) - LEFTCURLY // { - RIGHTCURLY // } - LEFTSQUARE // [ - RIGHTSQUARE // ] - COMMENT // / + tSEMICOLON // ; + tEQUALS // = + tQUOTE // " + tLEFTPAREN // ( + tRIGHTPAREN // ) + tLEFTCURLY // { + tRIGHTCURLY // } + tLEFTSQUARE // [ + tRIGHTSQUARE // ] + tCOMMENT // / // Keywords - - SYNTAX - SERVICE - RPC - RETURNS - MESSAGE - IMPORT - PACKAGE - OPTION - REPEATED - WEAK - PUBLIC + tSYNTAX + tSERVICE + tRPC + tRETURNS + tMESSAGE + tIMPORT + tPACKAGE + tOPTION + tREPEATED + tWEAK + tPUBLIC // special fields - - ONEOF - ONEOFFIELD - MAP - RESERVED - ENUM + tONEOF + tONEOFFIELD + tMAP + tRESERVED + tENUM ) const typeTokens = "double float int32 int64 uint32 uint64 sint32 sint64 fixed32 sfixed32 sfixed64 bool string bytes" diff --git a/visitor.go b/visitor.go index 1da8bd1..1259f09 100644 --- a/visitor.go +++ b/visitor.go @@ -9,7 +9,7 @@ type Visitor interface { VisitOption(o *Option) } -// visitee is implemented by all Proto elements. -type visitee interface { - accept(v Visitor) +// Visitee is implemented by all Proto elements. +type Visitee interface { + Accept(v Visitor) }