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

Commit

Permalink
work on formatter, TODO one of,reserved
Browse files Browse the repository at this point in the history
  • Loading branch information
Ernest Micklei authored and Ernest Micklei committed Jan 28, 2017
1 parent 1ab5546 commit 6607212
Show file tree
Hide file tree
Showing 23 changed files with 481 additions and 61 deletions.
43 changes: 43 additions & 0 deletions cmd/proto3fmt/example0.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// example0
syntax = "proto3";




// using Any
import "google/protobuf/any.proto";

import public "testdata/test.proto";



/* This pkg
*/
package here.proto3_proto ;


// from a bottle
message Message
{

string name =1;
// this is thing
google.protobuf.Any anything = 2 [packed=true, danger=false];

repeated
Message
children
= 3;


enum Humour {
// something we dont know
UNKNOWN = 0;
PUNS = 1;
SLAPSTICK = 2;
/* who is this? */
BILL_BAILEY = 3;
}

map<string, Nested> terrain = 4;
}
128 changes: 128 additions & 0 deletions cmd/proto3fmt/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"errors"
"fmt"
"io"

"strings"

"github.com/emicklei/proto3"
)

type formatter struct {
w io.Writer
indentLevel int
lastStmt string
indentSeparator string
}

func (f *formatter) VisitComment(c *proto3.Comment) {
f.begin("comment")
if c.IsMultiline() {
fmt.Fprintln(f.w, "/*")
fmt.Fprint(f.w, strings.TrimSpace(c.Message))
fmt.Fprintf(f.w, "\n*/\n")
} else {
fmt.Fprintf(f.w, "//%s\n", c.Message)
}
}

func (f *formatter) VisitEnum(e *proto3.Enum) {
f.begin("enum")
fmt.Fprintf(f.w, "enum %s {\n", e.Name)
f.indentLevel++
for _, each := range e.Elements {
each.Accept(f)
}
f.indent(-1)
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitEnumField(e *proto3.EnumField) {
f.begin("field")
io.WriteString(f.w, paddedTo(e.Name, 10))
if e.ValueOption != nil {
e.ValueOption.Accept(f)
}
fmt.Fprintf(f.w, " = %d;\n", e.Integer)
}

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, "%q;\n", i.Filename)
}

func (f *formatter) VisitMessage(m *proto3.Message) {
f.begin("message")
fmt.Fprintf(f.w, "message %s {\n", m.Name)
f.indentLevel++
for _, each := range m.Elements {
each.Accept(f)
}
f.indentLevel++
io.WriteString(f.w, "}\n")
}

func (f *formatter) VisitOption(o *proto3.Option) {
panic(errors.New("Not implemented"))
}

func (f *formatter) VisitPackage(p *proto3.Package) {
f.begin("package")
fmt.Fprintf(f.w, "package %s;\n", p.Name)
}

func (f *formatter) VisitService(s *proto3.Service) {
panic(errors.New("Not implemented"))
}

func (f *formatter) VisitSyntax(s *proto3.Syntax) {
fmt.Fprintf(f.w, "syntax = %q;\n\n", s.Value)
}

func (f *formatter) VisitOneof(o *proto3.Oneof) {
panic(errors.New("Not implemented"))
}

func (f *formatter) VisitOneofField(o *proto3.OneOfField) {
panic(errors.New("Not implemented"))
}

// 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) {
io.WriteString(f.w, "\n")
}
}
f.indent(0)
f.lastStmt = stmt
}

func (f *formatter) indent(diff int) {
f.indentLevel += diff
for i := 0; i < f.indentLevel; i++ {
io.WriteString(f.w, f.indentSeparator)
}
}

func paddedTo(word string, length int) string {
if len(word) >= length {
return word
}
return word + strings.Repeat(" ", length-len(word))
}
9 changes: 7 additions & 2 deletions cmd/proto3fmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import (
"github.com/emicklei/proto3"
)

// go run *.go < example1.proto
// go run *.go < example0.proto
func main() {
p := proto3.NewParser(os.Stdin)
def, err := p.Parse()
if err != nil {
log.Fatalln("proto3fmt failed:", p.Line(), err)
log.Fatalln("proto3fmt failed, on line", p.Line(), err)
}
f := &formatter{w: os.Stdout, indentSeparator: " "}
for _, each := range def.Elements {
each.Accept(f)
}
log.Printf("%#v", def)
}
30 changes: 21 additions & 9 deletions enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import (

// Enum definition consists of a name and an enum body.
type Enum struct {
Line int
Name string
Options []*Option
EnumFields []*EnumField
Line int
Name string
Elements []Visitee
}

// Accept dispatches the call to the visitor.
func (e *Enum) Accept(v Visitor) {
v.VisitEnum(e)
}

// EnumField is part of the body of an Enum.
Expand All @@ -20,11 +24,17 @@ type EnumField struct {
ValueOption *Option
}

// Accept dispatches the call to the visitor.
func (f *EnumField) Accept(v Visitor) {
v.VisitEnumField(f)
}

func (f *EnumField) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
if tok != tIDENT {
return fmt.Errorf("found %q, expected identifier", lit)
}
f.Name = lit
tok, lit = p.scanIgnoreWhitespace()
if tok != tEQUALS {
return fmt.Errorf("found %q, expected =", lit)
Expand Down Expand Up @@ -64,24 +74,26 @@ func (e *Enum) parse(p *Parser) error {
for {
tok, lit = p.scanIgnoreWhitespace()
switch tok {
case tRIGHTCURLY:
goto done
case tSEMICOLON:
case tCOMMENT:
e.Elements = append(e.Elements, p.newComment(lit))
case tOPTION:
v := new(Option)
err := v.parse(p)
if err != nil {
return err
}
e.Options = append(e.Options, v)
e.Elements = append(e.Elements, v)
case tRIGHTCURLY:
goto done
case tSEMICOLON:
default:
p.unscan()
f := new(EnumField)
err := f.parse(p)
if err != nil {
return err
}
e.EnumFields = append(e.EnumFields, f)
e.Elements = append(e.Elements, f)
}
}
done:
Expand Down
7 changes: 4 additions & 3 deletions enum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ enum EnumAllowingAlias {
if err != nil {
t.Fatal(err)
}
if got, want := len(pr.Enums), 1; got != want {
if got, want := len(collect(pr).Enums()), 1; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := len(pr.Enums[0].EnumFields), 3; got != want {
if got, want := len(collect(pr).Enums()[0].Elements), 4; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := pr.Enums[0].EnumFields[0].Integer, 0; got != want {
e := collect(pr).Enums()[0].Elements[1].(*EnumField)
if got, want := e.Integer, 0; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
27 changes: 17 additions & 10 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,30 @@ package proto3
import (
"fmt"
"strconv"
"strings"
)

// Field is a message field.
type Field struct {
Name string
Type string
Repeated bool
Messages []*Message
Sequence int
Messages []*Message
}

// Accept dispatches the call to the visitor.
func (f *Field) Accept(v Visitor) {
v.VisitField(f)
}

func (f *Field) parse(p *Parser) error {
for {
tok, lit := p.scanIgnoreWhitespace()
switch tok {
case tIDENT:
// normal type?
if strings.Contains(typeTokens, lit) {
f.Type = lit
return parseNormalField(f, p)
}
//if tok == ONEOF {}
//if tok == ONEOFFIELD {}
case tMESSAGE:
f.Type = lit
return parseNormalField(f, p)
case tMESSAGE: // TODO here?
m := new(Message)
err := m.parse(p)
if err != nil {
Expand All @@ -37,6 +36,14 @@ func (f *Field) parse(p *Parser) error {
case tREPEATED:
f.Repeated = true
return f.parse(p)
case tMAP:
tok, lit := p.scanIgnoreWhitespace()
if tLESS != tok {
return fmt.Errorf("found %q, expected <", lit)
}
kvtypes := p.s.scanUntil('>')
f.Type = fmt.Sprintf("map<%s>", kvtypes)
return parseNormalField(f, p)
default:
goto done
}
Expand Down
5 changes: 5 additions & 0 deletions import.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ type Import struct {
Kind string // weak, public, <empty>
}

// Accept dispatches the call to the visitor.
func (i *Import) Accept(v Visitor) {
v.VisitImport(i)
}

func (i *Import) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
i.Line = p.s.line
Expand Down
Loading

0 comments on commit 6607212

Please sign in to comment.