mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-22 02:38:32 -05:00
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:
committed by
Ralf Haferkamp
parent
781dcba3f9
commit
21a061ab6c
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
|
||||
script: make test
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- tip
|
||||
+1
-1
@@ -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
@@ -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
@@ -1,7 +1,7 @@
|
||||
# pigeon - a PEG parser generator for Go
|
||||
|
||||
[](https://godoc.org/github.com/mna/pigeon)
|
||||
[](http://travis-ci.org/mna/pigeon)
|
||||
[](https://pkg.go.dev/github.com/mna/pigeon)
|
||||
[](https://github.com/mna/pigeon/actions?query=workflow%3AGo%20Matrix)
|
||||
[](https://goreportcard.com/report/github.com/mna/pigeon)
|
||||
[](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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+112
-112
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 ""
|
||||
}
|
||||
|
||||
Vendored
+6
-6
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user