From 26596d280f8e4da71097c71349d79c3476525df3 Mon Sep 17 00:00:00 2001 From: Ernest Micklei Date: Sat, 28 Jan 2017 00:44:43 +0100 Subject: [PATCH] tests pass again --- enum.go | 20 ++++++++++---------- enum_test.go | 6 ++++++ field.go | 3 ++- import.go | 6 +++++- message.go | 13 ++++++------- message_test.go | 9 ++++----- option.go | 7 +++++-- package.go | 9 +++++++++ parser_test.go | 38 -------------------------------------- proto.go | 34 +++++++--------------------------- scanner.go | 11 ++++++++--- service.go | 5 +++++ service_test.go | 31 +++++++++++++++++++++++++++++++ syntax.go | 25 +++++++++++++++++++++++++ syntax_test.go | 20 ++++++++++++++++++++ visitor.go | 10 ++++++++++ 16 files changed, 153 insertions(+), 94 deletions(-) create mode 100644 package.go create mode 100644 service_test.go create mode 100644 syntax.go create mode 100644 syntax_test.go create mode 100644 visitor.go diff --git a/enum.go b/enum.go index a611981..bc5cf69 100644 --- a/enum.go +++ b/enum.go @@ -1,6 +1,9 @@ package proto3parser -import "fmt" +import ( + "fmt" + "strconv" +) type Enum struct { Line int @@ -11,7 +14,7 @@ type Enum struct { type EnumField struct { Name string - Constant string + Integer int ValueOption *Option } @@ -24,15 +27,12 @@ func (f *EnumField) parse(p *Parser) error { if tok != EQUALS { return fmt.Errorf("found %q, expected =", lit) } - ns := p.s.scanIntegerString() - if len(ns) != 0 { - f.Constant = ns - } else { - tok, lit = p.scanIgnoreWhitespace() - if tok != IDENT { - return fmt.Errorf("found %q, expected string", lit) - } + is := p.s.scanIntegerString() + i, err := strconv.Atoi(is) + if err != nil { + return fmt.Errorf("found %q, expected integer", is) } + f.Integer = i tok, lit = p.scanIgnoreWhitespace() if tok == LEFTSQUARE { o := new(Option) diff --git a/enum_test.go b/enum_test.go index 0b335fa..c5258d5 100644 --- a/enum_test.go +++ b/enum_test.go @@ -38,4 +38,10 @@ enum EnumAllowingAlias { if got, want := len(pr.Enums), 1; got != want { t.Errorf("got [%v] want [%v]", got, want) } + if got, want := len(pr.Enums[0].EnumFields), 3; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } + if got, want := pr.Enums[0].EnumFields[0].Integer, 0; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } } diff --git a/field.go b/field.go index 3eb6811..723194c 100644 --- a/field.go +++ b/field.go @@ -28,7 +28,8 @@ func parseField(f *Field, p *Parser) error { //if tok == ONEOF {} //if tok == ONEOFFIELD {} case MESSAGE: - m, err := parseMessage(p) + m := new(Message) + err := m.parse(p) if err != nil { return err } diff --git a/import.go b/import.go index 87737ac..9687326 100644 --- a/import.go +++ b/import.go @@ -18,7 +18,11 @@ func (i *Import) parse(p *Parser) error { } i.Line = p.s.line i.Kind = lit - name := p.s.scanUntil('\n') + tok, lit = p.scanIgnoreWhitespace() + if tok != QUOTE { + return fmt.Errorf("found %q, expected \"", lit) + } + name := p.s.scanUntil('"') if len(name) == 0 { return fmt.Errorf("unexpected end of quoted string") } diff --git a/message.go b/message.go index 526a5c5..fb4a943 100644 --- a/message.go +++ b/message.go @@ -21,16 +21,15 @@ func (m Message) String() string { return buf.String() } -func parseMessage(p *Parser) (*Message, error) { - m := new(Message) +func (m *Message) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() if tok != IDENT { - return nil, fmt.Errorf("found %q, expected name", lit) + return fmt.Errorf("found %q, expected name", lit) } m.Name = lit tok, lit = p.scanIgnoreWhitespace() if tok != LEFTCURLY { - return nil, fmt.Errorf("found %q, expected {", lit) + return fmt.Errorf("found %q, expected {", lit) } for { tok, lit = p.scanIgnoreWhitespace() @@ -43,14 +42,14 @@ func parseMessage(p *Parser) (*Message, error) { f := new(Field) err := parseField(f, p) if err != nil { - return nil, err + return err } m.Fields = append(m.Fields, f) } } done: if tok != RIGHTCURLY { - return nil, fmt.Errorf("found %q, expected }", lit) + return fmt.Errorf("found %q, expected }", lit) } - return m, nil + return nil } diff --git a/message_test.go b/message_test.go index cfb1a57..63a38b2 100644 --- a/message_test.go +++ b/message_test.go @@ -3,15 +3,14 @@ package proto3parser import ( "strings" "testing" - - "github.com/davecgh/go-spew/spew" ) func TestMessage(t *testing.T) { proto := `message AccountOut {}` p := NewParser(strings.NewReader(proto)) p.scanIgnoreWhitespace() // consume first token - m, err := parseMessage(p) + m := new(Message) + err := m.parse(p) if err != nil { t.Fatal(err) } @@ -27,7 +26,8 @@ func TestMessageWithFields(t *testing.T) { }` p := NewParser(strings.NewReader(proto)) p.scanIgnoreWhitespace() // consume first token - m, err := parseMessage(p) + m := new(Message) + err := m.parse(p) if err != nil { t.Fatal(err) } @@ -37,5 +37,4 @@ func TestMessageWithFields(t *testing.T) { if got, want := len(m.Fields), 2; got != want { t.Errorf("got [%v] want [%v]", got, want) } - spew.Dump(m) } diff --git a/option.go b/option.go index e8ede42..9ccdfe3 100644 --- a/option.go +++ b/option.go @@ -3,6 +3,7 @@ package proto3parser import "fmt" type Option struct { + Line int Name string String string Boolean bool @@ -12,6 +13,7 @@ func (o *Option) parse(p *Parser) error { tok, lit := p.scanIgnoreWhitespace() switch tok { case IDENT: + o.Line = p.s.line o.Name = lit case LEFTPAREN: tok, lit = p.scanIgnoreWhitespace() @@ -32,8 +34,7 @@ func (o *Option) parse(p *Parser) error { } tok, lit = p.scanIgnoreWhitespace() if tok == QUOTE { - p.unscan() - ident := p.s.scanUntil('\n') + ident := p.s.scanUntil('"') if len(ident) == 0 { return fmt.Errorf("unexpected end of quoted string") // TODO create constant for this } @@ -42,6 +43,8 @@ func (o *Option) parse(p *Parser) error { } if TRUE == tok || FALSE == tok { o.Boolean = lit == "true" + } else { + return fmt.Errorf("found %q, expected true or false", lit) } return nil } diff --git a/package.go b/package.go new file mode 100644 index 0000000..41f203f --- /dev/null +++ b/package.go @@ -0,0 +1,9 @@ +package proto3parser + +type Package struct { + Name string +} + +func (a *Package) parse(p *Parser) error { + return nil +} diff --git a/parser_test.go b/parser_test.go index 101b4ee..5e991ca 100644 --- a/parser_test.go +++ b/parser_test.go @@ -6,44 +6,6 @@ import ( "testing" ) -func TestSyntax(t *testing.T) { - proto := `syntax = "proto3";` - p := NewParser(strings.NewReader(proto)) - p.scanIgnoreWhitespace() // consume first token - syntax, err := parseSyntax(p) - if err != nil { - t.Fatal(err) - } - if got, want := syntax, "proto3"; got != want { - t.Errorf("got [%v] want [%v]", got, want) - } -} - -func TestService(t *testing.T) { - proto := `service AccountService {}` - stmt, err := NewParser(strings.NewReader(proto)).Parse() - if err != nil { - t.Fatal(err) - } - if got, want := stmt.Services[0].Name, "AccountService"; got != want { - t.Errorf("got [%v] want [%v]", got, want) - } -} - -func TestServiceWithRPCs(t *testing.T) { - proto := `service AccountService { - rpc CreateAccount (CreateAccount) returns (ServiceFault) {} - rpc GetAccount (Int64) returns (Account) {} - }` - stmt, err := NewParser(strings.NewReader(proto)).Parse() - if err != nil { - t.Fatal(err) - } - if got, want := len(stmt.Services[0].RPCalls), 2; got != want { - t.Errorf("got [%v] want [%v]", got, want) - } -} - func TestCommentAroundSyntax(t *testing.T) { proto := ` // comment1 diff --git a/proto.go b/proto.go index 60f6ddb..d32e850 100644 --- a/proto.go +++ b/proto.go @@ -1,9 +1,7 @@ package proto3parser -import "fmt" - type Proto struct { - Syntax string + Syntax *Syntax Imports []*Import Enums []*Enum Services []*Service @@ -11,24 +9,6 @@ type Proto struct { Comments []*Comment } -// parseSyntax returns the syntax value. Parser has seen "syntax". -func parseSyntax(p *Parser) (string, error) { - if tok, lit := p.scanIgnoreWhitespace(); tok != EQUALS { - return "", fmt.Errorf("found %q, expected EQUALS", lit) - } - if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { - return "", fmt.Errorf("found %q, expected QUOTE", lit) - } - tok, lit := p.scanIgnoreWhitespace() - if tok != IDENT { - return "", fmt.Errorf("found %q, expected string", lit) - } - if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { - return "", fmt.Errorf("found %q, expected QUOTE", lit) - } - return lit, nil -} - // Comment holds a message and line number. type Comment struct { Line int @@ -46,11 +26,11 @@ func parseProto(proto *Proto, p *Parser) error { Message: lit, }) case SYNTAX: - if syntax, err := parseSyntax(p); err != nil { + s := new(Syntax) + if err := s.parse(p); err != nil { return err - } else { - proto.Syntax = syntax } + proto.Syntax = s case IMPORT: im := new(Import) if err := im.parse(p); err != nil { @@ -70,11 +50,11 @@ func parseProto(proto *Proto, p *Parser) error { proto.Services = append(proto.Services, service) } case MESSAGE: - if msg, err := parseMessage(p); err != nil { + msg := new(Message) + if err := msg.parse(p); err != nil { return err - } else { - proto.Messages = append(proto.Messages, msg) } + proto.Messages = append(proto.Messages, msg) case EOF: return nil } diff --git a/scanner.go b/scanner.go index 8a28367..a75e47e 100644 --- a/scanner.go +++ b/scanner.go @@ -93,6 +93,7 @@ func (s *scanner) scanWhitespace() (tok token, lit string) { } func (s *scanner) scanIntegerString() string { + s.scanWhitespace() // Create a buffer and read the current character into it. var buf bytes.Buffer buf.WriteRune(s.read()) @@ -153,6 +154,10 @@ func (s *scanner) scanIdent() (tok token, lit string) { return OPTION, buf.String() case "ENUM": return ENUM, buf.String() + case "TRUE": + return TRUE, buf.String() + case "FALSE": + return FALSE, buf.String() } // Otherwise return as a regular identifier. @@ -187,8 +192,8 @@ func isDigit(ch rune) bool { return (ch >= '0' && ch <= '9') } // eof represents a marker rune for the end of the reader. var eof = rune(0) -// scanUntil returns the string up to (not including) the rune or EOF. -func (s *scanner) scanUntil(ch rune) string { +// scanUntil returns the string up to (not including) the terminator or EOF. +func (s *scanner) scanUntil(terminator rune) string { // Create a buffer and read the current character into it. var buf bytes.Buffer buf.WriteRune(s.read()) @@ -197,7 +202,7 @@ func (s *scanner) scanUntil(ch rune) string { for { if ch := s.read(); ch == eof { break - } else if ch == '\n' { + } else if ch == terminator { break } else { buf.WriteRune(ch) diff --git a/service.go b/service.go index a7c35c6..1e40748 100644 --- a/service.go +++ b/service.go @@ -21,6 +21,11 @@ func (s Service) String() string { return buf.String() } +// Accept dispatches the call to the visitor. +func (s *Service) Accept(v Visitor) { + v.VisitService(s) +} + func parseService(p *Parser) (*Service, error) { s := new(Service) tok, lit := p.scanIgnoreWhitespace() diff --git a/service_test.go b/service_test.go new file mode 100644 index 0000000..45fa732 --- /dev/null +++ b/service_test.go @@ -0,0 +1,31 @@ +package proto3parser + +import ( + "strings" + "testing" +) + +func TestService(t *testing.T) { + proto := `service AccountService {}` + stmt, err := NewParser(strings.NewReader(proto)).Parse() + if err != nil { + t.Fatal(err) + } + if got, want := stmt.Services[0].Name, "AccountService"; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} + +func TestServiceWithRPCs(t *testing.T) { + proto := `service AccountService { + rpc CreateAccount (CreateAccount) returns (ServiceFault) {} + rpc GetAccount (Int64) returns (Account) {} + }` + stmt, err := NewParser(strings.NewReader(proto)).Parse() + if err != nil { + t.Fatal(err) + } + if got, want := len(stmt.Services[0].RPCalls), 2; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} diff --git a/syntax.go b/syntax.go new file mode 100644 index 0000000..5c36a27 --- /dev/null +++ b/syntax.go @@ -0,0 +1,25 @@ +package proto3parser + +import "fmt" + +type Syntax struct { + Value string +} + +func (s *Syntax) parse(p *Parser) error { + if tok, lit := p.scanIgnoreWhitespace(); tok != EQUALS { + return fmt.Errorf("found %q, expected EQUALS", lit) + } + if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { + return fmt.Errorf("found %q, expected QUOTE", lit) + } + tok, lit := p.scanIgnoreWhitespace() + if tok != IDENT { + return fmt.Errorf("found %q, expected string", lit) + } + if tok, lit := p.scanIgnoreWhitespace(); tok != QUOTE { + return fmt.Errorf("found %q, expected QUOTE", lit) + } + s.Value = lit + return nil +} diff --git a/syntax_test.go b/syntax_test.go new file mode 100644 index 0000000..699dfda --- /dev/null +++ b/syntax_test.go @@ -0,0 +1,20 @@ +package proto3parser + +import ( + "strings" + "testing" +) + +func TestSyntax(t *testing.T) { + proto := `syntax = "proto3";` + p := NewParser(strings.NewReader(proto)) + p.scanIgnoreWhitespace() // consume first token + s := new(Syntax) + err := s.parse(p) + if err != nil { + t.Fatal(err) + } + if got, want := s.Value, "proto3"; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} diff --git a/visitor.go b/visitor.go new file mode 100644 index 0000000..c1e536c --- /dev/null +++ b/visitor.go @@ -0,0 +1,10 @@ +package proto3parser + +type Visitor interface { + VisitMessage(m *Message) + VisitService(v *Service) +} + +type Visitee interface { + Accept(v Visitor) +}