From 67fc05eb678aa0f70a47bf678c2e1119f0220f3c Mon Sep 17 00:00:00 2001 From: Ernest Micklei <> Date: Sat, 25 Nov 2017 22:49:54 +0100 Subject: [PATCH] linenumbers work with new position --- comment.go | 12 ++++++------ comment_test.go | 12 ++++++------ enum.go | 30 +++++++++++++++--------------- enum_test.go | 6 +++--- extensions.go | 2 +- extensions_test.go | 2 +- field.go | 16 ++++++++-------- field_test.go | 2 +- group.go | 12 ++++++------ group_test.go | 2 +- import.go | 2 +- message.go | 44 ++++++++++++++++++++++---------------------- message_test.go | 8 ++++---- oneof.go | 20 ++++++++++---------- oneof_test.go | 4 ++-- option.go | 28 ++++++++++++++-------------- option_test.go | 16 ++++++++-------- package.go | 2 +- parser.go | 25 ++++++++++++------------- position.go | 10 ++++++++++ proto.go | 20 ++++++++++---------- reserved.go | 2 +- scanner.go | 4 ++-- scanner_test.go | 2 +- service.go | 46 +++++++++++++++++++++++----------------------- service_test.go | 4 ++-- syntax.go | 2 +- 27 files changed, 172 insertions(+), 163 deletions(-) diff --git a/comment.go b/comment.go index bb55d28..97c37eb 100644 --- a/comment.go +++ b/comment.go @@ -29,7 +29,7 @@ import ( // Comment holds a message. type Comment struct { - Pos Position + Position Position Lines []string Cstyle bool // refers to /* ... */, C++ style is using // ExtraSlash bool @@ -53,7 +53,7 @@ func newComment(pos Position, lit string) *Comment { nonEmpty = append(nonEmpty, lit) } } - return &Comment{Pos: pos, Lines: nonEmpty, Cstyle: len(lines) > 1, ExtraSlash: extraSlash} + return &Comment{Position: pos, Lines: nonEmpty, Cstyle: len(lines) > 1, ExtraSlash: extraSlash} } // columns is part of columnsPrintable @@ -119,7 +119,7 @@ func maybeScanInlineComment(p *Parser, c elementContainer) { line, tok, lit := p.scanIgnoreWhitespace() esize := len(c.elements()) // seen comment and on same line and elements have been added - if tCOMMENT == tok && p.s.line <= currentLine+1 && esize > 0 { + if tCOMMENT == tok && p.s.pos.Line <= currentPos.Line+1 && esize > 0 { // if the last added element can have an inline comment then set it last := c.elements()[esize-1] if inliner, ok := last.(commentInliner); ok { @@ -143,13 +143,13 @@ func takeLastComment(list []Visitee) (*Comment, []Visitee) { } // mergeOrReturnComment creates a new comment and tries to merge it with the last element (if is a comment and is on the next line). -func mergeOrReturnComment(elements []Visitee, lit string, lineNumber int) *Comment { - com := newComment(lineNumber, lit) +func mergeOrReturnComment(elements []Visitee, lit string, pos Position) *Comment { + com := newComment(pos, lit) // last element must be a comment to merge + // do not merge c-style comments + // last comment line was on previous line if esize := len(elements); esize > 0 { - if last, ok := elements[esize-1].(*Comment); ok && !last.Cstyle && lineNumber <= last.LineNumber+len(last.Lines) { // less than because last line of file could be inline comment + if last, ok := elements[esize-1].(*Comment); ok && !last.Cstyle && pos.Line <= last.Position.Line+len(last.Lines) { // less than because last line of file could be inline comment last.Merge(com) // mark as merged com = nil diff --git a/comment_test.go b/comment_test.go index 1758c70..bacf59f 100644 --- a/comment_test.go +++ b/comment_test.go @@ -28,11 +28,11 @@ import ( ) func TestCreateComment(t *testing.T) { - c0 := newComment(0, "") + c0 := newComment(startPosition, "") if got, want := len(c0.Lines), 1; got != want { t.Errorf("got [%v] want [%v]", got, want) } - c1 := newComment(0, `hello + c1 := newComment(startPosition, `hello world`) if got, want := len(c1.Lines), 2; got != want { t.Errorf("got [%v] want [%v]", got, want) @@ -49,8 +49,8 @@ world`) } func TestTakeLastComment(t *testing.T) { - c0 := newComment(0, "hi") - c1 := newComment(0, "there") + c0 := newComment(startPosition, "hi") + c1 := newComment(startPosition, "there") _, l := takeLastComment([]Visitee{c0, c1}) if got, want := len(l), 1; got != want { t.Fatalf("got [%v] want [%v]", got, want) @@ -83,7 +83,7 @@ func TestParseCommentWithEmptyLinesAndTripleSlash(t *testing.T) { if got, want := def.Elements[0].(*Comment).Lines[4], " comment 4"; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := def.Elements[0].(*Comment).LineNumber, 2; got != want { + if got, want := def.Elements[0].(*Comment).Position.Line, 2; got != want { t.Fatalf("got [%d] want [%d]", got, want) } } @@ -107,7 +107,7 @@ func TestParseCommentWithTripleSlash(t *testing.T) { if got, want := def.Elements[0].(*Comment).Lines[0], " comment 1"; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := def.Elements[0].(*Comment).LineNumber, 2; got != want { + if got, want := def.Elements[0].(*Comment).Position.Line, 2; got != want { t.Fatalf("got [%d] want [%d]", got, want) } } diff --git a/enum.go b/enum.go index 0952939..df3d122 100644 --- a/enum.go +++ b/enum.go @@ -27,10 +27,10 @@ import "strconv" // Enum definition consists of a name and an enum body. type Enum struct { - LineNumber int - Comment *Comment - Name string - Elements []Visitee + Position Position + Comment *Comment + Name string + Elements []Visitee } // Accept dispatches the call to the visitor. @@ -61,7 +61,7 @@ func (e *Enum) takeLastComment() (last *Comment) { } func (e *Enum) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "enum identifier", e) @@ -73,15 +73,15 @@ func (e *Enum) parse(p *Parser) error { return p.unexpected(lit, "enum opening {", e) } for { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() switch tok { case tCOMMENT: - if com := mergeOrReturnComment(e.elements(), lit, line); com != nil { // not merged? + if com := mergeOrReturnComment(e.elements(), lit, pos); com != nil { // not merged? e.Elements = append(e.Elements, com) } case tOPTION: v := new(Option) - v.LineNumber = line + v.Position = pos v.Comment = e.takeLastComment() err := v.parse(p) if err != nil { @@ -95,7 +95,7 @@ func (e *Enum) parse(p *Parser) error { default: p.unscan() f := new(EnumField) - f.LineNumber = line + f.Position = pos f.Comment = e.takeLastComment() err := f.parse(p) if err != nil { @@ -113,7 +113,7 @@ done: // EnumField is part of the body of an Enum. type EnumField struct { - LineNumber int + Position Position Comment *Comment Name string Integer int @@ -150,14 +150,14 @@ func (f EnumField) columns() (cols []aligned) { } func (f *EnumField) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "enum field identifier", f) } } f.Name = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tEQUALS { return p.unexpected(lit, "enum field =", f) } @@ -166,17 +166,17 @@ func (f *EnumField) parse(p *Parser) error { return p.unexpected(lit, "enum field integer", f) } f.Integer = i - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok == tLEFTSQUARE { o := new(Option) - o.LineNumber = line + o.Position = pos o.IsEmbedded = true err := o.parse(p) if err != nil { return err } f.ValueOption = o - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tRIGHTSQUARE { return p.unexpected(lit, "option closing ]", f) } diff --git a/enum_test.go b/enum_test.go index 3f293a0..1e6cb38 100644 --- a/enum_test.go +++ b/enum_test.go @@ -52,14 +52,14 @@ enum EnumAllowingAlias { if got, want := enums[0].Comment.Message(), " enum"; got != want { t.Errorf("got [%v] want [%v]", enums[0].Comment, want) } - if got, want := enums[0].LineNumber, 3; got != want { + if got, want := enums[0].Position.Line, 3; got != want { t.Errorf("got [%d] want [%d]", got, want) } ef1 := enums[0].Elements[1].(*EnumField) if got, want := ef1.Integer, 0; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := ef1.LineNumber, 5; got != want { + if got, want := ef1.Position.Line, 5; got != want { t.Errorf("got [%d] want [%d]", got, want) } ef3 := enums[0].Elements[3].(*EnumField) @@ -72,7 +72,7 @@ enum EnumAllowingAlias { if got, want := ef3.ValueOption.Constant.Source, "hello world"; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := ef3.LineNumber, 7; got != want { + if got, want := ef3.Position.Line, 7; got != want { t.Errorf("got [%d] want [%d]", got, want) } } diff --git a/extensions.go b/extensions.go index 566ac11..2ed4c28 100644 --- a/extensions.go +++ b/extensions.go @@ -26,7 +26,7 @@ package proto // Extensions declare that a range of field numbers in a message are available for third-party extensions. // proto2 only type Extensions struct { - LineNumber int + Position Position Comment *Comment Ranges []Range InlineComment *Comment diff --git a/extensions_test.go b/extensions_test.go index 70d0dbe..0f66cec 100644 --- a/extensions_test.go +++ b/extensions_test.go @@ -44,7 +44,7 @@ func TestExtensions(t *testing.T) { if got, want := len(f.Ranges), 2; got != want { t.Fatalf("got [%d] want [%d]", got, want) } - if got, want := f.LineNumber, 3; got != want { + if got, want := f.Position.Line, 3; got != want { t.Fatalf("got [%d] want [%d]", got, want) } if got, want := f.Ranges[1].String(), "20 to max"; got != want { diff --git a/field.go b/field.go index 6478fe4..cb66e35 100644 --- a/field.go +++ b/field.go @@ -27,7 +27,7 @@ import "strconv" // Field is an abstract message field. type Field struct { - LineNumber int + Position Position Comment *Comment Name string Type string @@ -95,8 +95,8 @@ func (f *NormalField) columns() (cols []aligned) { // [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";" func (f *NormalField) parse(p *Parser) error { for { - line, tok, lit := p.scanIgnoreWhitespace() - f.LineNumber = line + pos, tok, lit := p.scanIgnoreWhitespace() + f.Position = pos switch tok { case tREPEATED: f.Repeated = true @@ -118,14 +118,14 @@ done: // parseFieldAfterType expects: // fieldName "=" fieldNumber [ "[" fieldOptions "]" ] "; func parseFieldAfterType(f *Field, p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "field identifier", f) } } f.Name = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tEQUALS { return p.unexpected(lit, "field =", f) } @@ -135,7 +135,7 @@ func parseFieldAfterType(f *Field, p *Parser) error { } f.Sequence = i // see if there are options - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tLEFTSQUARE != tok { p.unscan() return nil @@ -143,7 +143,7 @@ func parseFieldAfterType(f *Field, p *Parser) error { // consume options for { o := new(Option) - o.LineNumber = line + o.Position = pos o.IsEmbedded = true err := o.parse(p) if err != nil { @@ -151,7 +151,7 @@ func parseFieldAfterType(f *Field, p *Parser) error { } f.Options = append(f.Options, o) - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tRIGHTSQUARE == tok { break } diff --git a/field_test.go b/field_test.go index cb5ae6a..dc0678f 100644 --- a/field_test.go +++ b/field_test.go @@ -60,7 +60,7 @@ func TestField(t *testing.T) { if got, want := f.Options[2].Constant.Source, "happy"; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := f.LineNumber, 1; got != want { + if got, want := f.Position.Line, 1; got != want { t.Errorf("got [%v] want [%v]", got, want) } } diff --git a/group.go b/group.go index 61abe08..d22b276 100644 --- a/group.go +++ b/group.go @@ -26,12 +26,12 @@ package proto // Group represents a (proto2 only) group. // https://developers.google.com/protocol-buffers/docs/reference/proto2-spec#group_field type Group struct { - LineNumber int - Comment *Comment - Name string - Optional bool - Sequence int - Elements []Visitee + Position Position + Comment *Comment + Name string + Optional bool + Sequence int + Elements []Visitee } // Accept dispatches the call to the visitor. diff --git a/group_test.go b/group_test.go index 2e8ae2f..255a05f 100644 --- a/group_test.go +++ b/group_test.go @@ -47,7 +47,7 @@ func TestGroup(t *testing.T) { if got, want := len(g.Elements), 1; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := g.LineNumber, 3; got != want { + if got, want := g.Position.Line, 3; got != want { t.Fatalf("got [%v] want [%v]", got, want) } if got, want := g.Comment != nil, true; got != want { diff --git a/import.go b/import.go index 01f3ef0..87a2d42 100644 --- a/import.go +++ b/import.go @@ -27,7 +27,7 @@ import "fmt" // Import holds a filename to another .proto definition. type Import struct { - LineNumber int + Position Position Comment *Comment Filename string Kind string // weak, public, diff --git a/message.go b/message.go index 4cca1e5..e74f37b 100644 --- a/message.go +++ b/message.go @@ -25,11 +25,11 @@ package proto // Message consists of a message name and a message body. type Message struct { - LineNumber int - Comment *Comment - Name string - IsExtend bool - Elements []Visitee + Position Position + Comment *Comment + Name string + IsExtend bool + Elements []Visitee } func (m *Message) groupName() string { @@ -58,20 +58,20 @@ func (m *Message) parse(p *Parser) error { // parseMessageBody parses elements after {. It consumes the closing } func parseMessageBody(p *Parser, c elementContainer) error { var ( - line int - tok token - lit string + pos Position + tok token + lit string ) for { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() switch tok { case tCOMMENT: - if com := mergeOrReturnComment(c.elements(), lit, line); com != nil { // not merged? + if com := mergeOrReturnComment(c.elements(), lit, pos); com != nil { // not merged? c.addElement(com) } case tENUM: e := new(Enum) - e.LineNumber = line + e.Position = pos e.Comment = c.takeLastComment() if err := e.parse(p); err != nil { return err @@ -79,7 +79,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(e) case tMESSAGE: msg := new(Message) - msg.LineNumber = line + msg.Position = pos msg.Comment = c.takeLastComment() if err := msg.parse(p); err != nil { return err @@ -87,7 +87,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(msg) case tOPTION: o := new(Option) - o.LineNumber = line + o.Position = pos o.Comment = c.takeLastComment() if err := o.parse(p); err != nil { return err @@ -95,7 +95,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(o) case tONEOF: o := new(Oneof) - o.LineNumber = line + o.Position = pos o.Comment = c.takeLastComment() if err := o.parse(p); err != nil { return err @@ -103,7 +103,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(o) case tMAP: f := newMapField() - f.LineNumber = line + f.Position = pos f.Comment = c.takeLastComment() if err := f.parse(p); err != nil { return err @@ -111,7 +111,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(f) case tRESERVED: r := new(Reserved) - r.LineNumber = line + r.Position = pos r.Comment = c.takeLastComment() if err := r.parse(p); err != nil { return err @@ -124,7 +124,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { _, tok, lit = p.scanIgnoreWhitespace() if tGROUP == tok { g := new(Group) - g.LineNumber = line + g.Position = pos g.Comment = c.takeLastComment() g.Optional = prevTok == tOPTIONAL if err := g.parse(p); err != nil { @@ -135,7 +135,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { // not a group, will be tFIELD p.unscan() f := newNormalField() - f.LineNumber = line + f.Position = pos f.Comment = c.takeLastComment() f.Optional = prevTok == tOPTIONAL f.Repeated = prevTok == tREPEATED @@ -147,7 +147,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { } case tGROUP: g := new(Group) - g.LineNumber = line + g.Position = pos g.Comment = c.takeLastComment() if err := g.parse(p); err != nil { return err @@ -155,7 +155,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(g) case tEXTENSIONS: e := new(Extensions) - e.LineNumber = line + e.Position = pos e.Comment = c.takeLastComment() if err := e.parse(p); err != nil { return err @@ -163,7 +163,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { c.addElement(e) case tEXTEND: e := new(Message) - e.LineNumber = line + e.Position = pos e.Comment = c.takeLastComment() e.IsExtend = true if err := e.parse(p); err != nil { @@ -180,7 +180,7 @@ func parseMessageBody(p *Parser, c elementContainer) error { // tFIELD p.unscan() f := newNormalField() - f.LineNumber = line + f.Position = pos f.Comment = c.takeLastComment() if err := f.parse(p); err != nil { return err diff --git a/message_test.go b/message_test.go index d7c784e..00bb5de 100644 --- a/message_test.go +++ b/message_test.go @@ -56,16 +56,16 @@ func TestMessage(t *testing.T) { if got, want := len(m.Elements), 6; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := m.Elements[0].(*NormalField).LineNumber, 4; got != want { + if got, want := m.Elements[0].(*NormalField).Position.Line, 4; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := m.Elements[0].(*NormalField).Comment.LineNumber, 3; got != want { + if got, want := m.Elements[0].(*NormalField).Comment.Position.Line, 3; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := m.Elements[3].(*Message).LineNumber, 12; got != want { + if got, want := m.Elements[3].(*Message).Position.Line, 12; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := m.Elements[3].(*Message).Elements[0].(*NormalField).LineNumber, 13; got != want { + if got, want := m.Elements[3].(*Message).Elements[0].(*NormalField).Position.Line, 13; got != want { t.Errorf("got [%v] want [%v]", got, want) } } diff --git a/oneof.go b/oneof.go index eccb8f8..6f1cbda 100644 --- a/oneof.go +++ b/oneof.go @@ -27,10 +27,10 @@ import "strconv" // Oneof is a field alternate. type Oneof struct { - LineNumber int - Comment *Comment - Name string - Elements []Visitee + Position Position + Comment *Comment + Name string + Elements []Visitee } // addElement is part of elementContainer @@ -53,27 +53,27 @@ func (o *Oneof) takeLastComment() (last *Comment) { // parse expects: // oneofName "{" { oneofField | emptyStatement } "}" func (o *Oneof) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "oneof identifier", o) } } o.Name = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tLEFTCURLY { return p.unexpected(lit, "oneof opening {", o) } for { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() switch tok { case tCOMMENT: - if com := mergeOrReturnComment(o.elements(), lit, p.s.line); com != nil { // not merged? + if com := mergeOrReturnComment(o.elements(), lit, p.s.pos); com != nil { // not merged? o.Elements = append(o.Elements, com) } case tIDENT: f := newOneOfField() - f.LineNumber = line + f.Position = pos f.Comment, o.Elements = takeLastComment(o.elements()) f.Type = lit if err := parseFieldAfterType(f.Field, p); err != nil { @@ -82,7 +82,7 @@ func (o *Oneof) parse(p *Parser) error { o.Elements = append(o.Elements, f) case tGROUP: g := new(Group) - g.LineNumber = line + g.Position = pos g.Comment, o.Elements = takeLastComment(o.elements()) if err := g.parse(p); err != nil { return err diff --git a/oneof_test.go b/oneof_test.go index 3b1209b..0ea74fb 100644 --- a/oneof_test.go +++ b/oneof_test.go @@ -48,7 +48,7 @@ func TestOneof(t *testing.T) { if got, want := first.Comment.Message(), " just a name"; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := first.LineNumber, 3; got != want { + if got, want := first.Position.Line, 3; got != want { t.Errorf("got [%v] want [%v]", got, want) } second := o.Elements[1].(*OneOfField) @@ -61,7 +61,7 @@ func TestOneof(t *testing.T) { if got, want := second.Sequence, 9; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := second.LineNumber, 4; got != want { + if got, want := second.Position.Line, 4; got != want { t.Errorf("got [%v] want [%v]", got, want) } } diff --git a/option.go b/option.go index 2f22dcb..24ec070 100644 --- a/option.go +++ b/option.go @@ -28,7 +28,7 @@ import "bytes" // Option is a protoc compiler option type Option struct { - LineNumber int + Position Position Comment *Comment Name string Constant Literal @@ -70,15 +70,15 @@ func (o *Option) keyValuePair(embedded bool) (cols []aligned) { // parse reads an Option body // ( ident | "(" fullIdent ")" ) { "." ident } "=" constant ";" func (o *Option) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tLEFTPAREN == tok { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "option full identifier", o) } } - line, tok, _ = p.scanIgnoreWhitespace() + pos, tok, _ = p.scanIgnoreWhitespace() if tok != tRIGHTPAREN { return p.unexpected(lit, "full identifier closing )", o) } @@ -92,15 +92,15 @@ func (o *Option) parse(p *Parser) error { } o.Name = lit } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tDOT == tok { // extend identifier - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tIDENT { return p.unexpected(lit, "option postfix identifier", o) } o.Name = fmt.Sprintf("%s.%s", o.Name, lit) - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() } if tEQUALS != tok { return p.unexpected(lit, "option constant =", o) @@ -112,7 +112,7 @@ func (o *Option) parse(p *Parser) error { } // non aggregate l := new(Literal) - l.LineNumber = line + l.Position = pos if err := l.parse(p); err != nil { return err } @@ -137,9 +137,9 @@ func (o *Option) Doc() *Comment { // Literal represents intLit,floatLit,strLit or boolLit type Literal struct { - LineNumber int - Source string - IsString bool + Position Position + Source string + IsString bool } // String returns the source (if quoted then use double quote). @@ -171,7 +171,7 @@ type NamedLiteral struct { func (o *Option) parseAggregate(p *Parser) error { o.AggregatedConstants = []*NamedLiteral{} for { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tRIGHTSQUARE == tok { p.unscan() // caller has checked for open square ; will consume rightsquare, rightcurly and semicolon @@ -193,12 +193,12 @@ func (o *Option) parseAggregate(p *Parser) error { return p.unexpected(lit, "option aggregate key", o) } key := lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tCOLON != tok { return p.unexpected(lit, "option aggregate key colon :", o) } l := new(Literal) - l.LineNumber = line + l.Position = pos if err := l.parse(p); err != nil { return err } diff --git a/option_test.go b/option_test.go index 14fb069..2e70e58 100644 --- a/option_test.go +++ b/option_test.go @@ -125,13 +125,13 @@ option Help = "me"; // inline` if got, want := o.InlineComment.Lines[0], " inline"; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := o.LineNumber, 3; got != want { + if got, want := o.Position.Line, 3; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := o.Comment.LineNumber, 2; got != want { + if got, want := o.Comment.Position.Line, 2; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := o.InlineComment.LineNumber, 3; got != want { + if got, want := o.InlineComment.Position.Line, 3; got != want { t.Fatalf("got [%v] want [%v]", got, want) } } @@ -173,19 +173,19 @@ message Bar { if got, want := ac[1].Source, "baz"; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := o.LineNumber, 3; got != want { + if got, want := o.Position.Line, 3; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := o.Comment.LineNumber, 2; got != want { + if got, want := o.Comment.Position.Line, 2; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := f.LineNumber, 5; got != want { + if got, want := f.Position.Line, 5; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := ac[0].LineNumber, 6; got != want { + if got, want := ac[0].Position.Line, 6; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := ac[1].LineNumber, 7; got != want { + if got, want := ac[1].Position.Line, 7; got != want { t.Fatalf("got [%v] want [%v]", got, want) } } diff --git a/package.go b/package.go index e2f8f65..3fa5167 100644 --- a/package.go +++ b/package.go @@ -25,7 +25,7 @@ package proto // Package specifies the namespace for all proto elements. type Package struct { - LineNumber int + Position Position Comment *Comment Name string InlineComment *Comment diff --git a/parser.go b/parser.go index ee6d095..4019d03 100644 --- a/parser.go +++ b/parser.go @@ -33,11 +33,10 @@ import ( type Parser struct { s *scanner buf struct { - line int // line (1+) in document where the token started - col int // column (1+) on the line where the token started - tok token // last read token - lit string // last read literal - n int // buffer size (max=1) + pos Position // location where last read token started + tok token // last read token + lit string // last read literal + n int // buffer size (max=1) } debug bool } @@ -55,27 +54,27 @@ func (p *Parser) Parse() (*Proto, error) { // scan returns the next token from the underlying scanner. // If a token has been unscanned then read that instead. -func (p *Parser) scan() (line int, tok token, lit string) { +func (p *Parser) scan() (pos Position, tok token, lit string) { // If we have a token on the buffer, then return it. if p.buf.n != 0 { p.buf.n = 0 - return p.buf.line, p.buf.tok, p.buf.lit + return p.buf.pos, p.buf.tok, p.buf.lit } // Otherwise read the next token from the scanner. - line, tok, lit = p.s.scan() + pos, tok, lit = p.s.scan() // Save it to the buffer in case we unscan later. - p.buf.line, p.buf.tok, p.buf.lit = line, tok, lit + p.buf.pos, p.buf.tok, p.buf.lit = pos, tok, lit return } // scanIgnoreWhitespace scans the next non-whitespace token. -func (p *Parser) scanIgnoreWhitespace() (line int, tok token, lit string) { - line, tok, lit = p.scan() +func (p *Parser) scanIgnoreWhitespace() (pos Position, tok token, lit string) { + pos, tok, lit = p.scan() if tok == tWS { - line, tok, lit = p.scan() + pos, tok, lit = p.scan() } return } @@ -89,5 +88,5 @@ func (p *Parser) unexpected(found, expected string, obj interface{}) error { _, file, line, _ := runtime.Caller(1) debug = fmt.Sprintf(" at %s:%d (with %#v)", file, line, obj) } - return fmt.Errorf("found %q on line %d, expected [%s]%s", found, p.s.line, expected, debug) + return fmt.Errorf("found %q on %v, expected [%s]%s", found, p.s.pos, expected, debug) } diff --git a/position.go b/position.go index a3f4e10..4252dec 100644 --- a/position.go +++ b/position.go @@ -41,3 +41,13 @@ func (pos Position) String() string { } return s } + +// p.s.line <= currentLine+1 + +func (pos Position) NextLine() Position { + return Position{Filename: pos.Filename, Line: pos.Line + 1, Column: 1} +} + +func (pos Position) PreviousLine() Position { + return Position{Filename: pos.Filename, Line: pos.Line - 1, Column: 1} +} diff --git a/proto.go b/proto.go index abb1c7a..9b73626 100644 --- a/proto.go +++ b/proto.go @@ -48,15 +48,15 @@ func (proto *Proto) takeLastComment() (last *Comment) { // parse parsers a complete .proto definition source. func (proto *Proto) parse(p *Parser) error { for { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() switch tok { case tCOMMENT: - if com := mergeOrReturnComment(proto.Elements, lit, line); com != nil { // not merged? + if com := mergeOrReturnComment(proto.Elements, lit, pos); com != nil { // not merged? proto.Elements = append(proto.Elements, com) } case tOPTION: o := new(Option) - o.LineNumber = line + o.Position = pos o.Comment, proto.Elements = takeLastComment(proto.Elements) if err := o.parse(p); err != nil { return err @@ -64,7 +64,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, o) case tSYNTAX: s := new(Syntax) - s.LineNumber = line + s.Position = pos s.Comment, proto.Elements = takeLastComment(proto.Elements) if err := s.parse(p); err != nil { return err @@ -72,7 +72,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, s) case tIMPORT: im := new(Import) - im.LineNumber = line + im.Position = pos im.Comment, proto.Elements = takeLastComment(proto.Elements) if err := im.parse(p); err != nil { return err @@ -80,7 +80,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, im) case tENUM: enum := new(Enum) - enum.LineNumber = line + enum.Position = pos enum.Comment, proto.Elements = takeLastComment(proto.Elements) if err := enum.parse(p); err != nil { return err @@ -88,7 +88,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, enum) case tSERVICE: service := new(Service) - service.LineNumber = line + service.Position = pos service.Comment, proto.Elements = takeLastComment(proto.Elements) err := service.parse(p) if err != nil { @@ -97,7 +97,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, service) case tPACKAGE: pkg := new(Package) - pkg.LineNumber = line + pkg.Position = pos pkg.Comment, proto.Elements = takeLastComment(proto.Elements) if err := pkg.parse(p); err != nil { return err @@ -105,7 +105,7 @@ func (proto *Proto) parse(p *Parser) error { proto.Elements = append(proto.Elements, pkg) case tMESSAGE: msg := new(Message) - msg.LineNumber = line + msg.Position = pos msg.Comment, proto.Elements = takeLastComment(proto.Elements) if err := msg.parse(p); err != nil { return err @@ -114,7 +114,7 @@ func (proto *Proto) parse(p *Parser) error { // BEGIN proto2 case tEXTEND: msg := new(Message) - msg.LineNumber = line + msg.Position = pos msg.Comment, proto.Elements = takeLastComment(proto.Elements) msg.IsExtend = true if err := msg.parse(p); err != nil { diff --git a/reserved.go b/reserved.go index 68dbbc3..62acdbf 100644 --- a/reserved.go +++ b/reserved.go @@ -25,7 +25,7 @@ package proto // Reserved statements declare a range of field numbers or field names that cannot be used in a message. type Reserved struct { - LineNumber int + Position Position Comment *Comment Ranges []Range FieldNames []string diff --git a/scanner.go b/scanner.go index cc03399..4d38a95 100644 --- a/scanner.go +++ b/scanner.go @@ -263,7 +263,7 @@ func (s *scanner) read() rune { return eof } if '\n' == ch { - s.line++ + s.pos = s.pos.NextLine() } return ch } @@ -273,7 +273,7 @@ func (s *scanner) read() rune { func (s *scanner) unread(ch rune) { _ = s.r.UnreadRune() if '\n' == ch { - s.line-- + s.pos = s.pos.PreviousLine() } } diff --git a/scanner_test.go b/scanner_test.go index 548c64f..3d3b850 100644 --- a/scanner_test.go +++ b/scanner_test.go @@ -36,7 +36,7 @@ world`) if got, want := v, "hello"; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := s.line, 2; got != want { + if got, want := s.pos.Line, 2; got != want { t.Errorf("got [%v] want [%v]", got, want) } } diff --git a/service.go b/service.go index 5950929..043af15 100644 --- a/service.go +++ b/service.go @@ -30,10 +30,10 @@ import ( // Service defines a set of RPC calls. type Service struct { - LineNumber int - Comment *Comment - Name string - Elements []Visitee + Position Position + Comment *Comment + Name string + Elements []Visitee } // Accept dispatches the call to the visitor. @@ -65,27 +65,27 @@ func (s *Service) takeLastComment() (last *Comment) { // parse continues after reading "service" func (s *Service) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { if !isKeyword(tok) { return p.unexpected(lit, "service identifier", s) } } s.Name = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tLEFTCURLY { return p.unexpected(lit, "service opening {", s) } for { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() switch tok { case tCOMMENT: - if com := mergeOrReturnComment(s.Elements, lit, p.s.line); com != nil { // not merged? + if com := mergeOrReturnComment(s.Elements, lit, p.s.pos); com != nil { // not merged? s.Elements = append(s.Elements, com) } case tRPC: rpc := new(RPC) - rpc.LineNumber = line + rpc.Position = pos rpc.Comment, s.Elements = takeLastComment(s.Elements) err := rpc.parse(p) if err != nil { @@ -106,7 +106,7 @@ done: // RPC represents an rpc entry in a message. type RPC struct { - LineNumber int + Position Position Comment *Comment Name string RequestType string @@ -179,50 +179,50 @@ func (r *RPC) columns() (cols []aligned) { // parse continues after reading "rpc" func (r *RPC) parse(p *Parser) error { - line, tok, lit := p.scanIgnoreWhitespace() + pos, tok, lit := p.scanIgnoreWhitespace() if tok != tIDENT { return p.unexpected(lit, "rpc method", r) } r.Name = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tLEFTPAREN { return p.unexpected(lit, "rpc type opening (", r) } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if iSTREAM == lit { r.StreamsRequest = true - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() } if tok != tIDENT { return p.unexpected(lit, "rpc stream | request type", r) } r.RequestType = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tRIGHTPAREN { return p.unexpected(lit, "rpc type closing )", r) } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tRETURNS { return p.unexpected(lit, "rpc returns", r) } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tLEFTPAREN { return p.unexpected(lit, "rpc type opening (", r) } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if iSTREAM == lit { r.StreamsReturns = true - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() } if tok != tIDENT { return p.unexpected(lit, "rpc stream | returns type", r) } r.ReturnsType = lit - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tok != tRIGHTPAREN { return p.unexpected(lit, "rpc type closing )", r) } - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tSEMICOLON == tok { p.s.unread(';') // allow for inline comment parsing return nil @@ -230,7 +230,7 @@ func (r *RPC) parse(p *Parser) error { if tLEFTCURLY == tok { // parse options for { - line, tok, lit = p.scanIgnoreWhitespace() + pos, tok, lit = p.scanIgnoreWhitespace() if tRIGHTCURLY == tok { break } @@ -242,7 +242,7 @@ func (r *RPC) parse(p *Parser) error { return p.unexpected(lit, "rpc option", r) } o := new(Option) - o.LineNumber = line + o.Position = pos if err := o.parse(p); err != nil { return err } diff --git a/service_test.go b/service_test.go index dd42c29..e6a6b3e 100644 --- a/service_test.go +++ b/service_test.go @@ -39,7 +39,7 @@ func TestService(t *testing.T) { if got, want := len(srv.Elements), 2; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := srv.LineNumber, 1; got != want { + if got, want := srv.Position.Line, 1; got != want { t.Fatalf("got [%v] want [%v]", got, want) } rpc1 := srv.Elements[0].(*RPC) @@ -49,7 +49,7 @@ func TestService(t *testing.T) { if got, want := rpc1.Doc().Message(), " comment"; got != want { t.Fatalf("got [%v] want [%v]", got, want) } - if got, want := rpc1.LineNumber, 3; got != want { + if got, want := rpc1.Position.Line, 3; got != want { t.Fatalf("got [%v] want [%v]", got, want) } rpc2 := srv.Elements[1].(*RPC) diff --git a/syntax.go b/syntax.go index 7122083..ad03539 100644 --- a/syntax.go +++ b/syntax.go @@ -25,7 +25,7 @@ package proto // Syntax should have value "proto" type Syntax struct { - LineNumber int + Position Position Comment *Comment Value string InlineComment *Comment