Skip to content

Commit

Permalink
Genericise the parser.
Browse files Browse the repository at this point in the history
`Build(&Grammar{})` becomes `Build[Grammar]()` and
`err = parser.Parse(filename, &grammar)` becomes
`grammar, err := parser.Parse(filename)`.
  • Loading branch information
alecthomas committed Jun 27, 2022
1 parent c68713a commit bf4573a
Show file tree
Hide file tree
Showing 54 changed files with 543 additions and 645 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ linters:
- errname
- nilnil
- maintidx
- unused # Does not work with type parameters

linters-settings:
govet:
Expand Down
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,13 @@ parser from the tutorial.
A parser is constructed from a grammar and a lexer:

```go
parser, err := participle.Build(&INI{})
parser, err := participle.Build[INI]()
```

Once constructed, the parser is applied to input to produce an AST:

```go
ast := &INI{}
err := parser.ParseString("", "size = 10", ast)
ast, err := parser.ParseString("", "size = 10")
// ast == &INI{
// Properties: []*Property{
// {Key: "size", Value: &Value{Int: &10}},
Expand Down Expand Up @@ -282,7 +281,7 @@ now supports this pattern. Simply construct your parser with the `Union[T](membe
option, eg.

```go
parser := participle.MustBuild(&AST{}, participle.Union[Value](Float{}, Int{}, String{}, Bool{}))
parser := participle.MustBuild[AST](participle.Union[Value](Float{}, Int{}, String{}, Bool{}))
```

Custom parsers may also be defined for union types with the [ParseTypeWith](https://pkg.go.dev/github.com/alecthomas/participle/v2#ParseTypeWith) option.
Expand Down Expand Up @@ -516,7 +515,7 @@ var (
{"Punct", `[-[!@#$%^&*()+_={}\|:;"'<,>.?/]|]`, nil},
{"Whitespace", `[ \t\n\r]+`, nil},
})
parser = participle.MustBuild(&File{},
parser = participle.MustBuild[File](
participle.Lexer(graphQLLexer),
participle.Elide("Comment", "Whitespace"),
participle.UseLookahead(2),
Expand All @@ -535,10 +534,9 @@ func main() {
ctx.Exit(0)
}
for _, file := range cli.Files {
ast := &File{}
r, err := os.Open(file)
ctx.FatalIfErrorf(err)
err = parser.Parse(file, r, ast)
ast, err := parser.Parse(file, r)
r.Close()
repr.Println(ast)
ctx.FatalIfErrorf(err)
Expand Down
9 changes: 4 additions & 5 deletions TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,21 +252,20 @@ To parse with this grammar we first construct the parser (we'll use the
default lexer for now):

```go
parser, err := participle.Build(&INI{})
parser, err := participle.Build[INI]()
```

Then create a root node and parse into it with `parser.Parse{,String,Bytes}()`:
Then parse a new INI file with `parser.Parse{,String,Bytes}()`:

```go
ini := &INI{}
err = parser.ParseString("", `
ini, err := parser.ParseString("", `
age = 21
name = "Bob Smith"
[address]
city = "Beverly Hills"
postal_code = 90210
`, ini)
`)
```

You can find the full example [here](_examples/ini/main.go), alongside
Expand Down
3 changes: 1 addition & 2 deletions _examples/basic/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (

// Parse a BASIC program.
func Parse(r io.Reader) (*Program, error) {
program := &Program{}
err := basicParser.Parse("", r, program)
program, err := basicParser.Parse("", r)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion _examples/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var (
{"whitespace", `[ \t]+`},
})

basicParser = participle.MustBuild(&Program{},
basicParser = participle.MustBuild[Program](
participle.Lexer(basicLexer),
participle.CaseInsensitive("Ident"),
participle.Unquote("String"),
Expand Down
5 changes: 2 additions & 3 deletions _examples/ebnf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (e *EBNF) String() string {
return w.String()
}

var parser = participle.MustBuild(&EBNF{})
var parser = participle.MustBuild[EBNF]()

func main() {
help := `An EBNF parser compatible with Go"s exp/ebnf. The grammar is
Expand All @@ -151,8 +151,7 @@ in the form:
`
ctx := kong.Parse(&cli, kong.Description(help))

ebnf := &EBNF{}
err := parser.Parse("", os.Stdin, ebnf)
ebnf, err := parser.Parse("", os.Stdin)
ctx.FatalIfErrorf(err, "")

if cli.JSON {
Expand Down
5 changes: 2 additions & 3 deletions _examples/ebnf/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import (
)

func TestExe(t *testing.T) {
ast := &EBNF{}
err := parser.ParseString("", `
_, err := parser.ParseString("", `
Production = name "=" [ Expression ] "." .
Expression = Alternative { "|" Alternative } .
Alternative = Term { Term } .
Term = name | token [ "…" token ] | Group | Option | Repetition .
Group = "(" Expression ")" .
Option = "[" Expression "]" .
Repetition = "{" Expression "}" .`, ast)
Repetition = "{" Expression "}" .`)
require.NoError(t, err)
}
5 changes: 2 additions & 3 deletions _examples/expr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,15 @@ func (e *Expression) Eval(ctx Context) float64 {

type Context map[string]float64

var parser = participle.MustBuild(&Expression{})
var parser = participle.MustBuild[Expression]()

func main() {
ctx := kong.Parse(&cli,
kong.Description("A basic expression parser and evaluator."),
kong.UsageOnError(),
)

expr := &Expression{}
err := parser.ParseString("", strings.Join(cli.Expression, " "), expr)
expr, err := parser.ParseString("", strings.Join(cli.Expression, " "))
ctx.FatalIfErrorf(err)

if cli.AST {
Expand Down
5 changes: 2 additions & 3 deletions _examples/expr/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
)

func TestExe(t *testing.T) {
expr := &Expression{}
err := parser.ParseString("", `1 + 2 / 3 * (1 + 2)`, expr)
require.NoError(t, err)
expr, err := parser.ParseString("", `1 + 2 / 3 * (1 + 2)`)
repr.Println(expr)
require.NoError(t, err)
}
5 changes: 2 additions & 3 deletions _examples/expr2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,15 @@ type Primary struct {
SubExpression *Expression `| "(" @@ ")" `
}

var parser = participle.MustBuild(&Expression{}, participle.UseLookahead(2))
var parser = participle.MustBuild[Expression](participle.UseLookahead(2))

func main() {
var cli struct {
Expr []string `arg required help:"Expression to parse."`
}
ctx := kong.Parse(&cli)

expr := &Expression{}
err := parser.ParseString("", strings.Join(cli.Expr, " "), expr)
expr, err := parser.ParseString("", strings.Join(cli.Expr, " "))
ctx.FatalIfErrorf(err)

repr.Println(expr)
Expand Down
5 changes: 2 additions & 3 deletions _examples/expr2/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
)

func TestExe(t *testing.T) {
expr := &Expression{}
err := parser.ParseString("", `1 + 2 / 3 * (1 + 2)`, expr)
require.NoError(t, err)
expr, err := parser.ParseString("", `1 + 2 / 3 * (1 + 2)`)
repr.Println(expr)
require.NoError(t, err)
}
5 changes: 2 additions & 3 deletions _examples/expr3/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type Expression struct {
X ExprPrecAll `@@`
}

var parser = participle.MustBuild(&Expression{},
var parser = participle.MustBuild[Expression](
// This grammar requires enough lookahead to see the entire expression before
// it can select the proper binary expression type - in other words, we only
// know that `1 * 2 * 3 * 4` isn't the left-hand side of an addition or subtraction
Expand All @@ -126,8 +126,7 @@ func main() {
}
ctx := kong.Parse(&cli)

expr := &Expression{}
err := parser.ParseString("", strings.Join(cli.Expr, " "), expr)
expr, err := parser.ParseString("", strings.Join(cli.Expr, " "))
ctx.FatalIfErrorf(err)

repr.Println(expr)
Expand Down
4 changes: 2 additions & 2 deletions _examples/expr3/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func TestExpressionParser(t *testing.T) {
},
},
} {
var actual Expression
require.NoError(t, parser.ParseString("<test>", c.src, &actual))
actual, err := parser.ParseString("<test>", c.src)
require.NoError(t, err)
require.Equal(t, c.expected, actual.X)
}
}
5 changes: 2 additions & 3 deletions _examples/expr4/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,15 @@ type Expression struct {
X Expr `@@`
}

var parser = participle.MustBuild(&Expression{}, participle.ParseTypeWith(parseExprAny))
var parser = participle.MustBuild[Expression](participle.ParseTypeWith(parseExprAny))

func main() {
var cli struct {
Expr []string `arg required help:"Expression to parse."`
}
ctx := kong.Parse(&cli)

expr := &Expression{}
err := parser.ParseString("", strings.Join(cli.Expr, " "), expr)
expr, err := parser.ParseString("", strings.Join(cli.Expr, " "))
ctx.FatalIfErrorf(err)

repr.Println(expr)
Expand Down
4 changes: 2 additions & 2 deletions _examples/expr4/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ func TestCustomExprParser(t *testing.T) {
},
},
} {
var actual Expression
require.NoError(t, parser.ParseString("", c.src, &actual))
actual, err := parser.ParseString("", c.src)
require.NoError(t, err)
require.Equal(t, c.expected, actual.X)
}
}
7 changes: 3 additions & 4 deletions _examples/generics/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,15 @@ type RHS struct {
RHS *Expr `@@`
}

var parser = participle.MustBuild(&Expr{}, participle.UseLookahead(1024))
var parser = participle.MustBuild[Expr](participle.UseLookahead(1024))

func main() {
expr := &Expr{}
err := parser.ParseString("", "hello < world * (1 + 3) && (world > 10)", expr)
expr, err := parser.ParseString("", "hello < world * (1 + 3) && (world > 10)")
if err != nil {
panic(err)
}
repr.Println(expr)
err = parser.ParseString("", "type<int, string>.method(1, 2, 3)", expr)
expr, err = parser.ParseString("", "type<int, string>.method(1, 2, 3)")
if err != nil {
panic(err)
}
Expand Down
5 changes: 2 additions & 3 deletions _examples/graphql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ var (
{"Punct", `[-[!@#$%^&*()+_={}\|:;"'<,>.?/]|]`},
{"Whitespace", `[ \t\n\r]+`},
})
parser = participle.MustBuild(&File{},
parser = participle.MustBuild[File](
participle.Lexer(graphQLLexer),
participle.Elide("Comment", "Whitespace"),
participle.UseLookahead(2),
Expand All @@ -87,10 +87,9 @@ func main() {
ctx.Exit(0)
}
for _, file := range cli.Files {
ast := &File{}
r, err := os.Open(file)
ctx.FatalIfErrorf(err)
err = parser.Parse("", r, ast)
ast, err := parser.Parse("", r)
r.Close()
repr.Println(ast)
ctx.FatalIfErrorf(err)
Expand Down
3 changes: 1 addition & 2 deletions _examples/graphql/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ func BenchmarkParser(b *testing.B) {
b.ReportAllocs()
b.ReportMetric(float64(len(source)*b.N), "B/s")
for i := 0; i < b.N; i++ {
ast := &File{}
_ = parser.ParseBytes("", source, ast)
_, _ = parser.ParseBytes("", source)
}
}
5 changes: 2 additions & 3 deletions _examples/hcl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ type Config struct {
Entries []*Entry `@@*`
}

var parser = participle.MustBuild(&Config{}, participle.Unquote())
var parser = participle.MustBuild[Config](participle.Unquote())

func main() {
expr := &Config{}
err := parser.Parse("", os.Stdin, expr)
expr, err := parser.Parse("", os.Stdin)
if err != nil {
panic(err)
}
Expand Down
7 changes: 3 additions & 4 deletions _examples/hcl/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
)

func TestExe(t *testing.T) {
ast := &Config{}
err := parser.ParseString("", `
ast, err := parser.ParseString("", `
region = "us-west-2"
access_key = "something"
secret_key = "something_else"
Expand All @@ -35,7 +34,7 @@ directory data {
pre_restore_script = "before_restore.sh"
post_restore_script = "after_restore.sh"
}
`, ast)
require.NoError(t, err)
`)
repr.Println(ast)
require.NoError(t, err)
}
7 changes: 3 additions & 4 deletions _examples/ini/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var (
{"comment", `[#;][^\n]*`},
{"whitespace", `\s+`},
})
parser = participle.MustBuild(&INI{},
parser = participle.MustBuild[INI](
participle.Lexer(iniLexer),
participle.Unquote("String"),
participle.Union[Value](String{}, Number{}),
Expand Down Expand Up @@ -57,10 +57,9 @@ type Number struct {
func (Number) value() {}

func main() {
ini := &INI{}
err := parser.Parse("", os.Stdin, ini)
ini, err := parser.Parse("", os.Stdin)
repr.Println(ini, repr.Indent(" "), repr.OmitEmpty(true))
if err != nil {
panic(err)
}
repr.Println(ini, repr.Indent(" "), repr.OmitEmpty(true))
}
5 changes: 2 additions & 3 deletions _examples/ini/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ import (
)

func TestExe(t *testing.T) {
ini := &INI{}
err := parser.ParseString("", `
ini, err := parser.ParseString("", `
global = 1
[section]
value = "str"
`, ini)
`)
require.NoError(t, err)
repr.Println(ini)
}
Loading

0 comments on commit bf4573a

Please sign in to comment.