mirror of
https://github.com/minio/minio.git
synced 2025-11-13 15:21:36 -05:00
Add new SQL parser to support S3 Select syntax (#7102)
- New parser written from scratch, allows easier and complete parsing of the full S3 Select SQL syntax. Parser definition is directly provided by the AST defined for the SQL grammar. - Bring support to parse and interpret SQL involving JSON path expressions; evaluation of JSON path expressions will be subsequently added. - Bring automatic type inference and conversion for untyped values (e.g. CSV data).
This commit is contained in:
committed by
Harshavardhana
parent
0a28c28a8c
commit
2786055df4
19
vendor/github.com/alecthomas/participle/COPYING
generated
vendored
Normal file
19
vendor/github.com/alecthomas/participle/COPYING
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2017 Alec Thomas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
345
vendor/github.com/alecthomas/participle/README.md
generated
vendored
Normal file
345
vendor/github.com/alecthomas/participle/README.md
generated
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
# A dead simple parser package for Go
|
||||
|
||||
[](http://godoc.org/github.com/alecthomas/participle) [](https://circleci.com/gh/alecthomas/participle)
|
||||
[](https://goreportcard.com/report/github.com/alecthomas/participle) [](https://gitter.im/alecthomas/Lobby)
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [Limitations](#limitations)
|
||||
3. [Tutorial](#tutorial)
|
||||
4. [Overview](#overview)
|
||||
5. [Annotation syntax](#annotation-syntax)
|
||||
6. [Capturing](#capturing)
|
||||
7. [Streaming](#streaming)
|
||||
8. [Lexing](#lexing)
|
||||
9. [Options](#options)
|
||||
10. [Examples](#examples)
|
||||
11. [Performance](#performance)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
<a id="markdown-introduction" name="introduction"></a>
|
||||
## Introduction
|
||||
|
||||
The goal of this package is to provide a simple, idiomatic and elegant way of
|
||||
defining parsers in Go.
|
||||
|
||||
Participle's method of defining grammars should be familiar to any Go
|
||||
programmer who has used the `encoding/json` package: struct field tags define
|
||||
what and how input is mapped to those same fields. This is not unusual for Go
|
||||
encoders, but is unusual for a parser.
|
||||
|
||||
<a id="markdown-limitations" name="limitations"></a>
|
||||
## Limitations
|
||||
|
||||
Participle parsers are recursive descent. Among other things, this means that they do not support left recursion.
|
||||
|
||||
There is an experimental lookahead option for using precomputed lookahead
|
||||
tables for disambiguation. You can enable this with the parser option
|
||||
`participle.UseLookahead()`.
|
||||
|
||||
Left recursion must be eliminated by restructuring your grammar.
|
||||
|
||||
<a id="markdown-tutorial" name="tutorial"></a>
|
||||
## Tutorial
|
||||
|
||||
A [tutorial](TUTORIAL.md) is available, walking through the creation of an .ini parser.
|
||||
|
||||
<a id="markdown-overview" name="overview"></a>
|
||||
## Overview
|
||||
|
||||
A grammar is an annotated Go structure used to both define the parser grammar,
|
||||
and be the AST output by the parser. As an example, following is the final INI
|
||||
parser from the tutorial.
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property `{ @@ }`
|
||||
Sections []*Section `{ @@ }`
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
Identifier string `"[" @Ident "]"`
|
||||
Properties []*Property `{ @@ }`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Key string `@Ident "="`
|
||||
Value *Value `@@`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
String *string ` @String`
|
||||
Number *float64 `| @Float`
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Participle also supports named struct tags (eg. <code>Hello string `parser:"@Ident"`</code>).
|
||||
|
||||
A parser is constructed from a grammar and a lexer:
|
||||
|
||||
```go
|
||||
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 == &INI{
|
||||
// Properties: []*Property{
|
||||
// {Key: "size", Value: &Value{Number: &10}},
|
||||
// },
|
||||
// }
|
||||
```
|
||||
|
||||
<a id="markdown-annotation-syntax" name="annotation-syntax"></a>
|
||||
## Annotation syntax
|
||||
|
||||
- `@<expr>` Capture expression into the field.
|
||||
- `@@` Recursively capture using the fields own type.
|
||||
- `<identifier>` Match named lexer token.
|
||||
- `( ... )` Group.
|
||||
- `"..."` Match the literal (note that the lexer must emit tokens matching this literal exactly).
|
||||
- `"...":<identifier>` Match the literal, specifying the exact lexer token type to match.
|
||||
- `<expr> <expr> ...` Match expressions.
|
||||
- `<expr> | <expr>` Match one of the alternatives.
|
||||
|
||||
The following modifiers can be used after any expression:
|
||||
|
||||
- `*` Expression can match zero or more times.
|
||||
- `+` Expression must match one or more times.
|
||||
- `?` Expression can match zero or once.
|
||||
- `!` Require a non-empty match (this is useful with a sequence of optional matches eg. `("a"? "b"? "c"?)!`).
|
||||
|
||||
Supported but deprecated:
|
||||
- `{ ... }` Match 0 or more times (**DEPRECATED** - prefer `( ... )*`).
|
||||
- `[ ... ]` Optional (**DEPRECATED** - prefer `( ... )?`).
|
||||
|
||||
Notes:
|
||||
|
||||
- Each struct is a single production, with each field applied in sequence.
|
||||
- `@<expr>` is the mechanism for capturing matches into the field.
|
||||
- if a struct field is not keyed with "parser", the entire struct tag
|
||||
will be used as the grammar fragment. This allows the grammar syntax to remain
|
||||
clear and simple to maintain.
|
||||
|
||||
<a id="markdown-capturing" name="capturing"></a>
|
||||
## Capturing
|
||||
|
||||
Prefixing any expression in the grammar with `@` will capture matching values
|
||||
for that expression into the corresponding field.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
// The grammar definition.
|
||||
type Grammar struct {
|
||||
Hello string `@Ident`
|
||||
}
|
||||
|
||||
// The source text to parse.
|
||||
source := "world"
|
||||
|
||||
// After parsing, the resulting AST.
|
||||
result == &Grammar{
|
||||
Hello: "world",
|
||||
}
|
||||
```
|
||||
|
||||
For slice and string fields, each instance of `@` will accumulate into the
|
||||
field (including repeated patterns). Accumulation into other types is not
|
||||
supported.
|
||||
|
||||
A successful capture match into a boolean field will set the field to true.
|
||||
|
||||
For integer and floating point types, a successful capture will be parsed
|
||||
with `strconv.ParseInt()` and `strconv.ParseBool()` respectively.
|
||||
|
||||
Custom control of how values are captured into fields can be achieved by a
|
||||
field type implementing the `Capture` interface (`Capture(values []string)
|
||||
error`).
|
||||
|
||||
<a id="markdown-streaming" name="streaming"></a>
|
||||
## Streaming
|
||||
|
||||
Participle supports streaming parsing. Simply pass a channel of your grammar into
|
||||
`Parse*()`. The grammar will be repeatedly parsed and sent to the channel. Note that
|
||||
the `Parse*()` call will not return until parsing completes, so it should generally be
|
||||
started in a goroutine.
|
||||
|
||||
```go
|
||||
type token struct {
|
||||
Str string ` @Ident`
|
||||
Num int `| @Int`
|
||||
}
|
||||
|
||||
parser, err := participle.Build(&token{})
|
||||
|
||||
tokens := make(chan *token, 128)
|
||||
err := parser.ParseString(`hello 10 11 12 world`, tokens)
|
||||
for token := range tokens {
|
||||
fmt.Printf("%#v\n", token)
|
||||
}
|
||||
```
|
||||
|
||||
<a id="markdown-lexing" name="lexing"></a>
|
||||
## Lexing
|
||||
|
||||
Participle operates on tokens and thus relies on a lexer to convert character
|
||||
streams to tokens.
|
||||
|
||||
Three lexers are provided, varying in speed and flexibility. The fastest lexer
|
||||
is based on the [text/scanner](https://golang.org/pkg/text/scanner/) package
|
||||
but only allows tokens provided by that package. Next fastest is the regexp
|
||||
lexer (`lexer.Regexp()`). The slowest is currently the EBNF based lexer, but it has a large potential for optimisation through code generation.
|
||||
|
||||
To use your own Lexer you will need to implement two interfaces:
|
||||
[Definition](https://godoc.org/github.com/alecthomas/participle/lexer#Definition)
|
||||
and [Lexer](https://godoc.org/github.com/alecthomas/participle/lexer#Lexer).
|
||||
|
||||
<a id="markdown-options" name="options"></a>
|
||||
## Options
|
||||
|
||||
The Parser's behaviour can be configured via [Options](https://godoc.org/github.com/alecthomas/participle#Option).
|
||||
|
||||
<a id="markdown-examples" name="examples"></a>
|
||||
## Examples
|
||||
|
||||
There are several [examples](https://github.com/alecthomas/participle/tree/master/_examples) included:
|
||||
|
||||
Example | Description
|
||||
--------|---------------
|
||||
[BASIC](https://github.com/alecthomas/participle/tree/master/_examples/basic) | A lexer, parser and interpreter for a [rudimentary dialect](https://caml.inria.fr/pub/docs/oreilly-book/html/book-ora058.html) of BASIC.
|
||||
[EBNF](https://github.com/alecthomas/participle/tree/master/_examples/ebnf) | Parser for the form of EBNF used by Go.
|
||||
[Expr](https://github.com/alecthomas/participle/tree/master/_examples/expr) | A basic mathematical expression parser and evaluator.
|
||||
[GraphQL](https://github.com/alecthomas/participle/tree/master/_examples/graphql) | Lexer+parser for GraphQL schemas
|
||||
[HCL](https://github.com/alecthomas/participle/tree/master/_examples/hcl) | A parser for the [HashiCorp Configuration Language](https://github.com/hashicorp/hcl).
|
||||
[INI](https://github.com/alecthomas/participle/tree/master/_examples/ini) | An INI file parser.
|
||||
[Protobuf](https://github.com/alecthomas/participle/tree/master/_examples/protobuf) | A full [Protobuf](https://developers.google.com/protocol-buffers/) version 2 and 3 parser.
|
||||
[SQL](https://github.com/alecthomas/participle/tree/master/_examples/sql) | A *very* rudimentary SQL SELECT parser.
|
||||
[Thrift](https://github.com/alecthomas/participle/tree/master/_examples/thrift) | A full [Thrift](https://thrift.apache.org/docs/idl) parser.
|
||||
[TOML](https://github.com/alecthomas/participle/blob/master/_examples/toml/main.go) | A [TOML](https://github.com/toml-lang/toml) parser.
|
||||
|
||||
Included below is a full GraphQL lexer and parser:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/alecthomas/repr"
|
||||
|
||||
"github.com/alecthomas/participle"
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
"github.com/alecthomas/participle/lexer/ebnf"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Entries []*Entry `{ @@ }`
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Type *Type ` @@`
|
||||
Schema *Schema `| @@`
|
||||
Enum *Enum `| @@`
|
||||
Scalar string `| "scalar" @Ident`
|
||||
}
|
||||
|
||||
type Enum struct {
|
||||
Name string `"enum" @Ident`
|
||||
Cases []string `"{" { @Ident } "}"`
|
||||
}
|
||||
|
||||
type Schema struct {
|
||||
Fields []*Field `"schema" "{" { @@ } "}"`
|
||||
}
|
||||
|
||||
type Type struct {
|
||||
Name string `"type" @Ident`
|
||||
Implements string `[ "implements" @Ident ]`
|
||||
Fields []*Field `"{" { @@ } "}"`
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string `@Ident`
|
||||
Arguments []*Argument `[ "(" [ @@ { "," @@ } ] ")" ]`
|
||||
Type *TypeRef `":" @@`
|
||||
Annotation string `[ "@" @Ident ]`
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Name string `@Ident`
|
||||
Type *TypeRef `":" @@`
|
||||
Default *Value `[ "=" @@ ]`
|
||||
}
|
||||
|
||||
type TypeRef struct {
|
||||
Array *TypeRef `( "[" @@ "]"`
|
||||
Type string ` | @Ident )`
|
||||
NonNullable bool `[ @"!" ]`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Symbol string `@Ident`
|
||||
}
|
||||
|
||||
var (
|
||||
graphQLLexer = lexer.Must(ebnf.New(`
|
||||
Comment = ("#" | "//") { "\u0000"…"\uffff"-"\n" } .
|
||||
Ident = (alpha | "_") { "_" | alpha | digit } .
|
||||
Number = ("." | digit) {"." | digit} .
|
||||
Whitespace = " " | "\t" | "\n" | "\r" .
|
||||
Punct = "!"…"/" | ":"…"@" | "["…`+"\"`\""+` | "{"…"~" .
|
||||
|
||||
alpha = "a"…"z" | "A"…"Z" .
|
||||
digit = "0"…"9" .
|
||||
`))
|
||||
|
||||
parser = participle.MustBuild(&File{},
|
||||
participle.Lexer(graphQLLexer),
|
||||
participle.Elide("Comment", "Whitespace"),
|
||||
)
|
||||
|
||||
cli struct {
|
||||
Files []string `arg:"" type:"existingfile" required:"" help:"GraphQL schema files to parse."`
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := kong.Parse(&cli)
|
||||
for _, file := range cli.Files {
|
||||
ast := &File{}
|
||||
r, err := os.Open(file)
|
||||
ctx.FatalIfErrorf(err)
|
||||
err = parser.Parse(r, ast)
|
||||
r.Close()
|
||||
repr.Println(ast)
|
||||
ctx.FatalIfErrorf(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<a id="markdown-performance" name="performance"></a>
|
||||
## Performance
|
||||
|
||||
One of the included examples is a complete Thrift parser
|
||||
(shell-style comments are not supported). This gives
|
||||
a convenient baseline for comparing to the PEG based
|
||||
[pigeon](https://github.com/PuerkitoBio/pigeon), which is the parser used by
|
||||
[go-thrift](https://github.com/samuel/go-thrift). Additionally, the pigeon
|
||||
parser is utilising a generated parser, while the participle parser is built at
|
||||
run time.
|
||||
|
||||
You can run the benchmarks yourself, but here's the output on my machine:
|
||||
|
||||
BenchmarkParticipleThrift-4 10000 221818 ns/op 48880 B/op 1240 allocs/op
|
||||
BenchmarkGoThriftParser-4 2000 804709 ns/op 170301 B/op 3086 allocs/op
|
||||
|
||||
On a real life codebase of 47K lines of Thrift, Participle takes 200ms and go-
|
||||
thrift takes 630ms, which aligns quite closely with the benchmarks.
|
||||
255
vendor/github.com/alecthomas/participle/TUTORIAL.md
generated
vendored
Normal file
255
vendor/github.com/alecthomas/participle/TUTORIAL.md
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
# Participle parser tutorial
|
||||
|
||||
<!-- MarkdownTOC -->
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
1. [The complete grammar](#the-complete-grammar)
|
||||
1. [Root of the .ini AST \(structure, fields\)](#root-of-the-ini-ast-structure-fields)
|
||||
1. [.ini properties \(named tokens, capturing, literals\)](#ini-properties-named-tokens-capturing-literals)
|
||||
1. [.ini property values \(alternates, recursive structs, sequences\)](#ini-property-values-alternates-recursive-structs-sequences)
|
||||
1. [Complete, but limited, .ini grammar \(top-level properties only\)](#complete-but-limited-ini-grammar-top-level-properties-only)
|
||||
1. [Extending our grammar to support sections](#extending-our-grammar-to-support-sections)
|
||||
1. [\(Optional\) Source positional information](#optional-source-positional-information)
|
||||
1. [Parsing using our grammar](#parsing-using-our-grammar)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## Introduction
|
||||
|
||||
Writing a parser in Participle typically involves starting from the "root" of
|
||||
the AST, annotating fields with the grammar, then recursively expanding until
|
||||
it is complete. The AST is expressed via Go data types and the grammar is
|
||||
expressed through struct field tags, as a form of EBNF.
|
||||
|
||||
The parser we're going to create for this tutorial parses .ini files
|
||||
like this:
|
||||
|
||||
```ini
|
||||
age = 21
|
||||
name = "Bob Smith"
|
||||
|
||||
[address]
|
||||
city = "Beverly Hills"
|
||||
postal_code = 90210
|
||||
```
|
||||
|
||||
## The complete grammar
|
||||
|
||||
I think it's useful to see the complete grammar first, to see what we're
|
||||
working towards. Read on below for details.
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property `@@*`
|
||||
Sections []*Section `@@*`
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
Identifier string `"[" @Ident "]"`
|
||||
Properties []*Property `@@*`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Key string `@Ident "="`
|
||||
Value *Value `@@`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
String *string ` @String`
|
||||
Number *float64 `| @Float`
|
||||
}
|
||||
```
|
||||
|
||||
## Root of the .ini AST (structure, fields)
|
||||
|
||||
The first step is to create a root struct for our grammar. In the case of our
|
||||
.ini parser, this struct will contain a sequence of properties:
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
}
|
||||
```
|
||||
|
||||
## .ini properties (named tokens, capturing, literals)
|
||||
|
||||
Each property in an .ini file has an identifier key:
|
||||
|
||||
```go
|
||||
type Property struct {
|
||||
Key string
|
||||
}
|
||||
```
|
||||
|
||||
The default lexer tokenises Go source code, and includes an `Ident` token type
|
||||
that matches identifiers. To match this token we simply use the token type
|
||||
name:
|
||||
|
||||
```go
|
||||
type Property struct {
|
||||
Key string `Ident`
|
||||
}
|
||||
```
|
||||
|
||||
This will *match* identifiers, but not *capture* them into the `Key` field. To
|
||||
capture input tokens into AST fields, prefix any grammar node with `@`:
|
||||
|
||||
```go
|
||||
type Property struct {
|
||||
Key string `@Ident`
|
||||
}
|
||||
```
|
||||
|
||||
In .ini files, each key is separated from its value with a literal `=`. To
|
||||
match a literal, enclose the literal in double quotes:
|
||||
|
||||
```go
|
||||
type Property struct {
|
||||
Key string `@Ident "="`
|
||||
}
|
||||
```
|
||||
|
||||
> Note: literals in the grammar must match tokens from the lexer *exactly*. In
|
||||
> this example if the lexer does not output `=` as a distinct token the
|
||||
> grammar will not match.
|
||||
|
||||
## .ini property values (alternates, recursive structs, sequences)
|
||||
|
||||
For the purposes of our example we are only going to support quoted string
|
||||
and numeric property values. As each value can be *either* a string or a float
|
||||
we'll need something akin to a sum type. Go's type system cannot express this
|
||||
directly, so we'll use the common approach of making each element a pointer.
|
||||
The selected "case" will *not* be nil.
|
||||
|
||||
```go
|
||||
type Value struct {
|
||||
String *string
|
||||
Number *float64
|
||||
}
|
||||
```
|
||||
|
||||
> Note: Participle will hydrate pointers as necessary.
|
||||
|
||||
To express matching a set of alternatives we use the `|` operator:
|
||||
|
||||
```go
|
||||
type Value struct {
|
||||
String *string ` @String`
|
||||
Number *float64 `| @Float`
|
||||
}
|
||||
```
|
||||
|
||||
> Note: the grammar can cross fields.
|
||||
|
||||
Next, we'll match values and capture them into the `Property`. To recursively
|
||||
capture structs use `@@` (capture self):
|
||||
|
||||
```go
|
||||
type Property struct {
|
||||
Key string `@Ident "="`
|
||||
Value *Value `@@`
|
||||
}
|
||||
```
|
||||
|
||||
Now that we can parse a `Property` we need to go back to the root of the
|
||||
grammar. We want to parse 0 or more properties. To do this, we use `<expr>*`.
|
||||
Participle will accumulate each match into the slice until matching fails,
|
||||
then move to the next node in the grammar.
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property `@@*`
|
||||
}
|
||||
```
|
||||
|
||||
> Note: tokens can also be accumulated into strings, appending each match.
|
||||
|
||||
## Complete, but limited, .ini grammar (top-level properties only)
|
||||
|
||||
We now have a functional, but limited, .ini parser!
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property `@@*`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Key string `@Ident "="`
|
||||
Value *Value `@@`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
String *string ` @String`
|
||||
Number *float64 `| @Float`
|
||||
}
|
||||
```
|
||||
|
||||
## Extending our grammar to support sections
|
||||
|
||||
Adding support for sections is simply a matter of utilising the constructs
|
||||
we've just learnt. A section consists of a header identifier, and a sequence
|
||||
of properties:
|
||||
|
||||
```go
|
||||
type Section struct {
|
||||
Identifier string `"[" @Ident "]"`
|
||||
Properties []*Property `@@*`
|
||||
}
|
||||
```
|
||||
|
||||
Simple!
|
||||
|
||||
Now we just add a sequence of `Section`s to our root node:
|
||||
|
||||
```go
|
||||
type INI struct {
|
||||
Properties []*Property `@@*`
|
||||
Sections []*Section `@@*`
|
||||
}
|
||||
```
|
||||
|
||||
And we're done!
|
||||
|
||||
## (Optional) Source positional information
|
||||
|
||||
If a grammar node includes a field with the name `Pos` and type `lexer.Position`, it will be automatically populated by positional information. eg.
|
||||
|
||||
```go
|
||||
type Value struct {
|
||||
Pos lexer.Position
|
||||
String *string ` @String`
|
||||
Number *float64 `| @Float`
|
||||
}
|
||||
```
|
||||
|
||||
This is useful for error reporting.
|
||||
|
||||
## Parsing using our grammar
|
||||
|
||||
To parse with this grammar we first construct the parser (we'll use the
|
||||
default lexer for now):
|
||||
|
||||
```go
|
||||
parser, err := participle.Build(&INI{})
|
||||
```
|
||||
|
||||
Then create a root node and parse into it with `parser.Parse{,String,Bytes}()`:
|
||||
|
||||
```go
|
||||
ini := &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
|
||||
other examples including an SQL `SELECT` parser and a full
|
||||
[Thrift](https://thrift.apache.org/) parser.
|
||||
19
vendor/github.com/alecthomas/participle/api.go
generated
vendored
Normal file
19
vendor/github.com/alecthomas/participle/api.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
// Capture can be implemented by fields in order to transform captured tokens into field values.
|
||||
type Capture interface {
|
||||
Capture(values []string) error
|
||||
}
|
||||
|
||||
// The Parseable interface can be implemented by any element in the grammar to provide custom parsing.
|
||||
type Parseable interface {
|
||||
// Parse into the receiver.
|
||||
//
|
||||
// Should return NextMatch if no tokens matched and parsing should continue.
|
||||
// Nil should be returned if parsing was successful.
|
||||
Parse(lex lexer.PeekingLexer) error
|
||||
}
|
||||
123
vendor/github.com/alecthomas/participle/context.go
generated
vendored
Normal file
123
vendor/github.com/alecthomas/participle/context.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
type contextFieldSet struct {
|
||||
pos lexer.Position
|
||||
strct reflect.Value
|
||||
field structLexerField
|
||||
fieldValue []reflect.Value
|
||||
}
|
||||
|
||||
// Context for a single parse.
|
||||
type parseContext struct {
|
||||
*rewinder
|
||||
lookahead int
|
||||
caseInsensitive map[rune]bool
|
||||
apply []*contextFieldSet
|
||||
}
|
||||
|
||||
func newParseContext(lex lexer.Lexer, lookahead int, caseInsensitive map[rune]bool) (*parseContext, error) {
|
||||
rew, err := newRewinder(lex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &parseContext{
|
||||
rewinder: rew,
|
||||
caseInsensitive: caseInsensitive,
|
||||
lookahead: lookahead,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Defer adds a function to be applied once a branch has been picked.
|
||||
func (p *parseContext) Defer(pos lexer.Position, strct reflect.Value, field structLexerField, fieldValue []reflect.Value) {
|
||||
p.apply = append(p.apply, &contextFieldSet{pos, strct, field, fieldValue})
|
||||
}
|
||||
|
||||
// Apply deferred functions.
|
||||
func (p *parseContext) Apply() error {
|
||||
for _, apply := range p.apply {
|
||||
if err := setField(apply.pos, apply.strct, apply.field, apply.fieldValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.apply = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Branch accepts the branch as the correct branch.
|
||||
func (p *parseContext) Accept(branch *parseContext) {
|
||||
p.apply = append(p.apply, branch.apply...)
|
||||
p.rewinder = branch.rewinder
|
||||
}
|
||||
|
||||
// Branch starts a new lookahead branch.
|
||||
func (p *parseContext) Branch() *parseContext {
|
||||
branch := &parseContext{}
|
||||
*branch = *p
|
||||
branch.apply = nil
|
||||
branch.rewinder = p.rewinder.Lookahead()
|
||||
return branch
|
||||
}
|
||||
|
||||
// Stop returns true if parsing should terminate after the given "branch" failed to match.
|
||||
func (p *parseContext) Stop(branch *parseContext) bool {
|
||||
if branch.cursor > p.cursor+p.lookahead {
|
||||
p.Accept(branch)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type rewinder struct {
|
||||
cursor, limit int
|
||||
tokens []lexer.Token
|
||||
}
|
||||
|
||||
func newRewinder(lex lexer.Lexer) (*rewinder, error) {
|
||||
r := &rewinder{}
|
||||
for {
|
||||
t, err := lex.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.EOF() {
|
||||
break
|
||||
}
|
||||
r.tokens = append(r.tokens, t)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *rewinder) Next() (lexer.Token, error) {
|
||||
if r.cursor >= len(r.tokens) {
|
||||
return lexer.EOFToken(lexer.Position{}), nil
|
||||
}
|
||||
r.cursor++
|
||||
return r.tokens[r.cursor-1], nil
|
||||
}
|
||||
|
||||
func (r *rewinder) Peek(n int) (lexer.Token, error) {
|
||||
i := r.cursor + n
|
||||
if i >= len(r.tokens) {
|
||||
return lexer.EOFToken(lexer.Position{}), nil
|
||||
}
|
||||
return r.tokens[i], nil
|
||||
}
|
||||
|
||||
// Lookahead returns a new rewinder usable for lookahead.
|
||||
func (r *rewinder) Lookahead() *rewinder {
|
||||
clone := &rewinder{}
|
||||
*clone = *r
|
||||
clone.limit = clone.cursor
|
||||
return clone
|
||||
}
|
||||
|
||||
// Keep this lookahead rewinder.
|
||||
func (r *rewinder) Keep() {
|
||||
r.limit = 0
|
||||
}
|
||||
73
vendor/github.com/alecthomas/participle/doc.go
generated
vendored
Normal file
73
vendor/github.com/alecthomas/participle/doc.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Package participle constructs parsers from definitions in struct tags and parses directly into
|
||||
// those structs. The approach is philosophically similar to how other marshallers work in Go,
|
||||
// "unmarshalling" an instance of a grammar into a struct.
|
||||
//
|
||||
// The supported annotation syntax is:
|
||||
//
|
||||
// - `@<expr>` Capture expression into the field.
|
||||
// - `@@` Recursively capture using the fields own type.
|
||||
// - `<identifier>` Match named lexer token.
|
||||
// - `( ... )` Group.
|
||||
// - `"..."` Match the literal (note that the lexer must emit tokens matching this literal exactly).
|
||||
// - `"...":<identifier>` Match the literal, specifying the exact lexer token type to match.
|
||||
// - `<expr> <expr> ...` Match expressions.
|
||||
// - `<expr> | <expr>` Match one of the alternatives.
|
||||
//
|
||||
// The following modifiers can be used after any expression:
|
||||
//
|
||||
// - `*` Expression can match zero or more times.
|
||||
// - `+` Expression must match one or more times.
|
||||
// - `?` Expression can match zero or once.
|
||||
// - `!` Require a non-empty match (this is useful with a sequence of optional matches eg. `("a"? "b"? "c"?)!`).
|
||||
//
|
||||
// Supported but deprecated:
|
||||
//
|
||||
// - `{ ... }` Match 0 or more times (**DEPRECATED** - prefer `( ... )*`).
|
||||
// - `[ ... ]` Optional (**DEPRECATED** - prefer `( ... )?`).
|
||||
//
|
||||
// Here's an example of an EBNF grammar.
|
||||
//
|
||||
// type Group struct {
|
||||
// Expression *Expression `"(" @@ ")"`
|
||||
// }
|
||||
//
|
||||
// type Option struct {
|
||||
// Expression *Expression `"[" @@ "]"`
|
||||
// }
|
||||
//
|
||||
// type Repetition struct {
|
||||
// Expression *Expression `"{" @@ "}"`
|
||||
// }
|
||||
//
|
||||
// type Literal struct {
|
||||
// Start string `@String` // lexer.Lexer token "String"
|
||||
// End string `("…" @String)?`
|
||||
// }
|
||||
//
|
||||
// type Term struct {
|
||||
// Name string ` @Ident`
|
||||
// Literal *Literal `| @@`
|
||||
// Group *Group `| @@`
|
||||
// Option *Option `| @@`
|
||||
// Repetition *Repetition `| @@`
|
||||
// }
|
||||
//
|
||||
// type Sequence struct {
|
||||
// Terms []*Term `@@+`
|
||||
// }
|
||||
//
|
||||
// type Expression struct {
|
||||
// Alternatives []*Sequence `@@ ("|" @@)*`
|
||||
// }
|
||||
//
|
||||
// type Expressions []*Expression
|
||||
//
|
||||
// type Production struct {
|
||||
// Name string `@Ident "="`
|
||||
// Expressions Expressions `@@+ "."`
|
||||
// }
|
||||
//
|
||||
// type EBNF struct {
|
||||
// Productions []*Production `@@*`
|
||||
// }
|
||||
package participle
|
||||
7
vendor/github.com/alecthomas/participle/go.mod
generated
vendored
Normal file
7
vendor/github.com/alecthomas/participle/go.mod
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module github.com/alecthomas/participle
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
)
|
||||
6
vendor/github.com/alecthomas/participle/go.sum
generated
vendored
Normal file
6
vendor/github.com/alecthomas/participle/go.sum
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
324
vendor/github.com/alecthomas/participle/grammar.go
generated
vendored
Normal file
324
vendor/github.com/alecthomas/participle/grammar.go
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"text/scanner"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
type generatorContext struct {
|
||||
lexer.Definition
|
||||
typeNodes map[reflect.Type]node
|
||||
symbolsToIDs map[rune]string
|
||||
}
|
||||
|
||||
func newGeneratorContext(lex lexer.Definition) *generatorContext {
|
||||
return &generatorContext{
|
||||
Definition: lex,
|
||||
typeNodes: map[reflect.Type]node{},
|
||||
symbolsToIDs: lexer.SymbolsByRune(lex),
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a type and builds a tree of nodes out of it.
|
||||
func (g *generatorContext) parseType(t reflect.Type) (_ node, returnedError error) {
|
||||
rt := t
|
||||
t = indirectType(t)
|
||||
if n, ok := g.typeNodes[t]; ok {
|
||||
return n, nil
|
||||
}
|
||||
if rt.Implements(parseableType) {
|
||||
return &parseable{rt.Elem()}, nil
|
||||
}
|
||||
if reflect.PtrTo(rt).Implements(parseableType) {
|
||||
return &parseable{rt}, nil
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Slice, reflect.Ptr:
|
||||
t = indirectType(t.Elem())
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("expected a struct but got %T", t)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Struct:
|
||||
slexer, err := lexStruct(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &strct{typ: t}
|
||||
g.typeNodes[t] = out // Ensure we avoid infinite recursion.
|
||||
if slexer.NumField() == 0 {
|
||||
return nil, fmt.Errorf("can not parse into empty struct %s", t)
|
||||
}
|
||||
defer decorate(&returnedError, func() string { return slexer.Field().Name })
|
||||
e, err := g.parseDisjunction(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if e == nil {
|
||||
return nil, fmt.Errorf("no grammar found in %s", t)
|
||||
}
|
||||
if token, _ := slexer.Peek(); !token.EOF() {
|
||||
return nil, fmt.Errorf("unexpected input %q", token.Value)
|
||||
}
|
||||
out.expr = e
|
||||
return out, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s should be a struct or should implement the Parseable interface", t)
|
||||
}
|
||||
|
||||
func (g *generatorContext) parseDisjunction(slexer *structLexer) (node, error) {
|
||||
out := &disjunction{}
|
||||
for {
|
||||
n, err := g.parseSequence(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.nodes = append(out.nodes, n)
|
||||
if token, _ := slexer.Peek(); token.Type != '|' {
|
||||
break
|
||||
}
|
||||
_, err = slexer.Next() // |
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(out.nodes) == 1 {
|
||||
return out.nodes[0], nil
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (g *generatorContext) parseSequence(slexer *structLexer) (node, error) {
|
||||
head := &sequence{}
|
||||
cursor := head
|
||||
loop:
|
||||
for {
|
||||
if token, err := slexer.Peek(); err != nil {
|
||||
return nil, err
|
||||
} else if token.Type == lexer.EOF {
|
||||
break loop
|
||||
}
|
||||
term, err := g.parseTerm(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if term == nil {
|
||||
break loop
|
||||
}
|
||||
if cursor.node == nil {
|
||||
cursor.head = true
|
||||
cursor.node = term
|
||||
} else {
|
||||
cursor.next = &sequence{node: term}
|
||||
cursor = cursor.next
|
||||
}
|
||||
}
|
||||
if head.node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if head.next == nil {
|
||||
return head.node, nil
|
||||
}
|
||||
return head, nil
|
||||
}
|
||||
|
||||
func (g *generatorContext) parseTermNoModifiers(slexer *structLexer) (node, error) {
|
||||
t, err := slexer.Peek()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out node
|
||||
switch t.Type {
|
||||
case '@':
|
||||
out, err = g.parseCapture(slexer)
|
||||
case scanner.String, scanner.RawString, scanner.Char:
|
||||
out, err = g.parseLiteral(slexer)
|
||||
case '[':
|
||||
return g.parseOptional(slexer)
|
||||
case '{':
|
||||
return g.parseRepetition(slexer)
|
||||
case '(':
|
||||
out, err = g.parseGroup(slexer)
|
||||
case scanner.Ident:
|
||||
out, err = g.parseReference(slexer)
|
||||
case lexer.EOF:
|
||||
_, _ = slexer.Next()
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (g *generatorContext) parseTerm(slexer *structLexer) (node, error) {
|
||||
out, err := g.parseTermNoModifiers(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return g.parseModifier(slexer, out)
|
||||
}
|
||||
|
||||
// Parse modifiers: ?, *, + and/or !
|
||||
func (g *generatorContext) parseModifier(slexer *structLexer, expr node) (node, error) {
|
||||
out := &group{expr: expr}
|
||||
t, err := slexer.Peek()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t.Type {
|
||||
case '!':
|
||||
out.mode = groupMatchNonEmpty
|
||||
case '+':
|
||||
out.mode = groupMatchOneOrMore
|
||||
case '*':
|
||||
out.mode = groupMatchZeroOrMore
|
||||
case '?':
|
||||
out.mode = groupMatchZeroOrOne
|
||||
default:
|
||||
return expr, nil
|
||||
}
|
||||
_, _ = slexer.Next()
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// @<expression> captures <expression> into the current field.
|
||||
func (g *generatorContext) parseCapture(slexer *structLexer) (node, error) {
|
||||
_, _ = slexer.Next()
|
||||
token, err := slexer.Peek()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
field := slexer.Field()
|
||||
if token.Type == '@' {
|
||||
_, _ = slexer.Next()
|
||||
n, err := g.parseType(field.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &capture{field, n}, nil
|
||||
}
|
||||
if indirectType(field.Type).Kind() == reflect.Struct && !field.Type.Implements(captureType) {
|
||||
return nil, fmt.Errorf("structs can only be parsed with @@ or by implementing the Capture interface")
|
||||
}
|
||||
n, err := g.parseTermNoModifiers(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &capture{field, n}, nil
|
||||
}
|
||||
|
||||
// A reference in the form <identifier> refers to a named token from the lexer.
|
||||
func (g *generatorContext) parseReference(slexer *structLexer) (node, error) { // nolint: interfacer
|
||||
token, err := slexer.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.Type != scanner.Ident {
|
||||
return nil, fmt.Errorf("expected identifier but got %q", token)
|
||||
}
|
||||
typ, ok := g.Symbols()[token.Value]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown token type %q", token)
|
||||
}
|
||||
return &reference{typ: typ, identifier: token.Value}, nil
|
||||
}
|
||||
|
||||
// [ <expression> ] optionally matches <expression>.
|
||||
func (g *generatorContext) parseOptional(slexer *structLexer) (node, error) {
|
||||
_, _ = slexer.Next() // [
|
||||
disj, err := g.parseDisjunction(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := &group{expr: disj, mode: groupMatchZeroOrOne}
|
||||
next, err := slexer.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if next.Type != ']' {
|
||||
return nil, fmt.Errorf("expected ] but got %q", next)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// { <expression> } matches 0 or more repititions of <expression>
|
||||
func (g *generatorContext) parseRepetition(slexer *structLexer) (node, error) {
|
||||
_, _ = slexer.Next() // {
|
||||
disj, err := g.parseDisjunction(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := &group{expr: disj, mode: groupMatchZeroOrMore}
|
||||
next, err := slexer.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if next.Type != '}' {
|
||||
return nil, fmt.Errorf("expected } but got %q", next)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ( <expression> ) groups a sub-expression
|
||||
func (g *generatorContext) parseGroup(slexer *structLexer) (node, error) {
|
||||
_, _ = slexer.Next() // (
|
||||
disj, err := g.parseDisjunction(slexer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next, err := slexer.Next() // )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if next.Type != ')' {
|
||||
return nil, fmt.Errorf("expected ) but got %q", next)
|
||||
}
|
||||
return &group{expr: disj}, nil
|
||||
}
|
||||
|
||||
// A literal string.
|
||||
//
|
||||
// Note that for this to match, the tokeniser must be able to produce this string. For example,
|
||||
// if the tokeniser only produces individual characters but the literal is "hello", or vice versa.
|
||||
func (g *generatorContext) parseLiteral(lex *structLexer) (node, error) { // nolint: interfacer
|
||||
token, err := lex.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.Type != scanner.String && token.Type != scanner.RawString && token.Type != scanner.Char {
|
||||
return nil, fmt.Errorf("expected quoted string but got %q", token)
|
||||
}
|
||||
s := token.Value
|
||||
t := rune(-1)
|
||||
token, err = lex.Peek()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.Value == ":" && (token.Type == scanner.Char || token.Type == ':') {
|
||||
_, _ = lex.Next()
|
||||
token, err = lex.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.Type != scanner.Ident {
|
||||
return nil, fmt.Errorf("expected identifier for literal type constraint but got %q", token)
|
||||
}
|
||||
var ok bool
|
||||
t, ok = g.Symbols()[token.Value]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown token type %q in literal type constraint", token)
|
||||
}
|
||||
}
|
||||
return &literal{s: s, t: t, tt: g.symbolsToIDs[t]}, nil
|
||||
}
|
||||
|
||||
func indirectType(t reflect.Type) reflect.Type {
|
||||
if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
|
||||
return indirectType(t.Elem())
|
||||
}
|
||||
return t
|
||||
}
|
||||
19
vendor/github.com/alecthomas/participle/lexer/doc.go
generated
vendored
Normal file
19
vendor/github.com/alecthomas/participle/lexer/doc.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Package lexer defines interfaces and implementations used by Participle to perform lexing.
|
||||
//
|
||||
// The primary interfaces are Definition and Lexer. There are three implementations of these
|
||||
// interfaces:
|
||||
//
|
||||
// TextScannerLexer is based on text/scanner. This is the fastest, but least flexible, in that
|
||||
// tokens are restricted to those supported by that package. It can scan about 5M tokens/second on a
|
||||
// late 2013 15" MacBook Pro.
|
||||
//
|
||||
// The second lexer is constructed via the Regexp() function, mapping regexp capture groups
|
||||
// to tokens. The complete input source is read into memory, so it is unsuitable for large inputs.
|
||||
//
|
||||
// The final lexer provided accepts a lexical grammar in EBNF. Each capitalised production is a
|
||||
// lexical token supported by the resulting Lexer. This is very flexible, but a bit slower, scanning
|
||||
// around 730K tokens/second on the same machine, though it is currently completely unoptimised.
|
||||
// This could/should be converted to a table-based lexer.
|
||||
//
|
||||
// Lexer implementations must use Panic/Panicf to report errors.
|
||||
package lexer
|
||||
26
vendor/github.com/alecthomas/participle/lexer/errors.go
generated
vendored
Normal file
26
vendor/github.com/alecthomas/participle/lexer/errors.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package lexer
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error represents an error while parsing.
|
||||
type Error struct {
|
||||
Message string
|
||||
Pos Position
|
||||
}
|
||||
|
||||
// Errorf creats a new Error at the given position.
|
||||
func Errorf(pos Position, format string, args ...interface{}) *Error {
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
Pos: pos,
|
||||
}
|
||||
}
|
||||
|
||||
// Error complies with the error interface and reports the position of an error.
|
||||
func (e *Error) Error() string {
|
||||
filename := e.Pos.Filename
|
||||
if filename == "" {
|
||||
filename = "<source>"
|
||||
}
|
||||
return fmt.Sprintf("%s:%d:%d: %s", filename, e.Pos.Line, e.Pos.Column, e.Message)
|
||||
}
|
||||
150
vendor/github.com/alecthomas/participle/lexer/lexer.go
generated
vendored
Normal file
150
vendor/github.com/alecthomas/participle/lexer/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// EOF represents an end of file.
|
||||
EOF rune = -(iota + 1)
|
||||
)
|
||||
|
||||
// EOFToken creates a new EOF token at the given position.
|
||||
func EOFToken(pos Position) Token {
|
||||
return Token{Type: EOF, Pos: pos}
|
||||
}
|
||||
|
||||
// Definition provides the parser with metadata for a lexer.
|
||||
type Definition interface {
|
||||
// Lex an io.Reader.
|
||||
Lex(io.Reader) (Lexer, error)
|
||||
// Symbols returns a map of symbolic names to the corresponding pseudo-runes for those symbols.
|
||||
// This is the same approach as used by text/scanner. For example, "EOF" might have the rune
|
||||
// value of -1, "Ident" might be -2, and so on.
|
||||
Symbols() map[string]rune
|
||||
}
|
||||
|
||||
// A Lexer returns tokens from a source.
|
||||
type Lexer interface {
|
||||
// Next consumes and returns the next token.
|
||||
Next() (Token, error)
|
||||
}
|
||||
|
||||
// A PeekingLexer returns tokens from a source and allows peeking.
|
||||
type PeekingLexer interface {
|
||||
Lexer
|
||||
// Peek at the next token.
|
||||
Peek(n int) (Token, error)
|
||||
}
|
||||
|
||||
// SymbolsByRune returns a map of lexer symbol names keyed by rune.
|
||||
func SymbolsByRune(def Definition) map[rune]string {
|
||||
out := map[rune]string{}
|
||||
for s, r := range def.Symbols() {
|
||||
out[r] = s
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// NameOfReader attempts to retrieve the filename of a reader.
|
||||
func NameOfReader(r interface{}) string {
|
||||
if nr, ok := r.(interface{ Name() string }); ok {
|
||||
return nr.Name()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Must takes the result of a Definition constructor call and returns the definition, but panics if
|
||||
// it errors
|
||||
//
|
||||
// eg.
|
||||
//
|
||||
// lex = lexer.Must(lexer.Build(`Symbol = "symbol" .`))
|
||||
func Must(def Definition, err error) Definition {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// ConsumeAll reads all tokens from a Lexer.
|
||||
func ConsumeAll(lexer Lexer) ([]Token, error) {
|
||||
tokens := []Token{}
|
||||
for {
|
||||
token, err := lexer.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokens = append(tokens, token)
|
||||
if token.Type == EOF {
|
||||
return tokens, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Position of a token.
|
||||
type Position struct {
|
||||
Filename string
|
||||
Offset int
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
func (p Position) GoString() string {
|
||||
return fmt.Sprintf("Position{Filename: %q, Offset: %d, Line: %d, Column: %d}",
|
||||
p.Filename, p.Offset, p.Line, p.Column)
|
||||
}
|
||||
|
||||
func (p Position) String() string {
|
||||
filename := p.Filename
|
||||
if filename == "" {
|
||||
filename = "<source>"
|
||||
}
|
||||
return fmt.Sprintf("%s:%d:%d", filename, p.Line, p.Column)
|
||||
}
|
||||
|
||||
// A Token returned by a Lexer.
|
||||
type Token struct {
|
||||
// Type of token. This is the value keyed by symbol as returned by Definition.Symbols().
|
||||
Type rune
|
||||
Value string
|
||||
Pos Position
|
||||
}
|
||||
|
||||
// RuneToken represents a rune as a Token.
|
||||
func RuneToken(r rune) Token {
|
||||
return Token{Type: r, Value: string(r)}
|
||||
}
|
||||
|
||||
// EOF returns true if this Token is an EOF token.
|
||||
func (t Token) EOF() bool {
|
||||
return t.Type == EOF
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
if t.EOF() {
|
||||
return "<EOF>"
|
||||
}
|
||||
return t.Value
|
||||
}
|
||||
|
||||
func (t Token) GoString() string {
|
||||
return fmt.Sprintf("Token{%d, %q}", t.Type, t.Value)
|
||||
}
|
||||
|
||||
// MakeSymbolTable builds a lookup table for checking token ID existence.
|
||||
//
|
||||
// For each symbolic name in "types", the returned map will contain the corresponding token ID as a key.
|
||||
func MakeSymbolTable(def Definition, types ...string) (map[rune]bool, error) {
|
||||
symbols := def.Symbols()
|
||||
table := map[rune]bool{}
|
||||
for _, symbol := range types {
|
||||
rn, ok := symbols[symbol]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("lexer does not support symbol %q", symbol)
|
||||
}
|
||||
table[rn] = true
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
37
vendor/github.com/alecthomas/participle/lexer/peek.go
generated
vendored
Normal file
37
vendor/github.com/alecthomas/participle/lexer/peek.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package lexer
|
||||
|
||||
// Upgrade a Lexer to a PeekingLexer with arbitrary lookahead.
|
||||
func Upgrade(lexer Lexer) PeekingLexer {
|
||||
if peeking, ok := lexer.(PeekingLexer); ok {
|
||||
return peeking
|
||||
}
|
||||
return &lookaheadLexer{Lexer: lexer}
|
||||
}
|
||||
|
||||
type lookaheadLexer struct {
|
||||
Lexer
|
||||
peeked []Token
|
||||
}
|
||||
|
||||
func (l *lookaheadLexer) Peek(n int) (Token, error) {
|
||||
for len(l.peeked) <= n {
|
||||
t, err := l.Lexer.Next()
|
||||
if err != nil {
|
||||
return Token{}, err
|
||||
}
|
||||
if t.EOF() {
|
||||
return t, nil
|
||||
}
|
||||
l.peeked = append(l.peeked, t)
|
||||
}
|
||||
return l.peeked[n], nil
|
||||
}
|
||||
|
||||
func (l *lookaheadLexer) Next() (Token, error) {
|
||||
if len(l.peeked) > 0 {
|
||||
t := l.peeked[0]
|
||||
l.peeked = l.peeked[1:]
|
||||
return t, nil
|
||||
}
|
||||
return l.Lexer.Next()
|
||||
}
|
||||
112
vendor/github.com/alecthomas/participle/lexer/regexp.go
generated
vendored
Normal file
112
vendor/github.com/alecthomas/participle/lexer/regexp.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var eolBytes = []byte("\n")
|
||||
|
||||
type regexpDefinition struct {
|
||||
re *regexp.Regexp
|
||||
symbols map[string]rune
|
||||
}
|
||||
|
||||
// Regexp creates a lexer definition from a regular expression.
|
||||
//
|
||||
// Each named sub-expression in the regular expression matches a token. Anonymous sub-expressions
|
||||
// will be matched and discarded.
|
||||
//
|
||||
// eg.
|
||||
//
|
||||
// def, err := Regexp(`(?P<Ident>[a-z]+)|(\s+)|(?P<Number>\d+)`)
|
||||
func Regexp(pattern string) (Definition, error) {
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
symbols := map[string]rune{
|
||||
"EOF": EOF,
|
||||
}
|
||||
for i, sym := range re.SubexpNames()[1:] {
|
||||
if sym != "" {
|
||||
symbols[sym] = EOF - 1 - rune(i)
|
||||
}
|
||||
}
|
||||
return ®expDefinition{re: re, symbols: symbols}, nil
|
||||
}
|
||||
|
||||
func (d *regexpDefinition) Lex(r io.Reader) (Lexer, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ®expLexer{
|
||||
pos: Position{
|
||||
Filename: NameOfReader(r),
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
},
|
||||
b: b,
|
||||
re: d.re,
|
||||
names: d.re.SubexpNames(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *regexpDefinition) Symbols() map[string]rune {
|
||||
return d.symbols
|
||||
}
|
||||
|
||||
type regexpLexer struct {
|
||||
pos Position
|
||||
b []byte
|
||||
re *regexp.Regexp
|
||||
names []string
|
||||
}
|
||||
|
||||
func (r *regexpLexer) Next() (Token, error) {
|
||||
nextToken:
|
||||
for len(r.b) != 0 {
|
||||
matches := r.re.FindSubmatchIndex(r.b)
|
||||
if matches == nil || matches[0] != 0 {
|
||||
rn, _ := utf8.DecodeRune(r.b)
|
||||
return Token{}, Errorf(r.pos, "invalid token %q", rn)
|
||||
}
|
||||
match := r.b[:matches[1]]
|
||||
token := Token{
|
||||
Pos: r.pos,
|
||||
Value: string(match),
|
||||
}
|
||||
|
||||
// Update lexer state.
|
||||
r.pos.Offset += matches[1]
|
||||
lines := bytes.Count(match, eolBytes)
|
||||
r.pos.Line += lines
|
||||
// Update column.
|
||||
if lines == 0 {
|
||||
r.pos.Column += utf8.RuneCount(match)
|
||||
} else {
|
||||
r.pos.Column = utf8.RuneCount(match[bytes.LastIndex(match, eolBytes):])
|
||||
}
|
||||
// Move slice along.
|
||||
r.b = r.b[matches[1]:]
|
||||
|
||||
// Finally, assign token type. If it is not a named group, we continue to the next token.
|
||||
for i := 2; i < len(matches); i += 2 {
|
||||
if matches[i] != -1 {
|
||||
if r.names[i/2] == "" {
|
||||
continue nextToken
|
||||
}
|
||||
token.Type = EOF - rune(i/2)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
return EOFToken(r.pos), nil
|
||||
}
|
||||
125
vendor/github.com/alecthomas/participle/lexer/text_scanner.go
generated
vendored
Normal file
125
vendor/github.com/alecthomas/participle/lexer/text_scanner.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TextScannerLexer is a lexer that uses the text/scanner module.
|
||||
var (
|
||||
TextScannerLexer Definition = &defaultDefinition{}
|
||||
|
||||
// DefaultDefinition defines properties for the default lexer.
|
||||
DefaultDefinition = TextScannerLexer
|
||||
)
|
||||
|
||||
type defaultDefinition struct{}
|
||||
|
||||
func (d *defaultDefinition) Lex(r io.Reader) (Lexer, error) {
|
||||
return Lex(r), nil
|
||||
}
|
||||
|
||||
func (d *defaultDefinition) Symbols() map[string]rune {
|
||||
return map[string]rune{
|
||||
"EOF": scanner.EOF,
|
||||
"Char": scanner.Char,
|
||||
"Ident": scanner.Ident,
|
||||
"Int": scanner.Int,
|
||||
"Float": scanner.Float,
|
||||
"String": scanner.String,
|
||||
"RawString": scanner.RawString,
|
||||
"Comment": scanner.Comment,
|
||||
}
|
||||
}
|
||||
|
||||
// textScannerLexer is a Lexer based on text/scanner.Scanner
|
||||
type textScannerLexer struct {
|
||||
scanner *scanner.Scanner
|
||||
filename string
|
||||
err error
|
||||
}
|
||||
|
||||
// Lex an io.Reader with text/scanner.Scanner.
|
||||
//
|
||||
// This provides very fast lexing of source code compatible with Go tokens.
|
||||
//
|
||||
// Note that this differs from text/scanner.Scanner in that string tokens will be unquoted.
|
||||
func Lex(r io.Reader) Lexer {
|
||||
lexer := lexWithScanner(r, &scanner.Scanner{})
|
||||
lexer.scanner.Error = func(s *scanner.Scanner, msg string) {
|
||||
// This is to support single quoted strings. Hacky.
|
||||
if msg != "illegal char literal" {
|
||||
lexer.err = Errorf(Position(lexer.scanner.Pos()), msg)
|
||||
}
|
||||
}
|
||||
return lexer
|
||||
}
|
||||
|
||||
// LexWithScanner creates a Lexer from a user-provided scanner.Scanner.
|
||||
//
|
||||
// Useful if you need to customise the Scanner.
|
||||
func LexWithScanner(r io.Reader, scan *scanner.Scanner) Lexer {
|
||||
return lexWithScanner(r, scan)
|
||||
}
|
||||
|
||||
func lexWithScanner(r io.Reader, scan *scanner.Scanner) *textScannerLexer {
|
||||
lexer := &textScannerLexer{
|
||||
filename: NameOfReader(r),
|
||||
scanner: scan,
|
||||
}
|
||||
lexer.scanner.Init(r)
|
||||
return lexer
|
||||
}
|
||||
|
||||
// LexBytes returns a new default lexer over bytes.
|
||||
func LexBytes(b []byte) Lexer {
|
||||
return Lex(bytes.NewReader(b))
|
||||
}
|
||||
|
||||
// LexString returns a new default lexer over a string.
|
||||
func LexString(s string) Lexer {
|
||||
return Lex(strings.NewReader(s))
|
||||
}
|
||||
|
||||
func (t *textScannerLexer) Next() (Token, error) {
|
||||
typ := t.scanner.Scan()
|
||||
text := t.scanner.TokenText()
|
||||
pos := Position(t.scanner.Position)
|
||||
pos.Filename = t.filename
|
||||
if t.err != nil {
|
||||
return Token{}, t.err
|
||||
}
|
||||
return textScannerTransform(Token{
|
||||
Type: typ,
|
||||
Value: text,
|
||||
Pos: pos,
|
||||
})
|
||||
}
|
||||
|
||||
func textScannerTransform(token Token) (Token, error) {
|
||||
// Unquote strings.
|
||||
switch token.Type {
|
||||
case scanner.Char:
|
||||
// FIXME(alec): This is pretty hacky...we convert a single quoted char into a double
|
||||
// quoted string in order to support single quoted strings.
|
||||
token.Value = fmt.Sprintf("\"%s\"", token.Value[1:len(token.Value)-1])
|
||||
fallthrough
|
||||
case scanner.String:
|
||||
s, err := strconv.Unquote(token.Value)
|
||||
if err != nil {
|
||||
return Token{}, Errorf(token.Pos, "%s: %q", err.Error(), token.Value)
|
||||
}
|
||||
token.Value = s
|
||||
if token.Type == scanner.Char && utf8.RuneCountInString(s) > 1 {
|
||||
token.Type = scanner.String
|
||||
}
|
||||
case scanner.RawString:
|
||||
token.Value = token.Value[1 : len(token.Value)-1]
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
118
vendor/github.com/alecthomas/participle/map.go
generated
vendored
Normal file
118
vendor/github.com/alecthomas/participle/map.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
type mapperByToken struct {
|
||||
symbols []string
|
||||
mapper Mapper
|
||||
}
|
||||
|
||||
// DropToken can be returned by a Mapper to remove a token from the stream.
|
||||
var DropToken = errors.New("drop token") // nolint: golint
|
||||
|
||||
// Mapper function for mutating tokens before being applied to the AST.
|
||||
//
|
||||
// If the Mapper func returns an error of DropToken, the token will be removed from the stream.
|
||||
type Mapper func(token lexer.Token) (lexer.Token, error)
|
||||
|
||||
// Map is an Option that configures the Parser to apply a mapping function to each Token from the lexer.
|
||||
//
|
||||
// This can be useful to eg. upper-case all tokens of a certain type, or dequote strings.
|
||||
//
|
||||
// "symbols" specifies the token symbols that the Mapper will be applied to. If empty, all tokens will be mapped.
|
||||
func Map(mapper Mapper, symbols ...string) Option {
|
||||
return func(p *Parser) error {
|
||||
p.mappers = append(p.mappers, mapperByToken{
|
||||
mapper: mapper,
|
||||
symbols: symbols,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Unquote applies strconv.Unquote() to tokens of the given types.
|
||||
//
|
||||
// Tokens of type "String" will be unquoted if no other types are provided.
|
||||
func Unquote(types ...string) Option {
|
||||
if len(types) == 0 {
|
||||
types = []string{"String"}
|
||||
}
|
||||
return Map(func(t lexer.Token) (lexer.Token, error) {
|
||||
value, err := unquote(t.Value)
|
||||
if err != nil {
|
||||
return t, lexer.Errorf(t.Pos, "invalid quoted string %q: %s", t.Value, err.Error())
|
||||
}
|
||||
t.Value = value
|
||||
return t, nil
|
||||
}, types...)
|
||||
}
|
||||
|
||||
func unquote(s string) (string, error) {
|
||||
quote := s[0]
|
||||
s = s[1 : len(s)-1]
|
||||
out := ""
|
||||
for s != "" {
|
||||
value, _, tail, err := strconv.UnquoteChar(s, quote)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s = tail
|
||||
out += string(value)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Upper is an Option that upper-cases all tokens of the given type. Useful for case normalisation.
|
||||
func Upper(types ...string) Option {
|
||||
return Map(func(token lexer.Token) (lexer.Token, error) {
|
||||
token.Value = strings.ToUpper(token.Value)
|
||||
return token, nil
|
||||
}, types...)
|
||||
}
|
||||
|
||||
// Elide drops tokens of the specified types.
|
||||
func Elide(types ...string) Option {
|
||||
return Map(func(token lexer.Token) (lexer.Token, error) {
|
||||
return lexer.Token{}, DropToken
|
||||
}, types...)
|
||||
}
|
||||
|
||||
// Apply a Mapping to all tokens coming out of a Lexer.
|
||||
type mappingLexerDef struct {
|
||||
lexer.Definition
|
||||
mapper Mapper
|
||||
}
|
||||
|
||||
func (m *mappingLexerDef) Lex(r io.Reader) (lexer.Lexer, error) {
|
||||
lexer, err := m.Definition.Lex(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mappingLexer{lexer, m.mapper}, nil
|
||||
}
|
||||
|
||||
type mappingLexer struct {
|
||||
lexer.Lexer
|
||||
mapper Mapper
|
||||
}
|
||||
|
||||
func (m *mappingLexer) Next() (lexer.Token, error) {
|
||||
for {
|
||||
t, err := m.Lexer.Next()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
t, err = m.mapper(t)
|
||||
if err == DropToken {
|
||||
continue
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
}
|
||||
575
vendor/github.com/alecthomas/participle/nodes.go
generated
vendored
Normal file
575
vendor/github.com/alecthomas/participle/nodes.go
generated
vendored
Normal file
@@ -0,0 +1,575 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxIterations limits the number of elements capturable by {}.
|
||||
MaxIterations = 1000000
|
||||
|
||||
positionType = reflect.TypeOf(lexer.Position{})
|
||||
captureType = reflect.TypeOf((*Capture)(nil)).Elem()
|
||||
parseableType = reflect.TypeOf((*Parseable)(nil)).Elem()
|
||||
|
||||
// NextMatch should be returned by Parseable.Parse() method implementations to indicate
|
||||
// that the node did not match and that other matches should be attempted, if appropriate.
|
||||
NextMatch = errors.New("no match") // nolint: golint
|
||||
)
|
||||
|
||||
// A node in the grammar.
|
||||
type node interface {
|
||||
// Parse from scanner into value.
|
||||
//
|
||||
// Returned slice will be nil if the node does not match.
|
||||
Parse(ctx *parseContext, parent reflect.Value) ([]reflect.Value, error)
|
||||
|
||||
// Return a decent string representation of the Node.
|
||||
String() string
|
||||
}
|
||||
|
||||
func decorate(err *error, name func() string) {
|
||||
if *err == nil {
|
||||
return
|
||||
}
|
||||
switch realError := (*err).(type) {
|
||||
case *lexer.Error:
|
||||
*err = &lexer.Error{Message: name() + ": " + realError.Message, Pos: realError.Pos}
|
||||
default:
|
||||
*err = fmt.Errorf("%s: %s", name(), realError)
|
||||
}
|
||||
}
|
||||
|
||||
// A node that proxies to an implementation that implements the Parseable interface.
|
||||
type parseable struct {
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
func (p *parseable) String() string { return stringer(p) }
|
||||
|
||||
func (p *parseable) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
rv := reflect.New(p.t)
|
||||
v := rv.Interface().(Parseable)
|
||||
err = v.Parse(ctx)
|
||||
if err != nil {
|
||||
if err == NextMatch {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return []reflect.Value{rv.Elem()}, nil
|
||||
}
|
||||
|
||||
type strct struct {
|
||||
typ reflect.Type
|
||||
expr node
|
||||
}
|
||||
|
||||
func (s *strct) String() string { return stringer(s) }
|
||||
|
||||
func (s *strct) maybeInjectPos(pos lexer.Position, v reflect.Value) {
|
||||
if f := v.FieldByName("Pos"); f.IsValid() && f.Type() == positionType {
|
||||
f.Set(reflect.ValueOf(pos))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *strct) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
sv := reflect.New(s.typ).Elem()
|
||||
t, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.maybeInjectPos(t.Pos, sv)
|
||||
if out, err = s.expr.Parse(ctx, sv); err != nil {
|
||||
_ = ctx.Apply()
|
||||
return []reflect.Value{sv}, err
|
||||
} else if out == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return []reflect.Value{sv}, ctx.Apply()
|
||||
}
|
||||
|
||||
type groupMatchMode int
|
||||
|
||||
const (
|
||||
groupMatchOnce groupMatchMode = iota
|
||||
groupMatchZeroOrOne = iota
|
||||
groupMatchZeroOrMore = iota
|
||||
groupMatchOneOrMore = iota
|
||||
groupMatchNonEmpty = iota
|
||||
)
|
||||
|
||||
// ( <expr> ) - match once
|
||||
// ( <expr> )* - match zero or more times
|
||||
// ( <expr> )+ - match one or more times
|
||||
// ( <expr> )? - match zero or once
|
||||
// ( <expr> )! - must be a non-empty match
|
||||
//
|
||||
// The additional modifier "!" forces the content of the group to be non-empty if it does match.
|
||||
type group struct {
|
||||
expr node
|
||||
mode groupMatchMode
|
||||
}
|
||||
|
||||
func (g *group) String() string { return stringer(g) }
|
||||
func (g *group) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
// Configure min/max matches.
|
||||
min := 1
|
||||
max := 1
|
||||
switch g.mode {
|
||||
case groupMatchNonEmpty:
|
||||
out, err = g.expr.Parse(ctx, parent)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if len(out) == 0 {
|
||||
t, _ := ctx.Peek(0)
|
||||
return out, lexer.Errorf(t.Pos, "sub-expression %s cannot be empty", g)
|
||||
}
|
||||
return out, nil
|
||||
case groupMatchOnce:
|
||||
return g.expr.Parse(ctx, parent)
|
||||
case groupMatchZeroOrOne:
|
||||
min = 0
|
||||
case groupMatchZeroOrMore:
|
||||
min = 0
|
||||
max = MaxIterations
|
||||
case groupMatchOneOrMore:
|
||||
min = 1
|
||||
max = MaxIterations
|
||||
}
|
||||
matches := 0
|
||||
for ; matches < max; matches++ {
|
||||
branch := ctx.Branch()
|
||||
v, err := g.expr.Parse(branch, parent)
|
||||
out = append(out, v...)
|
||||
if err != nil {
|
||||
// Optional part failed to match.
|
||||
if ctx.Stop(branch) {
|
||||
return out, err
|
||||
}
|
||||
break
|
||||
} else {
|
||||
ctx.Accept(branch)
|
||||
}
|
||||
if v == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
// fmt.Printf("%d < %d < %d: out == nil? %v\n", min, matches, max, out == nil)
|
||||
t, _ := ctx.Peek(0)
|
||||
if matches >= MaxIterations {
|
||||
panic(lexer.Errorf(t.Pos, "too many iterations of %s (> %d)", g, MaxIterations))
|
||||
}
|
||||
if matches < min {
|
||||
return out, lexer.Errorf(t.Pos, "sub-expression %s must match at least once", g)
|
||||
}
|
||||
// The idea here is that something like "a"? is a successful match and that parsing should proceed.
|
||||
if min == 0 && out == nil {
|
||||
out = []reflect.Value{}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// <expr> {"|" <expr>}
|
||||
type disjunction struct {
|
||||
nodes []node
|
||||
}
|
||||
|
||||
func (d *disjunction) String() string { return stringer(d) }
|
||||
|
||||
func (d *disjunction) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
var (
|
||||
deepestError = 0
|
||||
firstError error
|
||||
firstValues []reflect.Value
|
||||
)
|
||||
for _, a := range d.nodes {
|
||||
branch := ctx.Branch()
|
||||
if value, err := a.Parse(branch, parent); err != nil {
|
||||
// If this branch progressed too far and still didn't match, error out.
|
||||
if ctx.Stop(branch) {
|
||||
return value, err
|
||||
}
|
||||
// Show the closest error returned. The idea here is that the further the parser progresses
|
||||
// without error, the more difficult it is to trace the error back to its root.
|
||||
if err != nil && branch.cursor >= deepestError {
|
||||
firstError = err
|
||||
firstValues = value
|
||||
deepestError = branch.cursor
|
||||
}
|
||||
} else if value != nil {
|
||||
ctx.Accept(branch)
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
if firstError != nil {
|
||||
return firstValues, firstError
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// <node> ...
|
||||
type sequence struct {
|
||||
head bool
|
||||
node node
|
||||
next *sequence
|
||||
}
|
||||
|
||||
func (s *sequence) String() string { return stringer(s) }
|
||||
|
||||
func (s *sequence) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
for n := s; n != nil; n = n.next {
|
||||
child, err := n.node.Parse(ctx, parent)
|
||||
out = append(out, child...)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if child == nil {
|
||||
// Early exit if first value doesn't match, otherwise all values must match.
|
||||
if n == s {
|
||||
return nil, nil
|
||||
}
|
||||
token, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, lexer.Errorf(token.Pos, "unexpected %q (expected %s)", token, n)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// @<expr>
|
||||
type capture struct {
|
||||
field structLexerField
|
||||
node node
|
||||
}
|
||||
|
||||
func (c *capture) String() string { return stringer(c) }
|
||||
|
||||
func (c *capture) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
token, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pos := token.Pos
|
||||
v, err := c.node.Parse(ctx, parent)
|
||||
if err != nil {
|
||||
if v != nil {
|
||||
ctx.Defer(pos, parent, c.field, v)
|
||||
}
|
||||
return []reflect.Value{parent}, err
|
||||
}
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ctx.Defer(pos, parent, c.field, v)
|
||||
return []reflect.Value{parent}, nil
|
||||
}
|
||||
|
||||
// <identifier> - named lexer token reference
|
||||
type reference struct {
|
||||
typ rune
|
||||
identifier string // Used for informational purposes.
|
||||
}
|
||||
|
||||
func (r *reference) String() string { return stringer(r) }
|
||||
|
||||
func (r *reference) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
token, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.Type != r.typ {
|
||||
return nil, nil
|
||||
}
|
||||
_, _ = ctx.Next()
|
||||
return []reflect.Value{reflect.ValueOf(token.Value)}, nil
|
||||
}
|
||||
|
||||
// [ <expr> ] <sequence>
|
||||
type optional struct {
|
||||
node node
|
||||
}
|
||||
|
||||
func (o *optional) String() string { return stringer(o) }
|
||||
|
||||
func (o *optional) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
branch := ctx.Branch()
|
||||
out, err = o.node.Parse(branch, parent)
|
||||
if err != nil {
|
||||
// Optional part failed to match.
|
||||
if ctx.Stop(branch) {
|
||||
return out, err
|
||||
}
|
||||
} else {
|
||||
ctx.Accept(branch)
|
||||
}
|
||||
if out == nil {
|
||||
out = []reflect.Value{}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// { <expr> } <sequence>
|
||||
type repetition struct {
|
||||
node node
|
||||
}
|
||||
|
||||
func (r *repetition) String() string { return stringer(r) }
|
||||
|
||||
// Parse a repetition. Once a repetition is encountered it will always match, so grammars
|
||||
// should ensure that branches are differentiated prior to the repetition.
|
||||
func (r *repetition) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
i := 0
|
||||
for ; i < MaxIterations; i++ {
|
||||
branch := ctx.Branch()
|
||||
v, err := r.node.Parse(branch, parent)
|
||||
out = append(out, v...)
|
||||
if err != nil {
|
||||
// Optional part failed to match.
|
||||
if ctx.Stop(branch) {
|
||||
return out, err
|
||||
}
|
||||
break
|
||||
} else {
|
||||
ctx.Accept(branch)
|
||||
}
|
||||
if v == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i >= MaxIterations {
|
||||
t, _ := ctx.Peek(0)
|
||||
panic(lexer.Errorf(t.Pos, "too many iterations of %s (> %d)", r, MaxIterations))
|
||||
}
|
||||
if out == nil {
|
||||
out = []reflect.Value{}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Match a token literal exactly "..."[:<type>].
|
||||
type literal struct {
|
||||
s string
|
||||
t rune
|
||||
tt string // Used for display purposes - symbolic name of t.
|
||||
}
|
||||
|
||||
func (l *literal) String() string { return stringer(l) }
|
||||
|
||||
func (l *literal) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
|
||||
token, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
equal := false // nolint: ineffassign
|
||||
if ctx.caseInsensitive[token.Type] {
|
||||
equal = strings.EqualFold(token.Value, l.s)
|
||||
} else {
|
||||
equal = token.Value == l.s
|
||||
}
|
||||
if equal && (l.t == -1 || l.t == token.Type) {
|
||||
next, err := ctx.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []reflect.Value{reflect.ValueOf(next.Value)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Attempt to transform values to given type.
|
||||
//
|
||||
// This will dereference pointers, and attempt to parse strings into integer values, floats, etc.
|
||||
func conform(t reflect.Type, values []reflect.Value) (out []reflect.Value, err error) {
|
||||
for _, v := range values {
|
||||
for t != v.Type() && t.Kind() == reflect.Ptr && v.Kind() != reflect.Ptr {
|
||||
// This can occur during partial failure.
|
||||
if !v.CanAddr() {
|
||||
return
|
||||
}
|
||||
v = v.Addr()
|
||||
}
|
||||
|
||||
// Already of the right kind, don't bother converting.
|
||||
if v.Kind() == t.Kind() {
|
||||
out = append(out, v)
|
||||
continue
|
||||
}
|
||||
|
||||
kind := t.Kind()
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, err := strconv.ParseInt(v.String(), 0, sizeOfKind(kind))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid integer %q: %s", v.String(), err)
|
||||
}
|
||||
v = reflect.New(t).Elem()
|
||||
v.SetInt(n)
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
n, err := strconv.ParseUint(v.String(), 0, sizeOfKind(kind))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid integer %q: %s", v.String(), err)
|
||||
}
|
||||
v = reflect.New(t).Elem()
|
||||
v.SetUint(n)
|
||||
|
||||
case reflect.Bool:
|
||||
v = reflect.ValueOf(true)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
n, err := strconv.ParseFloat(v.String(), sizeOfKind(kind))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid integer %q: %s", v.String(), err)
|
||||
}
|
||||
v = reflect.New(t).Elem()
|
||||
v.SetFloat(n)
|
||||
}
|
||||
|
||||
out = append(out, v)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func sizeOfKind(kind reflect.Kind) int {
|
||||
switch kind {
|
||||
case reflect.Int8, reflect.Uint8:
|
||||
return 8
|
||||
case reflect.Int16, reflect.Uint16:
|
||||
return 16
|
||||
case reflect.Int32, reflect.Uint32, reflect.Float32:
|
||||
return 32
|
||||
case reflect.Int64, reflect.Uint64, reflect.Float64:
|
||||
return 64
|
||||
case reflect.Int, reflect.Uint:
|
||||
return strconv.IntSize
|
||||
}
|
||||
panic("unsupported kind " + kind.String())
|
||||
}
|
||||
|
||||
// Set field.
|
||||
//
|
||||
// If field is a pointer the pointer will be set to the value. If field is a string, value will be
|
||||
// appended. If field is a slice, value will be appended to slice.
|
||||
//
|
||||
// For all other types, an attempt will be made to convert the string to the corresponding
|
||||
// type (int, float32, etc.).
|
||||
func setField(pos lexer.Position, strct reflect.Value, field structLexerField, fieldValue []reflect.Value) (err error) { // nolint: gocyclo
|
||||
defer decorate(&err, func() string { return pos.String() + ": " + strct.Type().String() + "." + field.Name })
|
||||
|
||||
f := strct.FieldByIndex(field.Index)
|
||||
switch f.Kind() {
|
||||
case reflect.Slice:
|
||||
fieldValue, err = conform(f.Type().Elem(), fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Set(reflect.Append(f, fieldValue...))
|
||||
return nil
|
||||
|
||||
case reflect.Ptr:
|
||||
if f.IsNil() {
|
||||
fv := reflect.New(f.Type().Elem()).Elem()
|
||||
f.Set(fv.Addr())
|
||||
f = fv
|
||||
} else {
|
||||
f = f.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.Struct {
|
||||
if pf := f.FieldByName("Pos"); pf.IsValid() && pf.Type() == positionType {
|
||||
pf.Set(reflect.ValueOf(pos))
|
||||
}
|
||||
}
|
||||
|
||||
if f.CanAddr() {
|
||||
if d, ok := f.Addr().Interface().(Capture); ok {
|
||||
ifv := []string{}
|
||||
for _, v := range fieldValue {
|
||||
ifv = append(ifv, v.Interface().(string))
|
||||
}
|
||||
err := d.Capture(ifv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Strings concatenate all captured tokens.
|
||||
if f.Kind() == reflect.String {
|
||||
fieldValue, err = conform(f.Type(), fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range fieldValue {
|
||||
f.Set(reflect.ValueOf(f.String() + v.String()).Convert(f.Type()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Coalesce multiple tokens into one. This allows eg. ["-", "10"] to be captured as separate tokens but
|
||||
// parsed as a single string "-10".
|
||||
if len(fieldValue) > 1 {
|
||||
out := []string{}
|
||||
for _, v := range fieldValue {
|
||||
out = append(out, v.String())
|
||||
}
|
||||
fieldValue = []reflect.Value{reflect.ValueOf(strings.Join(out, ""))}
|
||||
}
|
||||
|
||||
fieldValue, err = conform(f.Type(), fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fv := fieldValue[0]
|
||||
|
||||
switch f.Kind() {
|
||||
// Numeric types will increment if the token can not be coerced.
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if fv.Type() != f.Type() {
|
||||
f.SetInt(f.Int() + 1)
|
||||
} else {
|
||||
f.Set(fv)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if fv.Type() != f.Type() {
|
||||
f.SetUint(f.Uint() + 1)
|
||||
} else {
|
||||
f.Set(fv)
|
||||
}
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if fv.Type() != f.Type() {
|
||||
f.SetFloat(f.Float() + 1)
|
||||
} else {
|
||||
f.Set(fv)
|
||||
}
|
||||
|
||||
case reflect.Bool, reflect.Struct:
|
||||
if fv.Type() != f.Type() {
|
||||
return fmt.Errorf("value %q is not correct type %s", fv, f.Type())
|
||||
}
|
||||
f.Set(fv)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported field type %s for field %s", f.Type(), field.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error is an error returned by the parser internally to differentiate from non-Participle errors.
|
||||
type Error string
|
||||
|
||||
func (e Error) Error() string { return string(e) }
|
||||
39
vendor/github.com/alecthomas/participle/options.go
generated
vendored
Normal file
39
vendor/github.com/alecthomas/participle/options.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
// An Option to modify the behaviour of the Parser.
|
||||
type Option func(p *Parser) error
|
||||
|
||||
// Lexer is an Option that sets the lexer to use with the given grammar.
|
||||
func Lexer(def lexer.Definition) Option {
|
||||
return func(p *Parser) error {
|
||||
p.lex = def
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UseLookahead allows branch lookahead up to "n" tokens.
|
||||
//
|
||||
// If parsing cannot be disambiguated before "n" tokens of lookahead, parsing will fail.
|
||||
//
|
||||
// Note that increasing lookahead has a minor performance impact, but also
|
||||
// reduces the accuracy of error reporting.
|
||||
func UseLookahead(n int) Option {
|
||||
return func(p *Parser) error {
|
||||
p.useLookahead = n
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CaseInsensitive allows the specified token types to be matched case-insensitively.
|
||||
func CaseInsensitive(tokens ...string) Option {
|
||||
return func(p *Parser) error {
|
||||
for _, token := range tokens {
|
||||
p.caseInsensitive[token] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
229
vendor/github.com/alecthomas/participle/parser.go
generated
vendored
Normal file
229
vendor/github.com/alecthomas/participle/parser.go
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
// A Parser for a particular grammar and lexer.
|
||||
type Parser struct {
|
||||
root node
|
||||
lex lexer.Definition
|
||||
typ reflect.Type
|
||||
useLookahead int
|
||||
caseInsensitive map[string]bool
|
||||
mappers []mapperByToken
|
||||
}
|
||||
|
||||
// MustBuild calls Build(grammar, options...) and panics if an error occurs.
|
||||
func MustBuild(grammar interface{}, options ...Option) *Parser {
|
||||
parser, err := Build(grammar, options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parser
|
||||
}
|
||||
|
||||
// Build constructs a parser for the given grammar.
|
||||
//
|
||||
// If "Lexer()" is not provided as an option, a default lexer based on text/scanner will be used. This scans typical Go-
|
||||
// like tokens.
|
||||
//
|
||||
// See documentation for details
|
||||
func Build(grammar interface{}, options ...Option) (parser *Parser, err error) {
|
||||
// Configure Parser struct with defaults + options.
|
||||
p := &Parser{
|
||||
lex: lexer.TextScannerLexer,
|
||||
caseInsensitive: map[string]bool{},
|
||||
useLookahead: 1,
|
||||
}
|
||||
for _, option := range options {
|
||||
if option == nil {
|
||||
return nil, fmt.Errorf("nil Option passed, signature has changed; " +
|
||||
"if you intended to provide a custom Lexer, try participle.Build(grammar, participle.Lexer(lexer))")
|
||||
}
|
||||
if err = option(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.mappers) > 0 {
|
||||
mappers := map[rune][]Mapper{}
|
||||
symbols := p.lex.Symbols()
|
||||
for _, mapper := range p.mappers {
|
||||
if len(mapper.symbols) == 0 {
|
||||
mappers[lexer.EOF] = append(mappers[lexer.EOF], mapper.mapper)
|
||||
} else {
|
||||
for _, symbol := range mapper.symbols {
|
||||
if rn, ok := symbols[symbol]; !ok {
|
||||
return nil, fmt.Errorf("mapper %#v uses unknown token %q", mapper, symbol)
|
||||
} else { // nolint: golint
|
||||
mappers[rn] = append(mappers[rn], mapper.mapper)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.lex = &mappingLexerDef{p.lex, func(t lexer.Token) (lexer.Token, error) {
|
||||
combined := make([]Mapper, 0, len(mappers[t.Type])+len(mappers[lexer.EOF]))
|
||||
combined = append(combined, mappers[lexer.EOF]...)
|
||||
combined = append(combined, mappers[t.Type]...)
|
||||
|
||||
var err error
|
||||
for _, m := range combined {
|
||||
t, err = m(t)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}}
|
||||
}
|
||||
|
||||
context := newGeneratorContext(p.lex)
|
||||
v := reflect.ValueOf(grammar)
|
||||
if v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
p.typ = v.Type()
|
||||
p.root, err = context.parseType(p.typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Lex uses the parser's lexer to tokenise input.
|
||||
func (p *Parser) Lex(r io.Reader) ([]lexer.Token, error) {
|
||||
lex, err := p.lex.Lex(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lexer.ConsumeAll(lex)
|
||||
}
|
||||
|
||||
// Parse from r into grammar v which must be of the same type as the grammar passed to
|
||||
// participle.Build().
|
||||
func (p *Parser) Parse(r io.Reader, v interface{}) (err error) {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() == reflect.Interface {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
var stream reflect.Value
|
||||
if rv.Kind() == reflect.Chan {
|
||||
stream = rv
|
||||
rt := rv.Type().Elem()
|
||||
rv = reflect.New(rt).Elem()
|
||||
}
|
||||
rt := rv.Type()
|
||||
if rt != p.typ {
|
||||
return fmt.Errorf("must parse into value of type %s not %T", p.typ, v)
|
||||
}
|
||||
baseLexer, err := p.lex.Lex(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lex := lexer.Upgrade(baseLexer)
|
||||
caseInsensitive := map[rune]bool{}
|
||||
for sym, rn := range p.lex.Symbols() {
|
||||
if p.caseInsensitive[sym] {
|
||||
caseInsensitive[rn] = true
|
||||
}
|
||||
}
|
||||
ctx, err := newParseContext(lex, p.useLookahead, caseInsensitive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the grammar implements Parseable, use it.
|
||||
if parseable, ok := v.(Parseable); ok {
|
||||
return p.rootParseable(ctx, parseable)
|
||||
}
|
||||
if rt.Kind() != reflect.Ptr || rt.Elem().Kind() != reflect.Struct {
|
||||
return fmt.Errorf("target must be a pointer to a struct, not %s", rt)
|
||||
}
|
||||
if stream.IsValid() {
|
||||
return p.parseStreaming(ctx, stream)
|
||||
}
|
||||
return p.parseOne(ctx, rv)
|
||||
}
|
||||
|
||||
func (p *Parser) parseStreaming(ctx *parseContext, rv reflect.Value) error {
|
||||
t := rv.Type().Elem().Elem()
|
||||
for {
|
||||
if token, _ := ctx.Peek(0); token.EOF() {
|
||||
rv.Close()
|
||||
return nil
|
||||
}
|
||||
v := reflect.New(t)
|
||||
if err := p.parseInto(ctx, v); err != nil {
|
||||
return err
|
||||
}
|
||||
rv.Send(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseOne(ctx *parseContext, rv reflect.Value) error {
|
||||
err := p.parseInto(ctx, rv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token, err := ctx.Peek(0)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !token.EOF() {
|
||||
return lexer.Errorf(token.Pos, "unexpected trailing token %q", token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseInto(ctx *parseContext, rv reflect.Value) error {
|
||||
if rv.IsNil() {
|
||||
return fmt.Errorf("target must be a non-nil pointer to a struct, but is a nil %s", rv.Type())
|
||||
}
|
||||
pv, err := p.root.Parse(ctx, rv.Elem())
|
||||
if len(pv) > 0 && pv[0].Type() == rv.Elem().Type() {
|
||||
rv.Elem().Set(reflect.Indirect(pv[0]))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pv == nil {
|
||||
token, _ := ctx.Peek(0)
|
||||
return lexer.Errorf(token.Pos, "invalid syntax")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) rootParseable(lex lexer.PeekingLexer, parseable Parseable) error {
|
||||
peek, err := lex.Peek(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = parseable.Parse(lex)
|
||||
if err == NextMatch {
|
||||
return lexer.Errorf(peek.Pos, "invalid syntax")
|
||||
}
|
||||
if err == nil && !peek.EOF() {
|
||||
return lexer.Errorf(peek.Pos, "unexpected token %q", peek)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseString is a convenience around Parse().
|
||||
func (p *Parser) ParseString(s string, v interface{}) error {
|
||||
return p.Parse(strings.NewReader(s), v)
|
||||
}
|
||||
|
||||
// ParseBytes is a convenience around Parse().
|
||||
func (p *Parser) ParseBytes(b []byte, v interface{}) error {
|
||||
return p.Parse(bytes.NewReader(b), v)
|
||||
}
|
||||
|
||||
// String representation of the grammar.
|
||||
func (p *Parser) String() string {
|
||||
return stringern(p.root, 128)
|
||||
}
|
||||
118
vendor/github.com/alecthomas/participle/stringer.go
generated
vendored
Normal file
118
vendor/github.com/alecthomas/participle/stringer.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
type stringerVisitor struct {
|
||||
bytes.Buffer
|
||||
seen map[node]bool
|
||||
}
|
||||
|
||||
func stringern(n node, depth int) string {
|
||||
v := &stringerVisitor{seen: map[node]bool{}}
|
||||
v.visit(n, depth, false)
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func stringer(n node) string {
|
||||
return stringern(n, 1)
|
||||
}
|
||||
|
||||
func (s *stringerVisitor) visit(n node, depth int, disjunctions bool) {
|
||||
if s.seen[n] || depth <= 0 {
|
||||
fmt.Fprintf(s, "...")
|
||||
return
|
||||
}
|
||||
s.seen[n] = true
|
||||
|
||||
switch n := n.(type) {
|
||||
case *disjunction:
|
||||
for i, c := range n.nodes {
|
||||
if i > 0 {
|
||||
fmt.Fprint(s, " | ")
|
||||
}
|
||||
s.visit(c, depth, disjunctions || len(n.nodes) > 1)
|
||||
}
|
||||
|
||||
case *strct:
|
||||
s.visit(n.expr, depth, disjunctions)
|
||||
|
||||
case *sequence:
|
||||
c := n
|
||||
for i := 0; c != nil && depth-i > 0; c, i = c.next, i+1 {
|
||||
if c != n {
|
||||
fmt.Fprint(s, " ")
|
||||
}
|
||||
s.visit(c.node, depth-i, disjunctions)
|
||||
}
|
||||
if c != nil {
|
||||
fmt.Fprint(s, " ...")
|
||||
}
|
||||
|
||||
case *parseable:
|
||||
fmt.Fprintf(s, "<%s>", strings.ToLower(n.t.Name()))
|
||||
|
||||
case *capture:
|
||||
if _, ok := n.node.(*parseable); ok {
|
||||
fmt.Fprintf(s, "<%s>", strings.ToLower(n.field.Name))
|
||||
} else {
|
||||
if n.node == nil {
|
||||
fmt.Fprintf(s, "<%s>", strings.ToLower(n.field.Name))
|
||||
} else {
|
||||
s.visit(n.node, depth, disjunctions)
|
||||
}
|
||||
}
|
||||
|
||||
case *reference:
|
||||
fmt.Fprintf(s, "<%s>", strings.ToLower(n.identifier))
|
||||
|
||||
case *optional:
|
||||
fmt.Fprint(s, "[ ")
|
||||
s.visit(n.node, depth, disjunctions)
|
||||
fmt.Fprint(s, " ]")
|
||||
|
||||
case *repetition:
|
||||
fmt.Fprint(s, "{ ")
|
||||
s.visit(n.node, depth, disjunctions)
|
||||
fmt.Fprint(s, " }")
|
||||
|
||||
case *literal:
|
||||
fmt.Fprintf(s, "%q", n.s)
|
||||
if n.t != lexer.EOF && n.s == "" {
|
||||
fmt.Fprintf(s, ":%s", n.tt)
|
||||
}
|
||||
|
||||
case *group:
|
||||
fmt.Fprint(s, "(")
|
||||
if child, ok := n.expr.(*group); ok && child.mode == groupMatchOnce {
|
||||
s.visit(child.expr, depth, disjunctions)
|
||||
} else if child, ok := n.expr.(*capture); ok {
|
||||
if grandchild, ok := child.node.(*group); ok && grandchild.mode == groupMatchOnce {
|
||||
s.visit(grandchild.expr, depth, disjunctions)
|
||||
} else {
|
||||
s.visit(n.expr, depth, disjunctions)
|
||||
}
|
||||
} else {
|
||||
s.visit(n.expr, depth, disjunctions)
|
||||
}
|
||||
fmt.Fprint(s, ")")
|
||||
switch n.mode {
|
||||
case groupMatchNonEmpty:
|
||||
fmt.Fprintf(s, "!")
|
||||
case groupMatchZeroOrOne:
|
||||
fmt.Fprintf(s, "?")
|
||||
case groupMatchZeroOrMore:
|
||||
fmt.Fprintf(s, "*")
|
||||
case groupMatchOneOrMore:
|
||||
fmt.Fprintf(s, "+")
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unsupported")
|
||||
}
|
||||
}
|
||||
126
vendor/github.com/alecthomas/participle/struct.go
generated
vendored
Normal file
126
vendor/github.com/alecthomas/participle/struct.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package participle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/alecthomas/participle/lexer"
|
||||
)
|
||||
|
||||
// A structLexer lexes over the tags of struct fields while tracking the current field.
|
||||
type structLexer struct {
|
||||
s reflect.Type
|
||||
field int
|
||||
indexes [][]int
|
||||
lexer lexer.PeekingLexer
|
||||
}
|
||||
|
||||
func lexStruct(s reflect.Type) (*structLexer, error) {
|
||||
indexes, err := collectFieldIndexes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
slex := &structLexer{
|
||||
s: s,
|
||||
indexes: indexes,
|
||||
}
|
||||
if len(slex.indexes) > 0 {
|
||||
tag := fieldLexerTag(slex.Field().StructField)
|
||||
slex.lexer = lexer.Upgrade(lexer.LexString(tag))
|
||||
}
|
||||
return slex, nil
|
||||
}
|
||||
|
||||
// NumField returns the number of fields in the struct associated with this structLexer.
|
||||
func (s *structLexer) NumField() int {
|
||||
return len(s.indexes)
|
||||
}
|
||||
|
||||
type structLexerField struct {
|
||||
reflect.StructField
|
||||
Index []int
|
||||
}
|
||||
|
||||
// Field returns the field associated with the current token.
|
||||
func (s *structLexer) Field() structLexerField {
|
||||
return s.GetField(s.field)
|
||||
}
|
||||
|
||||
func (s *structLexer) GetField(field int) structLexerField {
|
||||
if field >= len(s.indexes) {
|
||||
field = len(s.indexes) - 1
|
||||
}
|
||||
return structLexerField{
|
||||
StructField: s.s.FieldByIndex(s.indexes[field]),
|
||||
Index: s.indexes[field],
|
||||
}
|
||||
}
|
||||
|
||||
func (s *structLexer) Peek() (lexer.Token, error) {
|
||||
field := s.field
|
||||
lex := s.lexer
|
||||
for {
|
||||
token, err := lex.Peek(0)
|
||||
if err != nil {
|
||||
return token, err
|
||||
}
|
||||
if !token.EOF() {
|
||||
token.Pos.Line = field + 1
|
||||
return token, nil
|
||||
}
|
||||
field++
|
||||
if field >= s.NumField() {
|
||||
return lexer.EOFToken(token.Pos), nil
|
||||
}
|
||||
tag := fieldLexerTag(s.GetField(field).StructField)
|
||||
lex = lexer.Upgrade(lexer.LexString(tag))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *structLexer) Next() (lexer.Token, error) {
|
||||
token, err := s.lexer.Next()
|
||||
if err != nil {
|
||||
return token, err
|
||||
}
|
||||
if !token.EOF() {
|
||||
token.Pos.Line = s.field + 1
|
||||
return token, nil
|
||||
}
|
||||
if s.field+1 >= s.NumField() {
|
||||
return lexer.EOFToken(token.Pos), nil
|
||||
}
|
||||
s.field++
|
||||
tag := fieldLexerTag(s.Field().StructField)
|
||||
s.lexer = lexer.Upgrade(lexer.LexString(tag))
|
||||
return s.Next()
|
||||
}
|
||||
|
||||
func fieldLexerTag(field reflect.StructField) string {
|
||||
if tag, ok := field.Tag.Lookup("parser"); ok {
|
||||
return tag
|
||||
}
|
||||
return string(field.Tag)
|
||||
}
|
||||
|
||||
// Recursively collect flattened indices for top-level fields and embedded fields.
|
||||
func collectFieldIndexes(s reflect.Type) (out [][]int, err error) {
|
||||
if s.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("expected a struct but got %q", s)
|
||||
}
|
||||
defer decorate(&err, s.String)
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
if f.Anonymous {
|
||||
children, err := collectFieldIndexes(f.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, idx := range children {
|
||||
out = append(out, append(f.Index, idx...))
|
||||
}
|
||||
} else if fieldLexerTag(f) != "" {
|
||||
out = append(out, f.Index)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user