Skip to content

Commit

Permalink
Add optional lexer interfaces for fast-pathing strings and byte slices.
Browse files Browse the repository at this point in the history
  • Loading branch information
alecthomas committed Nov 26, 2020
1 parent 5cd9bd8 commit 759b88b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 21 deletions.
12 changes: 12 additions & 0 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ type Definition interface {
Lex(filename string, r io.Reader) (Lexer, error)
}

// StringDefinition is an optional interface lexer Definition's can implement
// to offer a fast path for lexing strings.
type StringDefinition interface {
LexString(filename string, input string) (Lexer, error)
}

// BytesDefinition is an optional interface lexer Definition's can implement
// to offer a fast path for lexing byte slices.
type BytesDefinition interface {
LexBytes(filename string, input []byte) (Lexer, error)
}

// A Lexer returns tokens from a source.
type Lexer interface {
// Next consumes and returns the next token.
Expand Down
18 changes: 9 additions & 9 deletions lexer/stateful/codegen/codegen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,23 @@ func TestGenerate(t *testing.T) {
err := codegen.Generate(w, "codegen_test", exprLexer)
require.NoError(t, err)
source := w.String()
cmd := exec.Command("pbcopy")
cmd.Stdin = strings.NewReader(source)
err = cmd.Run()
require.NoError(t, err)
// cmd := exec.Command("pbcopy")
// cmd.Stdin = strings.NewReader(source)
// err = cmd.Run()
// require.NoError(t, err)

formatted := &bytes.Buffer{}
cmd = exec.Command("goimports")
cmd := exec.Command("gofmt", "-s")
cmd.Stdin = strings.NewReader(source)
cmd.Stdout = formatted
cmd.Stderr = os.Stderr
err = cmd.Run()
require.NoError(t, err, source)

cmd = exec.Command("pbcopy")
cmd.Stdin = formatted
err = cmd.Run()
require.NoError(t, err)
// cmd = exec.Command("pbcopy")
// cmd.Stdin = formatted
// err = cmd.Run()
// require.NoError(t, err)
}

func BenchmarkStatefulGenerated(b *testing.B) {
Expand Down
40 changes: 28 additions & 12 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ func (p *Parser) ParseFromLexer(lex *lexer.PeekingLexer, v interface{}, options
return p.parseOne(ctx, rv)
}

func (p *Parser) parse(lex lexer.Lexer, v interface{}, options ...ParseOption) (err error) {
peeker, err := lexer.Upgrade(lex, p.getElidedTypes()...)
if err != nil {
return err
}
return p.ParseFromLexer(peeker, v, options...)
}

// Parse from r into grammar v which must be of the same type as the grammar passed to
// Build().
//
// This may return a Error.
// This may return an Error.
func (p *Parser) Parse(filename string, r io.Reader, v interface{}, options ...ParseOption) (err error) {
if filename == "" {
filename = lexer.NameOfReader(r)
Expand All @@ -164,27 +172,35 @@ func (p *Parser) Parse(filename string, r io.Reader, v interface{}, options ...P
if err != nil {
return err
}
peeker, err := lexer.Upgrade(lex, p.getElidedTypes()...)
if err != nil {
return err
}
return p.ParseFromLexer(peeker, v, options...)
return p.parse(lex, v, options...)
}

// ParseString from s into grammar v which must be of the same type as the grammar passed to
// Build().
//
// This may return a Error.
func (p *Parser) ParseString(filename string, s string, v interface{}, options ...ParseOption) error {
return p.Parse(filename, strings.NewReader(s), v, options...)
// This may return an Error.
func (p *Parser) ParseString(filename string, s string, v interface{}, options ...ParseOption) (err error) {
var lex lexer.Lexer
if sl, ok := p.lex.(lexer.StringDefinition); ok {
lex, err = sl.LexString(filename, s)
} else {
lex, err = p.lex.Lex(filename, strings.NewReader(s))
}
return p.parse(lex, v, options...)
}

// ParseBytes from b into grammar v which must be of the same type as the grammar passed to
// Build().
//
// This may return a Error.
func (p *Parser) ParseBytes(filename string, b []byte, v interface{}, options ...ParseOption) error {
return p.Parse(filename, bytes.NewReader(b), v, options...)
// This may return an Error.
func (p *Parser) ParseBytes(filename string, b []byte, v interface{}, options ...ParseOption) (err error) {
var lex lexer.Lexer
if sl, ok := p.lex.(lexer.BytesDefinition); ok {
lex, err = sl.LexBytes(filename, b)
} else {
lex, err = p.lex.Lex(filename, bytes.NewReader(b))
}
return p.parse(lex, v, options...)
}

func (p *Parser) parseStreaming(ctx *parseContext, rv reflect.Value) error {
Expand Down

0 comments on commit 759b88b

Please sign in to comment.