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

Commit

Permalink
work on other literals for option constant
Browse files Browse the repository at this point in the history
  • Loading branch information
Ernest Micklei authored and Ernest Micklei committed Jan 31, 2017
1 parent 72d7e16 commit 9e6d66e
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 90 deletions.
35 changes: 16 additions & 19 deletions cmd/proto3fmt/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (f *formatter) VisitComment(c *proto3.Comment) {

func (f *formatter) VisitEnum(e *proto3.Enum) {
f.begin("enum")
fmt.Fprintf(f.w, "enum %s {\n", e.Name)
fmt.Fprintf(f.w, "enum %s {", e.Name)
f.indentLevel++
for _, each := range e.Elements {
each.Accept(f)
Expand All @@ -50,20 +50,12 @@ func (f *formatter) VisitEnumField(e *proto3.EnumField) {
}
}

func (f *formatter) VisitField(f1 *proto3.Field) {
f.begin("field")
if f1.Repeated {
io.WriteString(f.w, "repeated ")
}
fmt.Fprintf(f.w, "%s %s = %d;\n", f1.Type, f1.Name, f1.Sequence)
}

func (f *formatter) VisitImport(i *proto3.Import) {
f.begin("import")
if len(i.Kind) > 0 {
fmt.Fprintf(f.w, "%s ", i.Kind)
fmt.Fprintf(f.w, "import %s ", i.Kind)
}
fmt.Fprintf(f.w, "%q;\n", i.Filename)
fmt.Fprintf(f.w, "import %q;\n", i.Filename)
}

func (f *formatter) VisitMessage(m *proto3.Message) {
Expand Down Expand Up @@ -92,11 +84,7 @@ func (f *formatter) VisitOption(o *proto3.Option) {
io.WriteString(f.w, ")")
}
io.WriteString(f.w, " = ")
if len(o.String) > 0 {
fmt.Fprintf(f.w, "%q", o.String)
} else {
fmt.Fprintf(f.w, "%s", o.Identifier)
}
io.WriteString(f.w, o.Constant.String())
if o.IsEmbedded {
io.WriteString(f.w, "];\n")
} else {
Expand Down Expand Up @@ -176,15 +164,24 @@ func (f *formatter) VisitRPC(r *proto3.RPC) {
}

func (f *formatter) VisitMapField(m *proto3.MapField) {
panic("VisitMapField")
f.begin("map")
fmt.Fprintf(f.w, "map<%s,%s> %s = %d;\n", m.KeyType, m.Type, m.Name, m.Sequence)
}

func (f *formatter) VisitNormalField(f1 *proto3.NormalField) {
f.begin("field")
if f1.Repeated {
io.WriteString(f.w, "repeated ")
}
fmt.Fprintf(f.w, "%s %s = %d;\n", f1.Type, f1.Name, f1.Sequence)
}

// Utils

func (f *formatter) begin(stmt string) {
if f.lastStmt != stmt && len(f.lastStmt) > 0 { // not the first line
// add separator because stmt is changed, unless it was comment or a nested thingy
if !strings.Contains("comment message enum", f.lastStmt) {
// add separator because stmt is changed, unless it nested thingy
if !strings.Contains("comment", f.lastStmt) {
io.WriteString(f.w, "\n")
}
}
Expand Down
10 changes: 3 additions & 7 deletions enum.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package proto3

import (
"fmt"
"strconv"
)
import "fmt"

// Enum definition consists of a name and an enum body.
type Enum struct {
Expand Down Expand Up @@ -39,10 +36,9 @@ func (f *EnumField) parse(p *Parser) error {
if tok != tEQUALS {
return p.unexpected(lit, "=")
}
is := p.s.scanIntegerString()
i, err := strconv.Atoi(is)
i, err := p.s.scanInteger()
if err != nil {
return fmt.Errorf("found %q, expected integer", is)
return fmt.Errorf("found %q, expected integer", err)
}
f.Integer = i
tok, lit = p.scanIgnoreWhitespace()
Expand Down
2 changes: 1 addition & 1 deletion enum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ enum EnumAllowingAlias {
if got, want := ef3.ValueOption.Name, "custom_option"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := ef3.ValueOption.String, "hello world"; got != want {
if got, want := ef3.ValueOption.Constant.Source, "hello world"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
5 changes: 1 addition & 4 deletions field.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package proto3

import "strconv"

// Field is an abstract message field.
type Field struct {
Name string
Expand Down Expand Up @@ -55,8 +53,7 @@ func parseFieldAfterType(f *Field, p *Parser) error {
if tok != tEQUALS {
return p.unexpected(lit, "=")
}
lit = p.s.scanIntegerString()
i, err := strconv.Atoi(lit)
i, err := p.s.scanInteger()
if err != nil {
return p.unexpected(lit, "sequence number")
}
Expand Down
10 changes: 5 additions & 5 deletions field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package proto3
import "testing"

func TestField(t *testing.T) {
proto := `repeated foo.bar lots = 1 [option1=a, option2=b, option3="happy"];`
proto := `repeated foo.bar lots =1 [option1=a, option2=b, option3="happy"];`
p := newParserOn(proto)
f := newNormalField()
err := f.parse(p)
Expand All @@ -25,16 +25,16 @@ func TestField(t *testing.T) {
if got, want := f.Options[0].Name, "option1"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.Options[0].Identifier, "a"; got != want {
if got, want := f.Options[0].Constant.Source, "a"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.Options[1].Name, "option2"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.Options[1].Identifier, "b"; got != want {
if got, want := f.Options[1].Constant.Source, "b"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.Options[2].String, "happy"; got != want {
if got, want := f.Options[2].Constant.Source, "happy"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
Expand Down Expand Up @@ -62,7 +62,7 @@ func TestFieldSimple(t *testing.T) {
if got, want := f.Options[0].Name, "ctype"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.Options[0].Identifier, "STRING_PIECE"; got != want {
if got, want := f.Options[0].Constant.Source, "STRING_PIECE"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
Expand Down
50 changes: 42 additions & 8 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import "fmt"
// Option is a protoc compiler option
type Option struct {
Name string
String string // will be quoted
Identifier string // will not be quoted
Constant Literal
IsEmbedded bool
IsCustom bool // TODO needed?
}
Expand Down Expand Up @@ -49,19 +48,54 @@ func (o *Option) parse(p *Parser) error {
if tok != tEQUALS {
return p.unexpected(lit, "=")
}
tok, lit = p.scanIgnoreWhitespace()
l := new(Literal)
if err := l.parse(p); err != nil {
return err
}
o.Constant = *l
return nil
}

// Literal represents intLit,floatLit,strLit or boolLit
type Literal struct {
Source string
IsString bool
}

// String returns the source (if quoted then use double quote).
func (l Literal) String() string {
if l.IsString {
return "\"" + l.Source + "\""
}
return l.Source
}

func (l *Literal) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
// stringLiteral?
if tok == tQUOTE {
ident := p.s.scanUntil('"')
if len(ident) == 0 {
return fmt.Errorf("unexpected end of quoted string") // TODO create constant for this
return p.unexpected(lit, "quoted string")
}
l.Source, l.IsString = ident, true
return nil
}
// stringLiteral?
if tok == tSINGLEQUOTE {
ident := p.s.scanUntil('\'')
if len(ident) == 0 {
return p.unexpected(lit, "single quoted string")
}
o.String = ident
l.Source, l.IsString = ident, true
return nil
}
if tIDENT != tok {
return p.unexpected(lit, "constant")
// float, bool or intLit ?
if lit == "-" { // TODO token?
_, rem := p.s.scanIdent()
l.Source = "-" + rem
return nil
}
o.Identifier = lit
l.Source = lit
return nil
}
64 changes: 46 additions & 18 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,51 @@ package proto3
import "testing"

func TestOption(t *testing.T) {
proto := `option (full.java_package) = "com.example.foo";`
p := newParserOn(proto)
pr, err := p.Parse()
if err != nil {
t.Fatal(err)
}
if got, want := len(pr.Elements), 1; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
o := pr.Elements[0].(*Option)
if got, want := o.Name, "full.java_package"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := o.String, "com.example.foo"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := o.IsEmbedded, false; got != want {
t.Errorf("got [%v] want [%v]", got, want)
for i, each := range []struct {
proto string
name string
strLit string
nonStrLit string
}{{
`option (full.java_package) = "com.example.foo";`,
"full.java_package",
"com.example.foo",
"",
}, {
`option Bool = true;`,
"Bool",
"",
"true",
}, {
`option Float = -3.14E1;`,
"Float",
"",
"-3.14E1",
}} {
p := newParserOn(each.proto)
pr, err := p.Parse()
if err != nil {
t.Fatal(err)
}
if got, want := len(pr.Elements), 1; got != want {
t.Errorf("[%d] got [%v] want [%v]", i, got, want)
}
o := pr.Elements[0].(*Option)
if got, want := o.Name, each.name; got != want {
t.Errorf("[%d] got [%v] want [%v]", i, got, want)
}
if len(each.strLit) > 0 {
if got, want := o.Constant.Source, each.strLit; got != want {
t.Errorf("[%d] got [%v] want [%v]", i, got, want)
}
}
if len(each.nonStrLit) > 0 {
if got, want := o.Constant.Source, each.nonStrLit; got != want {
t.Errorf("[%d] got [%v] want [%v]", i, got, want)
}
}
if got, want := o.IsEmbedded, false; got != want {
t.Errorf("[%d] got [%v] want [%v]", i, got, want)
}
}
}
21 changes: 21 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,24 @@ func (p *Parser) unexpected(found, expected string) error {
}
return fmt.Errorf("found %q on line %d, expected %s%s", found, p.s.line, expected, debug)
}

// read a single or double-quoted single-line string
// TODO used?
func (p *Parser) scanStringLiteral() (string, error) {
tok, lit := p.scanIgnoreWhitespace()
if tok == tQUOTE {
s := p.s.scanUntil('"')
if len(s) == 0 {
return "", p.unexpected(lit, "quoted string")
}
return s, nil
}
if tok == tSINGLEQUOTE {
s := p.s.scanUntil('\'')
if len(s) == 0 {
return "", p.unexpected(lit, "single quoted string")
}
return s, nil
}
return "", p.unexpected(lit, "single or double quoted string")
}
25 changes: 7 additions & 18 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func (s *scanner) scan() (tok token, lit string) {
return tEQUALS, string(ch)
case '"':
return tQUOTE, string(ch)
case '\'':
return tSINGLEQUOTE, string(ch)
case '(':
return tLEFTPAREN, string(ch)
case ')':
Expand Down Expand Up @@ -95,25 +97,12 @@ func (s *scanner) scanWhitespace() (tok token, lit string) {
return tWS, buf.String()
}

func (s *scanner) scanIntegerString() string {
s.scanWhitespace()
// Create a buffer.
var buf bytes.Buffer

// Read every subsequent digit character into the buffer.
// Non-digit characters and EOF will cause the loop to exit.
// TODO handle sign correctly
for {
if ch := s.read(); ch == eof {
break
} else if !isDigit(ch) && ch != '-' {
s.unread(ch)
break
} else {
_, _ = buf.WriteRune(ch)
}
func (s *scanner) scanInteger() (int, error) {
var i int
if _, err := fmt.Fscanf(s.r, "%d", &i); err != nil {
return i, err
}
return buf.String()
return i, nil
}

// scanIdent consumes the current rune and all contiguous ident runes.
Expand Down
4 changes: 2 additions & 2 deletions scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ func TestScanSingleLineComment(t *testing.T) {
func TestScanIntegerString(t *testing.T) {
r := strings.NewReader("-1234;")
s := newScanner(r)
if got, want := s.scanIntegerString(), "1234"; got != want {
i, _ := s.scanInteger()
if got, want := i, -1234; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}

}
Loading

0 comments on commit 9e6d66e

Please sign in to comment.