Skip to content
This repository has been archived by the owner on Jan 5, 2019. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
* allow nested aggregated constants
  • Loading branch information
emicklei authored Feb 28, 2018
1 parent 93f1d61 commit 04b3b05
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 14 deletions.
48 changes: 36 additions & 12 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,42 +155,66 @@ type NamedLiteral struct {
Name string
}

// parseAggregate reads options written using aggregate syntax
// parseAggregate reads options written using aggregate syntax.
// tLEFTCURLY { has been consumed
func (o *Option) parseAggregate(p *Parser) error {
o.AggregatedConstants = []*NamedLiteral{}
constants, err := parseAggregateConstants(p, o)
o.AggregatedConstants = constants
return err
}

func parseAggregateConstants(p *Parser, container interface{}) (list []*NamedLiteral, err error) {
for {
pos, tok, lit := p.next()
if tRIGHTSQUARE == tok {
p.nextPut(pos, tok, lit)
// caller has checked for open square ; will consume rightsquare, rightcurly and semicolon
return nil
return
}
if tRIGHTCURLY == tok {
return nil
return
}
if tSEMICOLON == tok {
p.nextPut(pos, tok, lit) // allow for inline comment parsing
return nil
return
}
if tCOMMA == tok {
if len(o.AggregatedConstants) == 0 {
return p.unexpected(lit, "non-empty option aggregate key", o)
if len(list) == 0 {
err = p.unexpected(lit, "non-empty option aggregate key", container)
return
}
continue
}
if tIDENT != tok {
return p.unexpected(lit, "option aggregate key", o)
err = p.unexpected(lit, "option aggregate key", container)
return
}
key := lit
pos, tok, lit = p.next()
if tLEFTCURLY == tok {
nested, fault := parseAggregateConstants(p, container)
if fault != nil {
err = fault
return
}
// flatten the constants
for _, each := range nested {
flatten := &NamedLiteral{
Name: key + "." + each.Name,
Literal: each.Literal}
list = append(list, flatten)
}
continue
}
if tCOLON != tok {
return p.unexpected(lit, "option aggregate key colon :", o)
err = p.unexpected(lit, "option aggregate key colon :", container)
return
}
l := new(Literal)
l.Position = pos
if err := l.parse(p); err != nil {
return err
if err = l.parse(p); err != nil {
return
}
o.AggregatedConstants = append(o.AggregatedConstants, &NamedLiteral{Name: key, Literal: l})
list = append(list, &NamedLiteral{Name: key, Literal: l})
}
}
57 changes: 55 additions & 2 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@

package proto

import "testing"
import (
"testing"
)

func TestOptionCases(t *testing.T) {
for i, each := range []struct {
Expand Down Expand Up @@ -136,7 +138,7 @@ option Help = "me"; // inline`
}
}

func TestIssue8(t *testing.T) {
func TestAggregateSyntax(t *testing.T) {
proto := `
// usage:
message Bar {
Expand Down Expand Up @@ -241,3 +243,54 @@ func TestFieldCustomOptions(t *testing.T) {
t.Errorf("got [%v] want [%v]", got, want)
}
}

// issue #50
func TestNestedAggregateConstants(t *testing.T) {
src := `syntax = "proto3";
package baz;
option (foo.bar) = {
woot: 100
foo {
hello: 200
hello2: 300
bar {
hello3: 400
}
}
};`
p := newParserOn(src)
proto, err := p.Parse()
if err != nil {
t.Error(err)
}
option := proto.Elements[2].(*Option)
if got, want := option.Name, "(foo.bar)"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := len(option.AggregatedConstants), 4; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[0].Name, "woot"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[1].Name, "foo.hello"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[2].Name, "foo.hello2"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[3].Name, "foo.bar.hello3"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[1].Literal.SourceRepresentation(), "200"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[2].Literal.SourceRepresentation(), "300"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := option.AggregatedConstants[3].Literal.SourceRepresentation(), "400"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

0 comments on commit 04b3b05

Please sign in to comment.