Skip to content

Commit

Permalink
mrclean
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfus committed Nov 16, 2023
1 parent c94aa09 commit 7d89510
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 77 deletions.
25 changes: 0 additions & 25 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,28 +137,3 @@ func (e *EmptyDeclNode) oneofElement() {}
func (e *EmptyDeclNode) enumElement() {}
func (e *EmptyDeclNode) serviceElement() {}
func (e *EmptyDeclNode) methodElement() {}

type NodeWithEmptyDecls[T Node] struct {
Decl T
EmptyDecls []*EmptyDeclNode
}

func NewNodeWithEmptyDecls[T Node](decl T, extraSemicolons []*RuneNode) NodeWithEmptyDecls[T] {
emptyDecls := make([]*EmptyDeclNode, len(extraSemicolons))
for i, extraSemicolon := range extraSemicolons {
emptyDecls[i] = NewEmptyDeclNode(extraSemicolon)
}
return NodeWithEmptyDecls[T]{
Decl: decl,
EmptyDecls: emptyDecls,
}
}

func ToServiceElements[T ServiceElement](nodes NodeWithEmptyDecls[T]) []ServiceElement {
serviceElements := make([]ServiceElement, len(nodes.EmptyDecls)+1)
serviceElements[0] = nodes.Decl
for i, emptyDecl := range nodes.EmptyDecls {
serviceElements[i+1] = emptyDecl
}
return serviceElements
}
7 changes: 5 additions & 2 deletions ast/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,13 @@ func NewOptionNode(keyword *KeywordNode, name *OptionNameNode, equals *RuneNode,
if val == nil {
panic("val is nil")
}
var children []Node
if semicolon == nil {
panic("semicolon is nil")
children = []Node{keyword, name, equals, val}
} else {
children = []Node{keyword, name, equals, val, semicolon}
}
children := []Node{keyword, name, equals, val, semicolon}

return &OptionNode{
compositeNode: compositeNode{
children: children,
Expand Down
37 changes: 37 additions & 0 deletions parser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,40 @@ func (list *messageFieldList) toNodes() ([]*ast.MessageFieldNode, []*ast.RuneNod
}
return fields, delimiters
}

func newEmptyDeclNodes(semicolons []*ast.RuneNode) []*ast.EmptyDeclNode {
emptyDecls := make([]*ast.EmptyDeclNode, len(semicolons))
for i, semicolon := range semicolons {
emptyDecls[i] = ast.NewEmptyDeclNode(semicolon)
}
return emptyDecls
}

func newEmptyServiceElements(semicolons []*ast.RuneNode) []ast.ServiceElement {
emptyDecls := make([]ast.ServiceElement, len(semicolons))
for i, semicolon := range semicolons {
emptyDecls[i] = ast.NewEmptyDeclNode(semicolon)
}
return emptyDecls
}

type nodeWithEmptyDecls[T ast.Node] struct {
Node T
EmptyDecls []*ast.EmptyDeclNode
}

func newNodeWithEmptyDecls[T ast.Node](node T, extraSemicolons []*ast.RuneNode) nodeWithEmptyDecls[T] {
return nodeWithEmptyDecls[T]{
Node: node,
EmptyDecls: newEmptyDeclNodes(extraSemicolons),
}
}

func toServiceElements[T ast.ServiceElement](nodes nodeWithEmptyDecls[T]) []ast.ServiceElement {
serviceElements := make([]ast.ServiceElement, 1+len(nodes.EmptyDecls))
serviceElements[0] = nodes.Node
for i, emptyDecl := range nodes.EmptyDecls {
serviceElements[i+1] = emptyDecl
}
return serviceElements
}
8 changes: 8 additions & 0 deletions parser/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,3 +761,11 @@ func (l *protoLex) errWithCurrentPos(err error, offset int) reporter.ErrorWithPo
pos := l.info.SourcePos(l.input.offset() + offset)
return reporter.Error(ast.NewSourceSpan(pos, pos), err)
}

func (l *protoLex) requireSemicolon(semicolons []*ast.RuneNode) (*ast.RuneNode, []*ast.RuneNode) {
if len(semicolons) == 0 {
l.Error("expected ';'")
return nil, nil
}
return semicolons[0], semicolons[1:]
}
39 changes: 31 additions & 8 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,44 @@ func TestJunkParse(t *testing.T) {

func TestLenientParse_SemicolonLess(t *testing.T) {
t.Parallel()
inputs := map[string]string{
"method": `syntax = "proto3";
service Foo {
;
rpc Bar (Baz) returns (Qux)
rpc Qux (Baz) returns (Qux);;
}`,
inputs := map[string]struct {
Error string
NoError string
}{
"method": {
Error: `syntax = "proto3";
service Foo {
;
rpc Bar (Baz) returns (Qux)
rpc Qux (Baz) returns (Qux);;
}`,
NoError: `syntax = "proto3";
service Foo {
;
rpc Bar (Baz) returns (Qux);
rpc Qux (Baz) returns (Qux);;
}`,
},
"service-options": {
Error: `syntax = "proto3";
service Foo {
option (foo) = { bar: 1 }
}`,
NoError: `syntax = "proto3";
service Foo {
option (foo) = { bar: 1 };
}`,
},
}
for name, input := range inputs {
name, input := name, input
t.Run(name, func(t *testing.T) {
t.Parallel()
errHandler := reporter.NewHandler(nil)
protoName := fmt.Sprintf("%s.proto", name)
_, err := Parse(protoName, strings.NewReader(input), errHandler)
_, err := Parse(protoName, strings.NewReader(input.NoError), errHandler)
require.NoError(t, err)
_, err = Parse(protoName, strings.NewReader(input.Error), errHandler)
require.ErrorContains(t, err, "expected ';'")
})
}
Expand Down
31 changes: 10 additions & 21 deletions parser/proto.y
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ import (
svc *ast.ServiceNode
svcElement ast.ServiceElement
svcElements []ast.ServiceElement
mtd ast.NodeWithEmptyDecls[*ast.RPCNode]
mtd nodeWithEmptyDecls[*ast.RPCNode]
mtdMsgType *ast.RPCTypeNode
mtdElement ast.RPCElement
mtdElements []ast.RPCElement
opt *ast.OptionNode
optN ast.NodeWithEmptyDecls[*ast.OptionNode]
optN nodeWithEmptyDecls[*ast.OptionNode]
opts *compactOptionSlices
ref *ast.FieldReferenceNode
optNms *fieldRefSlices
Expand Down Expand Up @@ -318,12 +318,8 @@ optionDecl : _OPTION optionName '=' optionValue ';' {

optionDeclNew : _OPTION optionName '=' optionValue semicolon {
optName := ast.NewOptionNameNode($2.refs, $2.dots)
if len($5) == 0 {
protolex.(*protoLex).Error("expected ';'")
$$ = ast.NewNodeWithEmptyDecls(ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, nil), nil)
} else {
$$ = ast.NewNodeWithEmptyDecls(ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, $5[0]), $5[1:])
}
semi, extra := protolex.(*protoLex).requireSemicolon($5)
$$ = newNodeWithEmptyDecls(ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, semi), extra)
}

optionName : identifier {
Expand Down Expand Up @@ -965,10 +961,7 @@ serviceDecl : _SERVICE identifier '{' serviceBody '}' {
}

serviceBody : semicolon {
$$ = make([]ast.ServiceElement, len($1))
for i, s := range $1 {
$$[i] = ast.NewEmptyDeclNode(s)
}
$$ = newEmptyServiceElements($1)
}
| semicolon serviceElements {
$$ = make([]ast.ServiceElement, len($1) + len($2))
Expand All @@ -991,25 +984,21 @@ serviceElements : serviceElements serviceElement {
// it does not appear to be supported in protoc (doc is likely from grammar for
// Google-internal version of protoc, with support for streaming stubby)
serviceElement : optionDeclNew {
$$ = ast.ToServiceElements($1)
$$ = toServiceElements($1)
}
| methodDecl {
$$ = ast.ToServiceElements($1)
$$ = toServiceElements($1)
}
| error {
$$ = nil
}

methodDecl : _RPC identifier methodMessageType _RETURNS methodMessageType semicolon {
if len($6) == 0 {
protolex.(*protoLex).Error("expected ';'")
$$ = ast.NewNodeWithEmptyDecls(ast.NewRPCNode($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, nil), nil)
} else {
$$ = ast.NewNodeWithEmptyDecls(ast.NewRPCNode($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6[0]), $6[1:])
}
semi, extra := protolex.(*protoLex).requireSemicolon($6)
$$ = newNodeWithEmptyDecls(ast.NewRPCNode($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, semi), extra)
}
| _RPC identifier methodMessageType _RETURNS methodMessageType '{' methodBody '}' semicolon {
$$ = ast.NewNodeWithEmptyDecls(ast.NewRPCNodeWithBody($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6, $7, $8), $9)
$$ = newNodeWithEmptyDecls(ast.NewRPCNodeWithBody($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6, $7, $8), $9)
}

methodMessageType : '(' _STREAM typeName ')' {
Expand Down
31 changes: 10 additions & 21 deletions parser/proto.y.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7d89510

Please sign in to comment.