Skip to content

Commit de1e17f

Browse files
Alfuskralicky
authored andcommitted
Tolerate missing ';' after package, imports, file options, and method options (bufbuild#212)
While still producing an error. See bufbuild#200
1 parent 3dbf5f1 commit de1e17f

File tree

6 files changed

+717
-622
lines changed

6 files changed

+717
-622
lines changed

ast/file.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,10 @@ func NewImportNode(keyword *KeywordNode, public *KeywordNode, weak *KeywordNode,
275275
if name == nil {
276276
panic("name is nil")
277277
}
278+
numChildren := 2
278279
if semicolon == nil {
279-
panic("semicolon is nil")
280+
numChildren++
280281
}
281-
numChildren := 3
282282
if public != nil || weak != nil {
283283
numChildren++
284284
}
@@ -289,7 +289,10 @@ func NewImportNode(keyword *KeywordNode, public *KeywordNode, weak *KeywordNode,
289289
} else if weak != nil {
290290
children = append(children, weak)
291291
}
292-
children = append(children, name, semicolon)
292+
children = append(children, name)
293+
if semicolon != nil {
294+
children = append(children, semicolon)
295+
}
293296

294297
return &ImportNode{
295298
compositeNode: compositeNode{
@@ -328,10 +331,12 @@ func NewPackageNode(keyword *KeywordNode, name IdentValueNode, semicolon *RuneNo
328331
if name == nil {
329332
panic("name is nil")
330333
}
334+
var children []Node
331335
if semicolon == nil {
332-
panic("semicolon is nil")
336+
children = []Node{keyword, name}
337+
} else {
338+
children = []Node{keyword, name, semicolon}
333339
}
334-
children := []Node{keyword, name, semicolon}
335340
return &PackageNode{
336341
compositeNode: compositeNode{
337342
children: children,

parser/ast.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ func newServiceElements(semicolons []*ast.RuneNode, elements []ast.ServiceElemen
108108
return elems
109109
}
110110

111+
func newMethodElements(semicolons []*ast.RuneNode, elements []ast.RPCElement) []ast.RPCElement {
112+
elems := make([]ast.RPCElement, 0, len(semicolons)+len(elements))
113+
for _, semicolon := range semicolons {
114+
elems = append(elems, ast.NewEmptyDeclNode(semicolon))
115+
}
116+
elems = append(elems, elements...)
117+
return elems
118+
}
119+
120+
func newFileElements(semicolons []*ast.RuneNode, elements []ast.FileElement) []ast.FileElement {
121+
elems := make([]ast.FileElement, 0, len(semicolons)+len(elements))
122+
for _, semicolon := range semicolons {
123+
elems = append(elems, ast.NewEmptyDeclNode(semicolon))
124+
}
125+
elems = append(elems, elements...)
126+
return elems
127+
}
128+
111129
type nodeWithEmptyDecls[T ast.Node] struct {
112130
Node T
113131
EmptyDecls []*ast.EmptyDeclNode
@@ -121,10 +139,28 @@ func newNodeWithEmptyDecls[T ast.Node](node T, extraSemicolons []*ast.RuneNode)
121139
}
122140

123141
func toServiceElements[T ast.ServiceElement](nodes nodeWithEmptyDecls[T]) []ast.ServiceElement {
124-
serviceElements := make([]ast.ServiceElement, 1+len(nodes.EmptyDecls))
125-
serviceElements[0] = nodes.Node
142+
elements := make([]ast.ServiceElement, 1+len(nodes.EmptyDecls))
143+
elements[0] = nodes.Node
144+
for i, emptyDecl := range nodes.EmptyDecls {
145+
elements[i+1] = emptyDecl
146+
}
147+
return elements
148+
}
149+
150+
func toMethodElements[T ast.RPCElement](nodes nodeWithEmptyDecls[T]) []ast.RPCElement {
151+
elements := make([]ast.RPCElement, 1+len(nodes.EmptyDecls))
152+
elements[0] = nodes.Node
153+
for i, emptyDecl := range nodes.EmptyDecls {
154+
elements[i+1] = emptyDecl
155+
}
156+
return elements
157+
}
158+
159+
func toFileElements[T ast.FileElement](nodes nodeWithEmptyDecls[T]) []ast.FileElement {
160+
elements := make([]ast.FileElement, 1+len(nodes.EmptyDecls))
161+
elements[0] = nodes.Node
126162
for i, emptyDecl := range nodes.EmptyDecls {
127-
serviceElements[i+1] = emptyDecl
163+
elements[i+1] = emptyDecl
128164
}
129-
return serviceElements
165+
return elements
130166
}

parser/parser_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,30 @@ func TestLenientParse_SemicolonLess(t *testing.T) {
8888
Error string
8989
NoError string
9090
}{
91+
"package": {
92+
Error: `syntax = "proto3";
93+
package foo
94+
message Foo {}`,
95+
NoError: `syntax = "proto3";
96+
package foo;
97+
message Foo {};`,
98+
},
99+
"import": {
100+
Error: `syntax = "proto3";
101+
import "foo.proto"
102+
message Foo {}`,
103+
NoError: `syntax = "proto3";
104+
import "foo.proto";;
105+
message Foo {};`,
106+
},
107+
"file-options": {
108+
Error: `syntax = "proto3";
109+
option (foo) = 1
110+
message Foo {}`,
111+
NoError: `syntax = "proto3";
112+
option (foo) = 1;;
113+
message Foo {};`,
114+
},
91115
"method": {
92116
Error: `syntax = "proto3";
93117
service Foo {
@@ -112,6 +136,22 @@ func TestLenientParse_SemicolonLess(t *testing.T) {
112136
option (foo) = { bar: 1 };
113137
}`,
114138
},
139+
"method-options": {
140+
Error: `syntax = "proto3";
141+
service Foo {
142+
rpc Bar (Baz) returns (Qux) {
143+
;
144+
option (foo) = { bar: 1 }
145+
}
146+
}`,
147+
NoError: `syntax = "proto3";
148+
service Foo {
149+
rpc Bar (Baz) returns (Qux) {
150+
;
151+
option (foo) = { bar: 1 };;
152+
}
153+
}`,
154+
},
115155
}
116156
for name, input := range inputs {
117157
name, input := name, input

0 commit comments

Comments
 (0)