Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #50 #60

Merged
merged 4 commits into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}