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

Commit

Permalink
enum, import, option. broke some tests
Browse files Browse the repository at this point in the history
Change-Id: Ifaf1e6157255a4f358c6c767a4000e8ddedec5c8
  • Loading branch information
emicklei committed Jan 27, 2017
1 parent 06bb4b2 commit 0261da8
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 82 deletions.
90 changes: 90 additions & 0 deletions enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package proto3parser

import "fmt"

type Enum struct {
Line int
Name string
Options []*Option
EnumFields []*EnumField
}

type EnumField struct {
Name string
Constant string
ValueOption *Option
}

func (f *EnumField) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
if tok != IDENT {
return fmt.Errorf("found %q, expected identifier", lit)
}
tok, lit = p.scanIgnoreWhitespace()
if tok != EQUALS {
return fmt.Errorf("found %q, expected =", lit)
}
ns := p.s.scanIntegerString()
if len(ns) != 0 {
f.Constant = ns
} else {
tok, lit = p.scanIgnoreWhitespace()
if tok != IDENT {
return fmt.Errorf("found %q, expected string", lit)
}
}
tok, lit = p.scanIgnoreWhitespace()
if tok == LEFTSQUARE {
o := new(Option)
err := o.parse(p)
if err != nil {
return err
}
f.ValueOption = o
tok, lit = p.scanIgnoreWhitespace()
if tok != RIGHTSQUARE {
return fmt.Errorf("found %q, expected ]", lit)
}
}
return nil
}

func (e *Enum) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
if tok != IDENT {
return fmt.Errorf("found %q, expected identifier", lit)
}
e.Name = lit
tok, lit = p.scanIgnoreWhitespace()
if tok != LEFTCURLY {
return fmt.Errorf("found %q, expected {", lit)
}
for {
tok, lit = p.scanIgnoreWhitespace()
switch tok {
case RIGHTCURLY:
goto done
case SEMICOLON:
case OPTION:
v := new(Option)
err := v.parse(p)
if err != nil {
return err
}
e.Options = append(e.Options, v)
default:
p.unscan()
f := new(EnumField)
err := f.parse(p)
if err != nil {
return err
}
e.EnumFields = append(e.EnumFields, f)
}
}
done:
if tok != RIGHTCURLY {
return fmt.Errorf("found %q, expected }", lit)
}
return nil
}
41 changes: 41 additions & 0 deletions enum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package proto3parser

import (
"strings"
"testing"
)

func TestEnum(t *testing.T) {
proto := `enum EnumAllowingAlias {}`
p := NewParser(strings.NewReader(proto))
_, tok := p.scanIgnoreWhitespace() // consume first token
if got, want := tok, "enum"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
e := new(Enum)
err := e.parse(p)
if err != nil {
t.Fatal(err)
}
if got, want := e.Name, "EnumAllowingAlias"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestEnumWithBody(t *testing.T) {
proto := `
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 2 [(custom_option) = "hello world"];
}`
p := NewParser(strings.NewReader(proto))
pr, err := p.Parse()
if err != nil {
t.Fatal(err)
}
if got, want := len(pr.Enums), 1; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
27 changes: 27 additions & 0 deletions import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package proto3parser

import (
"fmt"
"strings"
)

type Import struct {
Line int
Filename string
Kind string // weak, public
}

func (i *Import) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
if tok != IDENT || !strings.Contains("weak public", lit) {
return fmt.Errorf("found %q, expected kind (weak|public)", lit)
}
i.Line = p.s.line
i.Kind = lit
name := p.s.scanUntil('\n')
if len(name) == 0 {
return fmt.Errorf("unexpected end of quoted string")
}
i.Filename = name
return nil
}
23 changes: 23 additions & 0 deletions import_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package proto3parser

import (
"strings"
"testing"
)

func TestParseImport(t *testing.T) {
proto := `import public "other.proto";`
p := NewParser(strings.NewReader(proto))
p.scanIgnoreWhitespace() // consume first token
i := new(Import)
err := i.parse(p)
if err != nil {
t.Fatal(err)
}
if got, want := i.Filename, "other.proto"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := i.Kind, "public"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}
1 change: 1 addition & 0 deletions message.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

type Message struct {
Line int
Name string
Fields []*Field
}
Expand Down
34 changes: 21 additions & 13 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,45 @@ package proto3parser
import "fmt"

type Option struct {
Name string
Constant string
Name string
String string
Boolean bool
}

func ParseOption(p *Parser) (*Option, error) {
o := new(Option)
func (o *Option) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
switch tok {
case IDENT:
o.Name = lit
case LEFTPAREN:
tok, lit = p.scanIgnoreWhitespace()
if tok != IDENT {
return nil, fmt.Errorf("found %q, expected identifier", lit)
return fmt.Errorf("found %q, expected identifier", lit)
}
o.Name = lit
tok, lit = p.scanIgnoreWhitespace()
if tok != RIGHTPAREN {
return nil, fmt.Errorf("found %q, expected )", lit)
return fmt.Errorf("found %q, expected )", lit)
}
default:
return nil, fmt.Errorf("found %q, expected identifier or (", lit)
return fmt.Errorf("found %q, expected identifier or (", lit)
}
tok, lit = p.scanIgnoreWhitespace()
if tok != EQUALS {
return nil, fmt.Errorf("found %q, expected =", lit)
return fmt.Errorf("found %q, expected =", lit)
}
ident, err := p.scanQuotedIdent()
if err != nil {
return nil, err
tok, lit = p.scanIgnoreWhitespace()
if tok == QUOTE {
p.unscan()
ident := p.s.scanUntil('\n')
if len(ident) == 0 {
return fmt.Errorf("unexpected end of quoted string") // TODO create constant for this
}
o.String = ident
return nil
}
if TRUE == tok || FALSE == tok {
o.Boolean = lit == "true"
}
o.Constant = ident
return o, nil
return nil
}
40 changes: 40 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package proto3parser

import (
"strings"
"testing"
)

func TestOption(t *testing.T) {
proto := `option java_package = "com.example.foo";`
p := NewParser(strings.NewReader(proto))
p.scanIgnoreWhitespace() // consume first token
o := new(Option)
err := o.parse(p)
if err != nil {
t.Fatal(err)
}
if got, want := o.Name, "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)
}
}

func TestOptionFull(t *testing.T) {
proto := `option (full.java_package) = "com.example.foo";`
p := NewParser(strings.NewReader(proto))
p.scanIgnoreWhitespace() // consume first token
o := new(Option)
err := o.parse(p)
if err != nil {
t.Fatal(err)
}
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)
}
}
29 changes: 2 additions & 27 deletions parser.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package proto3parser

import (
"fmt"
"io"
)
import "io"

// Parser represents a parser.
type Parser struct {
Expand Down Expand Up @@ -36,7 +33,7 @@ func (p *Parser) scan() (tok token, lit string) {
}

// Otherwise read the next token from the scanner.
tok, lit = p.s.Scan()
tok, lit = p.s.scan()

// Save it to the buffer in case we unscan later.
p.buf.tok, p.buf.lit = tok, lit
Expand All @@ -55,25 +52,3 @@ func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) {

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanQuotedIdent returns the identifier between 2 quotes.
func (p *Parser) scanQuotedIdent() (string, error) {
tok, lit := p.scanIgnoreWhitespace()
if tok != QUOTE {
return "", fmt.Errorf("found %q, expected \"", lit)
}
tok, lit = p.scanIgnoreWhitespace()
if tok != IDENT {
return "", fmt.Errorf("found %q, expected identifier", lit)
}
ident := lit
tok, lit = p.scanIgnoreWhitespace()
if tok != QUOTE {
return "", fmt.Errorf("found %q, expected \"", lit)
}
return ident, nil
}

func (p *Parser) scanUntilLineEnd() string {
return p.s.scanUntilLineEnd()
}
32 changes: 0 additions & 32 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,6 @@ func TestServiceWithRPCs(t *testing.T) {
}
}

func TestOption(t *testing.T) {
proto := `option java_package = "com.example.foo";`
p := NewParser(strings.NewReader(proto))
p.scanIgnoreWhitespace() // consume first token
o, err := ParseOption(p)
if err != nil {
t.Fatal(err)
}
if got, want := o.Name, "java_package"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := o.Constant, "com.example.foo"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestOptionFull(t *testing.T) {
proto := `option (full.java_package) = "com.example.foo";`
p := NewParser(strings.NewReader(proto))
p.scanIgnoreWhitespace() // consume first token
o, err := ParseOption(p)
if err != nil {
t.Fatal(err)
}
if got, want := o.Name, "full.java_package"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := o.Constant, "com.example.foo"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestCommentAroundSyntax(t *testing.T) {
proto := `
// comment1
Expand Down
Loading

0 comments on commit 0261da8

Please sign in to comment.