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

Commit

Permalink
fix for Issue emicklei#73 (emicklei#74)
Browse files Browse the repository at this point in the history
* ignore illegal escape chars while parsing option constant
  • Loading branch information
emicklei authored Mar 18, 2018
1 parent ae5d402 commit 47312e9
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 15 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,5 @@ Package in Go for parsing Google Protocol Buffers [.proto files version 2 + 3] (

See (https://github.com/emicklei/proto-contrib) for other contributions on top of this package such as protofmt, proto2xsd and proto2gql.

#### known issues

- the proto2 test file in (https://github.com/emicklei/proto-contrib/cmd/protofmt) folder contains character escape sequences that are currently not accepted by the scanner. See line 537 and 573.

© 2017, [ernestmicklei.com](http://ernestmicklei.com). MIT License. Contributions welcome.
31 changes: 19 additions & 12 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,25 @@ func (o *Option) parse(p *Parser) error {
return p.unexpected(lit, "option value assignment =", o)
}
r := p.peekNonWhitespace()
if '{' == r {
p.next() // consume {
return o.parseAggregate(p)
}
// non aggregate
l := new(Literal)
l.Position = pos
if err := l.parse(p); err != nil {
return err
}
o.Constant = *l
return nil
var err error
// values of an option can have illegal escape sequences
// for the standard Go scanner used by this package.
p.ignoreIllegalEscapesWhile(func() {
if '{' == r {
// aggregate
p.next() // consume {
err = o.parseAggregate(p)
} else {
// non aggregate
l := new(Literal)
l.Position = pos
if e := l.parse(p); e != nil {
err = e
}
o.Constant = *l
}
})
return err
}

// inlineComment is part of commentInliner.
Expand Down
35 changes: 35 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,41 @@ func TestFieldCustomOptions(t *testing.T) {
}
}

func TestIgnoreIllegalEscapeCharsInAggregatedConstants(t *testing.T) {
src := `syntax = "proto3";
message Person {
string name = 3 [(validate.rules).string = {
pattern: "^[^\d\s]+( [^\d\s]+)*$",
max_bytes: 256,
}];
}`
p := newParserOn(src)
d, err := p.Parse()
if err != nil {
t.Fatal(err)
}
f := d.Elements[1].(*Message).Elements[0].(*NormalField)
if got, want := f.Options[0].AggregatedConstants[0].Source, "^[^\\d\\s]+( [^\\d\\s]+)*$"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestIgnoreIllegalEscapeCharsInConstant(t *testing.T) {
src := `syntax = "proto2";
message Person {
optional string cpp_trigraph = 20 [default = "? \? ?? \?? \??? ??/ ?\?-"];
}`
p := newParserOn(src)
d, err := p.Parse()
if err != nil {
t.Fatal(err)
}
f := d.Elements[1].(*Message).Elements[0].(*NormalField)
if got, want := f.Options[0].Constant.Source, "? \\? ?? \\?? \\??? ??/ ?\\?-"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestFieldCustomOptionExtendedIdent(t *testing.T) {
proto := `Type field = 1 [(validate.rules).enum.defined_only = true];`
p := newParserOn(proto)
Expand Down
17 changes: 17 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"io"
"runtime"
"strconv"
"strings"
"text/scanner"
)

Expand Down Expand Up @@ -64,6 +65,22 @@ func (p *Parser) handleScanError(s *scanner.Scanner, msg string) {
fmt.Errorf("go scanner error at %v = %v", s.Position, msg))
}

// ignoreIllegalEscapesWhile is called for scanning constants of an option.
// Such content can have a syntax that is not acceptable by the Go scanner.
// This temporary installs a handler that ignores only one type of error: illegal char escape
func (p *Parser) ignoreIllegalEscapesWhile(block func()) {
// during block call change error handler
p.scanner.Error = func(s *scanner.Scanner, msg string) {
if strings.Contains(msg, "illegal char escape") { // too bad there is no constant for this in scanner pkg
return
}
p.handleScanError(s, msg)
}
block()
// restore
p.scanner.Error = p.handleScanError
}

// Parse parses a proto definition. May return a parse or scanner error.
func (p *Parser) Parse() (*Proto, error) {
proto := new(Proto)
Expand Down

0 comments on commit 47312e9

Please sign in to comment.