Bump github.com/mna/pigeon from 1.1.0 to 1.2.1

Bumps [github.com/mna/pigeon](https://github.com/mna/pigeon) from 1.1.0 to 1.2.1.
- [Release notes](https://github.com/mna/pigeon/releases)
- [Changelog](https://github.com/mna/pigeon/blob/master/.goreleaser.yml)
- [Commits](https://github.com/mna/pigeon/compare/v1.1.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/mna/pigeon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-10-24 09:19:19 +00:00
committed by Ralf Haferkamp
parent 781dcba3f9
commit 21a061ab6c
31 changed files with 2801 additions and 1429 deletions
+4 -4
View File
@@ -55,7 +55,7 @@ require (
github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5
github.com/libregraph/lico v0.60.1-0.20230811070109-1d4140be554d
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.1.0
github.com/mna/pigeon v1.2.1
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.10.2
github.com/oklog/run v1.1.0
@@ -95,7 +95,7 @@ require (
golang.org/x/image v0.13.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0
golang.org/x/sync v0.3.0
golang.org/x/sync v0.4.0
golang.org/x/term v0.13.0
golang.org/x/text v0.13.0
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d
@@ -325,10 +325,10 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
+8 -8
View File
@@ -1703,8 +1703,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mna/pigeon v1.1.0 h1:EjlvVbkGnNGemf8OrjeJX0nH8orujY/HkJgzJtd7kxc=
github.com/mna/pigeon v1.1.0/go.mod h1:rkFeDZ0gc+YbnrXPw0q2RlI0QRuKBBPu67fgYIyGRNg=
github.com/mna/pigeon v1.2.1 h1:m5FxEbGdQxLaiHF+QurbWUAjmRqd5cstjIPN89svYgg=
github.com/mna/pigeon v1.2.1/go.mod h1:BUZAoRldTdU7Ac3WYkXy8hzIHfCgj1doJxGjlB+AbLI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -2200,8 +2200,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2335,8 +2335,9 @@ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2534,7 +2535,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2591,8 +2591,8 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+34
View File
@@ -0,0 +1,34 @@
# This is an example .goreleaser.yml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
builds:
- main: .
binary: pigeon
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
archives:
- name_template: >-
{{- .Binary }}_
{{- .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end -}}
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
skip: true
release:
github:
owner: mna
name: pigeon
gomod:
proxy: true
-8
View File
@@ -1,8 +0,0 @@
language: go
script: make test
go:
- 1.11.x
- 1.12.x
- tip
+1 -1
View File
@@ -22,7 +22,7 @@ For a new feature, it must be thoroughly tested. New code without new test(s) is
Respect the coding style of the repository, which means essentially to respect the [coding guidelines of the Go community][2]. Use `gofmt` to format your code, and `goimports` to add and format the list of imported packages (or do it manually, but in a `goimports`-style).
Once all code is done and tests pass, regenerate the whole tree with `make`, run `make lint` to make sure the code is correct, and run tests again. You are now ready to submit the pull request.
Once all code is done and tests pass, regenerate the whole tree with `make`, run `make lint` (does need [`golangci-lint`](https://golangci-lint.run/))to make sure the code is correct, and run tests again. You are now ready to submit the pull request.
## Licensing
+64 -7
View File
@@ -167,17 +167,74 @@ $(TEST_DIR)/issue_70/optimized-grammar/issue_70.go: $(TEST_DIR)/issue_70/issue_7
$(BINDIR)/pigeon -nolint -optimize-grammar $< > $@
$(TEST_DIR)/issue_70b/issue_70b.go: $(TEST_DIR)/issue_70b/issue_70b.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint --optimize-grammar $< > $@
$(BINDIR)/pigeon -nolint -optimize-grammar -support-left-recursion $< > $@
$(TEST_DIR)/issue_79/issue_79.go: $(TEST_DIR)/issue_79/issue_79.peg $(BINDIR)/pigeon
@! $(BINDIR)/pigeon $< > $@ 2>/dev/null && exit 0 || echo "failure, expect build to fail due to left recursion!" && exit 1
$(BINDIR)/pigeon -support-left-recursion $< > $@
$(TEST_DIR)/issue_80/issue_80.go: $(TEST_DIR)/issue_80/issue_80.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint $< > $@
lint:
golint ./...
go vet ./...
$(TEST_DIR)/issue_115/issue_115.go: $(TEST_DIR)/issue_115/issue_115.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint $< > $@
gometalinter:
gometalinter ./...
$(TEST_DIR)/issue_134/issue_134.go: $(TEST_DIR)/issue_134/issue_134.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint $< > $@
$(TEST_DIR)/left_recursion/left_recursion.go: \
$(TEST_DIR)/left_recursion/standart/leftrecursion/left_recursion.go \
$(TEST_DIR)/left_recursion/optimized/leftrecursion/left_recursion.go \
$(BINDIR)/pigeon
$(TEST_DIR)/left_recursion/standart/leftrecursion/left_recursion.go: \
$(TEST_DIR)/left_recursion/left_recursion.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -support-left-recursion $< > $@
$(TEST_DIR)/left_recursion/optimized/leftrecursion/left_recursion.go: \
$(TEST_DIR)/left_recursion/left_recursion.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -optimize-parser -support-left-recursion $< > $@
$(TEST_DIR)/left_recursion/without_left_recursion.go: \
$(TEST_DIR)/left_recursion/standart/withoutleftrecursion/without_left_recursion.go \
$(TEST_DIR)/left_recursion/optimized/withoutleftrecursion/without_left_recursion.go \
$(BINDIR)/pigeon
$(TEST_DIR)/left_recursion/standart/withoutleftrecursion/without_left_recursion.go: \
$(TEST_DIR)/left_recursion/without_left_recursion.peg \
$(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint $< > $@
$(TEST_DIR)/left_recursion/optimized/withoutleftrecursion/without_left_recursion.go: \
$(TEST_DIR)/left_recursion/without_left_recursion.peg \
$(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -optimize-parser $< > $@
$(TEST_DIR)/left_recursion_state/left_recursion_state.go: \
$(TEST_DIR)/left_recursion_state/standart/left_recursion_state.go \
$(TEST_DIR)/left_recursion_state/optimized/left_recursion_state.go \
$(BINDIR)/pigeon
$(TEST_DIR)/left_recursion_state/standart/left_recursion_state.go: \
$(TEST_DIR)/left_recursion_state/left_recursion_state.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -support-left-recursion $< > $@
$(TEST_DIR)/left_recursion_state/optimized/left_recursion_state.go: \
$(TEST_DIR)/left_recursion_state/left_recursion_state.peg $(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -optimize-parser -support-left-recursion $< > $@
$(TEST_DIR)/left_recursion_labeled_failures/left_recursion_labeled_failures.go: \
$(TEST_DIR)/left_recursion_labeled_failures/left_recursion_labeled_failures.peg \
$(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -support-left-recursion $< > $@
$(TEST_DIR)/left_recursion_thrownrecover/left_recursion_thrownrecover.go: \
$(TEST_DIR)/left_recursion_thrownrecover/left_recursion_thrownrecover.peg \
$(BINDIR)/pigeon
$(BINDIR)/pigeon -nolint -support-left-recursion $< > $@
lint:
golangci-lint run ./...
cmp:
@boot=$$(mktemp) && $(BINDIR)/bootstrap-pigeon $(PIGEON_GRAMMAR) > $$boot && \
@@ -194,5 +251,5 @@ clean:
rm -f $(BOOTSTRAPPIGEON_DIR)/bootstrap_pigeon.go $(ROOT)/pigeon.go $(TEST_GENERATED_SRC) $(EXAMPLES_DIR)/json/optimized/json.go $(EXAMPLES_DIR)/json/optimized-grammar/json.go $(TEST_DIR)/staterestore/optimized/staterestore.go $(TEST_DIR)/staterestore/standard/staterestore.go $(TEST_DIR)/issue_65/optimized/issue_65.go $(TEST_DIR)/issue_65/optimized-grammar/issue_65.go
rm -rf $(BINDIR)
.PHONY: all clean lint gometalinter cmp test
.PHONY: all clean lint cmp test
+18 -12
View File
@@ -1,7 +1,7 @@
# pigeon - a PEG parser generator for Go
[![GoDoc](https://godoc.org/github.com/mna/pigeon?status.png)](https://godoc.org/github.com/mna/pigeon)
[![build status](https://secure.travis-ci.org/mna/pigeon.png?branch=master)](http://travis-ci.org/mna/pigeon)
[![Go Reference](https://pkg.go.dev/badge/github.com/mna/pigeon.svg)](https://pkg.go.dev/github.com/mna/pigeon)
[![Test Status](https://github.com/mna/pigeon/workflows/Go%20Matrix/badge.svg)](https://github.com/mna/pigeon/actions?query=workflow%3AGo%20Matrix)
[![GoReportCard](https://goreportcard.com/badge/github.com/mna/pigeon)](https://goreportcard.com/report/github.com/mna/pigeon)
[![Software License](https://img.shields.io/badge/license-BSD-blue.svg)](LICENSE)
@@ -16,11 +16,17 @@ See the [godoc page][3] for detailed usage. Also have a look at the [Pigeon Wiki
Github user [@mna][6] created the package in April 2015, and [@breml][5] is the package's maintainer as of May 2017.
### Breaking Changes since v1.0.0
### Release policy
* Removed support for Go < v1.11 to support go modules for dependency tracking.
Starting of June 2023, the backwards compatibility support for `pigeon` is changed to follow the official [Go Security Policy](https://github.com/golang/go/security/policy).
* Removed support for Go < v1.9 due to the requirement [golang.org/x/tools/imports](https://godoc.org/golang.org/x/tools/imports), which was updated to reflect changes in recent versions of Go. This is in compliance with the [Go Release Policy](https://golang.org/doc/devel/release.html#policy) respectively the [Go Release Maintenance](https://github.com/golang/go/wiki/Go-Release-Cycle#release-maintenance), which states support for each major release until there are two newer major releases.
Over time, the Go ecosystem is evolving.
On one hand side, packages like [golang.org/x/tools](https://pkg.go.dev/golang.org/x/tools), which are critical dependencies of `pigeon`, do follow the official Security Policy and with `pigeon` not following the same guidelines, it was no longer possible to include recent versions of these dependencies and with this it was no longer possible to include critical bugfixes.
On the other hand there are changes to what is considered good practice by the greater community (e.g. change from `interface{}` to `any`). For users following (or even enforcing) these good practices, the code generated by `pigeon` does no longer meet the bar of expectations.
Last but not least, following the Go Security Policy over the last years has been a smooth experience and therefore updating Go on a regular bases feels like duty that is reasonable to be put on users of `pigeon`.
This observations lead to the decision to follow the same Security Policy as Go.
## Installation
@@ -63,18 +69,18 @@ var ops = map[string]func(int, int) int {
},
}
func toIfaceSlice(v interface{}) []interface{} {
func toAnySlice(v any) []any {
if v == nil {
return nil
}
return v.([]interface{})
return v.([]any)
}
func eval(first, rest interface{}) int {
func eval(first, rest any) int {
l := first.(int)
restSl := toIfaceSlice(rest)
restSl := toAnySlice(rest)
for _, v := range restSl {
restExpr := toIfaceSlice(v)
restExpr := toAnySlice(v)
r := restExpr[3].(int)
op := restExpr[1].(string)
l = ops[op](l, r)
@@ -129,7 +135,7 @@ The generated parser can parse simple arithmetic operations, e.g.:
More examples can be found in the `examples/` subdirectory.
See the [godoc page][3] for detailed usage.
See the [package documentation][3] for detailed usage.
## Contributing
@@ -142,7 +148,7 @@ The [BSD 3-Clause license][4]. See the LICENSE file.
[0]: http://en.wikipedia.org/wiki/Parsing_expression_grammar
[1]: http://pegjs.org/
[2]: http://www.codeproject.com/Articles/29713/Parsing-Expression-Grammar-Support-for-C-Part
[3]: https://godoc.org/github.com/mna/pigeon
[3]: https://pkg.go.dev/github.com/mna/pigeon
[4]: http://opensource.org/licenses/BSD-3-Clause
[5]: https://github.com/breml
[6]: https://github.com/mna
+25
View File
@@ -0,0 +1,25 @@
----------------------------------------------------------------------
License notice for github.com/stretchr/testify
----------------------------------------------------------------------
MIT License
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
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.
+471 -2
View File
@@ -35,6 +35,8 @@ type Grammar struct {
Rules []*Rule
}
var _ Expression = (*Grammar)(nil)
// NewGrammar creates a new grammar at the specified position.
func NewGrammar(p Pos) *Grammar {
return &Grammar{p: p}
@@ -56,6 +58,21 @@ func (g *Grammar) String() string {
return buf.String()
}
// NullableVisit recursively determines whether an object is nullable.
func (g *Grammar) NullableVisit(rules map[string]*Rule) bool {
panic("NullableVisit should not be called on the Grammar")
}
// IsNullable returns the nullable attribute of the node.
func (g *Grammar) IsNullable() bool {
panic("IsNullable should not be called on the Grammar")
}
// InitialNames returns names of nodes with which an expression can begin.
func (g *Grammar) InitialNames() map[string]struct{} {
panic("InitialNames should not be called on the Grammar")
}
// Rule represents a rule in the PEG grammar. It has a name, an optional
// display name to be used in error messages, and an expression.
type Rule struct {
@@ -63,8 +80,16 @@ type Rule struct {
Name *Identifier
DisplayName *StringLit
Expr Expression
// Fields below to work with left recursion.
Visited bool
Nullable bool
LeftRecursive bool
Leader bool
}
var _ Expression = (*Rule)(nil)
// NewRule creates a rule with at the specified position and with the
// specified name as identifier.
func NewRule(p Pos, name *Identifier) *Rule {
@@ -80,9 +105,36 @@ func (r *Rule) String() string {
r.p, r, r.Name, r.DisplayName, r.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (r *Rule) NullableVisit(rules map[string]*Rule) bool {
if r.Visited {
// A left-recursive rule is considered non-nullable.
return false
}
r.Visited = true
r.Nullable = r.Expr.NullableVisit(rules)
r.Visited = false
return r.Nullable
}
// IsNullable returns the nullable attribute of the node.
func (r *Rule) IsNullable() bool {
return r.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (r *Rule) InitialNames() map[string]struct{} {
return r.Expr.InitialNames()
}
// Expression is the interface implemented by all expression types.
type Expression interface {
Pos() Pos
// for work with left recursion
NullableVisit(rules map[string]*Rule) bool
IsNullable() bool
InitialNames() map[string]struct{}
}
// ChoiceExpr is an ordered sequence of expressions. The parser tries to
@@ -91,8 +143,12 @@ type Expression interface {
type ChoiceExpr struct {
p Pos
Alternatives []Expression
Nullable bool
}
var _ Expression = (*ChoiceExpr)(nil)
// NewChoiceExpr creates a choice expression at the specified position.
func NewChoiceExpr(p Pos) *ChoiceExpr {
return &ChoiceExpr{p: p}
@@ -113,7 +169,35 @@ func (c *ChoiceExpr) String() string {
return buf.String()
}
// FailureLabel is an identifier, which can by thrown and recovered in a grammar
// NullableVisit recursively determines whether an object is nullable.
func (c *ChoiceExpr) NullableVisit(rules map[string]*Rule) bool {
for _, alt := range c.Alternatives {
if alt.NullableVisit(rules) {
c.Nullable = true
return true
}
}
c.Nullable = false
return false
}
// IsNullable returns the nullable attribute of the node.
func (c *ChoiceExpr) IsNullable() bool {
return c.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (c *ChoiceExpr) InitialNames() map[string]struct{} {
names := make(map[string]struct{})
for _, alt := range c.Alternatives {
for name := range alt.InitialNames() {
names[name] = struct{}{}
}
}
return names
}
// FailureLabel is an identifier, which can by thrown and recovered in a grammar.
type FailureLabel string
// RecoveryExpr is an ordered sequence of expressions. The parser tries to
@@ -124,8 +208,12 @@ type RecoveryExpr struct {
Expr Expression
RecoverExpr Expression
Labels []FailureLabel
Nullable bool
}
var _ Expression = (*RecoveryExpr)(nil)
// NewRecoveryExpr creates a choice expression at the specified position.
func NewRecoveryExpr(p Pos) *RecoveryExpr {
return &RecoveryExpr{p: p}
@@ -139,7 +227,7 @@ func (r *RecoveryExpr) String() string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("%s: %T{Expr: %v, RecoverExpr: %v", r.p, r, r.Expr, r.RecoverExpr))
buf.WriteString(fmt.Sprintf(", Labels: [\n"))
buf.WriteString(", Labels: [\n")
for _, e := range r.Labels {
buf.WriteString(fmt.Sprintf("%s,\n", e))
}
@@ -147,6 +235,29 @@ func (r *RecoveryExpr) String() string {
return buf.String()
}
// NullableVisit recursively determines whether an object is nullable.
func (r *RecoveryExpr) NullableVisit(rules map[string]*Rule) bool {
r.Nullable = r.Expr.NullableVisit(rules) || r.RecoverExpr.NullableVisit(rules)
return r.Nullable
}
// IsNullable returns the nullable attribute of the node.
func (r *RecoveryExpr) IsNullable() bool {
return r.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (r *RecoveryExpr) InitialNames() map[string]struct{} {
names := make(map[string]struct{})
for name := range r.Expr.InitialNames() {
names[name] = struct{}{}
}
for name := range r.RecoverExpr.InitialNames() {
names[name] = struct{}{}
}
return names
}
// ActionExpr is an expression that has an associated block of code to
// execute when the expression matches.
type ActionExpr struct {
@@ -154,8 +265,12 @@ type ActionExpr struct {
Expr Expression
Code *CodeBlock
FuncIx int
Nullable bool
}
var _ Expression = (*ActionExpr)(nil)
// NewActionExpr creates a new action expression at the specified position.
func NewActionExpr(p Pos) *ActionExpr {
return &ActionExpr{p: p}
@@ -169,6 +284,26 @@ func (a *ActionExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v, Code: %v}", a.p, a, a.Expr, a.Code)
}
// NullableVisit recursively determines whether an object is nullable.
func (a *ActionExpr) NullableVisit(rules map[string]*Rule) bool {
a.Nullable = a.Expr.NullableVisit(rules)
return a.Nullable
}
// IsNullable returns the nullable attribute of the node.
func (a *ActionExpr) IsNullable() bool {
return a.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (a *ActionExpr) InitialNames() map[string]struct{} {
names := make(map[string]struct{})
for name := range a.Expr.InitialNames() {
names[name] = struct{}{}
}
return names
}
// ThrowExpr is an expression that throws an FailureLabel to be catched by a
// RecoveryChoiceExpr.
type ThrowExpr struct {
@@ -176,6 +311,8 @@ type ThrowExpr struct {
Label string
}
var _ Expression = (*ThrowExpr)(nil)
// NewThrowExpr creates a new throw expression at the specified position.
func NewThrowExpr(p Pos) *ThrowExpr {
return &ThrowExpr{p: p}
@@ -189,13 +326,32 @@ func (t *ThrowExpr) String() string {
return fmt.Sprintf("%s: %T{Label: %v}", t.p, t, t.Label)
}
// NullableVisit recursively determines whether an object is nullable.
func (t *ThrowExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (t *ThrowExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (t *ThrowExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// SeqExpr is an ordered sequence of expressions, all of which must match
// if the SeqExpr is to be a match itself.
type SeqExpr struct {
p Pos
Exprs []Expression
Nullable bool
}
var _ Expression = (*SeqExpr)(nil)
// NewSeqExpr creates a new sequence expression at the specified position.
func NewSeqExpr(p Pos) *SeqExpr {
return &SeqExpr{p: p}
@@ -216,6 +372,37 @@ func (s *SeqExpr) String() string {
return buf.String()
}
// NullableVisit recursively determines whether an object is nullable.
func (s *SeqExpr) NullableVisit(rules map[string]*Rule) bool {
for _, item := range s.Exprs {
if !item.NullableVisit(rules) {
s.Nullable = false
return false
}
}
s.Nullable = true
return true
}
// IsNullable returns the nullable attribute of the node.
func (s *SeqExpr) IsNullable() bool {
return s.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (s *SeqExpr) InitialNames() map[string]struct{} {
names := make(map[string]struct{})
for _, item := range s.Exprs {
for name := range item.InitialNames() {
names[name] = struct{}{}
}
if !item.IsNullable() {
break
}
}
return names
}
// LabeledExpr is an expression that has an associated label. Code blocks
// can access the value of the expression using that label, that becomes
// a local variable in the code.
@@ -225,6 +412,8 @@ type LabeledExpr struct {
Expr Expression
}
var _ Expression = (*LabeledExpr)(nil)
// NewLabeledExpr creates a new labeled expression at the specified position.
func NewLabeledExpr(p Pos) *LabeledExpr {
return &LabeledExpr{p: p}
@@ -238,6 +427,21 @@ func (l *LabeledExpr) String() string {
return fmt.Sprintf("%s: %T{Label: %v, Expr: %v}", l.p, l, l.Label, l.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (l *LabeledExpr) NullableVisit(rules map[string]*Rule) bool {
return l.Expr.NullableVisit(rules)
}
// IsNullable returns the nullable attribute of the node.
func (l *LabeledExpr) IsNullable() bool {
return l.Expr.IsNullable()
}
// InitialNames returns names of nodes with which an expression can begin.
func (l *LabeledExpr) InitialNames() map[string]struct{} {
return l.Expr.InitialNames()
}
// AndExpr is a zero-length matcher that is considered a match if the
// expression it contains is a match.
type AndExpr struct {
@@ -250,6 +454,8 @@ func NewAndExpr(p Pos) *AndExpr {
return &AndExpr{p: p}
}
var _ Expression = (*AndExpr)(nil)
// Pos returns the starting position of the node.
func (a *AndExpr) Pos() Pos { return a.p }
@@ -258,6 +464,21 @@ func (a *AndExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v}", a.p, a, a.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (a *AndExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (a *AndExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (a *AndExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// NotExpr is a zero-length matcher that is considered a match if the
// expression it contains is not a match.
type NotExpr struct {
@@ -265,6 +486,8 @@ type NotExpr struct {
Expr Expression
}
var _ Expression = (*NotExpr)(nil)
// NewNotExpr creates a new not (!) expression at the specified position.
func NewNotExpr(p Pos) *NotExpr {
return &NotExpr{p: p}
@@ -278,12 +501,29 @@ func (n *NotExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v}", n.p, n, n.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (n *NotExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (n *NotExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (n *NotExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// ZeroOrOneExpr is an expression that can be matched zero or one time.
type ZeroOrOneExpr struct {
p Pos
Expr Expression
}
var _ Expression = (*ZeroOrOneExpr)(nil)
// NewZeroOrOneExpr creates a new zero or one expression at the specified
// position.
func NewZeroOrOneExpr(p Pos) *ZeroOrOneExpr {
@@ -298,12 +538,29 @@ func (z *ZeroOrOneExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v}", z.p, z, z.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (z *ZeroOrOneExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (z *ZeroOrOneExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (z *ZeroOrOneExpr) InitialNames() map[string]struct{} {
return z.Expr.InitialNames()
}
// ZeroOrMoreExpr is an expression that can be matched zero or more times.
type ZeroOrMoreExpr struct {
p Pos
Expr Expression
}
var _ Expression = (*ZeroOrMoreExpr)(nil)
// NewZeroOrMoreExpr creates a new zero or more expression at the specified
// position.
func NewZeroOrMoreExpr(p Pos) *ZeroOrMoreExpr {
@@ -318,12 +575,29 @@ func (z *ZeroOrMoreExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v}", z.p, z, z.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (z *ZeroOrMoreExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (z *ZeroOrMoreExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (z *ZeroOrMoreExpr) InitialNames() map[string]struct{} {
return z.Expr.InitialNames()
}
// OneOrMoreExpr is an expression that can be matched one or more times.
type OneOrMoreExpr struct {
p Pos
Expr Expression
}
var _ Expression = (*OneOrMoreExpr)(nil)
// NewOneOrMoreExpr creates a new one or more expression at the specified
// position.
func NewOneOrMoreExpr(p Pos) *OneOrMoreExpr {
@@ -338,12 +612,31 @@ func (o *OneOrMoreExpr) String() string {
return fmt.Sprintf("%s: %T{Expr: %v}", o.p, o, o.Expr)
}
// NullableVisit recursively determines whether an object is nullable.
func (o *OneOrMoreExpr) NullableVisit(rules map[string]*Rule) bool {
return false
}
// IsNullable returns the nullable attribute of the node.
func (o *OneOrMoreExpr) IsNullable() bool {
return false
}
// InitialNames returns names of nodes with which an expression can begin.
func (o *OneOrMoreExpr) InitialNames() map[string]struct{} {
return o.Expr.InitialNames()
}
// RuleRefExpr is an expression that references a rule by name.
type RuleRefExpr struct {
p Pos
Name *Identifier
Nullable bool
}
var _ Expression = (*RuleRefExpr)(nil)
// NewRuleRefExpr creates a new rule reference expression at the specified
// position.
func NewRuleRefExpr(p Pos) *RuleRefExpr {
@@ -358,6 +651,28 @@ func (r *RuleRefExpr) String() string {
return fmt.Sprintf("%s: %T{Name: %v}", r.p, r, r.Name)
}
// NullableVisit recursively determines whether an object is nullable.
func (r *RuleRefExpr) NullableVisit(rules map[string]*Rule) bool {
item, ok := rules[r.Name.Val]
if !ok {
// Token or unknown; never empty.
r.Nullable = false
return false
}
r.Nullable = item.NullableVisit(rules)
return r.Nullable
}
// IsNullable returns the nullable attribute of the node.
func (r *RuleRefExpr) IsNullable() bool {
return r.Nullable
}
// InitialNames returns names of nodes with which an expression can begin.
func (r *RuleRefExpr) InitialNames() map[string]struct{} {
return map[string]struct{}{r.Name.Val: {}}
}
// StateCodeExpr is an expression which can modify the internal state of the parser.
type StateCodeExpr struct {
p Pos
@@ -365,6 +680,8 @@ type StateCodeExpr struct {
FuncIx int
}
var _ Expression = (*StateCodeExpr)(nil)
// NewStateCodeExpr creates a new state (#) code expression at the specified
// position.
func NewStateCodeExpr(p Pos) *StateCodeExpr {
@@ -379,6 +696,21 @@ func (s *StateCodeExpr) String() string {
return fmt.Sprintf("%s: %T{Code: %v}", s.p, s, s.Code)
}
// NullableVisit recursively determines whether an object is nullable.
func (s *StateCodeExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (s *StateCodeExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (s *StateCodeExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// AndCodeExpr is a zero-length matcher that is considered a match if the
// code block returns true.
type AndCodeExpr struct {
@@ -387,6 +719,8 @@ type AndCodeExpr struct {
FuncIx int
}
var _ Expression = (*AndCodeExpr)(nil)
// NewAndCodeExpr creates a new and (&) code expression at the specified
// position.
func NewAndCodeExpr(p Pos) *AndCodeExpr {
@@ -401,6 +735,21 @@ func (a *AndCodeExpr) String() string {
return fmt.Sprintf("%s: %T{Code: %v}", a.p, a, a.Code)
}
// NullableVisit recursively determines whether an object is nullable.
func (a *AndCodeExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (a *AndCodeExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (a *AndCodeExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// NotCodeExpr is a zero-length matcher that is considered a match if the
// code block returns false.
type NotCodeExpr struct {
@@ -409,6 +758,8 @@ type NotCodeExpr struct {
FuncIx int
}
var _ Expression = (*NotCodeExpr)(nil)
// NewNotCodeExpr creates a new not (!) code expression at the specified
// position.
func NewNotCodeExpr(p Pos) *NotCodeExpr {
@@ -423,6 +774,21 @@ func (n *NotCodeExpr) String() string {
return fmt.Sprintf("%s: %T{Code: %v}", n.p, n, n.Code)
}
// NullableVisit recursively determines whether an object is nullable.
func (n *NotCodeExpr) NullableVisit(rules map[string]*Rule) bool {
return true
}
// IsNullable returns the nullable attribute of the node.
func (n *NotCodeExpr) IsNullable() bool {
return true
}
// InitialNames returns names of nodes with which an expression can begin.
func (n *NotCodeExpr) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// LitMatcher is a string literal matcher. The value to match may be a
// double-quoted string, a single-quoted single character, or a back-tick
// quoted raw string.
@@ -431,6 +797,8 @@ type LitMatcher struct {
IgnoreCase bool
}
var _ Expression = (*LitMatcher)(nil)
// NewLitMatcher creates a new literal matcher at the specified position and
// with the specified value.
func NewLitMatcher(p Pos, v string) *LitMatcher {
@@ -445,6 +813,22 @@ func (l *LitMatcher) String() string {
return fmt.Sprintf("%s: %T{Val: %q, IgnoreCase: %t}", l.p, l, l.Val, l.IgnoreCase)
}
// NullableVisit recursively determines whether an object is nullable.
func (l *LitMatcher) NullableVisit(rules map[string]*Rule) bool {
return l.IsNullable()
}
// IsNullable returns the nullable attribute of the node.
func (l *LitMatcher) IsNullable() bool {
// The string token '' is considered empty.
return len(l.Val) == 0
}
// InitialNames returns names of nodes with which an expression can begin.
func (l *LitMatcher) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// CharClassMatcher is a character class matcher. The value to match must
// be one of the specified characters, in a range of characters, or in the
// Unicode classes of characters.
@@ -457,6 +841,8 @@ type CharClassMatcher struct {
UnicodeClasses []string
}
var _ Expression = (*CharClassMatcher)(nil)
// NewCharClassMatcher creates a new character class matcher at the specified
// position and with the specified raw value. It parses the raw value into
// the list of characters, ranges and Unicode classes.
@@ -580,11 +966,28 @@ func (c *CharClassMatcher) String() string {
c.p, c, c.Val, c.IgnoreCase, c.Inverted)
}
// NullableVisit recursively determines whether an object is nullable.
func (c *CharClassMatcher) NullableVisit(rules map[string]*Rule) bool {
return c.IsNullable()
}
// IsNullable returns the nullable attribute of the node.
func (c *CharClassMatcher) IsNullable() bool {
return len(c.Chars) == 0 && len(c.Ranges) == 0 && len(c.UnicodeClasses) == 0
}
// InitialNames returns names of nodes with which an expression can begin.
func (c *CharClassMatcher) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// AnyMatcher is a matcher that matches any character except end-of-file.
type AnyMatcher struct {
posValue
}
var _ Expression = (*AnyMatcher)(nil)
// NewAnyMatcher creates a new any matcher at the specified position. The
// value is provided for completeness' sake, but it is always the dot.
func NewAnyMatcher(p Pos, v string) *AnyMatcher {
@@ -599,11 +1002,28 @@ func (a *AnyMatcher) String() string {
return fmt.Sprintf("%s: %T{Val: %q}", a.p, a, a.Val)
}
// NullableVisit recursively determines whether an object is nullable.
func (a *AnyMatcher) NullableVisit(rules map[string]*Rule) bool {
return false
}
// IsNullable returns the nullable attribute of the node.
func (a *AnyMatcher) IsNullable() bool {
return false
}
// InitialNames returns names of nodes with which an expression can begin.
func (a *AnyMatcher) InitialNames() map[string]struct{} {
return make(map[string]struct{})
}
// CodeBlock represents a code block.
type CodeBlock struct {
posValue
}
var _ Expression = (*CodeBlock)(nil)
// NewCodeBlock creates a new code block at the specified position and with
// the specified value. The value includes the outer braces.
func NewCodeBlock(p Pos, code string) *CodeBlock {
@@ -618,11 +1038,28 @@ func (c *CodeBlock) String() string {
return fmt.Sprintf("%s: %T{Val: %q}", c.p, c, c.Val)
}
// NullableVisit recursively determines whether an object is nullable.
func (c *CodeBlock) NullableVisit(rules map[string]*Rule) bool {
panic("NullableVisit should not be called on the CodeBlock")
}
// IsNullable returns the nullable attribute of the node.
func (c *CodeBlock) IsNullable() bool {
panic("IsNullable should not be called on the CodeBlock")
}
// InitialNames returns names of nodes with which an expression can begin.
func (c *CodeBlock) InitialNames() map[string]struct{} {
panic("InitialNames should not be called on the CodeBlock")
}
// Identifier represents an identifier.
type Identifier struct {
posValue
}
var _ Expression = (*Identifier)(nil)
// NewIdentifier creates a new identifier at the specified position and
// with the specified name.
func NewIdentifier(p Pos, name string) *Identifier {
@@ -637,11 +1074,28 @@ func (i *Identifier) String() string {
return fmt.Sprintf("%s: %T{Val: %q}", i.p, i, i.Val)
}
// NullableVisit recursively determines whether an object is nullable.
func (i *Identifier) NullableVisit(rules map[string]*Rule) bool {
panic("NullableVisit should not be called on the Identifier")
}
// IsNullable returns the nullable attribute of the node.
func (i *Identifier) IsNullable() bool {
panic("IsNullable should not be called on the Identifier")
}
// InitialNames returns names of nodes with which an expression can begin.
func (i *Identifier) InitialNames() map[string]struct{} {
panic("InitialNames should not be called on the Identifier")
}
// StringLit represents a string literal.
type StringLit struct {
posValue
}
var _ Expression = (*StringLit)(nil)
// NewStringLit creates a new string literal at the specified position and
// with the specified value.
func NewStringLit(p Pos, val string) *StringLit {
@@ -656,6 +1110,21 @@ func (s *StringLit) String() string {
return fmt.Sprintf("%s: %T{Val: %q}", s.p, s, s.Val)
}
// NullableVisit recursively determines whether an object is nullable.
func (s *StringLit) NullableVisit(rules map[string]*Rule) bool {
panic("NullableVisit should not be called on the StringLit")
}
// IsNullable returns the nullable attribute of the node.
func (s *StringLit) IsNullable() bool {
panic("IsNullable should not be called on the StringLit")
}
// InitialNames returns names of nodes with which an expression can begin.
func (s *StringLit) InitialNames() map[string]struct{} {
panic("InitialNames should not be called on the StringLit")
}
type posValue struct {
p Pos
Val string
+11 -11
View File
@@ -34,7 +34,7 @@ func newGrammarOptimizer(protectedRules []string) *grammarOptimizer {
// Visit is a generic Visitor to be used with Walk
// The actual function, which should be used during Walk
// is held in ruleRefOptimizer.visitor
// is held in ruleRefOptimizer.visitor.
func (r *grammarOptimizer) Visit(expr Expression) Visitor {
return r.visitor(expr)
}
@@ -265,7 +265,7 @@ func (r *grammarOptimizer) optimizeRule(expr Expression) Expression {
// cloneExpr takes an Expression and deep clones it (including all children)
// This is necessary because referenced Rules are denormalized and therefore
// have to become independent from their original Expression
// have to become independent from their original Expression.
func cloneExpr(expr Expression) Expression {
switch expr := expr.(type) {
case *ActionExpr:
@@ -359,7 +359,7 @@ func cloneExpr(expr Expression) Expression {
// The purpose of this function is to cleanup the redundancies created by the
// optimize Visitor. This includes to remove redundant entries in Chars, Ranges
// and UnicodeClasses of the given CharClassMatcher as well as regenerating the
// correct content for the Val field (string representation of the CharClassMatcher)
// correct content for the Val field (string representation of the CharClassMatcher).
func (r *grammarOptimizer) cleanupCharClassMatcher(expr0 Expression) Visitor {
// We are only interested in nodes of type *CharClassMatcher
if chr, ok := expr0.(*CharClassMatcher); ok {
@@ -441,14 +441,14 @@ func escapeRune(r rune) string {
// Optimize walks a given grammar and optimizes the grammar in regards
// of parsing performance. This is done with several optimizations:
// * removal of unreferenced rules
// * replace rule references with a copy of the referenced Rule, if the
// referenced rule it self has no references.
// * resolve nested choice expressions
// * resolve choice expressions with only one alternative
// * resolve nested sequences expression
// * resolve sequence expressions with only one element
// * combine character class matcher and literal matcher, where possible
// - removal of unreferenced rules
// - replace rule references with a copy of the referenced Rule, if the
// referenced rule it self has no references.
// - resolve nested choice expressions
// - resolve choice expressions with only one alternative
// - resolve nested sequences expression
// - resolve sequence expressions with only one element
// - combine character class matcher and literal matcher, where possible
func Optimize(g *Grammar, alternateEntrypoints ...string) {
entrypoints := alternateEntrypoints
if len(g.Rules) > 0 {
-1
View File
@@ -15,7 +15,6 @@ type Visitor interface {
// v.Visit(expr) is not nil, Walk is invoked recursively with visitor
// w for each of the non-nil children of Expression, followed by a call of
// w.Visit(nil).
//
func Walk(v Visitor, expr Expression) {
if v = v.Visit(expr); v == nil {
return
+39 -14
View File
@@ -6,13 +6,12 @@ import (
"bytes"
"fmt"
"io"
"regexp"
"strconv"
"strings"
"text/template"
"unicode"
"regexp"
"github.com/mna/pigeon/ast"
)
@@ -20,7 +19,7 @@ const codeGeneratedComment = "// Code generated by pigeon; DO NOT EDIT.\n\n"
// generated function templates
var (
onFuncTemplate = `func (%s *current) %s(%s) (interface{}, error) {
onFuncTemplate = `func (%s *current) %s(%s) (any, error) {
%s
}
`
@@ -32,7 +31,7 @@ var (
%s
}
`
callFuncTemplate = `func (p *parser) call%s() (interface{}, error) {
callFuncTemplate = `func (p *parser) call%s() (any, error) {
stack := p.vstack[len(p.vstack)-1]
_ = stack
return p.cur.%[1]s(%s)
@@ -78,9 +77,19 @@ func Optimize(optimize bool) Option {
}
}
// SupportLeftRecursion returns an option that specifies the supportLeftRecursion option.
// If supportLeftRecursion is true, LeftRecursion code is added to the resulting parser.
func SupportLeftRecursion(support bool) Option {
return func(b *builder) Option {
prev := b.supportLeftRecursion
b.supportLeftRecursion = support
return SupportLeftRecursion(prev)
}
}
// Nolint returns an option that specifies the nolint option
// If nolint is true, special '// nolint: ...' comments are added
// to the generated parser to suppress warnings by gometalinter.
// to the generated parser to suppress warnings by gometalinter or golangci-lint.
func Nolint(nolint bool) Option {
return func(b *builder) Option {
prev := b.nolint
@@ -119,6 +128,8 @@ type builder struct {
basicLatinLookupTable bool
globalState bool
nolint bool
supportLeftRecursion bool
haveLeftRecursion bool
ruleName string
exprIndex int
@@ -133,11 +144,19 @@ func (b *builder) setOptions(opts []Option) {
}
}
func (b *builder) buildParser(g *ast.Grammar) error {
b.writeInit(g.Init)
b.writeGrammar(g)
func (b *builder) buildParser(grammar *ast.Grammar) error {
haveLeftRecursion, err := PrepareGrammar(grammar)
if err != nil {
return fmt.Errorf("incorrect grammar: %w", err)
}
if !b.supportLeftRecursion && haveLeftRecursion {
return fmt.Errorf("incorrect grammar: %w", ErrHaveLeftRecursion)
}
b.haveLeftRecursion = haveLeftRecursion
for _, rule := range g.Rules {
b.writeInit(grammar.Init)
b.writeGrammar(grammar)
for _, rule := range grammar.Rules {
b.writeRuleCode(rule)
}
b.writeStaticCode()
@@ -184,6 +203,10 @@ func (b *builder) writeRule(r *ast.Rule) {
b.writelnf("\tpos: position{line: %d, col: %d, offset: %d},", pos.Line, pos.Col, pos.Off)
b.writef("\texpr: ")
b.writeExpr(r.Expr)
if b.haveLeftRecursion {
b.writelnf("\tleader: %t,", r.Leader)
b.writelnf("\tleftRecursive: %t,", r.LeftRecursive)
}
b.writelnf("},")
}
@@ -383,7 +406,7 @@ func (b *builder) writeChoiceExpr(ch *ast.ChoiceExpr) {
pos := ch.Pos()
b.writelnf("\tpos: position{line: %d, col: %d, offset: %d},", pos.Line, pos.Col, pos.Off)
if len(ch.Alternatives) > 0 {
b.writelnf("\talternatives: []interface{}{")
b.writelnf("\talternatives: []any{")
for _, alt := range ch.Alternatives {
b.writeExpr(alt)
}
@@ -515,7 +538,7 @@ func (b *builder) writeSeqExpr(seq *ast.SeqExpr) {
pos := seq.Pos()
b.writelnf("\tpos: position{line: %d, col: %d, offset: %d},", pos.Line, pos.Col, pos.Off)
if len(seq.Exprs) > 0 {
b.writelnf("\texprs: []interface{}{")
b.writelnf("\texprs: []any{")
for _, e := range seq.Exprs {
b.writeExpr(e)
}
@@ -735,7 +758,7 @@ func (b *builder) writeFunc(funcIx int, code *ast.CodeBlock, callTpl, funcTpl st
}
}
if args.Len() > 0 {
args.WriteString(" interface{}")
args.WriteString(" any")
}
fnNm := b.funcName(funcIx)
@@ -759,11 +782,13 @@ func (b *builder) writeStaticCode() {
Optimize bool
BasicLatinLookupTable bool
GlobalState bool
LeftRecursion bool
Nolint bool
}{
Optimize: b.optimize,
BasicLatinLookupTable: b.basicLatinLookupTable,
GlobalState: b.globalState,
LeftRecursion: b.haveLeftRecursion,
Nolint: b.nolint,
}
t := template.Must(template.New("static_code").Parse(staticCode))
@@ -800,13 +825,13 @@ func (b *builder) funcName(ix int) string {
return "on" + b.ruleName + strconv.Itoa(ix)
}
func (b *builder) writef(f string, args ...interface{}) {
func (b *builder) writef(f string, args ...any) {
if b.err == nil {
_, b.err = fmt.Fprintf(b.w, f, args...)
}
}
func (b *builder) writelnf(f string, args ...interface{}) {
func (b *builder) writelnf(f string, args ...any) {
b.writef(f+"\n", args...)
}
+277 -135
View File
@@ -62,18 +62,17 @@ func Entrypoint(ruleName string) Option {
//
// Example usage:
//
// input := "input"
// stats := Stats{}
// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match"))
// if err != nil {
// log.Panicln(err)
// }
// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ")
// if err != nil {
// log.Panicln(err)
// }
// fmt.Println(string(b))
//
// input := "input"
// stats := Stats{}
// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match"))
// if err != nil {
// log.Panicln(err)
// }
// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ")
// if err != nil {
// log.Panicln(err)
// }
// fmt.Println(string(b))
func Statistics(stats *Stats, choiceNoMatch string) Option {
return func(p *parser) Option {
oldStats := p.Stats
@@ -145,7 +144,7 @@ func Recover(b bool) Option {
// GlobalStore creates an Option to set a key to a certain value in
// the globalStore.
func GlobalStore(key string, value interface{}) Option {
func GlobalStore(key string, value any) Option {
return func(p *parser) Option {
old := p.cur.globalStore[key]
p.cur.globalStore[key] = value
@@ -157,7 +156,7 @@ func GlobalStore(key string, value interface{}) Option {
// InitState creates an Option to set a key to a certain value in
// the global "state" store.
func InitState(key string, value interface{}) Option {
func InitState(key string, value any) Option {
return func(p *parser) Option {
old := p.cur.state[key]
p.cur.state[key] = value
@@ -168,7 +167,7 @@ func InitState(key string, value interface{}) Option {
// {{ end }} ==template==
// ParseFile parses the file identified by filename.
func ParseFile(filename string, opts ...Option) (i interface{}, err error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
func ParseFile(filename string, opts ...Option) (i any, err error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
f, err := os.Open(filename)
if err != nil {
return nil, err
@@ -183,8 +182,8 @@ func ParseFile(filename string, opts ...Option) (i interface{}, err error) { //{
// ParseReader parses the data from r using filename as information in the
// error messages.
func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
b, err := ioutil.ReadAll(r)
func ParseReader(filename string, r io.Reader, opts ...Option) (any, error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
@@ -194,7 +193,7 @@ func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, err
// Parse parses the data from b using filename as information in the
// error messages.
func Parse(filename string, b []byte, opts ...Option) (interface{}, error) {
func Parse(filename string, b []byte, opts ...Option) (any, error) {
return newParser(filename, b, opts...).parse(g)
}
@@ -236,77 +235,84 @@ type current struct {
globalStore storeDict
}
type storeDict map[string]interface{}
type storeDict map[string]any
// the AST types...
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type grammar struct {
pos position
rules []*rule
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type rule struct {
pos position
name string
displayName string
expr interface{}
expr any
// ==template== {{ if .LeftRecursion }}
leader bool
leftRecursive bool
// {{ end }} ==template==
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type choiceExpr struct {
pos position
alternatives []interface{}
alternatives []any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type actionExpr struct {
pos position
expr interface{}
run func(*parser) (interface{}, error)
expr any
run func(*parser) (any, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type recoveryExpr struct {
pos position
expr interface{}
recoverExpr interface{}
expr any
recoverExpr any
failureLabel []string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type seqExpr struct {
pos position
exprs []interface{}
exprs []any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type throwExpr struct {
pos position
label string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type labeledExpr struct {
pos position
label string
expr interface{}
expr any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type expr struct {
pos position
expr interface{}
expr any
}
type andExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type notExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type zeroOrOneExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type zeroOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type oneOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type (
andExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
notExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
zeroOrOneExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
zeroOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
oneOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
)
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type ruleRefExpr struct {
pos position
name string
@@ -314,7 +320,7 @@ type ruleRefExpr struct {
// ==template== {{ if or .GlobalState (not .Optimize) }}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type stateCodeExpr struct {
pos position
run func(*parser) error
@@ -322,19 +328,19 @@ type stateCodeExpr struct {
// {{ end }} ==template==
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type andCodeExpr struct {
pos position
run func(*parser) (bool, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type notCodeExpr struct {
pos position
run func(*parser) (bool, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type litMatcher struct {
pos position
val string
@@ -342,7 +348,7 @@ type litMatcher struct {
want string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type charClassMatcher struct {
pos position
val string
@@ -456,14 +462,14 @@ func (p *parser) setOptions(opts []Option) {
}
}
//{{ if .Nolint }} nolint: structcheck,deadcode {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck,deadcode {{else}} ==template== {{ end }}
type resultTuple struct {
v interface{}
v any
b bool
end savepoint
}
//{{ if .Nolint }} nolint: varcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: varcheck {{else}} ==template== {{ end }}
const choiceNoMatch = -1
// Stats stores some statistics, gathered during parsing
@@ -488,7 +494,15 @@ type Stats struct {
ChoiceAltCnt map[string]map[string]int
}
//{{ if .Nolint }} nolint: structcheck,maligned {{else}} ==template== {{ end }}
// ==template== {{ if .LeftRecursion }}
type ruleWithExpsStack struct {
rule *rule
estack []any
}
// {{ end }} ==template==
// {{ if .Nolint }} nolint: structcheck,maligned {{else}} ==template== {{ end }}
type parser struct {
filename string
pt savepoint
@@ -503,15 +517,17 @@ type parser struct {
debug bool
memoize bool
// {{ end }} ==template==
// ==template== {{ if or .LeftRecursion (not .Optimize) }}
// memoization table for the packrat algorithm:
// map[offset in source] map[expression or rule] {value, match}
memo map[int]map[interface{}]resultTuple
memo map[int]map[any]resultTuple
// {{ end }} ==template==
// rules table, maps the rule identifier to the rule node
rules map[string]*rule
// variables stack, map of label to value
vstack []map[string]interface{}
vstack []map[string]any
// rule stack, allows identification of the current rule in errors
rstack []*rule
@@ -531,7 +547,7 @@ type parser struct {
choiceNoMatch string
// recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse
recoveryStack []map[string]interface{}
recoveryStack []map[string]any
}
// push a variable set on the vstack.
@@ -551,7 +567,7 @@ func (p *parser) pushV() {
return
}
m = make(map[string]interface{})
m = make(map[string]any)
p.vstack[len(p.vstack)-1] = m
}
@@ -567,7 +583,7 @@ func (p *parser) popV() {
}
// push a recovery expression with its labels to the recoveryStack
func (p *parser) pushRecovery(labels []string, expr interface{}) {
func (p *parser) pushRecovery(labels []string, expr any) {
if cap(p.recoveryStack) == len(p.recoveryStack) {
// create new empty slot in the stack
p.recoveryStack = append(p.recoveryStack, nil)
@@ -576,7 +592,7 @@ func (p *parser) pushRecovery(labels []string, expr interface{}) {
p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1]
}
m := make(map[string]interface{}, len(labels))
m := make(map[string]any, len(labels))
for _, fl := range labels {
m[fl] = expr
}
@@ -602,14 +618,19 @@ func (p *parser) print(prefix, s string) string {
return s
}
func (p *parser) printIndent(mark string, s string) string {
return p.print(strings.Repeat(" ", p.depth)+mark, s)
}
func (p *parser) in(s string) string {
res := p.printIndent(">", s)
p.depth++
return p.print(strings.Repeat(" ", p.depth)+">", s)
return res
}
func (p *parser) out(s string) string {
p.depth--
return p.print(strings.Repeat(" ", p.depth)+"<", s)
return p.printIndent("<", s)
}
// {{ end }} ==template==
@@ -703,11 +724,11 @@ func (p *parser) restore(pt savepoint) {
// copies of the state to allow the parser to properly restore the state in
// the case of backtracking.
type Cloner interface {
Clone() interface{}
Clone() any
}
var statePool = &sync.Pool{
New: func() interface{} { return make(storeDict) },
New: func() any { return make(storeDict) },
}
func (sd storeDict) Discard() {
@@ -755,8 +776,8 @@ func (p *parser) sliceFrom(start savepoint) []byte {
return p.data[start.position.offset:p.pt.position.offset]
}
// ==template== {{ if not .Optimize }}
func (p *parser) getMemoized(node interface{}) (resultTuple, bool) {
// ==template== {{ if or .LeftRecursion (not .Optimize) }}
func (p *parser) getMemoized(node any) (resultTuple, bool) {
if len(p.memo) == 0 {
return resultTuple{}, false
}
@@ -768,13 +789,13 @@ func (p *parser) getMemoized(node interface{}) (resultTuple, bool) {
return res, ok
}
func (p *parser) setMemoized(pt savepoint, node interface{}, tuple resultTuple) {
func (p *parser) setMemoized(pt savepoint, node any, tuple resultTuple) {
if p.memo == nil {
p.memo = make(map[int]map[interface{}]resultTuple)
p.memo = make(map[int]map[any]resultTuple)
}
m := p.memo[pt.offset]
if m == nil {
m = make(map[interface{}]resultTuple)
m = make(map[any]resultTuple)
p.memo[pt.offset] = m
}
m[node] = tuple
@@ -789,8 +810,8 @@ func (p *parser) buildRulesTable(g *grammar) {
}
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parse(g *grammar) (val interface{}, err error) {
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parse(g *grammar) (val any, err error) {
if len(g.rules) == 0 {
p.addErr(errNoRule)
return nil, p.errs.err()
@@ -828,7 +849,7 @@ func (p *parser) parse(g *grammar) (val interface{}, err error) {
}
p.read() // advance to first rune
val, ok = p.parseRule(startRule)
val, ok = p.parseRuleWrap(startRule)
if !ok {
if len(*p.errs) == 0 {
// If parsing fails, but no errors have been recorded, the expected values
@@ -869,45 +890,156 @@ func listJoin(list []string, sep string, lastSep string) string {
}
}
func (p *parser) parseRule(rule *rule) (interface{}, bool) {
// ==template== {{ if .LeftRecursion }}
func (p *parser) parseRuleRecursiveLeader(rule *rule) (any, bool) {
result, ok := p.getMemoized(rule)
if ok {
p.restore(result.end)
return result.v, result.b
}
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("recursive " + rule.name))
}
// {{ end }} ==template==
var (
depth = 0
startMark = p.pt
lastResult = resultTuple{nil, false, startMark}
lastErrors = *p.errs
)
for {
// ==template== {{ if or .GlobalState (not .Optimize) }}
lastState := p.cloneState()
// {{ end }} ==template==
p.setMemoized(startMark, rule, lastResult)
val, ok := p.parseRule(rule)
endMark := p.pt
// ==template== {{ if not .Optimize }}
if p.debug {
p.printIndent("RECURSIVE", fmt.Sprintf(
"Rule %s depth %d: %t -> %s",
rule.name, depth, ok, string(p.sliceFrom(startMark))))
}
// {{ end }} ==template==
if (!ok) || (endMark.offset <= lastResult.end.offset && depth != 0) {
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(lastState)
// {{ end }} ==template==
*p.errs = lastErrors
break
}
lastResult = resultTuple{val, ok, endMark}
lastErrors = *p.errs
p.restore(startMark)
depth++
}
p.restore(lastResult.end)
p.setMemoized(startMark, rule, lastResult)
return lastResult.v, lastResult.b
}
func (p *parser) parseRuleRecursiveNoLeader(rule *rule) (any, bool) {
return p.parseRule(rule)
}
// {{ end }} ==template==
// ==template== {{ if not .Optimize }}
func (p *parser) parseRuleMemoize(rule *rule) (any, bool) {
res, ok := p.getMemoized(rule)
if ok {
p.restore(res.end)
return res.v, res.b
}
startMark := p.pt
val, ok := p.parseRule(rule)
p.setMemoized(startMark, rule, resultTuple{val, ok, p.pt})
return val, ok
}
// {{ end }} ==template==
func (p *parser) parseRuleWrap(rule *rule) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRule " + rule.name))
}
if p.memoize {
res, ok := p.getMemoized(rule)
if ok {
p.restore(res.end)
return res.v, res.b
}
}
start := p.pt
// {{ end }} ==template==
p.rstack = append(p.rstack, rule)
p.pushV()
val, ok := p.parseExpr(rule.expr)
p.popV()
p.rstack = p.rstack[:len(p.rstack)-1]
var (
val any
ok bool
// ==template== {{ if not .Optimize }}
startMark = p.pt
// {{ end }} ==template==
)
// ==template== {{ if and .LeftRecursion (not .Optimize) }}
if p.memoize || rule.leftRecursive {
if rule.leader {
val, ok = p.parseRuleRecursiveLeader(rule)
} else if p.memoize && !rule.leftRecursive {
val, ok = p.parseRuleMemoize(rule)
} else {
val, ok = p.parseRuleRecursiveNoLeader(rule)
}
} else {
val, ok = p.parseRule(rule)
}
// {{ else if not .Optimize }}
if p.memoize {
val, ok = p.parseRuleMemoize(rule)
} else {
val, ok = p.parseRule(rule)
}
// {{ else if .LeftRecursion }}
if rule.leftRecursive {
if rule.leader {
val, ok = p.parseRuleRecursiveLeader(rule)
} else {
val, ok = p.parseRuleRecursiveNoLeader(rule)
}
} else {
val, ok = p.parseRule(rule)
}
// {{ else }}
val, ok = p.parseRule(rule)
// {{ end }} ==template==
// ==template== {{ if not .Optimize }}
if ok && p.debug {
p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start)))
}
if p.memoize {
p.setMemoized(start, rule, resultTuple{val, ok, p.pt})
p.printIndent("MATCH", string(p.sliceFrom(startMark)))
}
// {{ end }} ==template==
return val, ok
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
func (p *parser) parseRule(rule *rule) (any, bool) {
p.rstack = append(p.rstack, rule)
p.pushV()
val, ok := p.parseExprWrap(rule.expr)
p.popV()
p.rstack = p.rstack[:len(p.rstack)-1]
return val, ok
}
func (p *parser) parseExprWrap(expr any) (any, bool) {
// ==template== {{ if not .Optimize }}
var pt savepoint
// ==template== {{ if .LeftRecursion }}
isLeftRecusion := p.rstack[len(p.rstack)-1].leftRecursive
if p.memoize && !isLeftRecusion {
// {{ else }}
if p.memoize {
// {{ end }} ==template==
res, ok := p.getMemoized(expr)
if ok {
p.restore(res.end)
@@ -917,13 +1049,28 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
}
// {{ end }} ==template==
val, ok := p.parseExpr(expr)
// ==template== {{ if not .Optimize }}
// ==template== {{ if .LeftRecursion }}
if p.memoize && !isLeftRecusion {
// {{ else }}
if p.memoize {
// {{ end }} ==template==
p.setMemoized(pt, expr, resultTuple{val, ok, p.pt})
}
// {{ end }} ==template==
return val, ok
}
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseExpr(expr any) (any, bool) {
p.ExprCnt++
if p.ExprCnt > p.maxExprCnt {
panic(errMaxExprCnt)
}
var val interface{}
var val any
var ok bool
switch expr := expr.(type) {
case *actionExpr:
@@ -967,15 +1114,10 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
default:
panic(fmt.Sprintf("unknown expression type %T", expr))
}
// ==template== {{ if not .Optimize }}
if p.memoize {
p.setMemoized(pt, expr, resultTuple{val, ok, p.pt})
}
// {{ end }} ==template==
return val, ok
}
func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
func (p *parser) parseActionExpr(act *actionExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseActionExpr"))
@@ -983,7 +1125,7 @@ func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
// {{ end }} ==template==
start := p.pt
val, ok := p.parseExpr(act.expr)
val, ok := p.parseExprWrap(act.expr)
if ok {
p.cur.pos = start.position
p.cur.text = p.sliceFrom(start)
@@ -1002,13 +1144,13 @@ func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
}
// ==template== {{ if not .Optimize }}
if ok && p.debug {
p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start)))
p.printIndent("MATCH", string(p.sliceFrom(start)))
}
// {{ end }} ==template==
return val, ok
}
func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) {
func (p *parser) parseAndCodeExpr(and *andCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAndCodeExpr"))
@@ -1030,7 +1172,7 @@ func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) {
return nil, ok
}
func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
func (p *parser) parseAndExpr(and *andExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAndExpr"))
@@ -1042,7 +1184,7 @@ func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
state := p.cloneState()
// {{ end }} ==template==
p.pushV()
_, ok := p.parseExpr(and.expr)
_, ok := p.parseExprWrap(and.expr)
p.popV()
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(state)
@@ -1052,7 +1194,7 @@ func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
return nil, ok
}
func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) {
func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAnyMatcher"))
@@ -1070,8 +1212,8 @@ func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) {
return p.sliceFrom(start), true
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) {
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseCharClassMatcher"))
@@ -1170,13 +1312,13 @@ func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) {
// {{ end }} ==template==
func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
func (p *parser) parseChoiceExpr(ch *choiceExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseChoiceExpr"))
}
// {{ end }} ==template==
for altI, alt := range ch.alternatives {
// dummy assignment to prevent compile error if optimized
_ = altI
@@ -1186,7 +1328,7 @@ func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, ok := p.parseExpr(alt)
val, ok := p.parseExprWrap(alt)
p.popV()
if ok {
// ==template== {{ if not .Optimize }}
@@ -1204,7 +1346,7 @@ func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
return nil, false
}
func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseLabeledExpr"))
@@ -1212,7 +1354,7 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, ok := p.parseExpr(lab.expr)
val, ok := p.parseExprWrap(lab.expr)
p.popV()
if ok && lab.label != "" {
m := p.vstack[len(p.vstack)-1]
@@ -1221,7 +1363,7 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
return val, ok
}
func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseLitMatcher"))
@@ -1245,7 +1387,7 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
return p.sliceFrom(start), true
}
func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) {
func (p *parser) parseNotCodeExpr(not *notCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseNotCodeExpr"))
@@ -1267,7 +1409,7 @@ func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) {
return nil, !ok
}
func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
func (p *parser) parseNotExpr(not *notExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseNotExpr"))
@@ -1280,7 +1422,7 @@ func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
p.maxFailInvertExpected = !p.maxFailInvertExpected
_, ok := p.parseExpr(not.expr)
_, ok := p.parseExprWrap(not.expr)
p.maxFailInvertExpected = !p.maxFailInvertExpected
p.popV()
// ==template== {{ if or .GlobalState (not .Optimize) }}
@@ -1291,18 +1433,18 @@ func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
return nil, !ok
}
func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) {
func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseOneOrMoreExpr"))
}
// {{ end }} ==template==
var vals []interface{}
var vals []any
for {
p.pushV()
val, ok := p.parseExpr(expr.expr)
val, ok := p.parseExprWrap(expr.expr)
p.popV()
if !ok {
if len(vals) == 0 {
@@ -1315,7 +1457,7 @@ func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) {
}
}
func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) {
func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")"))
@@ -1324,13 +1466,13 @@ func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushRecovery(recover.failureLabel, recover.recoverExpr)
val, ok := p.parseExpr(recover.expr)
val, ok := p.parseExprWrap(recover.expr)
p.popRecovery()
return val, ok
}
func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) {
func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRuleRefExpr " + ref.name))
@@ -1346,24 +1488,24 @@ func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) {
p.addErr(fmt.Errorf("undefined rule: %s", ref.name))
return nil, false
}
return p.parseRule(rule)
return p.parseRuleWrap(rule)
}
func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseSeqExpr"))
}
// {{ end }} ==template==
vals := make([]interface{}, 0, len(seq.exprs))
vals := make([]any, 0, len(seq.exprs))
pt := p.pt
// ==template== {{ if or .GlobalState (not .Optimize) }}
state := p.cloneState()
// {{ end }} ==template==
for _, expr := range seq.exprs {
val, ok := p.parseExpr(expr)
val, ok := p.parseExprWrap(expr)
if !ok {
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(state)
@@ -1378,7 +1520,7 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
// ==template== {{ if or .GlobalState (not .Optimize) }}
func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) {
func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseStateCodeExpr"))
@@ -1394,7 +1536,7 @@ func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) {
// {{ end }} ==template==
func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseThrowExpr"))
@@ -1404,7 +1546,7 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
for i := len(p.recoveryStack) - 1; i >= 0; i-- {
if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok {
if val, ok := p.parseExpr(recoverExpr); ok {
if val, ok := p.parseExprWrap(recoverExpr); ok {
return val, ok
}
}
@@ -1413,18 +1555,18 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
return nil, false
}
func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) {
func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseZeroOrMoreExpr"))
}
// {{ end }} ==template==
var vals []interface{}
var vals []any
for {
p.pushV()
val, ok := p.parseExpr(expr.expr)
val, ok := p.parseExprWrap(expr.expr)
p.popV()
if !ok {
return vals, true
@@ -1433,7 +1575,7 @@ func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) {
}
}
func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) {
func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseZeroOrOneExpr"))
@@ -1441,7 +1583,7 @@ func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, _ := p.parseExpr(expr.expr)
val, _ := p.parseExprWrap(expr.expr)
p.popV()
// whether it matched or not, consider it a match
return val, true
+132
View File
@@ -0,0 +1,132 @@
package builder
import (
"errors"
"fmt"
"github.com/mna/pigeon/ast"
)
var (
// ErrNoLeader is no leader error.
ErrNoLeader = errors.New(
"SCC has no leadership candidate (no element is included in all cycles)")
// ErrHaveLeftRecursion is recursion error.
ErrHaveLeftRecursion = errors.New("grammar contains left recursion")
)
// PrepareGrammar evaluates parameters associated with left recursion.
func PrepareGrammar(grammar *ast.Grammar) (bool, error) {
mapRules := make(map[string]*ast.Rule, len(grammar.Rules))
for _, rule := range grammar.Rules {
mapRules[rule.Name.Val] = rule
}
ComputeNullables(mapRules)
haveLeftRecursion, err := ComputeLeftRecursives(mapRules)
if err != nil {
return false, fmt.Errorf("error compute left recursive: %w", err)
}
return haveLeftRecursion, nil
}
// ComputeNullables evaluates nullable nodes.
func ComputeNullables(rules map[string]*ast.Rule) {
// Compute which rules in a grammar are nullable
for _, rule := range rules {
rule.NullableVisit(rules)
}
}
func findLeader(
graph map[string]map[string]struct{}, scc map[string]struct{},
) (string, error) {
// Try to find a leader such that all cycles go through it.
leaders := make(map[string]struct{}, len(scc))
for k := range scc {
leaders[k] = struct{}{}
}
for start := range scc {
cycles, err := FindCyclesInSCC(graph, scc, start)
if err != nil {
return "", fmt.Errorf("error find cycles: %w", err)
}
for _, cycle := range cycles {
mapCycle := make(map[string]struct{}, len(cycle))
for _, k := range cycle {
mapCycle[k] = struct{}{}
}
for k := range scc {
if _, okCycle := mapCycle[k]; !okCycle {
delete(leaders, k)
}
}
if len(leaders) == 0 {
return "", ErrNoLeader
}
}
}
// Pick an arbitrary leader from the candidates.
var leader string
for k := range leaders {
leader = k // The only element.
break
}
return leader, nil
}
// ComputeLeftRecursives evaluates left recursion.
func ComputeLeftRecursives(rules map[string]*ast.Rule) (bool, error) {
graph := MakeFirstGraph(rules)
vertices := make([]string, 0, len(graph))
haveLeftRecursion := false
for k := range graph {
vertices = append(vertices, k)
}
sccs := StronglyConnectedComponents(vertices, graph)
for _, scc := range sccs {
if len(scc) > 1 {
for name := range scc {
rules[name].LeftRecursive = true
haveLeftRecursion = true
}
leader, err := findLeader(graph, scc)
if err != nil {
return false, fmt.Errorf("error find leader %v: %w", scc, err)
}
rules[leader].Leader = true
} else {
var name string
for k := range scc {
name = k // The only element.
break
}
if _, ok := graph[name][name]; ok {
rules[name].LeftRecursive = true
rules[name].Leader = true
haveLeftRecursion = true
}
}
}
return haveLeftRecursion, nil
}
// MakeFirstGraph compute the graph of left-invocations.
// There's an edge from A to B if A may invoke B at its initial position.
// Note that this requires the nullable flags to have been computed.
func MakeFirstGraph(rules map[string]*ast.Rule) map[string]map[string]struct{} {
graph := make(map[string]map[string]struct{})
vertices := make(map[string]struct{})
for rulename, rule := range rules {
names := rule.InitialNames()
graph[rulename] = names
for name := range names {
vertices[name] = struct{}{}
}
}
for vertex := range vertices {
if _, ok := graph[vertex]; !ok {
graph[vertex] = make(map[string]struct{})
}
}
return graph
}
+151
View File
@@ -0,0 +1,151 @@
package builder
import (
"errors"
"fmt"
)
// ErrInvalidParameters is parameters error.
var ErrInvalidParameters = errors.New("invalid parameters passed to function")
func min(a1 int, a2 int) int {
if a1 <= a2 {
return a1
}
return a2
}
// StronglyConnectedComponents compute strongly сonnected сomponents of a graph.
// Tarjan's strongly connected components algorithm.
func StronglyConnectedComponents(
vertices []string, edges map[string]map[string]struct{},
) []map[string]struct{} {
// Tarjan's strongly connected components algorithm
var (
identified = map[string]struct{}{}
stack = []string{}
index = map[string]int{}
lowlink = map[string]int{}
dfs func(v string) []map[string]struct{}
)
dfs = func(vertex string) []map[string]struct{} {
index[vertex] = len(stack)
stack = append(stack, vertex)
lowlink[vertex] = index[vertex]
sccs := []map[string]struct{}{}
for w := range edges[vertex] {
if _, ok := index[w]; !ok {
sccs = append(sccs, dfs(w)...)
lowlink[vertex] = min(lowlink[vertex], lowlink[w])
} else if _, ok := identified[w]; !ok {
lowlink[vertex] = min(lowlink[vertex], lowlink[w])
}
}
if lowlink[vertex] == index[vertex] {
scc := map[string]struct{}{}
for _, v := range stack[index[vertex]:] {
scc[v] = struct{}{}
}
stack = stack[:index[vertex]]
for v := range scc {
identified[v] = struct{}{}
}
sccs = append(sccs, scc)
}
return sccs
}
sccs := []map[string]struct{}{}
for _, v := range vertices {
if _, ok := index[v]; !ok {
sccs = append(sccs, dfs(v)...)
}
}
return sccs
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func reduceGraph(
graph map[string]map[string]struct{}, scc map[string]struct{},
) map[string]map[string]struct{} {
reduceGraph := map[string]map[string]struct{}{}
for src, dsts := range graph {
if _, ok := scc[src]; !ok {
continue
}
reduceGraph[src] = map[string]struct{}{}
for dst := range dsts {
if _, ok := scc[dst]; !ok {
continue
}
reduceGraph[src][dst] = struct{}{}
}
}
return reduceGraph
}
// FindCyclesInSCC find cycles in SCC emanating from start.
// Yields lists of the form ['A', 'B', 'C', 'A'], which means there's
// a path from A -> B -> C -> A. The first item is always the start
// argument, but the last item may be another element, e.g. ['A',
// 'B', 'C', 'B'] means there's a path from A to B and there's a
// cycle from B to C and back.
func FindCyclesInSCC(
graph map[string]map[string]struct{}, scc map[string]struct{}, start string,
) ([][]string, error) {
// Basic input checks.
if _, ok := scc[start]; !ok {
return nil, fmt.Errorf(
"%w: scc %v does not contain %q", ErrInvalidParameters, scc, start)
}
extravertices := []string{}
for k := range scc {
if _, ok := graph[k]; !ok {
extravertices = append(extravertices, k)
}
}
if len(extravertices) != 0 {
return nil, fmt.Errorf(
"%w: graph does not contain scc. %v",
ErrInvalidParameters, extravertices)
}
// Reduce the graph to nodes in the SCC.
graph = reduceGraph(graph, scc)
if _, ok := graph[start]; !ok {
return nil, fmt.Errorf(
"%w: graph %v does not contain %q",
ErrInvalidParameters, graph, start)
}
// Recursive helper that yields cycles.
var dfs func(node string, path []string) [][]string
dfs = func(node string, path []string) [][]string {
ret := [][]string{}
if contains(path, node) {
t := make([]string, 0, len(path)+1)
t = append(t, path...)
t = append(t, node)
ret = append(ret, t)
return ret
}
path = append(path, node) // TODO: Make this not quadratic.
for child := range graph[node] {
ret = append(ret, dfs(child, path)...)
}
return ret
}
return dfs(start, []string{}), nil
}
+278 -136
View File
@@ -1,5 +1,6 @@
//go:generate go run ../bootstrap/cmd/static_code_generator/main.go -- $GOFILE generated_$GOFILE staticCode
//go:build static_code
// +build static_code
package builder
@@ -9,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"sort"
@@ -80,18 +80,17 @@ func Entrypoint(ruleName string) Option {
//
// Example usage:
//
// input := "input"
// stats := Stats{}
// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match"))
// if err != nil {
// log.Panicln(err)
// }
// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ")
// if err != nil {
// log.Panicln(err)
// }
// fmt.Println(string(b))
//
// input := "input"
// stats := Stats{}
// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match"))
// if err != nil {
// log.Panicln(err)
// }
// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ")
// if err != nil {
// log.Panicln(err)
// }
// fmt.Println(string(b))
func Statistics(stats *Stats, choiceNoMatch string) Option {
return func(p *parser) Option {
oldStats := p.Stats
@@ -163,7 +162,7 @@ func Recover(b bool) Option {
// GlobalStore creates an Option to set a key to a certain value in
// the globalStore.
func GlobalStore(key string, value interface{}) Option {
func GlobalStore(key string, value any) Option {
return func(p *parser) Option {
old := p.cur.globalStore[key]
p.cur.globalStore[key] = value
@@ -175,7 +174,7 @@ func GlobalStore(key string, value interface{}) Option {
// InitState creates an Option to set a key to a certain value in
// the global "state" store.
func InitState(key string, value interface{}) Option {
func InitState(key string, value any) Option {
return func(p *parser) Option {
old := p.cur.state[key]
p.cur.state[key] = value
@@ -186,7 +185,7 @@ func InitState(key string, value interface{}) Option {
// {{ end }} ==template==
// ParseFile parses the file identified by filename.
func ParseFile(filename string, opts ...Option) (i interface{}, err error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
func ParseFile(filename string, opts ...Option) (i any, err error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
f, err := os.Open(filename)
if err != nil {
return nil, err
@@ -201,8 +200,8 @@ func ParseFile(filename string, opts ...Option) (i interface{}, err error) { //{
// ParseReader parses the data from r using filename as information in the
// error messages.
func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
b, err := ioutil.ReadAll(r)
func ParseReader(filename string, r io.Reader, opts ...Option) (any, error) { //{{ if .Nolint }} nolint: deadcode {{else}} ==template== {{ end }}
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
@@ -212,7 +211,7 @@ func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, err
// Parse parses the data from b using filename as information in the
// error messages.
func Parse(filename string, b []byte, opts ...Option) (interface{}, error) {
func Parse(filename string, b []byte, opts ...Option) (any, error) {
return newParser(filename, b, opts...).parse(g)
}
@@ -254,77 +253,84 @@ type current struct {
globalStore storeDict
}
type storeDict map[string]interface{}
type storeDict map[string]any
// the AST types...
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type grammar struct {
pos position
rules []*rule
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type rule struct {
pos position
name string
displayName string
expr interface{}
expr any
// ==template== {{ if .LeftRecursion }}
leader bool
leftRecursive bool
// {{ end }} ==template==
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type choiceExpr struct {
pos position
alternatives []interface{}
alternatives []any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type actionExpr struct {
pos position
expr interface{}
run func(*parser) (interface{}, error)
expr any
run func(*parser) (any, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type recoveryExpr struct {
pos position
expr interface{}
recoverExpr interface{}
expr any
recoverExpr any
failureLabel []string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type seqExpr struct {
pos position
exprs []interface{}
exprs []any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type throwExpr struct {
pos position
label string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type labeledExpr struct {
pos position
label string
expr interface{}
expr any
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type expr struct {
pos position
expr interface{}
expr any
}
type andExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type notExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type zeroOrOneExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type zeroOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type oneOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type (
andExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
notExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
zeroOrOneExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
zeroOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
oneOrMoreExpr expr //{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
)
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type ruleRefExpr struct {
pos position
name string
@@ -332,7 +338,7 @@ type ruleRefExpr struct {
// ==template== {{ if or .GlobalState (not .Optimize) }}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type stateCodeExpr struct {
pos position
run func(*parser) error
@@ -340,19 +346,19 @@ type stateCodeExpr struct {
// {{ end }} ==template==
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type andCodeExpr struct {
pos position
run func(*parser) (bool, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type notCodeExpr struct {
pos position
run func(*parser) (bool, error)
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type litMatcher struct {
pos position
val string
@@ -360,7 +366,7 @@ type litMatcher struct {
want string
}
//{{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck {{else}} ==template== {{ end }}
type charClassMatcher struct {
pos position
val string
@@ -474,14 +480,14 @@ func (p *parser) setOptions(opts []Option) {
}
}
//{{ if .Nolint }} nolint: structcheck,deadcode {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: structcheck,deadcode {{else}} ==template== {{ end }}
type resultTuple struct {
v interface{}
v any
b bool
end savepoint
}
//{{ if .Nolint }} nolint: varcheck {{else}} ==template== {{ end }}
// {{ if .Nolint }} nolint: varcheck {{else}} ==template== {{ end }}
const choiceNoMatch = -1
// Stats stores some statistics, gathered during parsing
@@ -506,7 +512,15 @@ type Stats struct {
ChoiceAltCnt map[string]map[string]int
}
//{{ if .Nolint }} nolint: structcheck,maligned {{else}} ==template== {{ end }}
// ==template== {{ if .LeftRecursion }}
type ruleWithExpsStack struct {
rule *rule
estack []any
}
// {{ end }} ==template==
// {{ if .Nolint }} nolint: structcheck,maligned {{else}} ==template== {{ end }}
type parser struct {
filename string
pt savepoint
@@ -521,15 +535,17 @@ type parser struct {
debug bool
memoize bool
// {{ end }} ==template==
// ==template== {{ if or .LeftRecursion (not .Optimize) }}
// memoization table for the packrat algorithm:
// map[offset in source] map[expression or rule] {value, match}
memo map[int]map[interface{}]resultTuple
memo map[int]map[any]resultTuple
// {{ end }} ==template==
// rules table, maps the rule identifier to the rule node
rules map[string]*rule
// variables stack, map of label to value
vstack []map[string]interface{}
vstack []map[string]any
// rule stack, allows identification of the current rule in errors
rstack []*rule
@@ -549,7 +565,7 @@ type parser struct {
choiceNoMatch string
// recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse
recoveryStack []map[string]interface{}
recoveryStack []map[string]any
}
// push a variable set on the vstack.
@@ -569,7 +585,7 @@ func (p *parser) pushV() {
return
}
m = make(map[string]interface{})
m = make(map[string]any)
p.vstack[len(p.vstack)-1] = m
}
@@ -585,7 +601,7 @@ func (p *parser) popV() {
}
// push a recovery expression with its labels to the recoveryStack
func (p *parser) pushRecovery(labels []string, expr interface{}) {
func (p *parser) pushRecovery(labels []string, expr any) {
if cap(p.recoveryStack) == len(p.recoveryStack) {
// create new empty slot in the stack
p.recoveryStack = append(p.recoveryStack, nil)
@@ -594,7 +610,7 @@ func (p *parser) pushRecovery(labels []string, expr interface{}) {
p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1]
}
m := make(map[string]interface{}, len(labels))
m := make(map[string]any, len(labels))
for _, fl := range labels {
m[fl] = expr
}
@@ -620,14 +636,19 @@ func (p *parser) print(prefix, s string) string {
return s
}
func (p *parser) printIndent(mark string, s string) string {
return p.print(strings.Repeat(" ", p.depth)+mark, s)
}
func (p *parser) in(s string) string {
res := p.printIndent(">", s)
p.depth++
return p.print(strings.Repeat(" ", p.depth)+">", s)
return res
}
func (p *parser) out(s string) string {
p.depth--
return p.print(strings.Repeat(" ", p.depth)+"<", s)
return p.printIndent("<", s)
}
// {{ end }} ==template==
@@ -721,11 +742,11 @@ func (p *parser) restore(pt savepoint) {
// copies of the state to allow the parser to properly restore the state in
// the case of backtracking.
type Cloner interface {
Clone() interface{}
Clone() any
}
var statePool = &sync.Pool{
New: func() interface{} { return make(storeDict) },
New: func() any { return make(storeDict) },
}
func (sd storeDict) Discard() {
@@ -773,8 +794,8 @@ func (p *parser) sliceFrom(start savepoint) []byte {
return p.data[start.position.offset:p.pt.position.offset]
}
// ==template== {{ if not .Optimize }}
func (p *parser) getMemoized(node interface{}) (resultTuple, bool) {
// ==template== {{ if or .LeftRecursion (not .Optimize) }}
func (p *parser) getMemoized(node any) (resultTuple, bool) {
if len(p.memo) == 0 {
return resultTuple{}, false
}
@@ -786,13 +807,13 @@ func (p *parser) getMemoized(node interface{}) (resultTuple, bool) {
return res, ok
}
func (p *parser) setMemoized(pt savepoint, node interface{}, tuple resultTuple) {
func (p *parser) setMemoized(pt savepoint, node any, tuple resultTuple) {
if p.memo == nil {
p.memo = make(map[int]map[interface{}]resultTuple)
p.memo = make(map[int]map[any]resultTuple)
}
m := p.memo[pt.offset]
if m == nil {
m = make(map[interface{}]resultTuple)
m = make(map[any]resultTuple)
p.memo[pt.offset] = m
}
m[node] = tuple
@@ -807,8 +828,8 @@ func (p *parser) buildRulesTable(g *grammar) {
}
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parse(g *grammar) (val interface{}, err error) {
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parse(g *grammar) (val any, err error) {
if len(g.rules) == 0 {
p.addErr(errNoRule)
return nil, p.errs.err()
@@ -846,7 +867,7 @@ func (p *parser) parse(g *grammar) (val interface{}, err error) {
}
p.read() // advance to first rune
val, ok = p.parseRule(startRule)
val, ok = p.parseRuleWrap(startRule)
if !ok {
if len(*p.errs) == 0 {
// If parsing fails, but no errors have been recorded, the expected values
@@ -887,45 +908,156 @@ func listJoin(list []string, sep string, lastSep string) string {
}
}
func (p *parser) parseRule(rule *rule) (interface{}, bool) {
// ==template== {{ if .LeftRecursion }}
func (p *parser) parseRuleRecursiveLeader(rule *rule) (any, bool) {
result, ok := p.getMemoized(rule)
if ok {
p.restore(result.end)
return result.v, result.b
}
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("recursive " + rule.name))
}
// {{ end }} ==template==
var (
depth = 0
startMark = p.pt
lastResult = resultTuple{nil, false, startMark}
lastErrors = *p.errs
)
for {
// ==template== {{ if or .GlobalState (not .Optimize) }}
lastState := p.cloneState()
// {{ end }} ==template==
p.setMemoized(startMark, rule, lastResult)
val, ok := p.parseRule(rule)
endMark := p.pt
// ==template== {{ if not .Optimize }}
if p.debug {
p.printIndent("RECURSIVE", fmt.Sprintf(
"Rule %s depth %d: %t -> %s",
rule.name, depth, ok, string(p.sliceFrom(startMark))))
}
// {{ end }} ==template==
if (!ok) || (endMark.offset <= lastResult.end.offset && depth != 0) {
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(lastState)
// {{ end }} ==template==
*p.errs = lastErrors
break
}
lastResult = resultTuple{val, ok, endMark}
lastErrors = *p.errs
p.restore(startMark)
depth++
}
p.restore(lastResult.end)
p.setMemoized(startMark, rule, lastResult)
return lastResult.v, lastResult.b
}
func (p *parser) parseRuleRecursiveNoLeader(rule *rule) (any, bool) {
return p.parseRule(rule)
}
// {{ end }} ==template==
// ==template== {{ if not .Optimize }}
func (p *parser) parseRuleMemoize(rule *rule) (any, bool) {
res, ok := p.getMemoized(rule)
if ok {
p.restore(res.end)
return res.v, res.b
}
startMark := p.pt
val, ok := p.parseRule(rule)
p.setMemoized(startMark, rule, resultTuple{val, ok, p.pt})
return val, ok
}
// {{ end }} ==template==
func (p *parser) parseRuleWrap(rule *rule) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRule " + rule.name))
}
if p.memoize {
res, ok := p.getMemoized(rule)
if ok {
p.restore(res.end)
return res.v, res.b
}
}
start := p.pt
// {{ end }} ==template==
p.rstack = append(p.rstack, rule)
p.pushV()
val, ok := p.parseExpr(rule.expr)
p.popV()
p.rstack = p.rstack[:len(p.rstack)-1]
var (
val any
ok bool
// ==template== {{ if not .Optimize }}
startMark = p.pt
// {{ end }} ==template==
)
// ==template== {{ if and .LeftRecursion (not .Optimize) }}
if p.memoize || rule.leftRecursive {
if rule.leader {
val, ok = p.parseRuleRecursiveLeader(rule)
} else if p.memoize && !rule.leftRecursive {
val, ok = p.parseRuleMemoize(rule)
} else {
val, ok = p.parseRuleRecursiveNoLeader(rule)
}
} else {
val, ok = p.parseRule(rule)
}
// {{ else if not .Optimize }}
if p.memoize {
val, ok = p.parseRuleMemoize(rule)
} else {
val, ok = p.parseRule(rule)
}
// {{ else if .LeftRecursion }}
if rule.leftRecursive {
if rule.leader {
val, ok = p.parseRuleRecursiveLeader(rule)
} else {
val, ok = p.parseRuleRecursiveNoLeader(rule)
}
} else {
val, ok = p.parseRule(rule)
}
// {{ else }}
val, ok = p.parseRule(rule)
// {{ end }} ==template==
// ==template== {{ if not .Optimize }}
if ok && p.debug {
p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start)))
}
if p.memoize {
p.setMemoized(start, rule, resultTuple{val, ok, p.pt})
p.printIndent("MATCH", string(p.sliceFrom(startMark)))
}
// {{ end }} ==template==
return val, ok
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
func (p *parser) parseRule(rule *rule) (any, bool) {
p.rstack = append(p.rstack, rule)
p.pushV()
val, ok := p.parseExprWrap(rule.expr)
p.popV()
p.rstack = p.rstack[:len(p.rstack)-1]
return val, ok
}
func (p *parser) parseExprWrap(expr any) (any, bool) {
// ==template== {{ if not .Optimize }}
var pt savepoint
// ==template== {{ if .LeftRecursion }}
isLeftRecusion := p.rstack[len(p.rstack)-1].leftRecursive
if p.memoize && !isLeftRecusion {
// {{ else }}
if p.memoize {
// {{ end }} ==template==
res, ok := p.getMemoized(expr)
if ok {
p.restore(res.end)
@@ -935,13 +1067,28 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
}
// {{ end }} ==template==
val, ok := p.parseExpr(expr)
// ==template== {{ if not .Optimize }}
// ==template== {{ if .LeftRecursion }}
if p.memoize && !isLeftRecusion {
// {{ else }}
if p.memoize {
// {{ end }} ==template==
p.setMemoized(pt, expr, resultTuple{val, ok, p.pt})
}
// {{ end }} ==template==
return val, ok
}
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseExpr(expr any) (any, bool) {
p.ExprCnt++
if p.ExprCnt > p.maxExprCnt {
panic(errMaxExprCnt)
}
var val interface{}
var val any
var ok bool
switch expr := expr.(type) {
case *actionExpr:
@@ -985,15 +1132,10 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
default:
panic(fmt.Sprintf("unknown expression type %T", expr))
}
// ==template== {{ if not .Optimize }}
if p.memoize {
p.setMemoized(pt, expr, resultTuple{val, ok, p.pt})
}
// {{ end }} ==template==
return val, ok
}
func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
func (p *parser) parseActionExpr(act *actionExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseActionExpr"))
@@ -1001,7 +1143,7 @@ func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
// {{ end }} ==template==
start := p.pt
val, ok := p.parseExpr(act.expr)
val, ok := p.parseExprWrap(act.expr)
if ok {
p.cur.pos = start.position
p.cur.text = p.sliceFrom(start)
@@ -1020,13 +1162,13 @@ func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
}
// ==template== {{ if not .Optimize }}
if ok && p.debug {
p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start)))
p.printIndent("MATCH", string(p.sliceFrom(start)))
}
// {{ end }} ==template==
return val, ok
}
func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) {
func (p *parser) parseAndCodeExpr(and *andCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAndCodeExpr"))
@@ -1048,7 +1190,7 @@ func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) {
return nil, ok
}
func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
func (p *parser) parseAndExpr(and *andExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAndExpr"))
@@ -1060,7 +1202,7 @@ func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
state := p.cloneState()
// {{ end }} ==template==
p.pushV()
_, ok := p.parseExpr(and.expr)
_, ok := p.parseExprWrap(and.expr)
p.popV()
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(state)
@@ -1070,7 +1212,7 @@ func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
return nil, ok
}
func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) {
func (p *parser) parseAnyMatcher(any *anyMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseAnyMatcher"))
@@ -1088,8 +1230,8 @@ func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) {
return p.sliceFrom(start), true
}
//{{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) {
// {{ if .Nolint }} nolint: gocyclo {{else}} ==template== {{ end }}
func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseCharClassMatcher"))
@@ -1188,13 +1330,13 @@ func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) {
// {{ end }} ==template==
func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
func (p *parser) parseChoiceExpr(ch *choiceExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseChoiceExpr"))
}
// {{ end }} ==template==
for altI, alt := range ch.alternatives {
// dummy assignment to prevent compile error if optimized
_ = altI
@@ -1204,7 +1346,7 @@ func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, ok := p.parseExpr(alt)
val, ok := p.parseExprWrap(alt)
p.popV()
if ok {
// ==template== {{ if not .Optimize }}
@@ -1222,7 +1364,7 @@ func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
return nil, false
}
func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
func (p *parser) parseLabeledExpr(lab *labeledExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseLabeledExpr"))
@@ -1230,7 +1372,7 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, ok := p.parseExpr(lab.expr)
val, ok := p.parseExprWrap(lab.expr)
p.popV()
if ok && lab.label != "" {
m := p.vstack[len(p.vstack)-1]
@@ -1239,7 +1381,7 @@ func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) {
return val, ok
}
func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
func (p *parser) parseLitMatcher(lit *litMatcher) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseLitMatcher"))
@@ -1263,7 +1405,7 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
return p.sliceFrom(start), true
}
func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) {
func (p *parser) parseNotCodeExpr(not *notCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseNotCodeExpr"))
@@ -1285,7 +1427,7 @@ func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) {
return nil, !ok
}
func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
func (p *parser) parseNotExpr(not *notExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseNotExpr"))
@@ -1298,7 +1440,7 @@ func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
p.maxFailInvertExpected = !p.maxFailInvertExpected
_, ok := p.parseExpr(not.expr)
_, ok := p.parseExprWrap(not.expr)
p.maxFailInvertExpected = !p.maxFailInvertExpected
p.popV()
// ==template== {{ if or .GlobalState (not .Optimize) }}
@@ -1309,18 +1451,18 @@ func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
return nil, !ok
}
func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) {
func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseOneOrMoreExpr"))
}
// {{ end }} ==template==
var vals []interface{}
var vals []any
for {
p.pushV()
val, ok := p.parseExpr(expr.expr)
val, ok := p.parseExprWrap(expr.expr)
p.popV()
if !ok {
if len(vals) == 0 {
@@ -1333,7 +1475,7 @@ func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) {
}
}
func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) {
func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")"))
@@ -1342,13 +1484,13 @@ func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushRecovery(recover.failureLabel, recover.recoverExpr)
val, ok := p.parseExpr(recover.expr)
val, ok := p.parseExprWrap(recover.expr)
p.popRecovery()
return val, ok
}
func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) {
func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseRuleRefExpr " + ref.name))
@@ -1364,24 +1506,24 @@ func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) {
p.addErr(fmt.Errorf("undefined rule: %s", ref.name))
return nil, false
}
return p.parseRule(rule)
return p.parseRuleWrap(rule)
}
func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
func (p *parser) parseSeqExpr(seq *seqExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseSeqExpr"))
}
// {{ end }} ==template==
vals := make([]interface{}, 0, len(seq.exprs))
vals := make([]any, 0, len(seq.exprs))
pt := p.pt
// ==template== {{ if or .GlobalState (not .Optimize) }}
state := p.cloneState()
// {{ end }} ==template==
for _, expr := range seq.exprs {
val, ok := p.parseExpr(expr)
val, ok := p.parseExprWrap(expr)
if !ok {
// ==template== {{ if or .GlobalState (not .Optimize) }}
p.restoreState(state)
@@ -1396,7 +1538,7 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
// ==template== {{ if or .GlobalState (not .Optimize) }}
func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) {
func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseStateCodeExpr"))
@@ -1412,7 +1554,7 @@ func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) {
// {{ end }} ==template==
func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
func (p *parser) parseThrowExpr(expr *throwExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseThrowExpr"))
@@ -1422,7 +1564,7 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
for i := len(p.recoveryStack) - 1; i >= 0; i-- {
if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok {
if val, ok := p.parseExpr(recoverExpr); ok {
if val, ok := p.parseExprWrap(recoverExpr); ok {
return val, ok
}
}
@@ -1431,18 +1573,18 @@ func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
return nil, false
}
func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) {
func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseZeroOrMoreExpr"))
}
// {{ end }} ==template==
var vals []interface{}
var vals []any
for {
p.pushV()
val, ok := p.parseExpr(expr.expr)
val, ok := p.parseExprWrap(expr.expr)
p.popV()
if !ok {
return vals, true
@@ -1451,7 +1593,7 @@ func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) {
}
}
func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) {
func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (any, bool) {
// ==template== {{ if not .Optimize }}
if p.debug {
defer p.out(p.in("parseZeroOrOneExpr"))
@@ -1459,7 +1601,7 @@ func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) {
// {{ end }} ==template==
p.pushV()
val, _ := p.parseExpr(expr.expr)
val, _ := p.parseExprWrap(expr.expr)
p.popV()
// whether it matched or not, consider it a match
return val, true
+67 -43
View File
@@ -36,7 +36,8 @@ The following options can be specified:
-debug : boolean, print debugging info to stdout (default: false).
-nolint: add '// nolint: ...' comments for generated parser to suppress
warnings by gometalinter (https://github.com/alecthomas/gometalinter).
warnings by gometalinter (https://github.com/alecthomas/gometalinter) or
golangci-lint (https://golangci-lint.run/).
-no-recover : boolean, if set, do not recover from a panic. Useful
to access the panic stack when debugging, otherwise the panic
@@ -89,6 +90,12 @@ The following options can be specified:
necessary if the -optimize-parser flag is set, as some rules may be optimized
out of the resulting parser.
-support-left-recursion : boolean, (EXPERIMENTAL FEATURE) if set, add support
for left recursion rules, including those with indirect recursion
(default: false).
E.g.:
expr = expr '*' term / expr '+' term
If the code blocks in the grammar (see below, section "Code block") are golint-
and go vet-compliant, then the resulting generated code will also be golint-
and go vet-compliant.
@@ -171,7 +178,7 @@ on the following:
For terminals (character and string literals, character classes and
the any matcher), the value is []byte. E.g.:
Rule = label:'a' { // label is []byte }
Rule = label:'a' { // label is []byte }
For predicates (& and !), the value is always nil. E.g.:
Rule = label:&'a' { // label is nil }
@@ -179,12 +186,12 @@ For predicates (& and !), the value is always nil. E.g.:
For a sequence, the value is a slice of empty interfaces, one for each
expression value in the sequence. The underlying types of each value
in the slice follow the same rules described here, recursively. E.g.:
Rule = label:('a' 'b') { // label is []interface{} }
Rule = label:('a' 'b') { // label is []any }
For a repetition (+ and *), the value is a slice of empty interfaces, one for
each repetition. The underlying types of each value in the slice follow
the same rules described here, recursively. E.g.:
Rule = label:[a-z]+ { // label is []interface{} }
Rule = label:[a-z]+ { // label is []any }
For a choice expression, the value is that of the matching choice. E.g.:
Rule = label:('a' / 'b') { // label is []byte }
@@ -293,8 +300,8 @@ clause). E.g.:
Action code blocks are code blocks declared after an expression in a rule.
Those code blocks are turned into a method on the "*current" type in the
generated source code. The method receives any labeled expression's value
as argument (as interface{}) and must return two values, the first being
the value of the expression (an interface{}), and the second an error.
as argument (as any) and must return two values, the first being
the value of the expression (an any), and the second an error.
If a non-nil error is returned, it is added to the list of errors that the
parser will return. E.g.:
RuleA = "A"+ {
@@ -306,7 +313,7 @@ parser will return. E.g.:
Predicate code blocks are code blocks declared immediately after the and "&"
or the not "!" operators. Like action code blocks, predicate code blocks
are turned into a method on the "*current" type in the generated source code.
The method receives any labeled expression's value as argument (as interface{})
The method receives any labeled expression's value as argument (as any)
and must return two opt, the first being a bool and the second an error.
If a non-nil error is returned, it is added to the list of errors that the
parser will return. E.g.:
@@ -320,18 +327,18 @@ modify values in the global "state" store (see below).
State change code blocks are turned into a method on the "*current" type
in the generated source code.
The method is passed any labeled expression's value as an argument (of type
interface{}) and must return a value of type error.
any) and must return a value of type error.
If a non-nil error is returned, it is added to the list of errors that the
parser will return, note that the parser does NOT backtrack if a non-nil
error is returned.
E.g:
Rule = [a] #{
c.state["a"]++
if c.state["a"] > 5 {
return fmt.Errorf("we have seen more than 5 a's") // parser will not backtrack
}
return nil
}
Rule = [a] #{
c.state["a"]++
if c.state["a"] > 5 {
return fmt.Errorf("we have seen more than 5 a's") // parser will not backtrack
}
return nil
}
The "*current" type is a struct that provides four useful fields that can be
accessed in action, state change, and predicate code blocks: "pos", "text",
"state" and "globalStore".
@@ -345,7 +352,7 @@ The "text" field is the slice of bytes of the current match. It is empty
in a predicate code block.
The "state" field is a global store, with backtrack support, of type
"map[string]interface{}". The values in the store are tied to the parser's
"map[string]any". The values in the store are tied to the parser's
backtracking, in particular if a rule fails to match then all updates to the
state that occurred in the process of matching the rule are rolled back. For a
key-value store that is not tied to the parser's backtracking, see the
@@ -369,7 +376,7 @@ IMPORTANT:
copy functionality may be used in the "Clone" method
(e.g. https://github.com/mitchellh/copystructure).
The "globalStore" field is a global store of type "map[string]interface{}",
The "globalStore" field is a global store of type "map[string]any",
which allows to store arbitrary values, which are available in action and
predicate code blocks for read as well as write access.
It is important to notice, that the global store is completely independent from
@@ -382,6 +389,23 @@ of pigeon and should not be used nor modified. Those keys are treated as
internal implementation details and therefore there are no guarantees given in
regards of API stability.
Left recursion
With options -support-left-recursion pigeon supports left recursion. E.g.:
expr = expr '*' term
Supports indirect recursion:
A = B / D
B = A / C
The implementation is based on the [Left-recursive PEG Grammars][9] article that
links to [Left Recursion in Parsing Expression Grammars][10] and
[Packrat Parsers Can Support Left Recursion][11] papers.
References:
[9]: https://medium.com/@gvanrossum_83706/left-recursive-peg-grammars-65dab3c580e1
[10]: https://arxiv.org/pdf/1207.0443.pdf
[11]: http://web.cs.ucla.edu/~todd/research/pepm08.pdf
Failure labels, throw and recover
pigeon supports an extension of the classical PEG syntax called failure labels,
@@ -430,13 +454,13 @@ Using the generated parser
The parser generated by pigeon exports a few symbols so that it can be used
as a package with public functions to parse input text. The exported API is:
- Parse(string, []byte, ...Option) (interface{}, error)
- ParseFile(string, ...Option) (interface{}, error)
- ParseReader(string, io.Reader, ...Option) (interface{}, error)
- Parse(string, []byte, ...Option) (any, error)
- ParseFile(string, ...Option) (any, error)
- ParseReader(string, io.Reader, ...Option) (any, error)
- AllowInvalidUTF8(bool) Option
- Debug(bool) Option
- Entrypoint(string) Option
- GlobalStore(string, interface{}) Option
- GlobalStore(string, any) Option
- MaxExpressions(uint64) Option
- Memoize(bool) Option
- Recover(bool) Option
@@ -549,32 +573,32 @@ as the Go 1 compatibility [5]. The following lists what part of the
current pigeon code falls under that guarantee (features may be added in
the future):
- The pigeon command-line flags and arguments: those will not be removed
and will maintain the same semantics.
- The pigeon command-line flags and arguments: those will not be removed
and will maintain the same semantics.
- The explicitly exported API generated by pigeon. See [6] for the
documentation of this API on a generated parser.
- The explicitly exported API generated by pigeon. See [6] for the
documentation of this API on a generated parser.
- The PEG syntax, as documented above.
- The PEG syntax, as documented above.
- The code blocks (except the initializer) will always be generated as
methods on the *current type, and this type is guaranteed to have
the fields pos (type position) and text (type []byte). There are no
guarantees on other fields and methods of this type.
- The code blocks (except the initializer) will always be generated as
methods on the *current type, and this type is guaranteed to have
the fields pos (type position) and text (type []byte). There are no
guarantees on other fields and methods of this type.
- The position type will always have the fields line, col and offset,
all defined as int. There are no guarantees on other fields and methods
of this type.
- The position type will always have the fields line, col and offset,
all defined as int. There are no guarantees on other fields and methods
of this type.
- The type of the error value returned by the Parse* functions, when
not nil, will always be errList defined as a []error. There are no
guarantees on methods of this type, other than the fact it implements the
error interface.
- The type of the error value returned by the Parse* functions, when
not nil, will always be errList defined as a []error. There are no
guarantees on methods of this type, other than the fact it implements the
error interface.
- Individual errors in the errList will always be of type *parserError,
and this type is guaranteed to have an Inner field that contains the
original error value. There are no guarantees on other fields and methods
of this type.
- Individual errors in the errList will always be of type *parserError,
and this type is guaranteed to have an Inner field that contains the
original error value. There are no guarantees on other fields and methods
of this type.
The above guarantee is given to the version 1.0 (https://github.com/mna/pigeon/releases/tag/v1.0.0)
of pigeon, which has entered maintenance mode (bug fixes only). The current
@@ -587,8 +611,8 @@ may occur at any time.
References:
[5]: https://golang.org/doc/go1compat
[6]: http://godoc.org/github.com/mna/pigeon/test/predicates
[5]: https://golang.org/doc/go1compat
[6]: http://godoc.org/github.com/mna/pigeon/test/predicates
*/
package main
+15 -8
View File
@@ -43,7 +43,7 @@ func main() {
dbgFlag = fs.Bool("debug", false, "set debug mode")
shortHelpFlag = fs.Bool("h", false, "show help page")
longHelpFlag = fs.Bool("help", false, "show help page")
nolint = fs.Bool("nolint", false, "add '// nolint: ...' comments to suppress warnings by gometalinter")
nolint = fs.Bool("nolint", false, "add '// nolint: ...' comments to suppress warnings by gometalinter or golangci-lint")
noRecoverFlag = fs.Bool("no-recover", false, "do not recover from panic")
outputFlag = fs.String("o", "", "output file, defaults to stdout")
optimizeBasicLatinFlag = fs.Bool("optimize-basic-latin", false, "generate optimized parser for Unicode Basic Latin character sets")
@@ -51,6 +51,7 @@ func main() {
optimizeParserFlag = fs.Bool("optimize-parser", false, "generate optimized parser without Debug and Memoize options")
recvrNmFlag = fs.String("receiver-name", "c", "receiver name for the generated methods")
noBuildFlag = fs.Bool("x", false, "do not build, only parse")
supportLeftRecursion = fs.Bool("support-left-recursion", false, "add support left recursion (EXPERIMENTAL FEATURE)")
altEntrypointsFlag ruleNamesFlag
)
@@ -136,7 +137,10 @@ func main() {
optimizeParser := builder.Optimize(*optimizeParserFlag)
basicLatinOptimize := builder.BasicLatinLookupTable(*optimizeBasicLatinFlag)
nolintOpt := builder.Nolint(*nolint)
if err := builder.BuildParser(outBuf, grammar, curNmOpt, optimizeParser, basicLatinOptimize, nolintOpt); err != nil {
leftRecursionSupporter := builder.SupportLeftRecursion(*supportLeftRecursion)
if err := builder.BuildParser(
outBuf, grammar, curNmOpt, optimizeParser, basicLatinOptimize,
nolintOpt, leftRecursionSupporter); err != nil {
fmt.Fprintln(os.Stderr, "build error: ", err)
exit(5)
}
@@ -185,7 +189,8 @@ the generated code is written to this file instead.
display this help message.
-nolint
add '// nolint: ...' comments for generated parser to suppress
warnings by gometalinter (https://github.com/alecthomas/gometalinter).
warnings by gometalinter (https://github.com/alecthomas/gometalinter) or
golangci-lint (https://golangci-lint.run/).
-no-recover
do not recover from a panic. Useful to access the panic stack
when debugging, otherwise the panic is converted to an error.
@@ -207,6 +212,8 @@ the generated code is written to this file instead.
comma-separated list of rule names that may be used as alternate
entrypoints for the parser, in addition to the first rule in the
grammar.
-support-left-recursion
add support left recursion (EXPERIMENTAL FEATURE)
See https://godoc.org/github.com/mna/pigeon for more information.
`
@@ -218,7 +225,7 @@ func usage() {
// argError prints an error message to stderr, prints the command usage
// and exits with the specified exit code.
func argError(exitCode int, msg string, args ...interface{}) {
func argError(exitCode int, msg string, args ...any) {
fmt.Fprintf(os.Stderr, msg, args...)
fmt.Fprintln(os.Stderr)
usage()
@@ -271,18 +278,18 @@ func (c *current) astPos() ast.Pos {
return ast.Pos{Line: c.pos.line, Col: c.pos.col, Off: c.pos.offset}
}
// toIfaceSlice is a helper function for the PEG grammar parser. It converts
// toAnySlice is a helper function for the PEG grammar parser. It converts
// v to a slice of empty interfaces.
func toIfaceSlice(v interface{}) []interface{} {
func toAnySlice(v any) []any {
if v == nil {
return nil
}
return v.([]interface{})
return v.([]any)
}
// validateUnicodeEscape checks that the provided escape sequence is a
// valid Unicode escape sequence.
func validateUnicodeEscape(escape, errMsg string) (interface{}, error) {
func validateUnicodeEscape(escape, errMsg string) (any, error) {
r, _, _, err := strconv.UnquoteChar("\\"+escape, '"')
if err != nil {
return nil, errors.New(errMsg)
+1054 -899
View File
File diff suppressed because it is too large Load Diff
+112 -112
View File
@@ -4,118 +4,118 @@
package main
var unicodeClasses = map[string]bool{
"ASCII_Hex_Digit": true,
"Arabic": true,
"Armenian": true,
"Avestan": true,
"Balinese": true,
"Bamum": true,
"Bassa_Vah": true,
"Batak": true,
"Bengali": true,
"Bidi_Control": true,
"Bopomofo": true,
"Brahmi": true,
"Braille": true,
"Buginese": true,
"Buhid": true,
"C": true,
"Canadian_Aboriginal": true,
"Carian": true,
"Caucasian_Albanian": true,
"Cc": true,
"Cf": true,
"Chakma": true,
"Cham": true,
"Cherokee": true,
"Co": true,
"Common": true,
"Coptic": true,
"Cs": true,
"Cuneiform": true,
"Cypriot": true,
"Cyrillic": true,
"Dash": true,
"Deprecated": true,
"Deseret": true,
"Devanagari": true,
"Diacritic": true,
"Duployan": true,
"Egyptian_Hieroglyphs": true,
"Elbasan": true,
"Ethiopic": true,
"Extender": true,
"Georgian": true,
"Glagolitic": true,
"Gothic": true,
"Grantha": true,
"Greek": true,
"Gujarati": true,
"Gurmukhi": true,
"Han": true,
"Hangul": true,
"Hanunoo": true,
"Hebrew": true,
"Hex_Digit": true,
"Hiragana": true,
"Hyphen": true,
"IDS_Binary_Operator": true,
"IDS_Trinary_Operator": true,
"Ideographic": true,
"Imperial_Aramaic": true,
"Inherited": true,
"Inscriptional_Pahlavi": true,
"Inscriptional_Parthian": true,
"Javanese": true,
"Join_Control": true,
"Kaithi": true,
"Kannada": true,
"Katakana": true,
"Kayah_Li": true,
"Kharoshthi": true,
"Khmer": true,
"Khojki": true,
"Khudawadi": true,
"L": true,
"Lao": true,
"Latin": true,
"Lepcha": true,
"Limbu": true,
"Linear_A": true,
"Linear_B": true,
"Lisu": true,
"Ll": true,
"Lm": true,
"Lo": true,
"Logical_Order_Exception": true,
"Lt": true,
"Lu": true,
"Lycian": true,
"Lydian": true,
"M": true,
"Mahajani": true,
"Malayalam": true,
"Mandaic": true,
"Manichaean": true,
"Mc": true,
"Me": true,
"Meetei_Mayek": true,
"Mende_Kikakui": true,
"Meroitic_Cursive": true,
"Meroitic_Hieroglyphs": true,
"Miao": true,
"Mn": true,
"Modi": true,
"Mongolian": true,
"Mro": true,
"Myanmar": true,
"N": true,
"Nabataean": true,
"Nd": true,
"New_Tai_Lue": true,
"Nko": true,
"Nl": true,
"No": true,
"ASCII_Hex_Digit": true,
"Arabic": true,
"Armenian": true,
"Avestan": true,
"Balinese": true,
"Bamum": true,
"Bassa_Vah": true,
"Batak": true,
"Bengali": true,
"Bidi_Control": true,
"Bopomofo": true,
"Brahmi": true,
"Braille": true,
"Buginese": true,
"Buhid": true,
"C": true,
"Canadian_Aboriginal": true,
"Carian": true,
"Caucasian_Albanian": true,
"Cc": true,
"Cf": true,
"Chakma": true,
"Cham": true,
"Cherokee": true,
"Co": true,
"Common": true,
"Coptic": true,
"Cs": true,
"Cuneiform": true,
"Cypriot": true,
"Cyrillic": true,
"Dash": true,
"Deprecated": true,
"Deseret": true,
"Devanagari": true,
"Diacritic": true,
"Duployan": true,
"Egyptian_Hieroglyphs": true,
"Elbasan": true,
"Ethiopic": true,
"Extender": true,
"Georgian": true,
"Glagolitic": true,
"Gothic": true,
"Grantha": true,
"Greek": true,
"Gujarati": true,
"Gurmukhi": true,
"Han": true,
"Hangul": true,
"Hanunoo": true,
"Hebrew": true,
"Hex_Digit": true,
"Hiragana": true,
"Hyphen": true,
"IDS_Binary_Operator": true,
"IDS_Trinary_Operator": true,
"Ideographic": true,
"Imperial_Aramaic": true,
"Inherited": true,
"Inscriptional_Pahlavi": true,
"Inscriptional_Parthian": true,
"Javanese": true,
"Join_Control": true,
"Kaithi": true,
"Kannada": true,
"Katakana": true,
"Kayah_Li": true,
"Kharoshthi": true,
"Khmer": true,
"Khojki": true,
"Khudawadi": true,
"L": true,
"Lao": true,
"Latin": true,
"Lepcha": true,
"Limbu": true,
"Linear_A": true,
"Linear_B": true,
"Lisu": true,
"Ll": true,
"Lm": true,
"Lo": true,
"Logical_Order_Exception": true,
"Lt": true,
"Lu": true,
"Lycian": true,
"Lydian": true,
"M": true,
"Mahajani": true,
"Malayalam": true,
"Mandaic": true,
"Manichaean": true,
"Mc": true,
"Me": true,
"Meetei_Mayek": true,
"Mende_Kikakui": true,
"Meroitic_Cursive": true,
"Meroitic_Hieroglyphs": true,
"Miao": true,
"Mn": true,
"Modi": true,
"Mongolian": true,
"Mro": true,
"Myanmar": true,
"N": true,
"Nabataean": true,
"Nd": true,
"New_Tai_Lue": true,
"Nko": true,
"Nl": true,
"No": true,
"Noncharacter_Code_Point": true,
"Ogham": true,
"Ol_Chiki": true,
+9
View File
@@ -31,6 +31,15 @@ func (p *panicError) Error() string {
return fmt.Sprintf("%v\n\n%s", p.value, p.stack)
}
func (p *panicError) Unwrap() error {
err, ok := p.value.(error)
if !ok {
return nil
}
return err
}
func newPanicError(v interface{}) error {
stack := debug.Stack()
+4 -1
View File
@@ -188,6 +188,8 @@ type Generator struct {
trimPrefix string
lineComment bool
logf func(format string, args ...interface{}) // test logging hook; nil when not testing
}
func (g *Generator) Printf(format string, args ...interface{}) {
@@ -221,13 +223,14 @@ func (g *Generator) parsePackage(patterns []string, tags []string) {
// in a separate pass? For later.
Tests: false,
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
Logf: g.logf,
}
pkgs, err := packages.Load(cfg, patterns...)
if err != nil {
log.Fatal(err)
}
if len(pkgs) != 1 {
log.Fatalf("error: %d packages found", len(pkgs))
log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
}
g.addPackage(pkgs[0])
}
+1 -1
View File
@@ -35,7 +35,7 @@ The Package struct provides basic information about the package, including
- Imports, a map from source import strings to the Packages they name;
- Types, the type information for the package's exported symbols;
- Syntax, the parsed syntax trees for the package's source code; and
- TypeInfo, the result of a complete type-check of the package syntax trees.
- TypesInfo, the result of a complete type-check of the package syntax trees.
(See the documentation for type Package for the complete list of fields
and more detailed descriptions.)
+3 -4
View File
@@ -9,7 +9,6 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path"
@@ -1109,7 +1108,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
if len(state.cfg.Overlay) == 0 {
return "", func() {}, nil
}
dir, err := ioutil.TempDir("", "gopackages-*")
dir, err := os.MkdirTemp("", "gopackages-*")
if err != nil {
return "", nil, err
}
@@ -1128,7 +1127,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
// Create a unique filename for the overlaid files, to avoid
// creating nested directories.
noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator))
if err != nil {
return "", func() {}, err
}
@@ -1146,7 +1145,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
}
// Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json")
if err := ioutil.WriteFile(filename, b, 0665); err != nil {
if err := os.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err
}
return filename, cleanup, nil
+1 -2
View File
@@ -16,7 +16,6 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
@@ -1127,7 +1126,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
var err error
if src == nil {
ioLimit <- true // wait
src, err = ioutil.ReadFile(filename)
src, err = os.ReadFile(filename)
<-ioLimit // signal
}
if err != nil {
+2 -2
View File
@@ -7,8 +7,8 @@
package imports // import "golang.org/x/tools/imports"
import (
"io/ioutil"
"log"
"os"
"golang.org/x/tools/internal/gocommand"
intimp "golang.org/x/tools/internal/imports"
@@ -44,7 +44,7 @@ var LocalPrefix string
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
var err error
if src == nil {
src, err = ioutil.ReadFile(filename)
src, err = os.ReadFile(filename)
if err != nil {
return nil, err
}
+7 -4
View File
@@ -8,7 +8,6 @@
package fastwalk
import (
"io/ioutil"
"os"
)
@@ -17,16 +16,20 @@ import (
// If fn returns a non-nil error, readDir returns with that error
// immediately.
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
fis, err := ioutil.ReadDir(dirName)
fis, err := os.ReadDir(dirName)
if err != nil {
return err
}
skipFiles := false
for _, fi := range fis {
if fi.Mode().IsRegular() && skipFiles {
info, err := fi.Info()
if err != nil {
return err
}
if info.Mode().IsRegular() && skipFiles {
continue
}
if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil {
if err := fn(dirName, fi.Name(), info.Mode()&os.ModeType); err != nil {
if err == ErrSkipFiles {
skipFiles = true
continue
+1 -2
View File
@@ -29,7 +29,6 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
switch hdr {
case "$$B\n":
var data []byte
data, err = ioutil.ReadAll(buf)
data, err = io.ReadAll(buf)
if err != nil {
break
}
+4 -3
View File
@@ -13,6 +13,7 @@ import (
"go/build"
"go/parser"
"go/token"
"io/fs"
"io/ioutil"
"os"
"path"
@@ -107,7 +108,7 @@ func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File {
considerTests := strings.HasSuffix(filename, "_test.go")
fileBase := filepath.Base(filename)
packageFileInfos, err := ioutil.ReadDir(srcDir)
packageFileInfos, err := os.ReadDir(srcDir)
if err != nil {
return nil
}
@@ -1469,11 +1470,11 @@ func VendorlessPath(ipath string) string {
func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) {
// Look for non-test, buildable .go files which could provide exports.
all, err := ioutil.ReadDir(dir)
all, err := os.ReadDir(dir)
if err != nil {
return "", nil, err
}
var files []os.FileInfo
var files []fs.DirEntry
for _, fi := range all {
name := fi.Name()
if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) {
+2 -3
View File
@@ -9,7 +9,6 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -265,7 +264,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON,
}
// Not cached. Read the filesystem.
pkgFiles, err := ioutil.ReadDir(pkgDir)
pkgFiles, err := os.ReadDir(pkgDir)
if err != nil {
continue
}
@@ -370,7 +369,7 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON
func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) {
readModName := func(modFile string) string {
modBytes, err := ioutil.ReadFile(modFile)
modBytes, err := os.ReadFile(modFile)
if err != nil {
return ""
}
+6 -6
View File
@@ -1347,8 +1347,8 @@ github.com/mitchellh/mapstructure
# github.com/mitchellh/reflectwalk v1.0.2
## explicit
github.com/mitchellh/reflectwalk
# github.com/mna/pigeon v1.1.0
## explicit
# github.com/mna/pigeon v1.2.1
## explicit; go 1.20
github.com/mna/pigeon
github.com/mna/pigeon/ast
github.com/mna/pigeon/builder
@@ -1988,8 +1988,8 @@ golang.org/x/image/math/fixed
golang.org/x/image/tiff
golang.org/x/image/tiff/lzw
golang.org/x/image/vector
# golang.org/x/mod v0.12.0
## explicit; go 1.17
# golang.org/x/mod v0.13.0
## explicit; go 1.18
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/module
golang.org/x/mod/semver
@@ -2020,7 +2020,7 @@ golang.org/x/net/trace
## explicit; go 1.18
golang.org/x/oauth2
golang.org/x/oauth2/internal
# golang.org/x/sync v0.3.0
# golang.org/x/sync v0.4.0
## explicit; go 1.17
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
@@ -2067,7 +2067,7 @@ golang.org/x/text/width
# golang.org/x/time v0.3.0
## explicit
golang.org/x/time/rate
# golang.org/x/tools v0.13.0
# golang.org/x/tools v0.14.0
## explicit; go 1.18
golang.org/x/tools/cmd/stringer
golang.org/x/tools/go/ast/astutil