Skip to content

Commit 2ee7161

Browse files
authored
allow comment in between tokens of a field definition ( #131 ) (#142)
* allow comment in between tokens of a field definition * also read comment in between sequence and options or comma * applied suggestions from PR
1 parent 7bb74f1 commit 2ee7161

File tree

5 files changed

+129
-17
lines changed

5 files changed

+129
-17
lines changed

comment_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,29 @@ message Foo {
310310
t.Fatalf("got [%v] want [%v]", got, want)
311311
}
312312
}
313+
314+
func TestNormalFieldInlineComment(t *testing.T) {
315+
src := `message Example {
316+
/* i'm */ optional /* a comment */ bool /* too */ field /* hard */ = /* to */ 1 /* read */;
317+
}`
318+
p := newParserOn(src)
319+
def, err := p.Parse()
320+
if err != nil {
321+
t.Fatal(err)
322+
}
323+
m := def.Elements[0].(*Message)
324+
f := m.Elements[1].(*NormalField)
325+
lines := f.Field.InlineComment.Lines
326+
if got, want := len(lines), 5; got != want {
327+
t.Fatalf("got [%v:%T] want [%v:%T]", got, got, want, want)
328+
}
329+
if got, want := lines[0], " a comment "; got != want {
330+
t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
331+
}
332+
if got, want := lines[1], " too "; got != want {
333+
t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
334+
}
335+
if got, want := lines[4], " read "; got != want {
336+
t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
337+
}
338+
}

field.go

+67-16
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,15 @@ func (f *NormalField) Doc() *Comment {
6868
// [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
6969
func (f *NormalField) parse(p *Parser) error {
7070
for {
71-
_, tok, lit := p.nextTypeName()
71+
pos, tok, lit := p.nextTypeName()
7272
switch tok {
73+
case tCOMMENT:
74+
c := newComment(pos, lit)
75+
if f.InlineComment == nil {
76+
f.InlineComment = c
77+
} else {
78+
f.InlineComment.Merge(c)
79+
}
7380
case tREPEATED:
7481
f.Repeated = true
7582
return f.parse(p)
@@ -90,24 +97,53 @@ done:
9097
// parseFieldAfterType expects:
9198
// fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";
9299
func parseFieldAfterType(f *Field, p *Parser, parent Visitee) error {
93-
pos, tok, lit := p.next()
94-
if tok != tIDENT {
95-
if !isKeyword(tok) {
96-
return p.unexpected(lit, "field identifier", f)
100+
expectedToken := tIDENT
101+
expected := "field identifier"
102+
103+
for {
104+
pos, tok, lit := p.next()
105+
if tok == tCOMMENT {
106+
c := newComment(pos, lit)
107+
if f.InlineComment == nil {
108+
f.InlineComment = c
109+
} else {
110+
f.InlineComment.Merge(c)
111+
}
112+
continue
113+
}
114+
if tok != expectedToken {
115+
return p.unexpected(lit, expected, f)
116+
}
117+
// found expected token
118+
if tok == tIDENT {
119+
if isKeyword(tok) {
120+
return p.unexpected(lit, expected, f)
121+
}
122+
f.Name = lit
123+
expectedToken = tEQUALS
124+
expected = "field ="
125+
continue
126+
}
127+
if tok == tEQUALS {
128+
expectedToken = tNUMBER
129+
expected = "field sequence number"
130+
continue
131+
}
132+
if tok == tNUMBER {
133+
// put it back so we can use the generic nextInteger
134+
p.nextPut(pos, tok, lit)
135+
i, err := p.nextInteger()
136+
if err != nil {
137+
return p.unexpected(lit, expected, f)
138+
}
139+
f.Sequence = i
140+
break
97141
}
98142
}
99-
f.Name = lit
100-
pos, tok, lit = p.next()
101-
if tok != tEQUALS {
102-
return p.unexpected(lit, "field =", f)
103-
}
104-
i, err := p.nextInteger()
105-
if err != nil {
106-
return p.unexpected(lit, "field sequence number", f)
107-
}
108-
f.Sequence = i
143+
consumeFieldComments(f, p)
144+
109145
// see if there are options
110-
pos, tok, _ = p.next()
146+
pos, tok, lit := p.next()
111147
if tLEFTSQUARE != tok {
112148
p.nextPut(pos, tok, lit)
113149
return nil
@@ -135,6 +171,21 @@ func parseFieldAfterType(f *Field, p *Parser, parent Visitee) error {
135171
return nil
136172
}
137173

174+
func consumeFieldComments(f *Field, p *Parser) {
175+
pos, tok, lit := p.next()
176+
for tok == tCOMMENT {
177+
c := newComment(pos, lit)
178+
if f.InlineComment == nil {
179+
f.InlineComment = c
180+
} else {
181+
f.InlineComment.Merge(c)
182+
}
183+
pos, tok, lit = p.next()
184+
}
185+
// no longer a comment, put it back
186+
p.nextPut(pos, tok, lit)
187+
}
188+
138189
// MapField represents a map entry in a message.
139190
type MapField struct {
140191
*Field

parser.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func (p *Parser) nextInteger() (i int, err error) {
190190
i, err = p.nextInteger()
191191
return i * -1, err
192192
}
193-
if tok != tIDENT {
193+
if tok != tNUMBER {
194194
return 0, errors.New("non integer")
195195
}
196196
if strings.HasPrefix(lit, "0x") || strings.HasPrefix(lit, "0X") {

token.go

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package proto
2525

2626
import (
27+
"strconv"
2728
"strings"
2829
)
2930

@@ -78,6 +79,9 @@ const (
7879
tENUM
7980
tSTREAM
8081

82+
// numbers (pos or neg, float)
83+
tNUMBER
84+
8185
// BEGIN proto2
8286
tOPTIONAL
8387
tGROUP
@@ -119,6 +123,15 @@ func isComment(lit string) bool {
119123
return strings.HasPrefix(lit, "//") || strings.HasPrefix(lit, "/*")
120124
}
121125

126+
func isNumber(lit string) bool {
127+
if strings.HasPrefix(lit, "0x") || strings.HasPrefix(lit, "0X") {
128+
_, err := strconv.ParseInt(lit, 0, 64)
129+
return err == nil
130+
}
131+
_, err := strconv.ParseFloat(lit, 64)
132+
return err == nil
133+
}
134+
122135
const doubleQuoteRune = rune('"')
123136

124137
// unQuote removes one matching leading and trailing single or double quote.
@@ -220,6 +233,9 @@ func asToken(literal string) token {
220233
return tREQUIRED
221234
default:
222235
// special cases
236+
if isNumber(literal) {
237+
return tNUMBER
238+
}
223239
if isComment(literal) {
224240
return tCOMMENT
225241
}

token_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,22 @@ func TestUnQuoteCases(t *testing.T) {
5151
}
5252
}
5353
}
54+
55+
func TestIsNumber(t *testing.T) {
56+
for i, each := range []struct {
57+
input string
58+
isNumber bool
59+
}{
60+
{`1`, true},
61+
{`1.2`, true},
62+
{`-1.02`, true},
63+
{`a1`, false},
64+
{`0x12`, true},
65+
{`0X77777`, true},
66+
} {
67+
got := isNumber(each.input)
68+
if got != each.isNumber {
69+
t.Errorf("[%d] got [%v] want [%v]", i, got, each.isNumber)
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)