├── .gitattributes
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── Makefile
├── README.md
├── cmd
└── php-parser
│ └── main.go
├── go.mod
├── go.sum
├── internal
├── php5
│ ├── node.go
│ ├── parser.go
│ ├── parser_test.go
│ ├── php5.go
│ ├── php5.y
│ ├── php5_bench_test.go
│ └── test.php
├── php7
│ ├── node.go
│ ├── parser.go
│ ├── parser_test.go
│ ├── php7.go
│ ├── php7.y
│ ├── php7_bench_test.go
│ └── test.php
├── position
│ ├── position.go
│ └── position_test.go
└── scanner
│ ├── lexer.go
│ ├── newline.go
│ ├── scanner.go
│ ├── scanner.rl
│ └── scanner_test.go
├── parser.jpg
└── pkg
├── ast
├── ast.go
└── node.go
├── conf
└── conf.go
├── errors
├── error.go
└── error_test.go
├── parser
├── doc.go
└── parser.go
├── position
├── pool.go
└── position.go
├── token
├── pool.go
├── pool_bench_test.go
├── token.go
└── token_string.go
├── version
├── version.go
└── version_test.go
└── visitor
├── dumper
├── dumper.go
└── dumper_test.go
├── formatter
├── formatter.go
└── formatter_test.go
├── nsresolver
├── namespace_resolver.go
└── namespace_resolver_test.go
├── null.go
├── printer
├── printer.go
├── printer_php5_test.go
├── printer_php7_test.go
└── printer_test.go
└── traverser
└── traverser.go
/.gitattributes:
--------------------------------------------------------------------------------
1 | internal/php5/php5.go -diff -merge
2 | internal/php5/php5.go linguist-generated=true
3 | internal/php7/php7.go -diff -merge
4 | internal/php7/php7.go linguist-generated=true
5 | internal/scanner/scanner.go -diff -merge
6 | internal/scanner/scanner.go linguist-generated=true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .idea
3 | php-parser
4 | **/*.test
5 |
6 | *example.php
7 |
8 | cpu.pprof
9 | mem.pprof
10 | trace.out
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | branches:
3 | only:
4 | - master
5 | before_script:
6 | - env GO111MODULE=on go mod download
7 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
8 | - chmod +x ./cc-test-reporter
9 | - ./cc-test-reporter before-build
10 | script:
11 | - env GO111MODULE=on go test -coverprofile c.out ./...
12 | after_script:
13 | - ./cc-test-reporter after-build --coverage-input-type gocov --exit-code $TRAVIS_TEST_RESULT
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at z7zmey@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Slizov Vadim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PHPFILE=example.php
2 |
3 | all: compile fmt build
4 |
5 | fmt:
6 | find . -type f -iregex '.*\.go' -exec gofmt -l -s -w '{}' +
7 |
8 | build:
9 | go generate ./...
10 | go build ./cmd/...
11 |
12 | test:
13 | go test ./...
14 |
15 | cover:
16 | go test ./... --cover
17 |
18 | bench:
19 | go test -benchmem -bench=. ./internal/php5
20 | go test -benchmem -bench=. ./internal/php7
21 |
22 | compile: ./internal/php5/php5.go ./internal/php7/php7.go ./internal/scanner/scanner.go
23 | sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php7/php7.go
24 | sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php5/php5.go
25 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php5/php5.go
26 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php7/php7.go
27 | sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/scanner/scanner.go
28 | rm -f y.output
29 |
30 | ./internal/scanner/scanner.go: ./internal/scanner/scanner.rl
31 | ragel -Z -G2 -o $@ $<
32 |
33 | ./internal/php5/php5.go: ./internal/php5/php5.y
34 | goyacc -o $@ $<
35 |
36 | ./internal/php7/php7.go: ./internal/php7/php7.y
37 | goyacc -o $@ $<
38 |
39 | cpu_pprof:
40 | go test -cpuprofile cpu.pprof -bench=. -benchtime=20s ./internal/php7
41 | go tool pprof ./php7.test cpu.pprof
42 |
43 | mem_pprof:
44 | go test -memprofile mem.pprof -bench=. -benchtime=20s -benchmem ./internal/php7
45 | go tool pprof -alloc_objects ./php7.test mem.pprof
46 |
47 | cpu_pprof_php5:
48 | go test -cpuprofile cpu.prof -bench=. -benchtime=20s ./internal/php5
49 | go tool pprof ./php5.test cpu.prof
50 |
51 | mem_pprof_php5:
52 | go test -memprofile mem.prof -bench=. -benchtime=20s -benchmem ./internal/php5
53 | go tool pprof -alloc_objects ./php5.test mem.prof
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHP Parser written in Go
2 | ========================
3 |
4 |
5 |
6 | [](https://godoc.org/github.com/z7zmey/php-parser)
7 | [](https://travis-ci.org/z7zmey/php-parser)
8 | [](https://goreportcard.com/report/github.com/z7zmey/php-parser)
9 | [](https://codeclimate.com/github/z7zmey/php-parser/maintainability)
10 | [](https://codeclimate.com/github/z7zmey/php-parser/test_coverage)
11 |
12 | This project uses [goyacc](https://godoc.org/golang.org/x/tools/cmd/goyacc) and [ragel](https://www.colm.net/open-source/ragel/) tools to create PHP parser. It parses source code into [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree). It can be used to write static analysis, refactoring, metrics, code style formatting tools.
13 |
14 | #### Try it online: [demo](https://php-parser.com)
15 |
16 | Features:
17 | ---------
18 |
19 | - Fully support PHP 5 and PHP 7 syntax
20 | - Abstract syntax tree (AST) representation
21 | - Traversing AST
22 | - Resolving namespaced names
23 | - Parsing syntax-invalid PHP files
24 | - Saving and printing free-floating comments and whitespaces
25 |
26 | Who Uses
27 | --------
28 |
29 | [VKCOM/noverify](https://github.com/VKCOM/noverify) - NoVerify is a pretty fast linter for PHP
30 |
31 | [quasilyte/phpgrep](https://github.com/quasilyte/phpgrep) - phpgrep is a tool for syntax-aware PHP code search
32 |
33 | Usage example
34 | -------
35 |
36 | ```Golang
37 | package main
38 |
39 | import (
40 | "log"
41 | "os"
42 |
43 | "github.com/z7zmey/php-parser/pkg/cfg"
44 | "github.com/z7zmey/php-parser/pkg/errors"
45 | "github.com/z7zmey/php-parser/pkg/parser"
46 | "github.com/z7zmey/php-parser/pkg/version"
47 | "github.com/z7zmey/php-parser/pkg/visitor/dumper"
48 | )
49 |
50 | func main() {
51 | src := []byte(` echo "Hello world";`)
52 |
53 | // Error handler
54 |
55 | var parserErrors []*errors.Error
56 | errorHandler := func(e *errors.Error) {
57 | parserErrors = append(parserErrors, e)
58 | }
59 |
60 | // Parse
61 |
62 | rootNode, err := parser.Parse(src, cfg.Config{
63 | Version: &version.Version{Major: 5, Minor: 6},
64 | ErrorHandlerFunc: errorHandler,
65 | })
66 |
67 | if err != nil {
68 | log.Fatal("Error:" + err.Error())
69 | }
70 |
71 | // Dump
72 |
73 | goDumper := dumper.NewDumper(os.Stdout).
74 | WithTokens().
75 | WithPositions()
76 |
77 | rootNode.Accept(goDumper)
78 | }
79 | ```
80 |
81 | Roadmap
82 | -------
83 |
84 | - Control Flow Graph (CFG)
85 | - PHP8
86 |
87 | Install
88 | -------
89 |
90 | ```
91 | go get github.com/z7zmey/php-parser/cmd/php-parser
92 | ```
93 |
94 | CLI
95 | ---
96 |
97 | ```
98 | php-parser [flags] ...
99 | ```
100 |
101 | | flag | type | description |
102 | | ------- | ------ | --------------------------------- |
103 | | -p | bool | print filepath |
104 | | -e | bool | print errors |
105 | | -d | bool | dump in golang format |
106 | | -r | bool | resolve names |
107 | | -prof | string | start profiler: [cpu, mem, trace] |
108 | | -phpver | string | php version (default: 7.4) |
109 |
110 | Namespace resolver
111 | ------------------
112 |
113 | Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[node.Node]string` structure
114 |
115 | - For `Class`, `Interface`, `Trait`, `Function`, `Constant` nodes it saves name with current namespace.
116 | - For `Name`, `Relative`, `FullyQualified` nodes it resolves `use` aliases and saves a fully qualified name.
117 |
--------------------------------------------------------------------------------
/cmd/php-parser/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "path/filepath"
12 | "runtime"
13 | "strconv"
14 | "sync"
15 | "time"
16 |
17 | "github.com/pkg/profile"
18 | "github.com/yookoala/realpath"
19 |
20 | "github.com/z7zmey/php-parser/pkg/ast"
21 | "github.com/z7zmey/php-parser/pkg/conf"
22 | "github.com/z7zmey/php-parser/pkg/errors"
23 | "github.com/z7zmey/php-parser/pkg/parser"
24 | "github.com/z7zmey/php-parser/pkg/version"
25 | "github.com/z7zmey/php-parser/pkg/visitor/dumper"
26 | "github.com/z7zmey/php-parser/pkg/visitor/nsresolver"
27 | "github.com/z7zmey/php-parser/pkg/visitor/printer"
28 | "github.com/z7zmey/php-parser/pkg/visitor/traverser"
29 | )
30 |
31 | var wg sync.WaitGroup
32 | var phpVersion *version.Version
33 | var profiler string
34 | var dump *bool
35 | var showResolvedNs *bool
36 | var printBack *bool
37 | var printPath *bool
38 | var printErrors *bool
39 | var printExecTime *bool
40 |
41 | type file struct {
42 | path string
43 | content []byte
44 | }
45 |
46 | type result struct {
47 | path string
48 | rootNode ast.Vertex
49 | errors []*errors.Error
50 | }
51 |
52 | func main() {
53 | start := time.Now()
54 | var phpVer string
55 |
56 | printExecTime = flag.Bool("time", false, "print execution time")
57 | showResolvedNs = flag.Bool("r", false, "resolve names")
58 | printBack = flag.Bool("pb", false, "print AST back into the parsed file")
59 | printPath = flag.Bool("p", false, "print filepath")
60 | printErrors = flag.Bool("e", false, "print errors")
61 | dump = flag.Bool("d", false, "dump")
62 | flag.StringVar(&profiler, "prof", "", "start profiler: [cpu, mem, trace]")
63 | flag.StringVar(&phpVer, "phpver", "7.4", "php version")
64 |
65 | flag.Parse()
66 |
67 | var err error
68 | phpVersion, err = version.New(phpVer)
69 | if err != nil {
70 | fmt.Println("Error: " + err.Error())
71 | os.Exit(1)
72 | }
73 |
74 | if len(flag.Args()) == 0 {
75 | flag.Usage()
76 | return
77 | }
78 |
79 | switch profiler {
80 | case "cpu":
81 | defer profile.Start(profile.ProfilePath("."), profile.NoShutdownHook).Stop()
82 | case "mem":
83 | defer profile.Start(profile.MemProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
84 | case "trace":
85 | defer profile.Start(profile.TraceProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop()
86 | }
87 |
88 | numCpu := runtime.GOMAXPROCS(0)
89 |
90 | fileCh := make(chan *file, numCpu)
91 | resultCh := make(chan result, numCpu)
92 |
93 | // run 4 concurrent parserWorkers
94 | for i := 0; i < numCpu; i++ {
95 | go parserWorker(fileCh, resultCh)
96 | }
97 |
98 | // run printer goroutine
99 | go printerWorker(resultCh)
100 |
101 | // process files
102 | processPath(flag.Args(), fileCh)
103 |
104 | // wait the all files done
105 | wg.Wait()
106 | close(fileCh)
107 | close(resultCh)
108 |
109 | elapsed := time.Since(start)
110 | if *printExecTime {
111 | log.Printf("took: %s", elapsed)
112 | }
113 | }
114 |
115 | func processPath(pathList []string, fileCh chan<- *file) {
116 | for _, path := range pathList {
117 | real, err := realpath.Realpath(path)
118 | checkErr(err)
119 |
120 | err = filepath.Walk(real, func(path string, f os.FileInfo, err error) error {
121 | if !f.IsDir() && filepath.Ext(path) == ".php" {
122 | wg.Add(1)
123 | content, err := ioutil.ReadFile(path)
124 | checkErr(err)
125 | fileCh <- &file{path, content}
126 | }
127 | return nil
128 | })
129 | checkErr(err)
130 | }
131 | }
132 |
133 | func parserWorker(fileCh <-chan *file, r chan<- result) {
134 | for {
135 | f, ok := <-fileCh
136 | if !ok {
137 | return
138 | }
139 |
140 | var parserErrors []*errors.Error
141 | rootNode, err := parser.Parse(f.content, conf.Config{
142 | Version: phpVersion,
143 | ErrorHandlerFunc: func(e *errors.Error) {
144 | parserErrors = append(parserErrors, e)
145 | },
146 | })
147 | if err != nil {
148 | fmt.Println("Error:" + err.Error())
149 | os.Exit(1)
150 | }
151 |
152 | r <- result{path: f.path, rootNode: rootNode, errors: parserErrors}
153 | }
154 | }
155 |
156 | func printerWorker(r <-chan result) {
157 | var counter int
158 |
159 | for {
160 | res, ok := <-r
161 | if !ok {
162 | return
163 | }
164 |
165 | counter++
166 |
167 | if *printPath {
168 | _, _ = io.WriteString(os.Stderr, "==> ["+strconv.Itoa(counter)+"] "+res.path+"\n")
169 | }
170 |
171 | if *printErrors {
172 | for _, e := range res.errors {
173 | _, _ = io.WriteString(os.Stderr, "==> "+e.String()+"\n")
174 | }
175 | }
176 |
177 | if *printBack {
178 | o := bytes.NewBuffer([]byte{})
179 | p := printer.NewPrinter(o)
180 | res.rootNode.Accept(p)
181 |
182 | err := ioutil.WriteFile(res.path, o.Bytes(), 0644)
183 | checkErr(err)
184 | }
185 |
186 | if *showResolvedNs {
187 | v := nsresolver.NewNamespaceResolver()
188 | traverser.NewTraverser(v).Traverse(res.rootNode)
189 | for _, n := range v.ResolvedNames {
190 | _, _ = io.WriteString(os.Stderr, "===> "+n+"\n")
191 | }
192 | }
193 |
194 | if *dump == true {
195 | dumper.NewDumper(os.Stdout).WithPositions().WithTokens().Dump(res.rootNode)
196 | }
197 |
198 | wg.Done()
199 | }
200 | }
201 |
202 | func checkErr(err error) {
203 | if err != nil {
204 | log.Fatal(err)
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/z7zmey/php-parser
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/google/go-cmp v0.4.0 // indirect
7 | github.com/pkg/errors v0.9.1 // indirect
8 | github.com/pkg/profile v1.4.0
9 | github.com/yookoala/realpath v1.0.0
10 | gotest.tools v2.2.0+incompatible
11 | )
12 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
2 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
5 | github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI=
6 | github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
7 | github.com/yookoala/realpath v1.0.0 h1:7OA9pj4FZd+oZDsyvXWQvjn5oBdcHRTV44PpdMSuImQ=
8 | github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE=
9 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
10 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
11 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
12 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
13 |
--------------------------------------------------------------------------------
/internal/php5/node.go:
--------------------------------------------------------------------------------
1 | package php5
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/ast"
5 | "github.com/z7zmey/php-parser/pkg/position"
6 | "github.com/z7zmey/php-parser/pkg/token"
7 | )
8 |
9 | type ParserBrackets struct {
10 | Position *position.Position
11 | OpenBracketTkn *token.Token
12 | Child ast.Vertex
13 | CloseBracketTkn *token.Token
14 | }
15 |
16 | func (n *ParserBrackets) Accept(v ast.Visitor) {
17 | // do nothing
18 | }
19 |
20 | func (n *ParserBrackets) GetPosition() *position.Position {
21 | return n.Position
22 | }
23 |
24 | type ParserSeparatedList struct {
25 | Position *position.Position
26 | Items []ast.Vertex
27 | SeparatorTkns []*token.Token
28 | }
29 |
30 | func (n *ParserSeparatedList) Accept(v ast.Visitor) {
31 | // do nothing
32 | }
33 |
34 | func (n *ParserSeparatedList) GetPosition() *position.Position {
35 | return n.Position
36 | }
37 |
38 | // TraitAdaptationList node
39 | type TraitAdaptationList struct {
40 | Position *position.Position
41 | OpenCurlyBracketTkn *token.Token
42 | Adaptations []ast.Vertex
43 | CloseCurlyBracketTkn *token.Token
44 | }
45 |
46 | func (n *TraitAdaptationList) Accept(v ast.Visitor) {
47 | // do nothing
48 | }
49 |
50 | func (n *TraitAdaptationList) GetPosition() *position.Position {
51 | return n.Position
52 | }
53 |
54 | // ArgumentList node
55 | type ArgumentList struct {
56 | Position *position.Position
57 | OpenParenthesisTkn *token.Token
58 | Arguments []ast.Vertex
59 | SeparatorTkns []*token.Token
60 | CloseParenthesisTkn *token.Token
61 | }
62 |
63 | func (n *ArgumentList) Accept(v ast.Visitor) {
64 | // do nothing
65 | }
66 |
67 | func (n *ArgumentList) GetPosition() *position.Position {
68 | return n.Position
69 | }
70 |
71 | // TraitMethodRef node
72 | type TraitMethodRef struct {
73 | Position *position.Position
74 | Trait ast.Vertex
75 | DoubleColonTkn *token.Token
76 | Method ast.Vertex
77 | }
78 |
79 | func (n *TraitMethodRef) Accept(v ast.Visitor) {
80 | // do nothing
81 | }
82 |
83 | func (n *TraitMethodRef) GetPosition() *position.Position {
84 | return n.Position
85 | }
86 |
--------------------------------------------------------------------------------
/internal/php5/parser.go:
--------------------------------------------------------------------------------
1 | package php5
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/internal/position"
5 | "github.com/z7zmey/php-parser/internal/scanner"
6 | "github.com/z7zmey/php-parser/pkg/ast"
7 | "github.com/z7zmey/php-parser/pkg/conf"
8 | "github.com/z7zmey/php-parser/pkg/errors"
9 | "github.com/z7zmey/php-parser/pkg/token"
10 | )
11 |
12 | // Parser structure
13 | type Parser struct {
14 | Lexer *scanner.Lexer
15 | currentToken *token.Token
16 | rootNode ast.Vertex
17 | errHandlerFunc func(*errors.Error)
18 | builder *position.Builder
19 | }
20 |
21 | // NewParser creates and returns new Parser
22 | func NewParser(lexer *scanner.Lexer, config conf.Config) *Parser {
23 | return &Parser{
24 | Lexer: lexer,
25 | errHandlerFunc: config.ErrorHandlerFunc,
26 | builder: position.NewBuilder(),
27 | }
28 | }
29 |
30 | // Lex proxy to scanner Lex
31 | func (p *Parser) Lex(lval *yySymType) int {
32 | t := p.Lexer.Lex()
33 |
34 | p.currentToken = t
35 | lval.token = t
36 |
37 | return int(t.ID)
38 | }
39 |
40 | func (p *Parser) Error(msg string) {
41 | if p.errHandlerFunc == nil {
42 | return
43 | }
44 |
45 | p.errHandlerFunc(errors.NewError(msg, p.currentToken.Position))
46 | }
47 |
48 | // Parse the php7 Parser entrypoint
49 | func (p *Parser) Parse() int {
50 | p.rootNode = nil
51 | return yyParse(p)
52 | }
53 |
54 | // GetRootNode returns root node
55 | func (p *Parser) GetRootNode() ast.Vertex {
56 | return p.rootNode
57 | }
58 |
59 | // helpers
60 |
61 | func lastNode(nn []ast.Vertex) ast.Vertex {
62 | if len(nn) == 0 {
63 | return nil
64 | }
65 | return nn[len(nn)-1]
66 | }
67 |
--------------------------------------------------------------------------------
/internal/php5/php5_bench_test.go:
--------------------------------------------------------------------------------
1 | package php5_test
2 |
3 | import (
4 | "io/ioutil"
5 | "testing"
6 |
7 | "github.com/z7zmey/php-parser/internal/php5"
8 | "github.com/z7zmey/php-parser/internal/scanner"
9 | "github.com/z7zmey/php-parser/pkg/conf"
10 | "github.com/z7zmey/php-parser/pkg/version"
11 | )
12 |
13 | func BenchmarkPhp5(b *testing.B) {
14 | src, err := ioutil.ReadFile("test.php")
15 | if err != nil {
16 | b.Fatal("can not read test.php: " + err.Error())
17 | }
18 |
19 | for n := 0; n < b.N; n++ {
20 | config := conf.Config{
21 | Version: &version.Version{
22 | Major: 5,
23 | Minor: 6,
24 | },
25 | }
26 | lexer := scanner.NewLexer(src, config)
27 | php5parser := php5.NewParser(lexer, config)
28 | php5parser.Parse()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/internal/php5/test.php:
--------------------------------------------------------------------------------
1 |
2 | foo($a, ...$b);
3 | $foo($a, ...$b);
4 | $foo->bar($a, ...$b);
5 | foo::bar($a, ...$b);
6 | $foo::bar($a, ...$b);
7 | new foo($a, ...$b);
8 |
9 | function foo(bar $bar=null, baz &...$baz) {}
10 | class foo {public function foo(bar $bar=null, baz &...$baz) {}}
11 | function(bar $bar=null, baz &...$baz) {};
12 | static function(bar $bar=null, baz &...$baz) {};
13 |
14 | 1234567890123456789;
15 | 12345678901234567890;
16 | 0.;
17 | 0b0111111111111111111111111111111111111111111111111111111111111111;
18 | 0b1111111111111111111111111111111111111111111111111111111111111111;
19 | 0x007111111111111111;
20 | 0x8111111111111111;
21 | __CLASS__;
22 | __DIR__;
23 | __FILE__;
24 | __FUNCTION__;
25 | __LINE__;
26 | __NAMESPACE__;
27 | __METHOD__;
28 | __TRAIT__;
29 |
30 | "test $var";
31 | "test $var[1]";
32 | "test $var[1234567890123456789012345678901234567890]";
33 | "test $var[bar]";
34 | "test $var[$bar]";
35 | "$foo $bar";
36 | "test $foo->bar()";
37 | "test ${foo}";
38 | "test ${foo[0]}";
39 | "test {$foo->bar()}";
40 |
41 | if ($a) :
42 | endif;
43 | if ($a) :
44 | elseif ($b):
45 | endif;
46 | if ($a) :
47 | else:
48 | endif;
49 | if ($a) :
50 | elseif ($b):
51 | elseif ($c):
52 | else:
53 | endif;
54 |
55 | while (1) { break; }
56 | while (1) { break 2; }
57 | while (1) : break(3); endwhile;
58 | class foo{ const FOO = 1, BAR = 2; }
59 | class foo{ function bar() {} }
60 | class foo{ public static function &bar() {} }
61 | class foo{ final private function bar() {} protected function baz() {} }
62 | abstract class foo{ abstract public function bar(); }
63 | final class foo extends bar { }
64 | final class foo implements bar { }
65 | final class foo implements bar, baz { }
66 |
67 | const FOO = 1, BAR = 2;
68 | while (1) { continue; }
69 | while (1) { continue 2; }
70 | while (1) { continue(3); }
71 | declare(ticks=1);
72 | declare(ticks=1, strict_types=1) {}
73 | declare(ticks=1): enddeclare;
74 | do {} while(1);
75 | echo $a, 1;
76 | echo($a);
77 | for($i = 0; $i < 10; $i++, $i++) {}
78 | for(; $i < 10; $i++) : endfor;
79 | foreach ($a as $v) {}
80 | foreach ([] as $v) {}
81 | foreach ($a as $v) : endforeach;
82 | foreach ($a as $k => $v) {}
83 | foreach ([] as $k => $v) {}
84 | foreach ($a as $k => &$v) {}
85 | foreach ($a as $k => list($v)) {}
86 | function foo() {}
87 |
88 | function foo() {
89 | function bar() {}
90 | class Baz {}
91 | return $a;
92 | }
93 |
94 | function foo(array $a, callable $b) {return;}
95 | function &foo() {return 1;}
96 | function &foo() {}
97 | global $a, $b, $$c, ${foo()};
98 | a:
99 | goto a;
100 | if ($a) {}
101 | if ($a) {} elseif ($b) {}
102 | if ($a) {} else {}
103 | if ($a) {} elseif ($b) {} elseif ($c) {} else {}
104 | if ($a) {} elseif ($b) {} else if ($c) {} else {}
105 | ?>
106 | interface Foo {}
107 | interface Foo extends Bar {}
108 | interface Foo extends Bar, Baz {}
109 | namespace Foo;
110 | namespace Foo\Bar {}
111 | namespace {}
112 | class foo {var $a;}
113 | class foo {public static $a, $b = 1;}
114 | class foo {public static $a = 1, $b;}
115 | static $a, $b = 1;
116 | static $a = 1, $b;
117 |
118 | switch (1) :
119 | case 1:
120 | default:
121 | case 2:
122 | endswitch;
123 |
124 | switch (1) :;
125 | case 1;
126 | case 2;
127 | endswitch;
128 |
129 | switch (1) {
130 | case 1: break;
131 | case 2: break;
132 | }
133 |
134 | switch (1) {;
135 | case 1; break;
136 | case 2; break;
137 | }
138 | throw $e;
139 | trait Foo {}
140 | class Foo { use Bar; }
141 | class Foo { use Bar, Baz {} }
142 | class Foo { use Bar, Baz { one as public; } }
143 | class Foo { use Bar, Baz { one as public two; } }
144 | class Foo { use Bar, Baz { Bar::one insteadof Baz, Quux; Baz::one as two; } }
145 |
146 | try {}
147 | try {} catch (Exception $e) {}
148 | try {} catch (Exception $e) {} catch (RuntimeException $e) {}
149 | try {} catch (Exception $e) {} catch (\RuntimeException $e) {} catch (namespace\AdditionException $e) {}
150 | try {} catch (Exception $e) {} finally {}
151 |
152 | unset($a, $b);
153 |
154 | use Foo;
155 | use \Foo;
156 | use \Foo as Bar;
157 | use Foo, Bar;
158 | use Foo, Bar as Baz;
159 | use function Foo, \Bar;
160 | use function Foo as foo, \Bar as bar;
161 | use const Foo, \Bar;
162 | use const Foo as foo, \Bar as bar;
163 |
164 | $a[1];
165 | $a[1][2];
166 | array();
167 | array(1);
168 | array(1=>1, &$b,);
169 | array(3 =>&$b);
170 | array(&$b, 1=>1, 1, 3 =>&$b);
171 | ~$a;
172 | !$a;
173 |
174 | Foo::Bar;
175 | clone($a);
176 | clone $a;
177 | function(){};
178 | function($a, $b) use ($c, &$d) {};
179 | function($a, $b) use (&$c, $d) {};
180 | function() {};
181 | foo;
182 | namespace\foo;
183 | \foo;
184 |
185 | empty($a);
186 | empty(Foo);
187 | @$a;
188 | eval($a);
189 | exit;
190 | exit($a);
191 | die();
192 | die($a);
193 | foo();
194 | namespace\foo(&$a);
195 | \foo([]);
196 | $foo(yield $a);
197 |
198 | $a--;
199 | $a++;
200 | --$a;
201 | ++$a;
202 |
203 | include $a;
204 | include_once $a;
205 | require $a;
206 | require_once $a;
207 |
208 | $a instanceof Foo;
209 | $a instanceof namespace\Foo;
210 | $a instanceof \Foo;
211 |
212 | isset($a, $b);
213 | isset(Foo);
214 | list() = $b;
215 | list($a, $b) = $b;
216 | list($a[]) = $b;
217 | list(list($a)) = $b;
218 |
219 | $a->foo();
220 | new Foo;
221 | new namespace\Foo();
222 | new \Foo();
223 | print($a);
224 | $a->foo;
225 | $a->foo[1];
226 | $a->foo->bar->baz()->quux[0];
227 | $a->foo()[1][1];
228 | `cmd $a`;
229 | `cmd`;
230 | ``;
231 | [];
232 | [1];
233 | [1=>1, &$b,];
234 |
235 | Foo::bar();
236 | namespace\Foo::bar();
237 | \Foo::bar();
238 | Foo::$bar();
239 | $foo::$bar();
240 | Foo::$bar;
241 | namespace\Foo::$bar;
242 | \Foo::$bar;
243 | $a ? $b : $c;
244 | $a ? : $c;
245 | $a ? $b ? $c : $d : $e;
246 | $a ? $b : $c ? $d : $e;
247 | -$a;
248 | +$a;
249 | $$a;
250 | $$$a;
251 | yield;
252 | yield $a;
253 | yield $a => $b;
254 | yield Foo::class;
255 | yield $a => Foo::class;
256 |
257 | (array)$a;
258 | (boolean)$a;
259 | (bool)$a;
260 | (double)$a;
261 | (float)$a;
262 | (integer)$a;
263 | (int)$a;
264 | (object)$a;
265 | (string)$a;
266 | (unset)$a;
267 |
268 | $a & $b;
269 | $a | $b;
270 | $a ^ $b;
271 | $a && $b;
272 | $a || $b;
273 | $a . $b;
274 | $a / $b;
275 | $a == $b;
276 | $a >= $b;
277 | $a > $b;
278 | $a === $b;
279 | $a and $b;
280 | $a or $b;
281 | $a xor $b;
282 | $a - $b;
283 | $a % $b;
284 | $a * $b;
285 | $a != $b;
286 | $a !== $b;
287 | $a + $b;
288 | $a ** $b;
289 | $a << $b;
290 | $a >> $b;
291 | $a <= $b;
292 | $a < $b;
293 |
294 | $a =& $b;
295 | $a =& new Foo;
296 | $a =& new Foo($b);
297 | $a = $b;
298 | $a &= $b;
299 | $a |= $b;
300 | $a ^= $b;
301 | $a .= $b;
302 | $a /= $b;
303 | $a -= $b;
304 | $a %= $b;
305 | $a *= $b;
306 | $a += $b;
307 | $a **= $b;
308 | $a <<= $b;
309 | $a >>= $b;
310 |
311 |
312 | (new \Foo());
313 | (new \Foo())->bar()->baz;
314 | (new \Foo())[0][0];
315 | (new \Foo())[0]->bar();
316 |
317 | array([0])[0][0];
318 | "foo"[0];
319 | foo[0];
320 | static::foo;
321 |
322 | new $foo;
323 | new $foo::$bar;
324 | new $a->b[0];
325 | new $a->b{$b ?: null}->$c->d[0];static $a = [1][0];
326 |
327 | static $a = !1;
328 | static $a = ~1;
329 | static $a = +1;
330 | static $a = -1;
331 | static $a = (1);
332 | static $a = 1 ?: 2;
333 | static $a = 1 ? 2 : 3;
334 | static $a = 1 & 2;
335 | static $a = 1 | 2;
336 | static $a = 1 ^ 2;
337 | static $a = 1 && 2;
338 | static $a = 1 || 2;
339 | static $a = 1 . 2;
340 | static $a = 1 / 2;
341 | static $a = 1 == 2;
342 | static $a = 1 >= 2;
343 | static $a = 1 > 2;
344 | static $a = 1 === 2;
345 | static $a = 1 and 2;
346 | static $a = 1 or 2;
347 | static $a = 1 xor 2;
348 | static $a = 1 - 2;
349 | static $a = 1 % 2;
350 | static $a = 1 * 2;
351 | static $a = 1 != 2;
352 | static $a = 1 !== 2;
353 | static $a = 1 + 2;
354 | static $a = 1 ** 2;
355 | static $a = 1 << 2;
356 | static $a = 1 >> 2;
357 | static $a = 1 <= 2;
358 | static $a = 1 < 2;
359 | static $a = Foo::bar;
360 | static $a = Foo::class;
361 | static $a = __CLASS__;
362 | static $a = Foo;
363 | static $a = namespace\Foo;
364 | static $a = \Foo;
365 | static $a = array();
366 | static $a = array(1 => 1, 2);
367 | static $a = [1, 2 => 2][0];
368 |
369 | if (yield 1) {}
370 | Foo::$$bar;
371 |
372 | $foo();
373 | $foo()[0][0];
374 | $a{$b};
375 | ${$a};
376 | $foo::{$bar}();
377 | $foo::bar;
378 |
379 | __halt_compiler();
380 |
381 | parsing process must be terminated
--------------------------------------------------------------------------------
/internal/php7/node.go:
--------------------------------------------------------------------------------
1 | package php7
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/ast"
5 | "github.com/z7zmey/php-parser/pkg/position"
6 | "github.com/z7zmey/php-parser/pkg/token"
7 | )
8 |
9 | type ParserBrackets struct {
10 | Position *position.Position
11 | OpenBracketTkn *token.Token
12 | Child ast.Vertex
13 | CloseBracketTkn *token.Token
14 | }
15 |
16 | func (n *ParserBrackets) Accept(v ast.Visitor) {
17 | // do nothing
18 | }
19 |
20 | func (n *ParserBrackets) GetPosition() *position.Position {
21 | return n.Position
22 | }
23 |
24 | type ParserSeparatedList struct {
25 | Position *position.Position
26 | Items []ast.Vertex
27 | SeparatorTkns []*token.Token
28 | }
29 |
30 | func (n *ParserSeparatedList) Accept(v ast.Visitor) {
31 | // do nothing
32 | }
33 |
34 | func (n *ParserSeparatedList) GetPosition() *position.Position {
35 | return n.Position
36 | }
37 |
38 | // TraitAdaptationList node
39 | type TraitAdaptationList struct {
40 | Position *position.Position
41 | OpenCurlyBracketTkn *token.Token
42 | Adaptations []ast.Vertex
43 | CloseCurlyBracketTkn *token.Token
44 | }
45 |
46 | func (n *TraitAdaptationList) Accept(v ast.Visitor) {
47 | // do nothing
48 | }
49 |
50 | func (n *TraitAdaptationList) GetPosition() *position.Position {
51 | return n.Position
52 | }
53 |
54 | // ArgumentList node
55 | type ArgumentList struct {
56 | Position *position.Position
57 | OpenParenthesisTkn *token.Token
58 | Arguments []ast.Vertex
59 | SeparatorTkns []*token.Token
60 | CloseParenthesisTkn *token.Token
61 | }
62 |
63 | func (n *ArgumentList) Accept(v ast.Visitor) {
64 | // do nothing
65 | }
66 |
67 | func (n *ArgumentList) GetPosition() *position.Position {
68 | return n.Position
69 | }
70 |
71 | type ReturnType struct {
72 | Position *position.Position
73 | ColonTkn *token.Token
74 | Type ast.Vertex
75 | }
76 |
77 | func (n *ReturnType) Accept(v ast.Visitor) {
78 | // do nothing
79 | }
80 |
81 | func (n *ReturnType) GetPosition() *position.Position {
82 | return n.Position
83 | }
84 |
85 | // TraitMethodRef node
86 | type TraitMethodRef struct {
87 | Position *position.Position
88 | Trait ast.Vertex
89 | DoubleColonTkn *token.Token
90 | Method ast.Vertex
91 | }
92 |
93 | func (n *TraitMethodRef) Accept(v ast.Visitor) {
94 | // do nothing
95 | }
96 |
97 | func (n *TraitMethodRef) GetPosition() *position.Position {
98 | return n.Position
99 | }
100 |
--------------------------------------------------------------------------------
/internal/php7/parser.go:
--------------------------------------------------------------------------------
1 | package php7
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/internal/position"
5 | "github.com/z7zmey/php-parser/internal/scanner"
6 | "github.com/z7zmey/php-parser/pkg/ast"
7 | "github.com/z7zmey/php-parser/pkg/conf"
8 | "github.com/z7zmey/php-parser/pkg/errors"
9 | "github.com/z7zmey/php-parser/pkg/token"
10 | )
11 |
12 | // Parser structure
13 | type Parser struct {
14 | Lexer *scanner.Lexer
15 | currentToken *token.Token
16 | rootNode ast.Vertex
17 | errHandlerFunc func(*errors.Error)
18 | builder *position.Builder
19 | }
20 |
21 | // NewParser creates and returns new Parser
22 | func NewParser(lexer *scanner.Lexer, config conf.Config) *Parser {
23 | return &Parser{
24 | Lexer: lexer,
25 | errHandlerFunc: config.ErrorHandlerFunc,
26 | builder: position.NewBuilder(),
27 | }
28 | }
29 |
30 | func (p *Parser) Lex(lval *yySymType) int {
31 | t := p.Lexer.Lex()
32 |
33 | p.currentToken = t
34 | lval.token = t
35 |
36 | return int(t.ID)
37 | }
38 |
39 | func (p *Parser) Error(msg string) {
40 | if p.errHandlerFunc == nil {
41 | return
42 | }
43 |
44 | p.errHandlerFunc(errors.NewError(msg, p.currentToken.Position))
45 | }
46 |
47 | // Parse the php7 Parser entrypoint
48 | func (p *Parser) Parse() int {
49 | p.rootNode = nil
50 |
51 | return yyParse(p)
52 | }
53 |
54 | // GetRootNode returns root node
55 | func (p *Parser) GetRootNode() ast.Vertex {
56 | return p.rootNode
57 | }
58 |
59 | // helpers
60 |
61 | func lastNode(nn []ast.Vertex) ast.Vertex {
62 | if len(nn) == 0 {
63 | return nil
64 | }
65 | return nn[len(nn)-1]
66 | }
67 |
--------------------------------------------------------------------------------
/internal/php7/php7_bench_test.go:
--------------------------------------------------------------------------------
1 | package php7_test
2 |
3 | import (
4 | "io/ioutil"
5 | "testing"
6 |
7 | "github.com/z7zmey/php-parser/internal/php7"
8 | "github.com/z7zmey/php-parser/internal/scanner"
9 | "github.com/z7zmey/php-parser/pkg/conf"
10 | "github.com/z7zmey/php-parser/pkg/version"
11 | )
12 |
13 | func BenchmarkPhp7(b *testing.B) {
14 | src, err := ioutil.ReadFile("test.php")
15 |
16 | if err != nil {
17 | b.Fatal("can not read test.php: " + err.Error())
18 | }
19 |
20 | for n := 0; n < b.N; n++ {
21 | config := conf.Config{
22 | Version: &version.Version{
23 | Major: 7,
24 | Minor: 4,
25 | },
26 | }
27 | lexer := scanner.NewLexer(src, config)
28 | php7parser := php7.NewParser(lexer, config)
29 | php7parser.Parse()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/internal/php7/test.php:
--------------------------------------------------------------------------------
1 |
2 | foo($a, ...$b);
3 | $foo($a, ...$b);
4 | $foo->bar($a, ...$b);
5 | foo::bar($a, ...$b);
6 | $foo::bar($a, ...$b);
7 | new foo($a, ...$b);
8 | /** anonymous class */
9 | new class ($a, ...$b) {};
10 | new class {};
11 | new $foo;
12 | new $foo[1];
13 | new $foo{$bar};
14 | new $foo->bar;
15 | new $foo::$bar;
16 | new static::$bar;
17 |
18 | function foo(?bar $bar=null, baz &...$baz) {}
19 | class foo {public function foo(?bar $bar=null, baz &...$baz) {}}
20 | function(?bar $bar=null, baz &...$baz) {};
21 | static function(?bar $bar=null, baz &...$baz) {};
22 |
23 | 1234567890123456789;
24 | 12345678901234567890;
25 | 0.;
26 | 0b0111111111111111111111111111111111111111111111111111111111111111;
27 | 0b1111111111111111111111111111111111111111111111111111111111111111;
28 | 0x007111111111111111;
29 | 0x8111111111111111;
30 | __CLASS__;
31 | __DIR__;
32 | __FILE__;
33 | __FUNCTION__;
34 | __LINE__;
35 | __NAMESPACE__;
36 | __METHOD__;
37 | __TRAIT__;
38 |
39 | "test $var";
40 | "test $var[1]";
41 | "test $var[-1]";
42 | "test $var[1234567890123456789012345678901234567890]";
43 | "test $var[-1234567890123456789012345678901234567890]";
44 | "test $var[bar]";
45 | "test $var[$bar]";
46 | "$foo $bar";
47 | "test $foo->bar()";
48 | "test ${foo}";
49 | "test ${foo[0]}";
50 | "test ${$foo}";
51 | "test {$foo->bar()}";
52 |
53 | if ($a) :
54 | endif;
55 | if ($a) :
56 | elseif ($b):
57 | endif;
58 | if ($a) :
59 | else:
60 | endif;
61 | if ($a) :
62 | elseif ($b):
63 | elseif ($c):
64 | else:
65 | endif;
66 |
67 | while (1) { break; }
68 | while (1) { break 2; }
69 | while (1) : break(3); endwhile;
70 | class foo{ public const FOO = 1, BAR = 2; }
71 | class foo{ const FOO = 1, BAR = 2; }
72 | class foo{ function bar() {} }
73 | class foo{ public static function &bar() {} }
74 | class foo{ public static function &bar(): void {} }
75 | abstract class foo{ }
76 | final class foo extends bar { }
77 | final class foo implements bar { }
78 | final class foo implements bar, baz { }
79 | new class() extends foo implements bar, baz { };
80 |
81 | const FOO = 1, BAR = 2;
82 | while (1) { continue; }
83 | while (1) { continue 2; }
84 | while (1) { continue(3); }
85 | declare(ticks=1);
86 | declare(ticks=1) {}
87 | declare(ticks=1): enddeclare;
88 | do {} while(1);
89 | echo $a, 1;
90 | echo($a);
91 | for($i = 0; $i < 10; $i++, $i++) {}
92 | for(; $i < 10; $i++, $i++) : endfor;
93 | foreach ($a as $v) {}
94 | foreach ($a as $v) : endforeach;
95 | foreach ($a as $k => $v) {}
96 | foreach ($a as $k => &$v) {}
97 | foreach ($a as $k => list($v)) {}
98 | foreach ($a as $k => [$v]) {}
99 | function foo() {}
100 | function foo() {return;}
101 | function &foo() {return 1;}
102 | function &foo(): void {}
103 | global $a, $b;
104 | a:
105 | goto a;
106 | if ($a) {}
107 | if ($a) {} elseif ($b) {}
108 | if ($a) {} else {}
109 | if ($a) {} elseif ($b) {} elseif ($c) {} else {}
110 | if ($a) {} elseif ($b) {} else if ($c) {} else {}
111 | ?>
112 | interface Foo {}
113 | interface Foo extends Bar {}
114 | interface Foo extends Bar, Baz {}
115 | namespace Foo;
116 | namespace Foo {}
117 | namespace {}
118 | class foo {var $a;}
119 | class foo {public static $a, $b = 1;}
120 | static $a, $b = 1;
121 |
122 | switch (1) :
123 | case 1:
124 | default:
125 | case 2:
126 | endswitch;
127 |
128 | switch (1) :;
129 | case 1;
130 | case 2;
131 | endswitch;
132 |
133 | switch (1) {
134 | case 1: break;
135 | case 2: break;
136 | }
137 |
138 | switch (1) {;
139 | case 1; break;
140 | case 2; break;
141 | }
142 |
143 | throw $e;
144 |
145 | trait Foo {}
146 | class Foo { use Bar; }
147 | class Foo { use Bar, Baz {} }
148 | class Foo { use Bar, Baz { one as include; } }
149 | class Foo { use Bar, Baz { one as public; } }
150 | class Foo { use Bar, Baz { one as public two; } }
151 | class Foo { use Bar, Baz { Bar::one insteadof Baz, Quux; Baz::one as two; } }
152 |
153 | try {}
154 | try {} catch (Exception $e) {}
155 | try {} catch (Exception|RuntimeException $e) {}
156 | try {} catch (Exception $e) {} catch (RuntimeException $e) {}
157 | try {} catch (Exception $e) {} finally {}
158 |
159 | unset($a, $b,);
160 |
161 | use Foo;
162 | use \Foo;
163 | use \Foo as Bar;
164 | use Foo, Bar;
165 | use Foo, Bar as Baz;
166 | use function Foo, \Bar;
167 | use function Foo as foo, \Bar as bar;
168 | use const Foo, \Bar;
169 | use const Foo as foo, \Bar as bar;
170 |
171 | use \Foo\{Bar, Baz};
172 | use Foo\{Bar, Baz as quux};
173 | use function Foo\{Bar, Baz};
174 | use const \Foo\{Bar, Baz};
175 | use Foo\{const Bar, function Baz};
176 |
177 | $a[1];
178 | $a[1][2];
179 | array();
180 | array(1);
181 | array(1=>1, &$b,);
182 | ~$a;
183 | !$a;
184 |
185 | Foo::Bar;
186 | $foo::Bar;
187 | clone($a);
188 | clone $a;
189 | function(){};
190 | function($a, $b) use ($c, &$d) {};
191 | function(): void {};
192 | foo;
193 | namespace\foo;
194 | \foo;
195 |
196 | empty($a);
197 | @$a;
198 | eval($a);
199 | exit;
200 | exit($a);
201 | die;
202 | die($a);
203 | foo();
204 | namespace\foo();
205 | \foo();
206 | $foo();
207 |
208 | $a--;
209 | $a++;
210 | --$a;
211 | ++$a;
212 |
213 | include $a;
214 | include_once $a;
215 | require $a;
216 | require_once $a;
217 |
218 | $a instanceof Foo;
219 | $a instanceof namespace\Foo;
220 | $a instanceof \Foo;
221 |
222 | isset($a, $b);
223 | list($a) = $b;
224 | list($a[]) = $b;
225 | list(list($a)) = $b;
226 |
227 | $a->foo();
228 | new Foo();
229 | new namespace\Foo();
230 | new \Foo();
231 | new class ($a, ...$b) {};
232 | print($a);
233 | $a->foo;
234 | `cmd $a`;
235 | `cmd`;
236 | ``;
237 | [];
238 | [1];
239 | [1=>1, &$b,];
240 |
241 | [$a] = $b;
242 | [$a[]] = $b;
243 | [list($a)] = $b;
244 | Foo::bar();
245 | namespace\Foo::bar();
246 | \Foo::bar();
247 | Foo::$bar;
248 | $foo::$bar;
249 | namespace\Foo::$bar;
250 | \Foo::$bar;
251 | $a ? $b : $c;
252 | $a ? : $c;
253 | $a ? $b ? $c : $d : $e;
254 | $a ? $b : $c ? $d : $e;
255 | -$a;
256 | +$a;
257 | $$a;
258 | yield;
259 | yield $a;
260 | yield $a => $b;
261 | yield from $a;
262 |
263 | (array)$a;
264 | (boolean)$a;
265 | (bool)$a;
266 | (double)$a;
267 | (float)$a;
268 | (integer)$a;
269 | (int)$a;
270 | (object)$a;
271 | (string)$a;
272 | (unset)$a;
273 |
274 | $a & $b;
275 | $a | $b;
276 | $a ^ $b;
277 | $a && $b;
278 | $a || $b;
279 | $a ?? $b;
280 | $a . $b;
281 | $a / $b;
282 | $a == $b;
283 | $a >= $b;
284 | $a > $b;
285 | $a === $b;
286 | $a and $b;
287 | $a or $b;
288 | $a xor $b;
289 | $a - $b;
290 | $a % $b;
291 | $a * $b;
292 | $a != $b;
293 | $a !== $b;
294 | $a + $b;
295 | $a ** $b;
296 | $a << $b;
297 | $a >> $b;
298 | $a <= $b;
299 | $a < $b;
300 | $a <=> $b;
301 |
302 | $a =& $b;
303 | $a = $b;
304 | $a &= $b;
305 | $a |= $b;
306 | $a ^= $b;
307 | $a .= $b;
308 | $a /= $b;
309 | $a -= $b;
310 | $a %= $b;
311 | $a *= $b;
312 | $a += $b;
313 | $a **= $b;
314 | $a <<= $b;
315 | $a >>= $b;
316 |
317 | class foo {public function class() {} }
318 | \foo\bar();
319 |
320 | function foo(&$a, ...$b) {
321 |
322 | function bar() {}
323 | class Baz {}
324 | trait Quux{}
325 | interface Quuux {}
326 | }
327 |
328 | function foo(&$a = 1, ...$b = 1, $c = 1) {}
329 | function foo(array $a, callable $b) {}
330 | abstract final class foo { abstract protected static function bar(); final private function baz() {} }
331 |
332 | (new Foo)->bar;
333 | (new Foo)();
334 | [$foo][0]();
335 | foo[1]();
336 | "foo"();
337 | [1]{$foo}();
338 | ${foo()};
339 |
340 | Foo::$bar();
341 | Foo::{$bar[0]}();
342 |
343 | $foo->$bar;
344 | $foo->{$bar[0]};
345 |
346 | [1=>&$a, 2=>list($b)];
347 |
348 | __halt_compiler();
349 |
350 | parsing process must be terminated
--------------------------------------------------------------------------------
/internal/position/position.go:
--------------------------------------------------------------------------------
1 | package position
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/ast"
5 | "github.com/z7zmey/php-parser/pkg/position"
6 | "github.com/z7zmey/php-parser/pkg/token"
7 | )
8 |
9 | type startPos struct {
10 | startLine int
11 | startPos int
12 | }
13 |
14 | type endPos struct {
15 | endLine int
16 | endPos int
17 | }
18 |
19 | type Builder struct {
20 | pool *position.Pool
21 | }
22 |
23 | func NewBuilder() *Builder {
24 | return &Builder{
25 | pool: position.NewPool(position.DefaultBlockSize),
26 | }
27 | }
28 |
29 | func getListStartPos(l []ast.Vertex) startPos {
30 | if l == nil {
31 | return startPos{-1, -1}
32 | }
33 |
34 | if len(l) == 0 {
35 | return startPos{-1, -1}
36 | }
37 |
38 | return getNodeStartPos(l[0])
39 | }
40 |
41 | func getNodeStartPos(n ast.Vertex) startPos {
42 | sl := -1
43 | sp := -1
44 |
45 | if n == nil {
46 | return startPos{-1, -1}
47 | }
48 |
49 | p := n.GetPosition()
50 | if p != nil {
51 | sl = p.StartLine
52 | sp = p.StartPos
53 | }
54 |
55 | return startPos{sl, sp}
56 | }
57 |
58 | func getListEndPos(l []ast.Vertex) endPos {
59 | if l == nil {
60 | return endPos{-1, -1}
61 | }
62 |
63 | if len(l) == 0 {
64 | return endPos{-1, -1}
65 | }
66 |
67 | return getNodeEndPos(l[len(l)-1])
68 | }
69 |
70 | func getNodeEndPos(n ast.Vertex) endPos {
71 | el := -1
72 | ep := -1
73 |
74 | if n == nil {
75 | return endPos{-1, -1}
76 | }
77 |
78 | p := n.GetPosition()
79 | if p != nil {
80 | el = p.EndLine
81 | ep = p.EndPos
82 | }
83 |
84 | return endPos{el, ep}
85 | }
86 |
87 | // NewNodeListPosition returns new Position
88 | func (b *Builder) NewNodeListPosition(list []ast.Vertex) *position.Position {
89 | pos := b.pool.Get()
90 |
91 | pos.StartLine = getListStartPos(list).startLine
92 | pos.EndLine = getListEndPos(list).endLine
93 | pos.StartPos = getListStartPos(list).startPos
94 | pos.EndPos = getListEndPos(list).endPos
95 |
96 | return pos
97 | }
98 |
99 | // NewNodePosition returns new Position
100 | func (b *Builder) NewNodePosition(n ast.Vertex) *position.Position {
101 | pos := b.pool.Get()
102 |
103 | pos.StartLine = getNodeStartPos(n).startLine
104 | pos.EndLine = getNodeEndPos(n).endLine
105 | pos.StartPos = getNodeStartPos(n).startPos
106 | pos.EndPos = getNodeEndPos(n).endPos
107 |
108 | return pos
109 | }
110 |
111 | // NewTokenPosition returns new Position
112 | func (b *Builder) NewTokenPosition(t *token.Token) *position.Position {
113 | pos := b.pool.Get()
114 |
115 | pos.StartLine = t.Position.StartLine
116 | pos.EndLine = t.Position.EndLine
117 | pos.StartPos = t.Position.StartPos
118 | pos.EndPos = t.Position.EndPos
119 |
120 | return pos
121 | }
122 |
123 | // NewTokensPosition returns new Position
124 | func (b *Builder) NewTokensPosition(startToken *token.Token, endToken *token.Token) *position.Position {
125 | pos := b.pool.Get()
126 |
127 | pos.StartLine = startToken.Position.StartLine
128 | pos.EndLine = endToken.Position.EndLine
129 | pos.StartPos = startToken.Position.StartPos
130 | pos.EndPos = endToken.Position.EndPos
131 |
132 | return pos
133 | }
134 |
135 | // NewTokenNodePosition returns new Position
136 | func (b *Builder) NewTokenNodePosition(t *token.Token, n ast.Vertex) *position.Position {
137 | pos := b.pool.Get()
138 |
139 | pos.StartLine = t.Position.StartLine
140 | pos.EndLine = getNodeEndPos(n).endLine
141 | pos.StartPos = t.Position.StartPos
142 | pos.EndPos = getNodeEndPos(n).endPos
143 |
144 | return pos
145 | }
146 |
147 | // NewNodeTokenPosition returns new Position
148 | func (b *Builder) NewNodeTokenPosition(n ast.Vertex, t *token.Token) *position.Position {
149 | pos := b.pool.Get()
150 |
151 | pos.StartLine = getNodeStartPos(n).startLine
152 | pos.EndLine = t.Position.EndLine
153 | pos.StartPos = getNodeStartPos(n).startPos
154 | pos.EndPos = t.Position.EndPos
155 |
156 | return pos
157 | }
158 |
159 | // NewNodesPosition returns new Position
160 | func (b *Builder) NewNodesPosition(startNode ast.Vertex, endNode ast.Vertex) *position.Position {
161 | pos := b.pool.Get()
162 |
163 | pos.StartLine = getNodeStartPos(startNode).startLine
164 | pos.EndLine = getNodeEndPos(endNode).endLine
165 | pos.StartPos = getNodeStartPos(startNode).startPos
166 | pos.EndPos = getNodeEndPos(endNode).endPos
167 |
168 | return pos
169 | }
170 |
171 | // NewNodeListTokenPosition returns new Position
172 | func (b *Builder) NewNodeListTokenPosition(list []ast.Vertex, t *token.Token) *position.Position {
173 | pos := b.pool.Get()
174 |
175 | pos.StartLine = getListStartPos(list).startLine
176 | pos.EndLine = t.Position.EndLine
177 | pos.StartPos = getListStartPos(list).startPos
178 | pos.EndPos = t.Position.EndPos
179 |
180 | return pos
181 | }
182 |
183 | // NewTokenNodeListPosition returns new Position
184 | func (b *Builder) NewTokenNodeListPosition(t *token.Token, list []ast.Vertex) *position.Position {
185 | pos := b.pool.Get()
186 |
187 | pos.StartLine = t.Position.StartLine
188 | pos.EndLine = getListEndPos(list).endLine
189 | pos.StartPos = t.Position.StartPos
190 | pos.EndPos = getListEndPos(list).endPos
191 |
192 | return pos
193 | }
194 |
195 | // NewNodeNodeListPosition returns new Position
196 | func (b *Builder) NewNodeNodeListPosition(n ast.Vertex, list []ast.Vertex) *position.Position {
197 | pos := b.pool.Get()
198 |
199 | pos.StartLine = getNodeStartPos(n).startLine
200 | pos.EndLine = getListEndPos(list).endLine
201 | pos.StartPos = getNodeStartPos(n).startPos
202 | pos.EndPos = getListEndPos(list).endPos
203 |
204 | return pos
205 | }
206 |
207 | // NewNodeListNodePosition returns new Position
208 | func (b *Builder) NewNodeListNodePosition(list []ast.Vertex, n ast.Vertex) *position.Position {
209 | pos := b.pool.Get()
210 |
211 | pos.StartLine = getListStartPos(list).startLine
212 | pos.EndLine = getNodeEndPos(n).endLine
213 | pos.StartPos = getListStartPos(list).startPos
214 | pos.EndPos = getNodeEndPos(n).endPos
215 |
216 | return pos
217 | }
218 |
219 | // NewOptionalListTokensPosition returns new Position
220 | func (b *Builder) NewOptionalListTokensPosition(list []ast.Vertex, t *token.Token, endToken *token.Token) *position.Position {
221 | pos := b.pool.Get()
222 |
223 | if list == nil {
224 | pos.StartLine = t.Position.StartLine
225 | pos.EndLine = endToken.Position.EndLine
226 | pos.StartPos = t.Position.StartPos
227 | pos.EndPos = endToken.Position.EndPos
228 |
229 | return pos
230 | }
231 | pos.StartLine = getListStartPos(list).startLine
232 | pos.EndLine = endToken.Position.EndLine
233 | pos.StartPos = getListStartPos(list).startPos
234 | pos.EndPos = endToken.Position.EndPos
235 |
236 | return pos
237 | }
238 |
--------------------------------------------------------------------------------
/internal/position/position_test.go:
--------------------------------------------------------------------------------
1 | package position_test
2 |
3 | import (
4 | "gotest.tools/assert"
5 | "testing"
6 |
7 | builder "github.com/z7zmey/php-parser/internal/position"
8 | "github.com/z7zmey/php-parser/pkg/ast"
9 | "github.com/z7zmey/php-parser/pkg/position"
10 | "github.com/z7zmey/php-parser/pkg/token"
11 | )
12 |
13 | func TestNewTokenPosition(t *testing.T) {
14 | tkn := &token.Token{
15 | Value: []byte(`foo`),
16 | Position: &position.Position{
17 | StartLine: 1,
18 | EndLine: 1,
19 | StartPos: 0,
20 | EndPos: 3,
21 | },
22 | }
23 |
24 | pos := builder.NewBuilder().NewTokenPosition(tkn)
25 |
26 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 1, EndPos: 3}, pos)
27 | }
28 |
29 | func TestNewTokensPosition(t *testing.T) {
30 | token1 := &token.Token{
31 | Value: []byte(`foo`),
32 | Position: &position.Position{
33 | StartLine: 1,
34 | EndLine: 1,
35 | StartPos: 0,
36 | EndPos: 3,
37 | },
38 | }
39 | token2 := &token.Token{
40 | Value: []byte(`foo`),
41 | Position: &position.Position{
42 | StartLine: 2,
43 | EndLine: 2,
44 | StartPos: 4,
45 | EndPos: 6,
46 | },
47 | }
48 |
49 | pos := builder.NewBuilder().NewTokensPosition(token1, token2)
50 |
51 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 6}, pos)
52 | }
53 |
54 | func TestNewNodePosition(t *testing.T) {
55 | n := &ast.Identifier{
56 | Position: &position.Position{
57 | StartLine: 1,
58 | EndLine: 1,
59 | StartPos: 0,
60 | EndPos: 3,
61 | },
62 | }
63 |
64 | pos := builder.NewBuilder().NewNodePosition(n)
65 |
66 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 1, EndPos: 3}, pos)
67 | }
68 |
69 | func TestNewTokenNodePosition(t *testing.T) {
70 | tkn := &token.Token{
71 | Value: []byte(`foo`),
72 | Position: &position.Position{
73 | StartLine: 1,
74 | EndLine: 1,
75 | StartPos: 0,
76 | EndPos: 3,
77 | },
78 | }
79 | n := &ast.Identifier{
80 | Position: &position.Position{
81 | StartLine: 2,
82 | EndLine: 2,
83 | StartPos: 4,
84 | EndPos: 12,
85 | },
86 | }
87 |
88 | pos := builder.NewBuilder().NewTokenNodePosition(tkn, n)
89 |
90 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 12}, pos)
91 | }
92 |
93 | func TestNewNodeTokenPosition(t *testing.T) {
94 | n := &ast.Identifier{
95 | Position: &position.Position{
96 | StartLine: 1,
97 | EndLine: 1,
98 | StartPos: 0,
99 | EndPos: 9,
100 | },
101 | }
102 |
103 | tkn := &token.Token{
104 | Value: []byte(`foo`),
105 | Position: &position.Position{
106 | StartLine: 2,
107 | EndLine: 2,
108 | StartPos: 10,
109 | EndPos: 12,
110 | },
111 | }
112 |
113 | pos := builder.NewBuilder().NewNodeTokenPosition(n, tkn)
114 |
115 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 12}, pos)
116 | }
117 |
118 | func TestNewNodeListPosition(t *testing.T) {
119 | n1 := &ast.Identifier{
120 | Position: &position.Position{
121 | StartLine: 1,
122 | EndLine: 1,
123 | StartPos: 0,
124 | EndPos: 9,
125 | },
126 | }
127 |
128 | n2 := &ast.Identifier{
129 | Position: &position.Position{
130 | StartLine: 2,
131 | EndLine: 2,
132 | StartPos: 10,
133 | EndPos: 19,
134 | },
135 | }
136 |
137 | pos := builder.NewBuilder().NewNodeListPosition([]ast.Vertex{n1, n2})
138 |
139 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 19}, pos)
140 | }
141 |
142 | func TestNewNodesPosition(t *testing.T) {
143 | n1 := &ast.Identifier{
144 | Position: &position.Position{
145 | StartLine: 1,
146 | EndLine: 1,
147 | StartPos: 0,
148 | EndPos: 9,
149 | },
150 | }
151 |
152 | n2 := &ast.Identifier{
153 | Position: &position.Position{
154 | StartLine: 2,
155 | EndLine: 2,
156 | StartPos: 10,
157 | EndPos: 19,
158 | },
159 | }
160 |
161 | pos := builder.NewBuilder().NewNodesPosition(n1, n2)
162 |
163 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 19}, pos)
164 | }
165 |
166 | func TestNewNodeListTokenPosition(t *testing.T) {
167 | n1 := &ast.Identifier{
168 | Position: &position.Position{
169 | StartLine: 1,
170 | EndLine: 1,
171 | StartPos: 0,
172 | EndPos: 9,
173 | },
174 | }
175 |
176 | n2 := &ast.Identifier{
177 | Position: &position.Position{
178 | StartLine: 2,
179 | EndLine: 2,
180 | StartPos: 10,
181 | EndPos: 19,
182 | },
183 | }
184 |
185 | tkn := &token.Token{
186 | Value: []byte(`foo`),
187 | Position: &position.Position{
188 | StartLine: 3,
189 | EndLine: 3,
190 | StartPos: 20,
191 | EndPos: 22,
192 | },
193 | }
194 |
195 | pos := builder.NewBuilder().NewNodeListTokenPosition([]ast.Vertex{n1, n2}, tkn)
196 |
197 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 22}, pos)
198 | }
199 |
200 | func TestNewTokenNodeListPosition(t *testing.T) {
201 | tkn := &token.Token{
202 | Value: []byte(`foo`),
203 | Position: &position.Position{
204 | StartLine: 1,
205 | EndLine: 1,
206 | StartPos: 0,
207 | EndPos: 2,
208 | },
209 | }
210 |
211 | n1 := &ast.Identifier{
212 | Position: &position.Position{
213 | StartLine: 2,
214 | EndLine: 2,
215 | StartPos: 3,
216 | EndPos: 10,
217 | },
218 | }
219 |
220 | n2 := &ast.Identifier{
221 | Position: &position.Position{
222 | StartLine: 3,
223 | EndLine: 3,
224 | StartPos: 11,
225 | EndPos: 20,
226 | },
227 | }
228 |
229 | pos := builder.NewBuilder().NewTokenNodeListPosition(tkn, []ast.Vertex{n1, n2})
230 |
231 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 20}, pos)
232 | }
233 |
234 | func TestNewNodeNodeListPosition(t *testing.T) {
235 | n1 := &ast.Identifier{
236 | Position: &position.Position{
237 | StartLine: 1,
238 | EndLine: 1,
239 | StartPos: 0,
240 | EndPos: 8,
241 | },
242 | }
243 |
244 | n2 := &ast.Identifier{
245 | Position: &position.Position{
246 | StartLine: 2,
247 | EndLine: 2,
248 | StartPos: 9,
249 | EndPos: 17,
250 | },
251 | }
252 |
253 | n3 := &ast.Identifier{
254 | Position: &position.Position{
255 | StartLine: 3,
256 | EndLine: 3,
257 | StartPos: 18,
258 | EndPos: 26,
259 | },
260 | }
261 |
262 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, []ast.Vertex{n2, n3})
263 |
264 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 26}, pos)
265 | }
266 |
267 | func TestNewNodeListNodePosition(t *testing.T) {
268 | n1 := &ast.Identifier{
269 | Position: &position.Position{
270 | StartLine: 1,
271 | EndLine: 1,
272 | StartPos: 0,
273 | EndPos: 8,
274 | },
275 | }
276 | n2 := &ast.Identifier{
277 | Position: &position.Position{
278 | StartLine: 2,
279 | EndLine: 2,
280 | StartPos: 9,
281 | EndPos: 17,
282 | },
283 | }
284 | n3 := &ast.Identifier{
285 | Position: &position.Position{
286 | StartLine: 3,
287 | EndLine: 3,
288 | StartPos: 18,
289 | EndPos: 26,
290 | },
291 | }
292 |
293 | pos := builder.NewBuilder().NewNodeListNodePosition([]ast.Vertex{n1, n2}, n3)
294 |
295 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 3, EndPos: 26}, pos)
296 | }
297 |
298 | func TestNewOptionalListTokensPosition(t *testing.T) {
299 | token1 := &token.Token{
300 | Value: []byte(`foo`),
301 | Position: &position.Position{
302 | StartLine: 1,
303 | EndLine: 1,
304 | StartPos: 0,
305 | EndPos: 3,
306 | },
307 | }
308 | token2 := &token.Token{
309 | Value: []byte(`foo`),
310 | Position: &position.Position{
311 | StartLine: 2,
312 | EndLine: 2,
313 | StartPos: 4,
314 | EndPos: 6,
315 | },
316 | }
317 |
318 | pos := builder.NewBuilder().NewOptionalListTokensPosition(nil, token1, token2)
319 |
320 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: 2, EndPos: 6}, pos)
321 | }
322 |
323 | func TestNewOptionalListTokensPosition2(t *testing.T) {
324 | n2 := &ast.Identifier{
325 | Position: &position.Position{
326 | StartLine: 2,
327 | EndLine: 2,
328 | StartPos: 9,
329 | EndPos: 17,
330 | },
331 | }
332 | n3 := &ast.Identifier{
333 | Position: &position.Position{
334 | StartLine: 3,
335 | EndLine: 3,
336 | StartPos: 18,
337 | EndPos: 26,
338 | },
339 | }
340 |
341 | token1 := &token.Token{
342 | Value: []byte(`foo`),
343 | Position: &position.Position{
344 | StartLine: 4,
345 | EndLine: 4,
346 | StartPos: 27,
347 | EndPos: 29,
348 | },
349 | }
350 | token2 := &token.Token{
351 | Value: []byte(`foo`),
352 | Position: &position.Position{
353 | StartLine: 5,
354 | EndLine: 5,
355 | StartPos: 30,
356 | EndPos: 32,
357 | },
358 | }
359 |
360 | pos := builder.NewBuilder().NewOptionalListTokensPosition([]ast.Vertex{n2, n3}, token1, token2)
361 |
362 | assert.DeepEqual(t, &position.Position{StartLine: 2, EndLine: 5, StartPos: 9, EndPos: 32}, pos)
363 | }
364 |
365 | func TestNilNodePos(t *testing.T) {
366 | pos := builder.NewBuilder().NewNodesPosition(nil, nil)
367 |
368 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: -1, StartPos: -1, EndPos: -1}, pos)
369 | }
370 |
371 | func TestNilNodeListPos(t *testing.T) {
372 | n1 := &ast.Identifier{
373 | Position: &position.Position{
374 | StartLine: 1,
375 | EndLine: 1,
376 | StartPos: 0,
377 | EndPos: 8,
378 | },
379 | }
380 |
381 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, nil)
382 |
383 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: -1, EndPos: -1}, pos)
384 | }
385 |
386 | func TestNilNodeListTokenPos(t *testing.T) {
387 | tkn := &token.Token{
388 | Value: []byte(`foo`),
389 | Position: &position.Position{
390 | StartLine: 1,
391 | EndLine: 1,
392 | StartPos: 0,
393 | EndPos: 3,
394 | },
395 | }
396 |
397 | pos := builder.NewBuilder().NewNodeListTokenPosition(nil, tkn)
398 |
399 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: 1, StartPos: -1, EndPos: 3}, pos)
400 | }
401 |
402 | func TestEmptyNodeListPos(t *testing.T) {
403 | n1 := &ast.Identifier{
404 | Position: &position.Position{
405 | StartLine: 1,
406 | EndLine: 1,
407 | StartPos: 0,
408 | EndPos: 8,
409 | },
410 | }
411 |
412 | pos := builder.NewBuilder().NewNodeNodeListPosition(n1, []ast.Vertex{})
413 |
414 | assert.DeepEqual(t, &position.Position{StartLine: 1, EndLine: -1, EndPos: -1}, pos)
415 | }
416 |
417 | func TestEmptyNodeListTokenPos(t *testing.T) {
418 | tkn := &token.Token{
419 | Value: []byte(`foo`),
420 | Position: &position.Position{
421 | StartLine: 1,
422 | EndLine: 1,
423 | StartPos: 0,
424 | EndPos: 3,
425 | },
426 | }
427 |
428 | pos := builder.NewBuilder().NewNodeListTokenPosition([]ast.Vertex{}, tkn)
429 |
430 | assert.DeepEqual(t, &position.Position{StartLine: -1, EndLine: 1, StartPos: -1, EndPos: 3}, pos)
431 | }
432 |
--------------------------------------------------------------------------------
/internal/scanner/lexer.go:
--------------------------------------------------------------------------------
1 | package scanner
2 |
3 | import (
4 | "bytes"
5 | "strings"
6 |
7 | "github.com/z7zmey/php-parser/pkg/conf"
8 | "github.com/z7zmey/php-parser/pkg/errors"
9 | "github.com/z7zmey/php-parser/pkg/position"
10 | "github.com/z7zmey/php-parser/pkg/token"
11 | "github.com/z7zmey/php-parser/pkg/version"
12 | )
13 |
14 | type Lexer struct {
15 | data []byte
16 | phpVersion *version.Version
17 | errHandlerFunc func(*errors.Error)
18 |
19 | p, pe, cs int
20 | ts, te, act int
21 | stack []int
22 | top int
23 |
24 | heredocLabel []byte
25 | tokenPool *token.Pool
26 | positionPool *position.Pool
27 | newLines NewLines
28 | }
29 |
30 | func NewLexer(data []byte, config conf.Config) *Lexer {
31 | lex := &Lexer{
32 | data: data,
33 | phpVersion: config.Version,
34 | errHandlerFunc: config.ErrorHandlerFunc,
35 |
36 | pe: len(data),
37 | stack: make([]int, 0),
38 |
39 | tokenPool: token.NewPool(position.DefaultBlockSize),
40 | positionPool: position.NewPool(token.DefaultBlockSize),
41 | newLines: NewLines{make([]int, 0, 128)},
42 | }
43 |
44 | initLexer(lex)
45 |
46 | return lex
47 | }
48 |
49 | func (lex *Lexer) setTokenPosition(token *token.Token) {
50 | pos := lex.positionPool.Get()
51 |
52 | pos.StartLine = lex.newLines.GetLine(lex.ts)
53 | pos.EndLine = lex.newLines.GetLine(lex.te - 1)
54 | pos.StartPos = lex.ts
55 | pos.EndPos = lex.te
56 |
57 | token.Position = pos
58 | }
59 |
60 | func (lex *Lexer) addFreeFloatingToken(t *token.Token, id token.ID, ps, pe int) {
61 | skippedTkn := lex.tokenPool.Get()
62 | skippedTkn.ID = id
63 | skippedTkn.Value = lex.data[ps:pe]
64 |
65 | lex.setTokenPosition(skippedTkn)
66 |
67 | if t.FreeFloating == nil {
68 | t.FreeFloating = make([]*token.Token, 0, 2)
69 | }
70 |
71 | t.FreeFloating = append(t.FreeFloating, skippedTkn)
72 | }
73 |
74 | func (lex *Lexer) isNotStringVar() bool {
75 | p := lex.p
76 | if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' {
77 | return true
78 | }
79 |
80 | if len(lex.data) < p+1 {
81 | return true
82 | }
83 |
84 | if lex.data[p] == '$' && (lex.data[p+1] == '{' || isValidVarNameStart(lex.data[p+1])) {
85 | return false
86 | }
87 |
88 | if lex.data[p] == '{' && lex.data[p+1] == '$' {
89 | return false
90 | }
91 |
92 | return true
93 | }
94 |
95 | func (lex *Lexer) isNotStringEnd(s byte) bool {
96 | p := lex.p
97 | if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' {
98 | return true
99 | }
100 |
101 | return !(lex.data[p] == s)
102 | }
103 |
104 | func (lex *Lexer) isHeredocEnd(p int) bool {
105 | o, err := version.New("7.3")
106 | if err != nil {
107 | panic(err)
108 | }
109 |
110 | if lex.phpVersion.GreaterOrEqual(o) {
111 | return lex.isHeredocEndSince73(p)
112 | }
113 |
114 | return lex.isHeredocEndBefore73(p)
115 | }
116 |
117 | func (lex *Lexer) isHeredocEndBefore73(p int) bool {
118 | if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
119 | return false
120 | }
121 |
122 | l := len(lex.heredocLabel)
123 | if len(lex.data) < p+l {
124 | return false
125 | }
126 |
127 | if len(lex.data) > p+l && lex.data[p+l] != ';' && lex.data[p+l] != '\r' && lex.data[p+l] != '\n' {
128 | return false
129 | }
130 |
131 | if len(lex.data) > p+l+1 && lex.data[p+l] == ';' && lex.data[p+l+1] != '\r' && lex.data[p+l+1] != '\n' {
132 | return false
133 | }
134 |
135 | return bytes.Equal(lex.heredocLabel, lex.data[p:p+l])
136 | }
137 |
138 | func (lex *Lexer) isHeredocEndSince73(p int) bool {
139 | if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
140 | return false
141 | }
142 |
143 | if p == len(lex.data) {
144 | return false
145 | }
146 |
147 | for lex.data[p] == ' ' || lex.data[p] == '\t' {
148 | p++
149 | }
150 |
151 | l := len(lex.heredocLabel)
152 | if len(lex.data) < p+l {
153 | return false
154 | }
155 |
156 | if len(lex.data) > p+l && isValidVarName(lex.data[p+l]) {
157 | return false
158 | }
159 |
160 | a := string(lex.heredocLabel)
161 | b := string(lex.data[p : p+l])
162 |
163 | _, _ = a, b
164 |
165 | if bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) {
166 | lex.p = p
167 | return true
168 | }
169 |
170 | return false
171 | }
172 |
173 | func (lex *Lexer) isNotHeredocEnd(p int) bool {
174 | return !lex.isHeredocEnd(p)
175 | }
176 |
177 | func (lex *Lexer) growCallStack() {
178 | if lex.top == len(lex.stack) {
179 | lex.stack = append(lex.stack, 0)
180 | }
181 | }
182 |
183 | func (lex *Lexer) isNotPhpCloseToken() bool {
184 | if lex.p+1 == len(lex.data) {
185 | return true
186 | }
187 |
188 | return lex.data[lex.p] != '?' || lex.data[lex.p+1] != '>'
189 | }
190 |
191 | func (lex *Lexer) isNotNewLine() bool {
192 | if lex.data[lex.p] == '\n' && lex.data[lex.p-1] == '\r' {
193 | return true
194 | }
195 |
196 | return lex.data[lex.p-1] != '\n' && lex.data[lex.p-1] != '\r'
197 | }
198 |
199 | func (lex *Lexer) call(state int, fnext int) {
200 | lex.growCallStack()
201 |
202 | lex.stack[lex.top] = state
203 | lex.top++
204 |
205 | lex.p++
206 | lex.cs = fnext
207 | }
208 |
209 | func (lex *Lexer) ret(n int) {
210 | lex.top = lex.top - n
211 | if lex.top < 0 {
212 | lex.top = 0
213 | }
214 | lex.cs = lex.stack[lex.top]
215 | lex.p++
216 | }
217 |
218 | func (lex *Lexer) ungetStr(s string) {
219 | tokenStr := string(lex.data[lex.ts:lex.te])
220 | if strings.HasSuffix(tokenStr, s) {
221 | lex.ungetCnt(len(s))
222 | }
223 | }
224 |
225 | func (lex *Lexer) ungetCnt(n int) {
226 | lex.p = lex.p - n
227 | lex.te = lex.te - n
228 | }
229 |
230 | func (lex *Lexer) error(msg string) {
231 | if lex.errHandlerFunc == nil {
232 | return
233 | }
234 |
235 | pos := position.NewPosition(
236 | lex.newLines.GetLine(lex.ts),
237 | lex.newLines.GetLine(lex.te-1),
238 | lex.ts,
239 | lex.te,
240 | )
241 |
242 | lex.errHandlerFunc(errors.NewError(msg, pos))
243 | }
244 |
245 | func isValidVarNameStart(r byte) bool {
246 | return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || r >= 0x80
247 | }
248 |
249 | func isValidVarName(r byte) bool {
250 | return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r >= 0x80
251 | }
252 |
--------------------------------------------------------------------------------
/internal/scanner/newline.go:
--------------------------------------------------------------------------------
1 | package scanner
2 |
3 | type NewLines struct {
4 | data []int
5 | }
6 |
7 | func (nl *NewLines) Append(p int) {
8 | if len(nl.data) == 0 || nl.data[len(nl.data)-1] < p {
9 | nl.data = append(nl.data, p)
10 | }
11 | }
12 |
13 | func (nl *NewLines) GetLine(p int) int {
14 | line := len(nl.data) + 1
15 |
16 | for i := len(nl.data) - 1; i >= 0; i-- {
17 | if p < nl.data[i] {
18 | line = i + 1
19 | } else {
20 | break
21 | }
22 | }
23 |
24 | return line
25 | }
26 |
--------------------------------------------------------------------------------
/internal/scanner/scanner.rl:
--------------------------------------------------------------------------------
1 | package scanner
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 |
8 | "github.com/z7zmey/php-parser/pkg/token"
9 | )
10 |
11 | %%{
12 | machine lexer;
13 | write data;
14 | access lex.;
15 | variable p lex.p;
16 | variable pe lex.pe;
17 | }%%
18 |
19 | func initLexer(lex *Lexer) {
20 | %% write init;
21 | }
22 |
23 | func (lex *Lexer) Lex() *token.Token {
24 | eof := lex.pe
25 | var tok token.ID
26 |
27 | tkn := lex.tokenPool.Get()
28 |
29 | lblStart := 0
30 | lblEnd := 0
31 |
32 | _, _ = lblStart, lblEnd
33 |
34 | %%{
35 | action heredoc_lbl_start {lblStart = lex.p}
36 | action heredoc_lbl_end {lblEnd = lex.p}
37 |
38 | action new_line {
39 | if lex.data[lex.p] == '\n' {
40 | lex.newLines.Append(lex.p+1)
41 | }
42 |
43 | if lex.data[lex.p] == '\r' && lex.data[lex.p+1] != '\n' {
44 | lex.newLines.Append(lex.p+1)
45 | }
46 | }
47 |
48 | action is_not_heredoc_end { lex.isNotHeredocEnd(lex.p) }
49 | action is_not_comment_end { lex.isNotPhpCloseToken() && lex.isNotNewLine() }
50 | action is_not_heredoc_end_or_var { lex.isNotHeredocEnd(lex.p) && lex.isNotStringVar() }
51 | action is_not_string_end_or_var { lex.isNotStringEnd('"') && lex.isNotStringVar() }
52 | action is_not_backqoute_end_or_var { lex.isNotStringEnd('`') && lex.isNotStringVar() }
53 |
54 | newline = ('\r\n' >(nl, 1) | '\r' >(nl, 0) | '\n' >(nl, 0)) $new_line %{};
55 | any_line = any | newline;
56 | whitespace = [\t\v\f ];
57 | whitespace_line = [\t\v\f ] | newline;
58 |
59 | lnum = [0-9]+('_'[0-9]+)*;
60 | dnum = (lnum?"." lnum)|(lnum"."lnum?);
61 | hnum = '0x'[0-9a-fA-F]+('_'[0-9a-fA-F]+)*;
62 | bnum = '0b'[01]+('_'[01]+)*;
63 |
64 | exponent_dnum = (lnum | dnum) ('e'|'E') ('+'|'-')? lnum;
65 | varname_first = [a-zA-Z_] | (0x0080..0x00FF);
66 | varname_second = varname_first | [0-9];
67 | varname = varname_first (varname_second)*;
68 | heredoc_label = varname >heredoc_lbl_start %heredoc_lbl_end;
69 | operators = ';'|':'|','|'.'|'['|']'|'('|')'|'|'|'/'|'^'|'&'|'+'|'-'|'*'|'='|'%'|'!'|'~'|'$'|'<'|'>'|'?'|'@';
70 |
71 | prepush { lex.growCallStack(); }
72 |
73 | constant_string =
74 | start: (
75 | "'" -> qoute
76 | | "b"i? '"' -> double_qoute
77 | ),
78 |
79 | # single qoute string
80 |
81 | qoute: (
82 | (any - [\\'\r\n]) -> qoute
83 | | "\r" @new_line -> qoute
84 | | "\n" @new_line -> qoute
85 | | "\\" -> qoute_any
86 | | "'" -> final
87 | ),
88 | qoute_any: (
89 | (any - [\r\n]) -> qoute
90 | | "\r" @new_line -> qoute
91 | | "\n" @new_line -> qoute
92 | ),
93 |
94 | # double qoute string
95 |
96 | double_qoute: (
97 | (any - [\\"${\r\n]) -> double_qoute
98 | | "\r" @new_line -> double_qoute
99 | | "\n" @new_line -> double_qoute
100 | | "\\" -> double_qoute_any
101 | | '"' -> final
102 | | '$' -> double_qoute_nonvarname
103 | | '{' -> double_qoute_nondollar
104 | ),
105 | double_qoute_any: (
106 | (any - [\r\n]) -> double_qoute
107 | | "\r" @new_line -> double_qoute
108 | | "\n" @new_line -> double_qoute
109 | ),
110 | double_qoute_nondollar: (
111 | (any - [\\$"\r\n]) -> double_qoute
112 | | "\r" @new_line -> double_qoute
113 | | "\n" @new_line -> double_qoute
114 | | "\\" -> double_qoute_any
115 | | '"' -> final
116 | ),
117 | double_qoute_nonvarname: (
118 | (any - [\\${"\r\n] - varname_first) -> double_qoute
119 | | "\r" @new_line -> double_qoute
120 | | "\n" @new_line -> double_qoute
121 | | "\\" -> double_qoute_any
122 | | '$' -> double_qoute_nonvarname
123 | | '"' -> final
124 | );
125 |
126 | main := |*
127 | "#!" any* :>> newline => {
128 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te)
129 | };
130 | any => {
131 | fnext html;
132 | lex.ungetCnt(1)
133 | };
134 | *|;
135 |
136 | html := |*
137 | any_line+ -- '' => {
138 | lex.ungetStr("<")
139 | lex.setTokenPosition(tkn)
140 | tok = token.T_INLINE_HTML;
141 | fbreak;
142 | };
143 | '' => {
144 | lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.te)
145 | fnext php;
146 | };
147 | ' {
148 | lex.ungetCnt(lex.te - lex.ts - 5)
149 | lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.ts+5)
150 | fnext php;
151 | };
152 | '='i => {
153 | lex.setTokenPosition(tkn);
154 | tok = token.T_ECHO;
155 | fnext php;
156 | fbreak;
157 | };
158 | *|;
159 |
160 | php := |*
161 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)};
162 | '?>' newline? => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;};
163 | ';' whitespace_line* '?>' newline? => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;};
164 |
165 | (dnum | exponent_dnum) => {lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;};
166 | bnum => {
167 | s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1)
168 | _, err := strconv.ParseInt(s, 2, 0)
169 |
170 | if err == nil {
171 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak;
172 | }
173 |
174 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;
175 | };
176 | lnum => {
177 | base := 10
178 | if lex.data[lex.ts] == '0' {
179 | base = 8
180 | }
181 |
182 | s := strings.Replace(string(lex.data[lex.ts:lex.te]), "_", "", -1)
183 | _, err := strconv.ParseInt(s, base, 0)
184 |
185 | if err == nil {
186 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak;
187 | }
188 |
189 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;
190 | };
191 | hnum => {
192 | s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1)
193 | _, err := strconv.ParseInt(s, 16, 0)
194 |
195 | if err == nil {
196 | lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak;
197 | }
198 |
199 | lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;
200 | };
201 |
202 | 'abstract'i => {lex.setTokenPosition(tkn); tok = token.T_ABSTRACT; fbreak;};
203 | 'array'i => {lex.setTokenPosition(tkn); tok = token.T_ARRAY; fbreak;};
204 | 'as'i => {lex.setTokenPosition(tkn); tok = token.T_AS; fbreak;};
205 | 'break'i => {lex.setTokenPosition(tkn); tok = token.T_BREAK; fbreak;};
206 | 'callable'i => {lex.setTokenPosition(tkn); tok = token.T_CALLABLE; fbreak;};
207 | 'case'i => {lex.setTokenPosition(tkn); tok = token.T_CASE; fbreak;};
208 | 'catch'i => {lex.setTokenPosition(tkn); tok = token.T_CATCH; fbreak;};
209 | 'class'i => {lex.setTokenPosition(tkn); tok = token.T_CLASS; fbreak;};
210 | 'clone'i => {lex.setTokenPosition(tkn); tok = token.T_CLONE; fbreak;};
211 | 'const'i => {lex.setTokenPosition(tkn); tok = token.T_CONST; fbreak;};
212 | 'continue'i => {lex.setTokenPosition(tkn); tok = token.T_CONTINUE; fbreak;};
213 | 'declare'i => {lex.setTokenPosition(tkn); tok = token.T_DECLARE; fbreak;};
214 | 'default'i => {lex.setTokenPosition(tkn); tok = token.T_DEFAULT; fbreak;};
215 | 'do'i => {lex.setTokenPosition(tkn); tok = token.T_DO; fbreak;};
216 | 'echo'i => {lex.setTokenPosition(tkn); tok = token.T_ECHO; fbreak;};
217 | 'else'i => {lex.setTokenPosition(tkn); tok = token.T_ELSE; fbreak;};
218 | 'elseif'i => {lex.setTokenPosition(tkn); tok = token.T_ELSEIF; fbreak;};
219 | 'empty'i => {lex.setTokenPosition(tkn); tok = token.T_EMPTY; fbreak;};
220 | 'enddeclare'i => {lex.setTokenPosition(tkn); tok = token.T_ENDDECLARE; fbreak;};
221 | 'endfor'i => {lex.setTokenPosition(tkn); tok = token.T_ENDFOR; fbreak;};
222 | 'endforeach'i => {lex.setTokenPosition(tkn); tok = token.T_ENDFOREACH; fbreak;};
223 | 'endif'i => {lex.setTokenPosition(tkn); tok = token.T_ENDIF; fbreak;};
224 | 'endswitch'i => {lex.setTokenPosition(tkn); tok = token.T_ENDSWITCH; fbreak;};
225 | 'endwhile'i => {lex.setTokenPosition(tkn); tok = token.T_ENDWHILE; fbreak;};
226 | 'eval'i => {lex.setTokenPosition(tkn); tok = token.T_EVAL; fbreak;};
227 | 'exit'i | 'die'i => {lex.setTokenPosition(tkn); tok = token.T_EXIT; fbreak;};
228 | 'extends'i => {lex.setTokenPosition(tkn); tok = token.T_EXTENDS; fbreak;};
229 | 'final'i => {lex.setTokenPosition(tkn); tok = token.T_FINAL; fbreak;};
230 | 'finally'i => {lex.setTokenPosition(tkn); tok = token.T_FINALLY; fbreak;};
231 | 'for'i => {lex.setTokenPosition(tkn); tok = token.T_FOR; fbreak;};
232 | 'foreach'i => {lex.setTokenPosition(tkn); tok = token.T_FOREACH; fbreak;};
233 | 'function'i | 'cfunction'i => {lex.setTokenPosition(tkn); tok = token.T_FUNCTION; fbreak;};
234 | 'fn'i => {lex.setTokenPosition(tkn); tok = token.T_FN; fbreak;};
235 | 'global'i => {lex.setTokenPosition(tkn); tok = token.T_GLOBAL; fbreak;};
236 | 'goto'i => {lex.setTokenPosition(tkn); tok = token.T_GOTO; fbreak;};
237 | 'if'i => {lex.setTokenPosition(tkn); tok = token.T_IF; fbreak;};
238 | 'isset'i => {lex.setTokenPosition(tkn); tok = token.T_ISSET; fbreak;};
239 | 'implements'i => {lex.setTokenPosition(tkn); tok = token.T_IMPLEMENTS; fbreak;};
240 | 'instanceof'i => {lex.setTokenPosition(tkn); tok = token.T_INSTANCEOF; fbreak;};
241 | 'insteadof'i => {lex.setTokenPosition(tkn); tok = token.T_INSTEADOF; fbreak;};
242 | 'interface'i => {lex.setTokenPosition(tkn); tok = token.T_INTERFACE; fbreak;};
243 | 'list'i => {lex.setTokenPosition(tkn); tok = token.T_LIST; fbreak;};
244 | 'namespace'i => {lex.setTokenPosition(tkn); tok = token.T_NAMESPACE; fbreak;};
245 | 'private'i => {lex.setTokenPosition(tkn); tok = token.T_PRIVATE; fbreak;};
246 | 'public'i => {lex.setTokenPosition(tkn); tok = token.T_PUBLIC; fbreak;};
247 | 'print'i => {lex.setTokenPosition(tkn); tok = token.T_PRINT; fbreak;};
248 | 'protected'i => {lex.setTokenPosition(tkn); tok = token.T_PROTECTED; fbreak;};
249 | 'return'i => {lex.setTokenPosition(tkn); tok = token.T_RETURN; fbreak;};
250 | 'static'i => {lex.setTokenPosition(tkn); tok = token.T_STATIC; fbreak;};
251 | 'switch'i => {lex.setTokenPosition(tkn); tok = token.T_SWITCH; fbreak;};
252 | 'throw'i => {lex.setTokenPosition(tkn); tok = token.T_THROW; fbreak;};
253 | 'trait'i => {lex.setTokenPosition(tkn); tok = token.T_TRAIT; fbreak;};
254 | 'try'i => {lex.setTokenPosition(tkn); tok = token.T_TRY; fbreak;};
255 | 'unset'i => {lex.setTokenPosition(tkn); tok = token.T_UNSET; fbreak;};
256 | 'use'i => {lex.setTokenPosition(tkn); tok = token.T_USE; fbreak;};
257 | 'var'i => {lex.setTokenPosition(tkn); tok = token.T_VAR; fbreak;};
258 | 'while'i => {lex.setTokenPosition(tkn); tok = token.T_WHILE; fbreak;};
259 | 'yield'i whitespace_line+ 'from'i => {lex.setTokenPosition(tkn); tok = token.T_YIELD_FROM; fbreak;};
260 | 'yield'i => {lex.setTokenPosition(tkn); tok = token.T_YIELD; fbreak;};
261 | 'include'i => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE; fbreak;};
262 | 'include_once'i => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE_ONCE; fbreak;};
263 | 'require'i => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE; fbreak;};
264 | 'require_once'i => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE_ONCE; fbreak;};
265 | '__CLASS__'i => {lex.setTokenPosition(tkn); tok = token.T_CLASS_C; fbreak;};
266 | '__DIR__'i => {lex.setTokenPosition(tkn); tok = token.T_DIR; fbreak;};
267 | '__FILE__'i => {lex.setTokenPosition(tkn); tok = token.T_FILE; fbreak;};
268 | '__FUNCTION__'i => {lex.setTokenPosition(tkn); tok = token.T_FUNC_C; fbreak;};
269 | '__LINE__'i => {lex.setTokenPosition(tkn); tok = token.T_LINE; fbreak;};
270 | '__NAMESPACE__'i => {lex.setTokenPosition(tkn); tok = token.T_NS_C; fbreak;};
271 | '__METHOD__'i => {lex.setTokenPosition(tkn); tok = token.T_METHOD_C; fbreak;};
272 | '__TRAIT__'i => {lex.setTokenPosition(tkn); tok = token.T_TRAIT_C; fbreak;};
273 | '__halt_compiler'i => {lex.setTokenPosition(tkn); tok = token.T_HALT_COMPILER; fnext halt_compiller_open_parenthesis; fbreak;};
274 | 'new'i => {lex.setTokenPosition(tkn); tok = token.T_NEW; fbreak;};
275 | 'and'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_AND; fbreak;};
276 | 'or'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_OR; fbreak;};
277 | 'xor'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_XOR; fbreak;};
278 | '\\' => {lex.setTokenPosition(tkn); tok = token.T_NS_SEPARATOR; fbreak;};
279 | '...' => {lex.setTokenPosition(tkn); tok = token.T_ELLIPSIS; fbreak;};
280 | '::' => {lex.setTokenPosition(tkn); tok = token.T_PAAMAYIM_NEKUDOTAYIM; fbreak;};
281 | '&&' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_AND; fbreak;};
282 | '||' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_OR; fbreak;};
283 | '&=' => {lex.setTokenPosition(tkn); tok = token.T_AND_EQUAL; fbreak;};
284 | '|=' => {lex.setTokenPosition(tkn); tok = token.T_OR_EQUAL; fbreak;};
285 | '.=' => {lex.setTokenPosition(tkn); tok = token.T_CONCAT_EQUAL; fbreak;};
286 | '*=' => {lex.setTokenPosition(tkn); tok = token.T_MUL_EQUAL; fbreak;};
287 | '**=' => {lex.setTokenPosition(tkn); tok = token.T_POW_EQUAL; fbreak;};
288 | '/=' => {lex.setTokenPosition(tkn); tok = token.T_DIV_EQUAL; fbreak;};
289 | '+=' => {lex.setTokenPosition(tkn); tok = token.T_PLUS_EQUAL; fbreak;};
290 | '-=' => {lex.setTokenPosition(tkn); tok = token.T_MINUS_EQUAL; fbreak;};
291 | '^=' => {lex.setTokenPosition(tkn); tok = token.T_XOR_EQUAL; fbreak;};
292 | '%=' => {lex.setTokenPosition(tkn); tok = token.T_MOD_EQUAL; fbreak;};
293 | '--' => {lex.setTokenPosition(tkn); tok = token.T_DEC; fbreak;};
294 | '++' => {lex.setTokenPosition(tkn); tok = token.T_INC; fbreak;};
295 | '=>' => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_ARROW; fbreak;};
296 | '<=>' => {lex.setTokenPosition(tkn); tok = token.T_SPACESHIP; fbreak;};
297 | '!=' | '<>' => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_EQUAL; fbreak;};
298 | '!==' => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_IDENTICAL; fbreak;};
299 | '==' => {lex.setTokenPosition(tkn); tok = token.T_IS_EQUAL; fbreak;};
300 | '===' => {lex.setTokenPosition(tkn); tok = token.T_IS_IDENTICAL; fbreak;};
301 | '<<=' => {lex.setTokenPosition(tkn); tok = token.T_SL_EQUAL; fbreak;};
302 | '>>=' => {lex.setTokenPosition(tkn); tok = token.T_SR_EQUAL; fbreak;};
303 | '>=' => {lex.setTokenPosition(tkn); tok = token.T_IS_GREATER_OR_EQUAL; fbreak;};
304 | '<=' => {lex.setTokenPosition(tkn); tok = token.T_IS_SMALLER_OR_EQUAL; fbreak;};
305 | '**' => {lex.setTokenPosition(tkn); tok = token.T_POW; fbreak;};
306 | '<<' => {lex.setTokenPosition(tkn); tok = token.T_SL; fbreak;};
307 | '>>' => {lex.setTokenPosition(tkn); tok = token.T_SR; fbreak;};
308 | '??' => {lex.setTokenPosition(tkn); tok = token.T_COALESCE; fbreak;};
309 | '??=' => {lex.setTokenPosition(tkn); tok = token.T_COALESCE_EQUAL; fbreak;};
310 |
311 | '(' whitespace* 'array'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_ARRAY_CAST; fbreak;};
312 | '(' whitespace* ('bool'i|'boolean'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_BOOL_CAST; fbreak;};
313 | '(' whitespace* ('real'i|'double'i|'float'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_CAST; fbreak;};
314 | '(' whitespace* ('int'i|'integer'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_INT_CAST; fbreak;};
315 | '(' whitespace* 'object'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_CAST; fbreak;};
316 | '(' whitespace* ('string'i|'binary'i) whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_STRING_CAST; fbreak;};
317 | '(' whitespace* 'unset'i whitespace* ')' => {lex.setTokenPosition(tkn); tok = token.T_UNSET_CAST; fbreak;};
318 |
319 | ('#' | '//') any_line* when is_not_comment_end => {
320 | lex.ungetStr("?>")
321 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te)
322 | };
323 | '/*' any_line* :>> '*/' {
324 | isDocComment := false;
325 | if lex.te - lex.ts > 4 && string(lex.data[lex.ts:lex.ts+3]) == "/**" {
326 | isDocComment = true;
327 | }
328 |
329 | if isDocComment {
330 | lex.addFreeFloatingToken(tkn, token.T_DOC_COMMENT, lex.ts, lex.te)
331 | } else {
332 | lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te)
333 | }
334 | };
335 |
336 | operators => {
337 | lex.setTokenPosition(tkn);
338 | tok = token.ID(int(lex.data[lex.ts]));
339 | fbreak;
340 | };
341 |
342 | "{" => { lex.setTokenPosition(tkn); tok = token.ID(int('{')); lex.call(ftargs, fentry(php)); goto _out; };
343 | "}" => { lex.setTokenPosition(tkn); tok = token.ID(int('}')); lex.ret(1); goto _out;};
344 | "$" varname => { lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak; };
345 | varname => { lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak; };
346 |
347 | "->" => { lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fnext property; fbreak; };
348 |
349 | constant_string => {
350 | lex.setTokenPosition(tkn);
351 | tok = token.T_CONSTANT_ENCAPSED_STRING;
352 | fbreak;
353 | };
354 |
355 | "b"i? "<<<" [ \t]* ( heredoc_label | ("'" heredoc_label "'") | ('"' heredoc_label '"') ) newline => {
356 | lex.heredocLabel = lex.data[lblStart:lblEnd]
357 | lex.setTokenPosition(tkn);
358 | tok = token.T_START_HEREDOC;
359 |
360 | if lex.isHeredocEnd(lex.p+1) {
361 | fnext heredoc_end;
362 | } else if lex.data[lblStart-1] == '\'' {
363 | fnext nowdoc;
364 | } else {
365 | fnext heredoc;
366 | }
367 | fbreak;
368 | };
369 | "`" => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext backqote; fbreak;};
370 | '"' => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext template_string; fbreak;};
371 |
372 | any_line => {
373 | c := lex.data[lex.p]
374 | lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c));
375 | };
376 | *|;
377 |
378 | property := |*
379 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)};
380 | "->" => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;};
381 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fnext php; fbreak;};
382 | any => {lex.ungetCnt(1); fgoto php;};
383 | *|;
384 |
385 | nowdoc := |*
386 | any_line* when is_not_heredoc_end => {
387 | lex.setTokenPosition(tkn);
388 | tok = token.T_ENCAPSED_AND_WHITESPACE;
389 | fnext heredoc_end;
390 | fbreak;
391 | };
392 | *|;
393 |
394 | heredoc := |*
395 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;};
396 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;};
397 | "$" => {lex.ungetCnt(1); fcall string_var;};
398 | any_line* when is_not_heredoc_end_or_var => {
399 | lex.setTokenPosition(tkn);
400 | tok = token.T_ENCAPSED_AND_WHITESPACE;
401 |
402 | if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
403 | fnext heredoc_end;
404 | }
405 | fbreak;
406 | };
407 | *|;
408 |
409 | backqote := |*
410 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;};
411 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;};
412 | "$" varname_first => {lex.ungetCnt(2); fcall string_var;};
413 | '`' => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext php; fbreak;};
414 | any_line* when is_not_backqoute_end_or_var => {
415 | lex.setTokenPosition(tkn);
416 | tok = token.T_ENCAPSED_AND_WHITESPACE;
417 | fbreak;
418 | };
419 | *|;
420 |
421 | template_string := |*
422 | "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;};
423 | "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;};
424 | "$" varname_first => {lex.ungetCnt(2); fcall string_var;};
425 | '"' => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext php; fbreak;};
426 | any_line* when is_not_string_end_or_var => {
427 | lex.setTokenPosition(tkn);
428 | tok = token.T_ENCAPSED_AND_WHITESPACE;
429 | fbreak;
430 | };
431 | *|;
432 |
433 | heredoc_end := |*
434 | varname -- ";" => {
435 | lex.setTokenPosition(tkn);
436 | tok = token.T_END_HEREDOC;
437 | fnext php;
438 | fbreak;
439 | };
440 | varname => {
441 | lex.setTokenPosition(tkn);
442 | tok = token.T_END_HEREDOC;
443 | fnext php;
444 | fbreak;
445 | };
446 | *|;
447 |
448 | string_var := |*
449 | '$' varname => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;};
450 | '->' varname_first => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;};
451 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;};
452 | '[' => {lex.setTokenPosition(tkn); tok = token.ID(int('[')); lex.call(ftargs, fentry(string_var_index)); goto _out;};
453 | any => {lex.ungetCnt(1); fret;};
454 | *|;
455 |
456 | string_var_index := |*
457 | lnum | hnum | bnum => {lex.setTokenPosition(tkn); tok = token.T_NUM_STRING; fbreak;};
458 | '$' varname => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;};
459 | varname => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;};
460 | whitespace_line | [\\'#] => {lex.setTokenPosition(tkn); tok = token.T_ENCAPSED_AND_WHITESPACE; lex.ret(2); goto _out;};
461 | operators > (svi, 1) => {lex.setTokenPosition(tkn); tok = token.ID(int(lex.data[lex.ts])); fbreak;};
462 | ']' > (svi, 2) => {lex.setTokenPosition(tkn); tok = token.ID(int(']')); lex.ret(2); goto _out;};
463 | any_line => {
464 | c := lex.data[lex.p]
465 | lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c));
466 | };
467 | *|;
468 |
469 | string_var_name := |*
470 | varname ("[" | "}") => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_STRING_VARNAME; fnext php; fbreak;};
471 | any => {lex.ungetCnt(1); fnext php;};
472 | *|;
473 |
474 | halt_compiller_open_parenthesis := |*
475 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)};
476 | "(" => {lex.setTokenPosition(tkn); tok = token.ID(int('(')); fnext halt_compiller_close_parenthesis; fbreak;};
477 | any => {lex.ungetCnt(1); fnext php;};
478 | *|;
479 |
480 | halt_compiller_close_parenthesis := |*
481 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)};
482 | ")" => {lex.setTokenPosition(tkn); tok = token.ID(int(')')); fnext halt_compiller_close_semicolon; fbreak;};
483 | any => {lex.ungetCnt(1); fnext php;};
484 | *|;
485 |
486 | halt_compiller_close_semicolon := |*
487 | whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)};
488 | ";" => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext halt_compiller_end; fbreak;};
489 | any => {lex.ungetCnt(1); fnext php;};
490 | *|;
491 |
492 | halt_compiller_end := |*
493 | any_line* => { lex.addFreeFloatingToken(tkn, token.T_HALT_COMPILER, lex.ts, lex.te); };
494 | *|;
495 |
496 | write exec;
497 | }%%
498 |
499 | tkn.Value = lex.data[lex.ts:lex.te]
500 | tkn.ID = token.ID(tok)
501 |
502 | return tkn
503 | }
--------------------------------------------------------------------------------
/parser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/z7zmey/php-parser/367eff9de651fb9479c98f4f3741db3195e549ad/parser.jpg
--------------------------------------------------------------------------------
/pkg/ast/ast.go:
--------------------------------------------------------------------------------
1 | package ast
2 |
3 | import "github.com/z7zmey/php-parser/pkg/position"
4 |
5 | type Vertex interface {
6 | Accept(v Visitor)
7 | GetPosition() *position.Position
8 | }
9 |
10 | type Visitor interface {
11 | Root(n *Root)
12 | Nullable(n *Nullable)
13 | Parameter(n *Parameter)
14 | Identifier(n *Identifier)
15 | Argument(n *Argument)
16 |
17 | StmtBreak(n *StmtBreak)
18 | StmtCase(n *StmtCase)
19 | StmtCatch(n *StmtCatch)
20 | StmtClass(n *StmtClass)
21 | StmtClassConstList(n *StmtClassConstList)
22 | StmtClassMethod(n *StmtClassMethod)
23 | StmtConstList(n *StmtConstList)
24 | StmtConstant(n *StmtConstant)
25 | StmtContinue(n *StmtContinue)
26 | StmtDeclare(n *StmtDeclare)
27 | StmtDefault(n *StmtDefault)
28 | StmtDo(n *StmtDo)
29 | StmtEcho(n *StmtEcho)
30 | StmtElse(n *StmtElse)
31 | StmtElseIf(n *StmtElseIf)
32 | StmtExpression(n *StmtExpression)
33 | StmtFinally(n *StmtFinally)
34 | StmtFor(n *StmtFor)
35 | StmtForeach(n *StmtForeach)
36 | StmtFunction(n *StmtFunction)
37 | StmtGlobal(n *StmtGlobal)
38 | StmtGoto(n *StmtGoto)
39 | StmtHaltCompiler(n *StmtHaltCompiler)
40 | StmtIf(n *StmtIf)
41 | StmtInlineHtml(n *StmtInlineHtml)
42 | StmtInterface(n *StmtInterface)
43 | StmtLabel(n *StmtLabel)
44 | StmtNamespace(n *StmtNamespace)
45 | StmtNop(n *StmtNop)
46 | StmtProperty(n *StmtProperty)
47 | StmtPropertyList(n *StmtPropertyList)
48 | StmtReturn(n *StmtReturn)
49 | StmtStatic(n *StmtStatic)
50 | StmtStaticVar(n *StmtStaticVar)
51 | StmtStmtList(n *StmtStmtList)
52 | StmtSwitch(n *StmtSwitch)
53 | StmtThrow(n *StmtThrow)
54 | StmtTrait(n *StmtTrait)
55 | StmtTraitUse(n *StmtTraitUse)
56 | StmtTraitUseAlias(n *StmtTraitUseAlias)
57 | StmtTraitUsePrecedence(n *StmtTraitUsePrecedence)
58 | StmtTry(n *StmtTry)
59 | StmtUnset(n *StmtUnset)
60 | StmtUse(n *StmtUseList)
61 | StmtGroupUse(n *StmtGroupUseList)
62 | StmtUseDeclaration(n *StmtUse)
63 | StmtWhile(n *StmtWhile)
64 |
65 | ExprArray(n *ExprArray)
66 | ExprArrayDimFetch(n *ExprArrayDimFetch)
67 | ExprArrayItem(n *ExprArrayItem)
68 | ExprArrowFunction(n *ExprArrowFunction)
69 | ExprBrackets(n *ExprBrackets)
70 | ExprBitwiseNot(n *ExprBitwiseNot)
71 | ExprBooleanNot(n *ExprBooleanNot)
72 | ExprClassConstFetch(n *ExprClassConstFetch)
73 | ExprClone(n *ExprClone)
74 | ExprClosure(n *ExprClosure)
75 | ExprClosureUse(n *ExprClosureUse)
76 | ExprConstFetch(n *ExprConstFetch)
77 | ExprEmpty(n *ExprEmpty)
78 | ExprErrorSuppress(n *ExprErrorSuppress)
79 | ExprEval(n *ExprEval)
80 | ExprExit(n *ExprExit)
81 | ExprFunctionCall(n *ExprFunctionCall)
82 | ExprInclude(n *ExprInclude)
83 | ExprIncludeOnce(n *ExprIncludeOnce)
84 | ExprInstanceOf(n *ExprInstanceOf)
85 | ExprIsset(n *ExprIsset)
86 | ExprList(n *ExprList)
87 | ExprMethodCall(n *ExprMethodCall)
88 | ExprNew(n *ExprNew)
89 | ExprPostDec(n *ExprPostDec)
90 | ExprPostInc(n *ExprPostInc)
91 | ExprPreDec(n *ExprPreDec)
92 | ExprPreInc(n *ExprPreInc)
93 | ExprPrint(n *ExprPrint)
94 | ExprPropertyFetch(n *ExprPropertyFetch)
95 | ExprRequire(n *ExprRequire)
96 | ExprRequireOnce(n *ExprRequireOnce)
97 | ExprShellExec(n *ExprShellExec)
98 | ExprStaticCall(n *ExprStaticCall)
99 | ExprStaticPropertyFetch(n *ExprStaticPropertyFetch)
100 | ExprTernary(n *ExprTernary)
101 | ExprUnaryMinus(n *ExprUnaryMinus)
102 | ExprUnaryPlus(n *ExprUnaryPlus)
103 | ExprVariable(n *ExprVariable)
104 | ExprYield(n *ExprYield)
105 | ExprYieldFrom(n *ExprYieldFrom)
106 |
107 | ExprAssign(n *ExprAssign)
108 | ExprAssignReference(n *ExprAssignReference)
109 | ExprAssignBitwiseAnd(n *ExprAssignBitwiseAnd)
110 | ExprAssignBitwiseOr(n *ExprAssignBitwiseOr)
111 | ExprAssignBitwiseXor(n *ExprAssignBitwiseXor)
112 | ExprAssignCoalesce(n *ExprAssignCoalesce)
113 | ExprAssignConcat(n *ExprAssignConcat)
114 | ExprAssignDiv(n *ExprAssignDiv)
115 | ExprAssignMinus(n *ExprAssignMinus)
116 | ExprAssignMod(n *ExprAssignMod)
117 | ExprAssignMul(n *ExprAssignMul)
118 | ExprAssignPlus(n *ExprAssignPlus)
119 | ExprAssignPow(n *ExprAssignPow)
120 | ExprAssignShiftLeft(n *ExprAssignShiftLeft)
121 | ExprAssignShiftRight(n *ExprAssignShiftRight)
122 |
123 | ExprBinaryBitwiseAnd(n *ExprBinaryBitwiseAnd)
124 | ExprBinaryBitwiseOr(n *ExprBinaryBitwiseOr)
125 | ExprBinaryBitwiseXor(n *ExprBinaryBitwiseXor)
126 | ExprBinaryBooleanAnd(n *ExprBinaryBooleanAnd)
127 | ExprBinaryBooleanOr(n *ExprBinaryBooleanOr)
128 | ExprBinaryCoalesce(n *ExprBinaryCoalesce)
129 | ExprBinaryConcat(n *ExprBinaryConcat)
130 | ExprBinaryDiv(n *ExprBinaryDiv)
131 | ExprBinaryEqual(n *ExprBinaryEqual)
132 | ExprBinaryGreater(n *ExprBinaryGreater)
133 | ExprBinaryGreaterOrEqual(n *ExprBinaryGreaterOrEqual)
134 | ExprBinaryIdentical(n *ExprBinaryIdentical)
135 | ExprBinaryLogicalAnd(n *ExprBinaryLogicalAnd)
136 | ExprBinaryLogicalOr(n *ExprBinaryLogicalOr)
137 | ExprBinaryLogicalXor(n *ExprBinaryLogicalXor)
138 | ExprBinaryMinus(n *ExprBinaryMinus)
139 | ExprBinaryMod(n *ExprBinaryMod)
140 | ExprBinaryMul(n *ExprBinaryMul)
141 | ExprBinaryNotEqual(n *ExprBinaryNotEqual)
142 | ExprBinaryNotIdentical(n *ExprBinaryNotIdentical)
143 | ExprBinaryPlus(n *ExprBinaryPlus)
144 | ExprBinaryPow(n *ExprBinaryPow)
145 | ExprBinaryShiftLeft(n *ExprBinaryShiftLeft)
146 | ExprBinaryShiftRight(n *ExprBinaryShiftRight)
147 | ExprBinarySmaller(n *ExprBinarySmaller)
148 | ExprBinarySmallerOrEqual(n *ExprBinarySmallerOrEqual)
149 | ExprBinarySpaceship(n *ExprBinarySpaceship)
150 |
151 | ExprCastArray(n *ExprCastArray)
152 | ExprCastBool(n *ExprCastBool)
153 | ExprCastDouble(n *ExprCastDouble)
154 | ExprCastInt(n *ExprCastInt)
155 | ExprCastObject(n *ExprCastObject)
156 | ExprCastString(n *ExprCastString)
157 | ExprCastUnset(n *ExprCastUnset)
158 |
159 | ScalarDnumber(n *ScalarDnumber)
160 | ScalarEncapsed(n *ScalarEncapsed)
161 | ScalarEncapsedStringPart(n *ScalarEncapsedStringPart)
162 | ScalarEncapsedStringVar(n *ScalarEncapsedStringVar)
163 | ScalarEncapsedStringBrackets(n *ScalarEncapsedStringBrackets)
164 | ScalarHeredoc(n *ScalarHeredoc)
165 | ScalarLnumber(n *ScalarLnumber)
166 | ScalarMagicConstant(n *ScalarMagicConstant)
167 | ScalarString(n *ScalarString)
168 |
169 | NameName(n *Name)
170 | NameFullyQualified(n *NameFullyQualified)
171 | NameRelative(n *NameRelative)
172 | NameNamePart(n *NamePart)
173 | }
174 |
--------------------------------------------------------------------------------
/pkg/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/errors"
5 | "github.com/z7zmey/php-parser/pkg/version"
6 | )
7 |
8 | type Config struct {
9 | Version *version.Version
10 | ErrorHandlerFunc func(e *errors.Error)
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/errors/error.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/z7zmey/php-parser/pkg/position"
7 | )
8 |
9 | // Error parsing error
10 | type Error struct {
11 | Msg string
12 | Pos *position.Position
13 | }
14 |
15 | // NewError creates and returns new Error
16 | func NewError(msg string, p *position.Position) *Error {
17 | return &Error{
18 | Msg: msg,
19 | Pos: p,
20 | }
21 | }
22 |
23 | func (e *Error) String() string {
24 | atLine := ""
25 | if e.Pos != nil {
26 | atLine = fmt.Sprintf(" at line %d", e.Pos.StartLine)
27 | }
28 |
29 | return fmt.Sprintf("%s%s", e.Msg, atLine)
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/errors/error_test.go:
--------------------------------------------------------------------------------
1 | package errors_test
2 |
3 | import (
4 | "testing"
5 |
6 | "gotest.tools/assert"
7 |
8 | "github.com/z7zmey/php-parser/pkg/errors"
9 | "github.com/z7zmey/php-parser/pkg/position"
10 | )
11 |
12 | func TestConstructor(t *testing.T) {
13 | pos := position.NewPosition(1, 2, 3, 4)
14 |
15 | actual := errors.NewError("message", pos)
16 |
17 | expected := &errors.Error{
18 | Msg: "message",
19 | Pos: pos,
20 | }
21 |
22 | assert.DeepEqual(t, expected, actual)
23 | }
24 |
25 | func TestPrint(t *testing.T) {
26 | pos := position.NewPosition(1, 2, 3, 4)
27 |
28 | Error := errors.NewError("message", pos)
29 |
30 | actual := Error.String()
31 |
32 | expected := "message at line 1"
33 |
34 | assert.DeepEqual(t, expected, actual)
35 | }
36 |
37 | func TestPrintWithotPos(t *testing.T) {
38 | Error := errors.NewError("message", nil)
39 |
40 | actual := Error.String()
41 |
42 | expected := "message"
43 |
44 | assert.DeepEqual(t, expected, actual)
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/parser/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | A Parser for PHP written in Go
4 |
5 | Package usage example:
6 |
7 | package main
8 |
9 | import (
10 | "log"
11 | "os"
12 |
13 | "github.com/z7zmey/php-parser/pkg/conf"
14 | "github.com/z7zmey/php-parser/pkg/errors"
15 | "github.com/z7zmey/php-parser/pkg/parser"
16 | "github.com/z7zmey/php-parser/pkg/version"
17 | "github.com/z7zmey/php-parser/pkg/visitor/dumper"
18 | )
19 |
20 | func main() {
21 | src := []byte(` echo "Hello world";`)
22 |
23 | // Error handler
24 |
25 | var parserErrors []*errors.Error
26 | errorHandler := func(e *errors.Error) {
27 | parsmakeerErrors = append(parserErrors, e)
28 | }
29 |
30 | // Parse
31 |
32 | rootNode, err := parser.Parse(src, conf.Config{
33 | Version: &version.Version{Major: 5, Minor: 6},
34 | ErrorHandlerFunc: errorHandler,
35 | })
36 |
37 | if err != nil {
38 | log.Fatal("Error:" + err.Error())
39 | }
40 |
41 | // Dump
42 |
43 | goDumper := dumper.NewDumper(os.Stdout).
44 | WithTokens().
45 | WithPositions()
46 |
47 | rootNode.Accept(goDumper)
48 | }
49 | */
50 | package parser
51 |
--------------------------------------------------------------------------------
/pkg/parser/parser.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/z7zmey/php-parser/internal/php5"
7 | "github.com/z7zmey/php-parser/internal/php7"
8 | "github.com/z7zmey/php-parser/internal/scanner"
9 | "github.com/z7zmey/php-parser/pkg/ast"
10 | "github.com/z7zmey/php-parser/pkg/conf"
11 | "github.com/z7zmey/php-parser/pkg/version"
12 | )
13 |
14 | var (
15 | // ErrVersionOutOfRange is returned if the version is not supported
16 | ErrVersionOutOfRange = errors.New("the version is out of supported range")
17 |
18 | php5RangeStart = &version.Version{Major: 5}
19 | php5RangeEnd = &version.Version{Major: 5, Minor: 6}
20 |
21 | php7RangeStart = &version.Version{Major: 7}
22 | php7RangeEnd = &version.Version{Major: 7, Minor: 4}
23 | )
24 |
25 | // Parser interface
26 | type Parser interface {
27 | Parse() int
28 | GetRootNode() ast.Vertex
29 | }
30 |
31 | func Parse(src []byte, config conf.Config) (ast.Vertex, error) {
32 | var parser Parser
33 |
34 | if config.Version == nil {
35 | config.Version = php7RangeEnd
36 | }
37 |
38 | if config.Version.InRange(php5RangeStart, php5RangeEnd) {
39 | lexer := scanner.NewLexer(src, config)
40 | parser = php5.NewParser(lexer, config)
41 | parser.Parse()
42 | return parser.GetRootNode(), nil
43 | }
44 |
45 | if config.Version.InRange(php7RangeStart, php7RangeEnd) {
46 | lexer := scanner.NewLexer(src, config)
47 | parser = php7.NewParser(lexer, config)
48 | parser.Parse()
49 | return parser.GetRootNode(), nil
50 | }
51 |
52 | return nil, ErrVersionOutOfRange
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/position/pool.go:
--------------------------------------------------------------------------------
1 | package position
2 |
3 | const DefaultBlockSize = 1024
4 |
5 | type Pool struct {
6 | block []Position
7 | off int
8 | }
9 |
10 | func NewPool(blockSize int) *Pool {
11 | return &Pool{
12 | block: make([]Position, blockSize),
13 | }
14 | }
15 |
16 | func (p *Pool) Get() *Position {
17 | if len(p.block) == 0 {
18 | return nil
19 | }
20 |
21 | if len(p.block) == p.off {
22 | p.block = make([]Position, len(p.block))
23 | p.off = 0
24 | }
25 |
26 | p.off++
27 |
28 | return &p.block[p.off-1]
29 | }
30 |
--------------------------------------------------------------------------------
/pkg/position/position.go:
--------------------------------------------------------------------------------
1 | package position
2 |
3 | // Position represents node position
4 | type Position struct {
5 | StartLine int
6 | EndLine int
7 | StartPos int
8 | EndPos int
9 | }
10 |
11 | // NewPosition Position constructor
12 | func NewPosition(StartLine int, EndLine int, StartPos int, EndPos int) *Position {
13 | return &Position{
14 | StartLine: StartLine,
15 | EndLine: EndLine,
16 | StartPos: StartPos,
17 | EndPos: EndPos,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/token/pool.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | const DefaultBlockSize = 1024
4 |
5 | type Pool struct {
6 | block []Token
7 | off int
8 | }
9 |
10 | func NewPool(blockSize int) *Pool {
11 | return &Pool{
12 | block: make([]Token, blockSize),
13 | }
14 | }
15 |
16 | func (p *Pool) Get() *Token {
17 | if len(p.block) == 0 {
18 | return nil
19 | }
20 |
21 | if len(p.block) == p.off {
22 | p.block = make([]Token, len(p.block))
23 | p.off = 0
24 | }
25 |
26 | p.off++
27 |
28 | return &p.block[p.off-1]
29 | }
30 |
--------------------------------------------------------------------------------
/pkg/token/pool_bench_test.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | const amount = 100000
8 |
9 | func BenchmarkPlain(b *testing.B) {
10 | for n := 0; n < b.N; n++ {
11 | buf := make([]*Token, 0, amount)
12 |
13 | for i := 0; i < amount; i++ {
14 | buf = append(buf, &Token{})
15 | }
16 | }
17 | }
18 |
19 | func BenchmarkSlice128(b *testing.B) {
20 | for n := 0; n < b.N; n++ {
21 | buf := make([]*Token, 0, amount)
22 | slc := make([]Token, 0, 128)
23 |
24 | for i := 0; i < amount; i++ {
25 | slc = append(slc, Token{})
26 | buf = append(buf, &slc[len(slc)-1])
27 | }
28 | }
29 | }
30 |
31 | func BenchmarkSlice512(b *testing.B) {
32 | for n := 0; n < b.N; n++ {
33 | buf := make([]*Token, 0, amount)
34 | slc := make([]Token, 0, 512)
35 |
36 | for i := 0; i < amount; i++ {
37 | slc = append(slc, Token{})
38 | buf = append(buf, &slc[len(slc)-1])
39 | }
40 | }
41 | }
42 |
43 | func BenchmarkSlice1024(b *testing.B) {
44 | for n := 0; n < b.N; n++ {
45 | buf := make([]*Token, 0, amount)
46 | slc := make([]Token, 0, 1024)
47 |
48 | for i := 0; i < amount; i++ {
49 | slc = append(slc, Token{})
50 | buf = append(buf, &slc[len(slc)-1])
51 | }
52 | }
53 | }
54 |
55 | func BenchmarkSlice2048(b *testing.B) {
56 | for n := 0; n < b.N; n++ {
57 | buf := make([]*Token, 0, amount)
58 | slc := make([]Token, 0, 2048)
59 |
60 | for i := 0; i < amount; i++ {
61 | slc = append(slc, Token{})
62 | buf = append(buf, &slc[len(slc)-1])
63 | }
64 | }
65 | }
66 |
67 | func BenchmarkBlockAppend128(b *testing.B) {
68 | for n := 0; n < b.N; n++ {
69 | buf := make([]*Token, 0, amount)
70 | slc := make([]Token, 0, 128)
71 |
72 | for i := 0; i < amount; i++ {
73 | if len(slc) == 128 {
74 | slc = make([]Token, 0, 128)
75 | }
76 |
77 | slc = append(slc, Token{})
78 | buf = append(buf, &slc[len(slc)-1])
79 | }
80 | }
81 | }
82 |
83 | func BenchmarkBlockAppend512(b *testing.B) {
84 | for n := 0; n < b.N; n++ {
85 | buf := make([]*Token, 0, amount)
86 | slc := make([]Token, 0, 512)
87 |
88 | for i := 0; i < amount; i++ {
89 | if len(slc) == 512 {
90 | slc = make([]Token, 0, 512)
91 | }
92 |
93 | slc = append(slc, Token{})
94 | buf = append(buf, &slc[len(slc)-1])
95 | }
96 | }
97 | }
98 |
99 | func BenchmarkBlockAppend1024(b *testing.B) {
100 | for n := 0; n < b.N; n++ {
101 | buf := make([]*Token, 0, amount)
102 | slc := make([]Token, 0, 1024)
103 |
104 | for i := 0; i < amount; i++ {
105 | if len(slc) == 1024 {
106 | slc = make([]Token, 0, 1024)
107 | }
108 |
109 | slc = append(slc, Token{})
110 | buf = append(buf, &slc[len(slc)-1])
111 | }
112 | }
113 | }
114 |
115 | func BenchmarkBlockAppend2048(b *testing.B) {
116 | for n := 0; n < b.N; n++ {
117 | buf := make([]*Token, 0, amount)
118 | slc := make([]Token, 0, 2048)
119 |
120 | for i := 0; i < amount; i++ {
121 | if len(slc) == 2048 {
122 | slc = make([]Token, 0, 2048)
123 | }
124 |
125 | slc = append(slc, Token{})
126 | buf = append(buf, &slc[len(slc)-1])
127 | }
128 | }
129 | }
130 |
131 | func BenchmarkPool128(b *testing.B) {
132 | for n := 0; n < b.N; n++ {
133 | pool := NewPool(128)
134 | buf := make([]*Token, 0, amount)
135 |
136 | for i := 0; i < amount; i++ {
137 | buf = append(buf, pool.Get())
138 | }
139 | }
140 | }
141 |
142 | func BenchmarkPool512(b *testing.B) {
143 | for n := 0; n < b.N; n++ {
144 | pool := NewPool(512)
145 | buf := make([]*Token, 0, amount)
146 |
147 | for i := 0; i < amount; i++ {
148 | buf = append(buf, pool.Get())
149 | }
150 | }
151 | }
152 |
153 | func BenchmarkPool1024(b *testing.B) {
154 | for n := 0; n < b.N; n++ {
155 | pool := NewPool(1024)
156 | buf := make([]*Token, 0, amount)
157 |
158 | for i := 0; i < amount; i++ {
159 | buf = append(buf, pool.Get())
160 | }
161 | }
162 | }
163 |
164 | func BenchmarkPool2048(b *testing.B) {
165 | for n := 0; n < b.N; n++ {
166 | pool := NewPool(2048)
167 | buf := make([]*Token, 0, amount)
168 |
169 | for i := 0; i < amount; i++ {
170 | buf = append(buf, pool.Get())
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/pkg/token/token.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import "github.com/z7zmey/php-parser/pkg/position"
4 |
5 | //go:generate stringer -type=ID -output ./token_string.go
6 | type ID int
7 |
8 | const (
9 | T_INCLUDE ID = iota + 57346
10 | T_INCLUDE_ONCE
11 | T_EXIT
12 | T_IF
13 | T_LNUMBER
14 | T_DNUMBER
15 | T_STRING
16 | T_STRING_VARNAME
17 | T_VARIABLE
18 | T_NUM_STRING
19 | T_INLINE_HTML
20 | T_CHARACTER
21 | T_BAD_CHARACTER
22 | T_ENCAPSED_AND_WHITESPACE
23 | T_CONSTANT_ENCAPSED_STRING
24 | T_ECHO
25 | T_DO
26 | T_WHILE
27 | T_ENDWHILE
28 | T_FOR
29 | T_ENDFOR
30 | T_FOREACH
31 | T_ENDFOREACH
32 | T_DECLARE
33 | T_ENDDECLARE
34 | T_AS
35 | T_SWITCH
36 | T_ENDSWITCH
37 | T_CASE
38 | T_DEFAULT
39 | T_BREAK
40 | T_CONTINUE
41 | T_GOTO
42 | T_FUNCTION
43 | T_FN
44 | T_CONST
45 | T_RETURN
46 | T_TRY
47 | T_CATCH
48 | T_FINALLY
49 | T_THROW
50 | T_USE
51 | T_INSTEADOF
52 | T_GLOBAL
53 | T_VAR
54 | T_UNSET
55 | T_ISSET
56 | T_EMPTY
57 | T_HALT_COMPILER
58 | T_CLASS
59 | T_TRAIT
60 | T_INTERFACE
61 | T_EXTENDS
62 | T_IMPLEMENTS
63 | T_OBJECT_OPERATOR
64 | T_DOUBLE_ARROW
65 | T_LIST
66 | T_ARRAY
67 | T_CALLABLE
68 | T_CLASS_C
69 | T_TRAIT_C
70 | T_METHOD_C
71 | T_FUNC_C
72 | T_LINE
73 | T_FILE
74 | T_COMMENT
75 | T_DOC_COMMENT
76 | T_OPEN_TAG
77 | T_OPEN_TAG_WITH_ECHO
78 | T_CLOSE_TAG
79 | T_WHITESPACE
80 | T_START_HEREDOC
81 | T_END_HEREDOC
82 | T_DOLLAR_OPEN_CURLY_BRACES
83 | T_CURLY_OPEN
84 | T_PAAMAYIM_NEKUDOTAYIM
85 | T_NAMESPACE
86 | T_NS_C
87 | T_DIR
88 | T_NS_SEPARATOR
89 | T_ELLIPSIS
90 | T_EVAL
91 | T_REQUIRE
92 | T_REQUIRE_ONCE
93 | T_LOGICAL_OR
94 | T_LOGICAL_XOR
95 | T_LOGICAL_AND
96 | T_INSTANCEOF
97 | T_NEW
98 | T_CLONE
99 | T_ELSEIF
100 | T_ELSE
101 | T_ENDIF
102 | T_PRINT
103 | T_YIELD
104 | T_STATIC
105 | T_ABSTRACT
106 | T_FINAL
107 | T_PRIVATE
108 | T_PROTECTED
109 | T_PUBLIC
110 | T_INC
111 | T_DEC
112 | T_YIELD_FROM
113 | T_INT_CAST
114 | T_DOUBLE_CAST
115 | T_STRING_CAST
116 | T_ARRAY_CAST
117 | T_OBJECT_CAST
118 | T_BOOL_CAST
119 | T_UNSET_CAST
120 | T_COALESCE
121 | T_SPACESHIP
122 | T_NOELSE
123 | T_PLUS_EQUAL
124 | T_MINUS_EQUAL
125 | T_MUL_EQUAL
126 | T_POW_EQUAL
127 | T_DIV_EQUAL
128 | T_CONCAT_EQUAL
129 | T_MOD_EQUAL
130 | T_AND_EQUAL
131 | T_OR_EQUAL
132 | T_XOR_EQUAL
133 | T_SL_EQUAL
134 | T_SR_EQUAL
135 | T_COALESCE_EQUAL
136 | T_BOOLEAN_OR
137 | T_BOOLEAN_AND
138 | T_POW
139 | T_SL
140 | T_SR
141 | T_IS_IDENTICAL
142 | T_IS_NOT_IDENTICAL
143 | T_IS_EQUAL
144 | T_IS_NOT_EQUAL
145 | T_IS_SMALLER_OR_EQUAL
146 | T_IS_GREATER_OR_EQUAL
147 | )
148 |
149 | type Token struct {
150 | ID ID
151 | Value []byte
152 | Position *position.Position
153 | FreeFloating []*Token
154 | }
155 |
156 | func (t *Token) GetPosition() *position.Position {
157 | return t.Position
158 | }
159 |
--------------------------------------------------------------------------------
/pkg/token/token_string.go:
--------------------------------------------------------------------------------
1 | // Code generated by "stringer -type=ID -output ./token_string.go"; DO NOT EDIT.
2 |
3 | package token
4 |
5 | import "strconv"
6 |
7 | func _() {
8 | // An "invalid array index" compiler error signifies that the constant values have changed.
9 | // Re-run the stringer command to generate them again.
10 | var x [1]struct{}
11 | _ = x[T_INCLUDE-57346]
12 | _ = x[T_INCLUDE_ONCE-57347]
13 | _ = x[T_EXIT-57348]
14 | _ = x[T_IF-57349]
15 | _ = x[T_LNUMBER-57350]
16 | _ = x[T_DNUMBER-57351]
17 | _ = x[T_STRING-57352]
18 | _ = x[T_STRING_VARNAME-57353]
19 | _ = x[T_VARIABLE-57354]
20 | _ = x[T_NUM_STRING-57355]
21 | _ = x[T_INLINE_HTML-57356]
22 | _ = x[T_CHARACTER-57357]
23 | _ = x[T_BAD_CHARACTER-57358]
24 | _ = x[T_ENCAPSED_AND_WHITESPACE-57359]
25 | _ = x[T_CONSTANT_ENCAPSED_STRING-57360]
26 | _ = x[T_ECHO-57361]
27 | _ = x[T_DO-57362]
28 | _ = x[T_WHILE-57363]
29 | _ = x[T_ENDWHILE-57364]
30 | _ = x[T_FOR-57365]
31 | _ = x[T_ENDFOR-57366]
32 | _ = x[T_FOREACH-57367]
33 | _ = x[T_ENDFOREACH-57368]
34 | _ = x[T_DECLARE-57369]
35 | _ = x[T_ENDDECLARE-57370]
36 | _ = x[T_AS-57371]
37 | _ = x[T_SWITCH-57372]
38 | _ = x[T_ENDSWITCH-57373]
39 | _ = x[T_CASE-57374]
40 | _ = x[T_DEFAULT-57375]
41 | _ = x[T_BREAK-57376]
42 | _ = x[T_CONTINUE-57377]
43 | _ = x[T_GOTO-57378]
44 | _ = x[T_FUNCTION-57379]
45 | _ = x[T_FN-57380]
46 | _ = x[T_CONST-57381]
47 | _ = x[T_RETURN-57382]
48 | _ = x[T_TRY-57383]
49 | _ = x[T_CATCH-57384]
50 | _ = x[T_FINALLY-57385]
51 | _ = x[T_THROW-57386]
52 | _ = x[T_USE-57387]
53 | _ = x[T_INSTEADOF-57388]
54 | _ = x[T_GLOBAL-57389]
55 | _ = x[T_VAR-57390]
56 | _ = x[T_UNSET-57391]
57 | _ = x[T_ISSET-57392]
58 | _ = x[T_EMPTY-57393]
59 | _ = x[T_HALT_COMPILER-57394]
60 | _ = x[T_CLASS-57395]
61 | _ = x[T_TRAIT-57396]
62 | _ = x[T_INTERFACE-57397]
63 | _ = x[T_EXTENDS-57398]
64 | _ = x[T_IMPLEMENTS-57399]
65 | _ = x[T_OBJECT_OPERATOR-57400]
66 | _ = x[T_DOUBLE_ARROW-57401]
67 | _ = x[T_LIST-57402]
68 | _ = x[T_ARRAY-57403]
69 | _ = x[T_CALLABLE-57404]
70 | _ = x[T_CLASS_C-57405]
71 | _ = x[T_TRAIT_C-57406]
72 | _ = x[T_METHOD_C-57407]
73 | _ = x[T_FUNC_C-57408]
74 | _ = x[T_LINE-57409]
75 | _ = x[T_FILE-57410]
76 | _ = x[T_COMMENT-57411]
77 | _ = x[T_DOC_COMMENT-57412]
78 | _ = x[T_OPEN_TAG-57413]
79 | _ = x[T_OPEN_TAG_WITH_ECHO-57414]
80 | _ = x[T_CLOSE_TAG-57415]
81 | _ = x[T_WHITESPACE-57416]
82 | _ = x[T_START_HEREDOC-57417]
83 | _ = x[T_END_HEREDOC-57418]
84 | _ = x[T_DOLLAR_OPEN_CURLY_BRACES-57419]
85 | _ = x[T_CURLY_OPEN-57420]
86 | _ = x[T_PAAMAYIM_NEKUDOTAYIM-57421]
87 | _ = x[T_NAMESPACE-57422]
88 | _ = x[T_NS_C-57423]
89 | _ = x[T_DIR-57424]
90 | _ = x[T_NS_SEPARATOR-57425]
91 | _ = x[T_ELLIPSIS-57426]
92 | _ = x[T_EVAL-57427]
93 | _ = x[T_REQUIRE-57428]
94 | _ = x[T_REQUIRE_ONCE-57429]
95 | _ = x[T_LOGICAL_OR-57430]
96 | _ = x[T_LOGICAL_XOR-57431]
97 | _ = x[T_LOGICAL_AND-57432]
98 | _ = x[T_INSTANCEOF-57433]
99 | _ = x[T_NEW-57434]
100 | _ = x[T_CLONE-57435]
101 | _ = x[T_ELSEIF-57436]
102 | _ = x[T_ELSE-57437]
103 | _ = x[T_ENDIF-57438]
104 | _ = x[T_PRINT-57439]
105 | _ = x[T_YIELD-57440]
106 | _ = x[T_STATIC-57441]
107 | _ = x[T_ABSTRACT-57442]
108 | _ = x[T_FINAL-57443]
109 | _ = x[T_PRIVATE-57444]
110 | _ = x[T_PROTECTED-57445]
111 | _ = x[T_PUBLIC-57446]
112 | _ = x[T_INC-57447]
113 | _ = x[T_DEC-57448]
114 | _ = x[T_YIELD_FROM-57449]
115 | _ = x[T_INT_CAST-57450]
116 | _ = x[T_DOUBLE_CAST-57451]
117 | _ = x[T_STRING_CAST-57452]
118 | _ = x[T_ARRAY_CAST-57453]
119 | _ = x[T_OBJECT_CAST-57454]
120 | _ = x[T_BOOL_CAST-57455]
121 | _ = x[T_UNSET_CAST-57456]
122 | _ = x[T_COALESCE-57457]
123 | _ = x[T_SPACESHIP-57458]
124 | _ = x[T_NOELSE-57459]
125 | _ = x[T_PLUS_EQUAL-57460]
126 | _ = x[T_MINUS_EQUAL-57461]
127 | _ = x[T_MUL_EQUAL-57462]
128 | _ = x[T_POW_EQUAL-57463]
129 | _ = x[T_DIV_EQUAL-57464]
130 | _ = x[T_CONCAT_EQUAL-57465]
131 | _ = x[T_MOD_EQUAL-57466]
132 | _ = x[T_AND_EQUAL-57467]
133 | _ = x[T_OR_EQUAL-57468]
134 | _ = x[T_XOR_EQUAL-57469]
135 | _ = x[T_SL_EQUAL-57470]
136 | _ = x[T_SR_EQUAL-57471]
137 | _ = x[T_COALESCE_EQUAL-57472]
138 | _ = x[T_BOOLEAN_OR-57473]
139 | _ = x[T_BOOLEAN_AND-57474]
140 | _ = x[T_POW-57475]
141 | _ = x[T_SL-57476]
142 | _ = x[T_SR-57477]
143 | _ = x[T_IS_IDENTICAL-57478]
144 | _ = x[T_IS_NOT_IDENTICAL-57479]
145 | _ = x[T_IS_EQUAL-57480]
146 | _ = x[T_IS_NOT_EQUAL-57481]
147 | _ = x[T_IS_SMALLER_OR_EQUAL-57482]
148 | _ = x[T_IS_GREATER_OR_EQUAL-57483]
149 | }
150 |
151 | const _ID_name = "T_INCLUDET_INCLUDE_ONCET_EXITT_IFT_LNUMBERT_DNUMBERT_STRINGT_STRING_VARNAMET_VARIABLET_NUM_STRINGT_INLINE_HTMLT_CHARACTERT_BAD_CHARACTERT_ENCAPSED_AND_WHITESPACET_CONSTANT_ENCAPSED_STRINGT_ECHOT_DOT_WHILET_ENDWHILET_FORT_ENDFORT_FOREACHT_ENDFOREACHT_DECLARET_ENDDECLARET_AST_SWITCHT_ENDSWITCHT_CASET_DEFAULTT_BREAKT_CONTINUET_GOTOT_FUNCTIONT_FNT_CONSTT_RETURNT_TRYT_CATCHT_FINALLYT_THROWT_USET_INSTEADOFT_GLOBALT_VART_UNSETT_ISSETT_EMPTYT_HALT_COMPILERT_CLASST_TRAITT_INTERFACET_EXTENDST_IMPLEMENTST_OBJECT_OPERATORT_DOUBLE_ARROWT_LISTT_ARRAYT_CALLABLET_CLASS_CT_TRAIT_CT_METHOD_CT_FUNC_CT_LINET_FILET_COMMENTT_DOC_COMMENTT_OPEN_TAGT_OPEN_TAG_WITH_ECHOT_CLOSE_TAGT_WHITESPACET_START_HEREDOCT_END_HEREDOCT_DOLLAR_OPEN_CURLY_BRACEST_CURLY_OPENT_PAAMAYIM_NEKUDOTAYIMT_NAMESPACET_NS_CT_DIRT_NS_SEPARATORT_ELLIPSIST_EVALT_REQUIRET_REQUIRE_ONCET_LOGICAL_ORT_LOGICAL_XORT_LOGICAL_ANDT_INSTANCEOFT_NEWT_CLONET_ELSEIFT_ELSET_ENDIFT_PRINTT_YIELDT_STATICT_ABSTRACTT_FINALT_PRIVATET_PROTECTEDT_PUBLICT_INCT_DECT_YIELD_FROMT_INT_CASTT_DOUBLE_CASTT_STRING_CASTT_ARRAY_CASTT_OBJECT_CASTT_BOOL_CASTT_UNSET_CASTT_COALESCET_SPACESHIPT_NOELSET_PLUS_EQUALT_MINUS_EQUALT_MUL_EQUALT_POW_EQUALT_DIV_EQUALT_CONCAT_EQUALT_MOD_EQUALT_AND_EQUALT_OR_EQUALT_XOR_EQUALT_SL_EQUALT_SR_EQUALT_COALESCE_EQUALT_BOOLEAN_ORT_BOOLEAN_ANDT_POWT_SLT_SRT_IS_IDENTICALT_IS_NOT_IDENTICALT_IS_EQUALT_IS_NOT_EQUALT_IS_SMALLER_OR_EQUALT_IS_GREATER_OR_EQUAL"
152 |
153 | var _ID_index = [...]uint16{0, 9, 23, 29, 33, 42, 51, 59, 75, 85, 97, 110, 121, 136, 161, 187, 193, 197, 204, 214, 219, 227, 236, 248, 257, 269, 273, 281, 292, 298, 307, 314, 324, 330, 340, 344, 351, 359, 364, 371, 380, 387, 392, 403, 411, 416, 423, 430, 437, 452, 459, 466, 477, 486, 498, 515, 529, 535, 542, 552, 561, 570, 580, 588, 594, 600, 609, 622, 632, 652, 663, 675, 690, 703, 729, 741, 763, 774, 780, 785, 799, 809, 815, 824, 838, 850, 863, 876, 888, 893, 900, 908, 914, 921, 928, 935, 943, 953, 960, 969, 980, 988, 993, 998, 1010, 1020, 1033, 1046, 1058, 1071, 1082, 1094, 1104, 1115, 1123, 1135, 1148, 1159, 1170, 1181, 1195, 1206, 1217, 1227, 1238, 1248, 1258, 1274, 1286, 1299, 1304, 1308, 1312, 1326, 1344, 1354, 1368, 1389, 1410}
154 |
155 | func (i ID) String() string {
156 | i -= 57346
157 | if i < 0 || i >= ID(len(_ID_index)-1) {
158 | return "ID(" + strconv.FormatInt(int64(i+57346), 10) + ")"
159 | }
160 | return _ID_name[_ID_index[i]:_ID_index[i+1]]
161 | }
162 |
--------------------------------------------------------------------------------
/pkg/version/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | import (
4 | "errors"
5 | "strconv"
6 | "strings"
7 | )
8 |
9 | type Version struct {
10 | Major, Minor uint64
11 | }
12 |
13 | var (
14 | // ErrInvalidSemVer is returned if a version can not be parsed
15 | ErrInvalidSemVer = errors.New("invalid semantic version")
16 |
17 | // ErrUnsupportedVer is returned if a version out of supported range
18 | ErrUnsupportedVer = errors.New("the version is out of supported range")
19 |
20 | php5RangeStart = &Version{Major: 5}
21 | php5RangeEnd = &Version{Major: 5, Minor: 6}
22 |
23 | php7RangeStart = &Version{Major: 7}
24 | php7RangeEnd = &Version{Major: 7, Minor: 4}
25 | )
26 |
27 | func New(v string) (*Version, error) {
28 | // Split the parts into [0]Major, [1]Minor
29 | parts := strings.SplitN(v, ".", 2)
30 | if len(parts) != 2 {
31 | return nil, ErrInvalidSemVer
32 | }
33 |
34 | var ver = new(Version)
35 | var err error
36 |
37 | ver.Major, err = strconv.ParseUint(parts[0], 10, 64)
38 | if err != nil {
39 | return nil, err
40 | }
41 |
42 | ver.Minor, err = strconv.ParseUint(parts[1], 10, 64)
43 | if err != nil {
44 | return nil, err
45 | }
46 |
47 | return ver, nil
48 | }
49 |
50 | func (v *Version) Validate() error {
51 | if !v.InRange(php5RangeStart, php5RangeEnd) && !v.InRange(php7RangeStart, php7RangeEnd) {
52 | return ErrUnsupportedVer
53 | }
54 |
55 | return nil
56 | }
57 |
58 | // Less tests if one version is less than another one
59 | func (v *Version) Less(o *Version) bool {
60 | return v.Compare(o) < 0
61 | }
62 |
63 | // LessOrEqual tests if one version is less than another one or equal
64 | func (v *Version) LessOrEqual(o *Version) bool {
65 | return v.Compare(o) <= 0
66 | }
67 |
68 | // Greater tests if one version is greater than another one
69 | func (v *Version) Greater(o *Version) bool {
70 | return v.Compare(o) > 0
71 | }
72 |
73 | // GreaterOrEqual tests if one version is greater than another one or equal
74 | func (v *Version) GreaterOrEqual(o *Version) bool {
75 | return v.Compare(o) >= 0
76 | }
77 |
78 | // GreaterOrEqual tests if one version is greater than another one or equal
79 | func (v *Version) InRange(s, e *Version) bool {
80 | return v.Compare(s) >= 0 && v.Compare(e) <= 0
81 | }
82 |
83 | // Compare compares this version to another one. It returns -1, 0, or 1 if
84 | // the version smaller, equal, or larger than the other version.
85 | func (v *Version) Compare(o *Version) int {
86 | if d := compareSegment(v.Major, o.Major); d != 0 {
87 | return d
88 | }
89 |
90 | return compareSegment(v.Minor, o.Minor)
91 | }
92 |
93 | func compareSegment(v, o uint64) int {
94 | if v < o {
95 | return -1
96 | }
97 | if v > o {
98 | return 1
99 | }
100 |
101 | return 0
102 | }
103 |
--------------------------------------------------------------------------------
/pkg/version/version_test.go:
--------------------------------------------------------------------------------
1 | package version_test
2 |
3 | import (
4 | "gotest.tools/assert"
5 | "testing"
6 |
7 | "github.com/z7zmey/php-parser/pkg/version"
8 | )
9 |
10 | func Test(t *testing.T) {
11 | ver, err := version.New("7.4")
12 | assert.NilError(t, err)
13 |
14 | assert.Equal(t, *ver, version.Version{
15 | Major: 7,
16 | Minor: 4,
17 | })
18 | }
19 |
20 | func TestLeadingZero(t *testing.T) {
21 | ver, err := version.New("07.04")
22 | assert.NilError(t, err)
23 |
24 | assert.Equal(t, *ver, version.Version{
25 | Major: 7,
26 | Minor: 4,
27 | })
28 | }
29 |
30 | func TestInRange(t *testing.T) {
31 | s, err := version.New("7.0")
32 | assert.NilError(t, err)
33 |
34 | e, err := version.New("7.4")
35 | assert.NilError(t, err)
36 |
37 | ver, err := version.New("7.0")
38 | assert.NilError(t, err)
39 | assert.Assert(t, ver.InRange(s, e))
40 |
41 | ver, err = version.New("7.2")
42 | assert.NilError(t, err)
43 | assert.Assert(t, ver.InRange(s, e))
44 |
45 | ver, err = version.New("7.4")
46 | assert.NilError(t, err)
47 | assert.Assert(t, ver.InRange(s, e))
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/visitor/dumper/dumper_test.go:
--------------------------------------------------------------------------------
1 | package dumper_test
2 |
3 | import (
4 | "bytes"
5 | "github.com/z7zmey/php-parser/pkg/position"
6 | "github.com/z7zmey/php-parser/pkg/token"
7 | "github.com/z7zmey/php-parser/pkg/visitor/dumper"
8 | "testing"
9 |
10 | "github.com/z7zmey/php-parser/pkg/ast"
11 | )
12 |
13 | func TestDumper_root(t *testing.T) {
14 | o := bytes.NewBufferString("")
15 |
16 | p := dumper.NewDumper(o).WithTokens().WithPositions()
17 | n := &ast.Root{
18 | Position: &position.Position{
19 | StartLine: 1,
20 | EndLine: 2,
21 | StartPos: 3,
22 | EndPos: 4,
23 | },
24 | Stmts: []ast.Vertex{
25 | &ast.StmtNop{},
26 | },
27 | EndTkn: &token.Token{
28 | FreeFloating: []*token.Token{
29 | {
30 | ID: token.T_WHITESPACE,
31 | Value: []byte(" "),
32 | Position: &position.Position{
33 | StartLine: 1,
34 | EndLine: 2,
35 | StartPos: 3,
36 | EndPos: 4,
37 | },
38 | },
39 | },
40 | },
41 | }
42 | n.Accept(p)
43 |
44 | expected := `&ast.Root{
45 | Position: &position.Position{
46 | StartLine: 1,
47 | EndLine: 2,
48 | StartPos: 3,
49 | EndPos: 4,
50 | },
51 | Stmts: []ast.Vertex{
52 | &ast.StmtNop{
53 | },
54 | },
55 | EndTkn: &token.Token{
56 | FreeFloating: []*token.Token{
57 | {
58 | ID: token.T_WHITESPACE,
59 | Val: []byte(" "),
60 | Position: &position.Position{
61 | StartLine: 1,
62 | EndLine: 2,
63 | StartPos: 3,
64 | EndPos: 4,
65 | },
66 | },
67 | },
68 | },
69 | },
70 | `
71 | actual := o.String()
72 |
73 | if expected != actual {
74 | t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/visitor/nsresolver/namespace_resolver.go:
--------------------------------------------------------------------------------
1 | // Package visitor contains walker.visitor implementations
2 | package nsresolver
3 |
4 | import (
5 | "errors"
6 | "github.com/z7zmey/php-parser/pkg/ast"
7 | "github.com/z7zmey/php-parser/pkg/visitor"
8 | "strings"
9 | )
10 |
11 | // NamespaceResolver visitor
12 | type NamespaceResolver struct {
13 | visitor.Null
14 | Namespace *Namespace
15 | ResolvedNames map[ast.Vertex]string
16 |
17 | goDeep bool
18 | }
19 |
20 | // NewNamespaceResolver NamespaceResolver type constructor
21 | func NewNamespaceResolver() *NamespaceResolver {
22 | return &NamespaceResolver{
23 | Namespace: NewNamespace(""),
24 | ResolvedNames: map[ast.Vertex]string{},
25 | goDeep: true,
26 | }
27 | }
28 |
29 | func (nsr *NamespaceResolver) EnterNode(n ast.Vertex) bool {
30 | n.Accept(nsr)
31 |
32 | if !nsr.goDeep {
33 | nsr.goDeep = true
34 | return false
35 | }
36 |
37 | return true
38 | }
39 |
40 | func (nsr *NamespaceResolver) StmtNamespace(n *ast.StmtNamespace) {
41 | if n.Name == nil {
42 | nsr.Namespace = NewNamespace("")
43 | } else {
44 | NSParts := n.Name.(*ast.Name).Parts
45 | nsr.Namespace = NewNamespace(concatNameParts(NSParts))
46 | }
47 | }
48 |
49 | func (nsr *NamespaceResolver) StmtUse(n *ast.StmtUseList) {
50 | useType := ""
51 | if n.Type != nil {
52 | useType = string(n.Type.(*ast.Identifier).Value)
53 | }
54 |
55 | for _, nn := range n.Uses {
56 | nsr.AddAlias(useType, nn, nil)
57 | }
58 |
59 | nsr.goDeep = false
60 | }
61 |
62 | func (nsr *NamespaceResolver) StmtGroupUse(n *ast.StmtGroupUseList) {
63 | useType := ""
64 | if n.Type != nil {
65 | useType = string(n.Type.(*ast.Identifier).Value)
66 | }
67 |
68 | for _, nn := range n.Uses {
69 | nsr.AddAlias(useType, nn, n.Prefix.(*ast.Name).Parts)
70 | }
71 |
72 | nsr.goDeep = false
73 | }
74 |
75 | func (nsr *NamespaceResolver) StmtClass(n *ast.StmtClass) {
76 | if n.Extends != nil {
77 | nsr.ResolveName(n.Extends, "")
78 | }
79 |
80 | if n.Implements != nil {
81 | for _, interfaceName := range n.Implements {
82 | nsr.ResolveName(interfaceName, "")
83 | }
84 | }
85 |
86 | if n.Name != nil {
87 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value))
88 | }
89 | }
90 |
91 | func (nsr *NamespaceResolver) StmtInterface(n *ast.StmtInterface) {
92 | if n.Extends != nil {
93 | for _, interfaceName := range n.Extends {
94 | nsr.ResolveName(interfaceName, "")
95 | }
96 | }
97 |
98 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value))
99 | }
100 |
101 | func (nsr *NamespaceResolver) StmtTrait(n *ast.StmtTrait) {
102 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value))
103 | }
104 |
105 | func (nsr *NamespaceResolver) StmtFunction(n *ast.StmtFunction) {
106 | nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value))
107 |
108 | for _, parameter := range n.Params {
109 | nsr.ResolveType(parameter.(*ast.Parameter).Type)
110 | }
111 |
112 | if n.ReturnType != nil {
113 | nsr.ResolveType(n.ReturnType)
114 | }
115 | }
116 |
117 | func (nsr *NamespaceResolver) StmtClassMethod(n *ast.StmtClassMethod) {
118 | for _, parameter := range n.Params {
119 | nsr.ResolveType(parameter.(*ast.Parameter).Type)
120 | }
121 |
122 | if n.ReturnType != nil {
123 | nsr.ResolveType(n.ReturnType)
124 | }
125 | }
126 |
127 | func (nsr *NamespaceResolver) ExprClosure(n *ast.ExprClosure) {
128 | for _, parameter := range n.Params {
129 | nsr.ResolveType(parameter.(*ast.Parameter).Type)
130 | }
131 |
132 | if n.ReturnType != nil {
133 | nsr.ResolveType(n.ReturnType)
134 | }
135 | }
136 |
137 | func (nsr *NamespaceResolver) StmtPropertyList(n *ast.StmtPropertyList) {
138 | if n.Type != nil {
139 | nsr.ResolveType(n.Type)
140 | }
141 | }
142 |
143 | func (nsr *NamespaceResolver) StmtConstList(n *ast.StmtConstList) {
144 | for _, constant := range n.Consts {
145 | nsr.AddNamespacedName(constant, string(constant.(*ast.StmtConstant).Name.(*ast.Identifier).Value))
146 | }
147 | }
148 |
149 | func (nsr *NamespaceResolver) ExprStaticCall(n *ast.ExprStaticCall) {
150 | nsr.ResolveName(n.Class, "")
151 | }
152 |
153 | func (nsr *NamespaceResolver) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) {
154 | nsr.ResolveName(n.Class, "")
155 | }
156 |
157 | func (nsr *NamespaceResolver) ExprClassConstFetch(n *ast.ExprClassConstFetch) {
158 | nsr.ResolveName(n.Class, "")
159 | }
160 |
161 | func (nsr *NamespaceResolver) ExprNew(n *ast.ExprNew) {
162 | nsr.ResolveName(n.Class, "")
163 | }
164 |
165 | func (nsr *NamespaceResolver) ExprInstanceOf(n *ast.ExprInstanceOf) {
166 | nsr.ResolveName(n.Class, "")
167 | }
168 |
169 | func (nsr *NamespaceResolver) StmtCatch(n *ast.StmtCatch) {
170 | for _, t := range n.Types {
171 | nsr.ResolveName(t, "")
172 | }
173 | }
174 |
175 | func (nsr *NamespaceResolver) ExprFunctionCall(n *ast.ExprFunctionCall) {
176 | nsr.ResolveName(n.Function, "function")
177 | }
178 |
179 | func (nsr *NamespaceResolver) ExprConstFetch(n *ast.ExprConstFetch) {
180 | nsr.ResolveName(n.Const, "const")
181 | }
182 |
183 | func (nsr *NamespaceResolver) StmtTraitUse(n *ast.StmtTraitUse) {
184 | for _, t := range n.Traits {
185 | nsr.ResolveName(t, "")
186 | }
187 |
188 | for _, a := range n.Adaptations {
189 | switch aa := a.(type) {
190 | case *ast.StmtTraitUsePrecedence:
191 | refTrait := aa.Trait
192 | if refTrait != nil {
193 | nsr.ResolveName(refTrait, "")
194 | }
195 | for _, insteadOf := range aa.Insteadof {
196 | nsr.ResolveName(insteadOf, "")
197 | }
198 |
199 | case *ast.StmtTraitUseAlias:
200 | refTrait := aa.Trait
201 | if refTrait != nil {
202 | nsr.ResolveName(refTrait, "")
203 | }
204 | }
205 | }
206 | }
207 |
208 | // LeaveNode is invoked after node process
209 | func (nsr *NamespaceResolver) LeaveNode(n ast.Vertex) {
210 | switch nn := n.(type) {
211 | case *ast.StmtNamespace:
212 | if nn.Stmts != nil {
213 | nsr.Namespace = NewNamespace("")
214 | }
215 | }
216 | }
217 |
218 | // AddAlias adds a new alias
219 | func (nsr *NamespaceResolver) AddAlias(useType string, nn ast.Vertex, prefix []ast.Vertex) {
220 | switch use := nn.(type) {
221 | case *ast.StmtUse:
222 | if use.Type != nil {
223 | useType = string(use.Type.(*ast.Identifier).Value)
224 | }
225 |
226 | useNameParts := use.Use.(*ast.Name).Parts
227 | var alias string
228 | if use.Alias == nil {
229 | alias = string(useNameParts[len(useNameParts)-1].(*ast.NamePart).Value)
230 | } else {
231 | alias = string(use.Alias.(*ast.Identifier).Value)
232 | }
233 |
234 | nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias)
235 | }
236 | }
237 |
238 | // AddNamespacedName adds namespaced name by node
239 | func (nsr *NamespaceResolver) AddNamespacedName(nn ast.Vertex, nodeName string) {
240 | if nsr.Namespace.Namespace == "" {
241 | nsr.ResolvedNames[nn] = nodeName
242 | } else {
243 | nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName
244 | }
245 | }
246 |
247 | // ResolveName adds a resolved fully qualified name by node
248 | func (nsr *NamespaceResolver) ResolveName(nameNode ast.Vertex, aliasType string) {
249 | resolved, err := nsr.Namespace.ResolveName(nameNode, aliasType)
250 | if err == nil {
251 | nsr.ResolvedNames[nameNode] = resolved
252 | }
253 | }
254 |
255 | // ResolveType adds a resolved fully qualified type name
256 | func (nsr *NamespaceResolver) ResolveType(n ast.Vertex) {
257 | switch nn := n.(type) {
258 | case *ast.Nullable:
259 | nsr.ResolveType(nn.Expr)
260 | case *ast.Name:
261 | nsr.ResolveName(n, "")
262 | case *ast.NameRelative:
263 | nsr.ResolveName(n, "")
264 | case *ast.NameFullyQualified:
265 | nsr.ResolveName(n, "")
266 | }
267 | }
268 |
269 | // Namespace context
270 | type Namespace struct {
271 | Namespace string
272 | Aliases map[string]map[string]string
273 | }
274 |
275 | // NewNamespace constructor
276 | func NewNamespace(NSName string) *Namespace {
277 | return &Namespace{
278 | Namespace: NSName,
279 | Aliases: map[string]map[string]string{
280 | "": {},
281 | "const": {},
282 | "function": {},
283 | },
284 | }
285 | }
286 |
287 | // AddAlias adds a new alias
288 | func (ns *Namespace) AddAlias(aliasType string, aliasName string, alias string) {
289 | aliasType = strings.ToLower(aliasType)
290 |
291 | if aliasType == "const" {
292 | ns.Aliases[aliasType][alias] = aliasName
293 | } else {
294 | ns.Aliases[aliasType][strings.ToLower(alias)] = aliasName
295 | }
296 | }
297 |
298 | // ResolveName returns a resolved fully qualified name
299 | func (ns *Namespace) ResolveName(nameNode ast.Vertex, aliasType string) (string, error) {
300 | switch n := nameNode.(type) {
301 | case *ast.NameFullyQualified:
302 | // Fully qualifid name is already resolved
303 | return concatNameParts(n.Parts), nil
304 |
305 | case *ast.NameRelative:
306 | if ns.Namespace == "" {
307 | return concatNameParts(n.Parts), nil
308 | }
309 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil
310 |
311 | case *ast.Name:
312 | if aliasType == "const" && len(n.Parts) == 1 {
313 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value))
314 | if part == "true" || part == "false" || part == "null" {
315 | return part, nil
316 | }
317 | }
318 |
319 | if aliasType == "" && len(n.Parts) == 1 {
320 | part := strings.ToLower(string(n.Parts[0].(*ast.NamePart).Value))
321 |
322 | switch part {
323 | case "self":
324 | fallthrough
325 | case "static":
326 | fallthrough
327 | case "parent":
328 | fallthrough
329 | case "int":
330 | fallthrough
331 | case "float":
332 | fallthrough
333 | case "bool":
334 | fallthrough
335 | case "string":
336 | fallthrough
337 | case "void":
338 | fallthrough
339 | case "iterable":
340 | fallthrough
341 | case "object":
342 | return part, nil
343 | }
344 | }
345 |
346 | aliasName, err := ns.ResolveAlias(nameNode, aliasType)
347 | if err != nil {
348 | // resolve as relative name if alias not found
349 | if ns.Namespace == "" {
350 | return concatNameParts(n.Parts), nil
351 | }
352 | return ns.Namespace + "\\" + concatNameParts(n.Parts), nil
353 | }
354 |
355 | if len(n.Parts) > 1 {
356 | // if name qualified, replace first part by alias
357 | return aliasName + "\\" + concatNameParts(n.Parts[1:]), nil
358 | }
359 |
360 | return aliasName, nil
361 | }
362 |
363 | return "", errors.New("must be instance of name.Names")
364 | }
365 |
366 | // ResolveAlias returns alias or error if not found
367 | func (ns *Namespace) ResolveAlias(nameNode ast.Vertex, aliasType string) (string, error) {
368 | aliasType = strings.ToLower(aliasType)
369 | nameParts := nameNode.(*ast.Name).Parts
370 |
371 | firstPartStr := string(nameParts[0].(*ast.NamePart).Value)
372 |
373 | if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias type
374 | firstPartStr = strings.ToLower(firstPartStr)
375 | aliasType = ""
376 | } else {
377 | if aliasType != "const" { // constants are case-sensitive
378 | firstPartStr = strings.ToLower(firstPartStr)
379 | }
380 | }
381 |
382 | aliasName, ok := ns.Aliases[aliasType][firstPartStr]
383 | if !ok {
384 | return "", errors.New("Not found")
385 | }
386 |
387 | return aliasName, nil
388 | }
389 |
390 | func concatNameParts(parts ...[]ast.Vertex) string {
391 | str := ""
392 |
393 | for _, p := range parts {
394 | for _, n := range p {
395 | if str == "" {
396 | str = string(n.(*ast.NamePart).Value)
397 | } else {
398 | str = str + "\\" + string(n.(*ast.NamePart).Value)
399 | }
400 | }
401 | }
402 |
403 | return str
404 | }
405 |
--------------------------------------------------------------------------------
/pkg/visitor/nsresolver/namespace_resolver_test.go:
--------------------------------------------------------------------------------
1 | package nsresolver_test
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/visitor/nsresolver"
5 | "github.com/z7zmey/php-parser/pkg/visitor/traverser"
6 | "testing"
7 |
8 | "gotest.tools/assert"
9 |
10 | "github.com/z7zmey/php-parser/pkg/ast"
11 | )
12 |
13 | func TestResolveStaticCall(t *testing.T) {
14 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
15 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
16 |
17 | stxTree := &ast.StmtStmtList{
18 | Stmts: []ast.Vertex{
19 | &ast.StmtUseList{
20 | Uses: []ast.Vertex{
21 | &ast.StmtUse{
22 | Use: nameAB,
23 | },
24 | },
25 | },
26 | &ast.ExprStaticCall{
27 | Class: nameBC,
28 | Call: &ast.Identifier{Value: []byte("foo")},
29 | },
30 | },
31 | }
32 |
33 | expected := map[ast.Vertex]string{
34 | nameBC: "A\\B\\C",
35 | }
36 |
37 | nsResolver := nsresolver.NewNamespaceResolver()
38 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
39 |
40 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
41 | }
42 |
43 | func TestResolveStaticPropertyFetch(t *testing.T) {
44 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
45 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
46 |
47 | stxTree := &ast.StmtStmtList{
48 | Stmts: []ast.Vertex{
49 | &ast.StmtUseList{
50 | Uses: []ast.Vertex{
51 | &ast.StmtUse{
52 | Use: nameAB,
53 | },
54 | },
55 | },
56 | &ast.ExprStaticPropertyFetch{
57 | Class: nameBC,
58 | Prop: &ast.Identifier{Value: []byte("foo")},
59 | },
60 | },
61 | }
62 |
63 | expected := map[ast.Vertex]string{
64 | nameBC: "A\\B\\C",
65 | }
66 |
67 | nsResolver := nsresolver.NewNamespaceResolver()
68 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
69 |
70 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
71 | }
72 |
73 | func TestResolveClassConstFetch(t *testing.T) {
74 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
75 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
76 |
77 | stxTree := &ast.StmtStmtList{
78 | Stmts: []ast.Vertex{
79 | &ast.StmtUseList{
80 | Uses: []ast.Vertex{
81 | &ast.StmtUse{
82 | Use: nameAB,
83 | },
84 | },
85 | },
86 | &ast.ExprClassConstFetch{
87 | Class: nameBC,
88 | Const: &ast.Identifier{Value: []byte("FOO")},
89 | },
90 | },
91 | }
92 |
93 | expected := map[ast.Vertex]string{
94 | nameBC: "A\\B\\C",
95 | }
96 |
97 | nsResolver := nsresolver.NewNamespaceResolver()
98 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
99 |
100 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
101 | }
102 |
103 | func TestResolveNew(t *testing.T) {
104 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
105 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
106 |
107 | stxTree := &ast.StmtStmtList{
108 | Stmts: []ast.Vertex{
109 | &ast.StmtUseList{
110 | Uses: []ast.Vertex{
111 | &ast.StmtUse{
112 | Use: nameAB,
113 | },
114 | },
115 | },
116 | &ast.ExprNew{
117 | Class: nameBC,
118 | },
119 | },
120 | }
121 |
122 | expected := map[ast.Vertex]string{
123 | nameBC: "A\\B\\C",
124 | }
125 |
126 | nsResolver := nsresolver.NewNamespaceResolver()
127 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
128 |
129 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
130 | }
131 |
132 | func TestResolveInstanceOf(t *testing.T) {
133 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
134 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
135 |
136 | stxTree := &ast.StmtStmtList{
137 | Stmts: []ast.Vertex{
138 | &ast.StmtUseList{
139 | Uses: []ast.Vertex{
140 | &ast.StmtUse{
141 | Use: nameAB,
142 | },
143 | },
144 | },
145 | &ast.ExprInstanceOf{
146 | Expr: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
147 | Class: nameBC,
148 | },
149 | },
150 | }
151 |
152 | expected := map[ast.Vertex]string{
153 | nameBC: "A\\B\\C",
154 | }
155 |
156 | nsResolver := nsresolver.NewNamespaceResolver()
157 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
158 |
159 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
160 | }
161 |
162 | func TestResolveInstanceCatch(t *testing.T) {
163 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
164 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
165 |
166 | nameDE := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("D")}, &ast.NamePart{Value: []byte("E")}}}
167 | nameF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}}}
168 |
169 | stxTree := &ast.StmtStmtList{
170 | Stmts: []ast.Vertex{
171 | &ast.StmtUseList{
172 | Uses: []ast.Vertex{
173 | &ast.StmtUse{
174 | Use: nameAB,
175 | },
176 | &ast.StmtUse{
177 | Use: nameDE,
178 | Alias: &ast.Identifier{Value: []byte("F")},
179 | },
180 | },
181 | },
182 | &ast.StmtTry{
183 | Stmts: []ast.Vertex{},
184 | Catches: []ast.Vertex{
185 | &ast.StmtCatch{
186 | Types: []ast.Vertex{
187 | nameBC,
188 | nameF,
189 | },
190 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
191 | Stmts: []ast.Vertex{},
192 | },
193 | },
194 | },
195 | },
196 | }
197 |
198 | expected := map[ast.Vertex]string{
199 | nameBC: "A\\B\\C",
200 | nameF: "D\\E",
201 | }
202 |
203 | nsResolver := nsresolver.NewNamespaceResolver()
204 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
205 |
206 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
207 | }
208 |
209 | func TestResolveFunctionCall(t *testing.T) {
210 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
211 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}}
212 |
213 | stxTree := &ast.StmtStmtList{
214 | Stmts: []ast.Vertex{
215 | &ast.StmtUseList{
216 | Type: &ast.Identifier{Value: []byte("function")},
217 | Uses: []ast.Vertex{
218 | &ast.StmtUse{
219 | Use: nameAB,
220 | },
221 | },
222 | },
223 | &ast.ExprFunctionCall{
224 | Function: nameB,
225 | },
226 | },
227 | }
228 |
229 | expected := map[ast.Vertex]string{
230 | nameB: "A\\B",
231 | }
232 |
233 | nsResolver := nsresolver.NewNamespaceResolver()
234 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
235 |
236 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
237 | }
238 |
239 | func TestResolveConstFetch(t *testing.T) {
240 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
241 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}}
242 |
243 | stxTree := &ast.StmtStmtList{
244 | Stmts: []ast.Vertex{
245 | &ast.StmtUseList{
246 | Type: &ast.Identifier{Value: []byte("const")},
247 | Uses: []ast.Vertex{
248 | &ast.StmtUse{
249 | Use: nameAB,
250 | },
251 | },
252 | },
253 | &ast.ExprConstFetch{
254 | Const: nameB,
255 | },
256 | },
257 | }
258 |
259 | expected := map[ast.Vertex]string{
260 | nameB: "A\\B",
261 | }
262 |
263 | nsResolver := nsresolver.NewNamespaceResolver()
264 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
265 |
266 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
267 | }
268 |
269 | func TestResolveGroupUse(t *testing.T) {
270 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
271 | nameBD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("D")}}}
272 | nameE := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("E")}}}
273 | nameC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}}}
274 | nameF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}}}
275 |
276 | stxTree := &ast.StmtStmtList{
277 | Stmts: []ast.Vertex{
278 | &ast.StmtGroupUseList{
279 | Prefix: nameAB,
280 | Uses: []ast.Vertex{
281 | &ast.StmtUse{
282 | Type: &ast.Identifier{Value: []byte("Function")},
283 | Use: nameF,
284 | },
285 | &ast.StmtUse{
286 | Type: &ast.Identifier{Value: []byte("const")},
287 | Use: nameC,
288 | },
289 | },
290 | },
291 | &ast.StmtGroupUseList{
292 | Prefix: nameBD,
293 | Type: &ast.Identifier{Value: []byte("Function")},
294 | Uses: []ast.Vertex{
295 | &ast.StmtUse{
296 | Use: nameE,
297 | },
298 | },
299 | },
300 | &ast.ExprConstFetch{
301 | Const: nameC,
302 | },
303 | &ast.ExprFunctionCall{
304 | Function: nameF,
305 | },
306 | &ast.ExprFunctionCall{
307 | Function: nameE,
308 | },
309 | },
310 | }
311 |
312 | expected := map[ast.Vertex]string{
313 | nameC: "A\\B\\C",
314 | nameF: "A\\B\\F",
315 | nameE: "B\\D\\E",
316 | }
317 |
318 | nsResolver := nsresolver.NewNamespaceResolver()
319 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
320 |
321 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
322 | }
323 |
324 | func TestResolveTraitUse(t *testing.T) {
325 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
326 | nameB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}}
327 | nameD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("D")}}}
328 |
329 | fullyQualifiedNameB := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}}
330 | fullyQualifiedNameBC := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
331 | relativeNameB := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}}}
332 | relativeNameBC := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
333 |
334 | stxTree := &ast.StmtStmtList{
335 | Stmts: []ast.Vertex{
336 | &ast.StmtUseList{
337 | Uses: []ast.Vertex{
338 | &ast.StmtUse{
339 | Use: nameAB,
340 | },
341 | },
342 | },
343 | &ast.StmtTraitUse{
344 | Traits: []ast.Vertex{
345 | nameB,
346 | relativeNameB,
347 | },
348 | Adaptations: []ast.Vertex{
349 | &ast.StmtTraitUsePrecedence{
350 | Trait: fullyQualifiedNameB,
351 | Method: &ast.Identifier{Value: []byte("foo")},
352 | Insteadof: []ast.Vertex{fullyQualifiedNameBC},
353 | },
354 | &ast.StmtTraitUseAlias{
355 | Trait: relativeNameBC,
356 | Method: &ast.Identifier{Value: []byte("foo")},
357 | Alias: &ast.Identifier{Value: []byte("bar")},
358 | },
359 | },
360 | },
361 | &ast.StmtTraitUse{
362 | Traits: []ast.Vertex{
363 | nameD,
364 | },
365 | },
366 | },
367 | }
368 |
369 | expected := map[ast.Vertex]string{
370 | nameB: "A\\B",
371 | nameD: "D",
372 | relativeNameB: "B",
373 | fullyQualifiedNameB: "B",
374 | fullyQualifiedNameBC: "B\\C",
375 | relativeNameBC: "B\\C",
376 | }
377 |
378 | nsResolver := nsresolver.NewNamespaceResolver()
379 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
380 |
381 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
382 | }
383 |
384 | func TestResolveClassName(t *testing.T) {
385 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
386 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
387 |
388 | class := &ast.StmtClass{
389 | Name: &ast.Identifier{Value: []byte("A")},
390 | Extends: nameAB,
391 | Implements: []ast.Vertex{
392 | nameBC,
393 | },
394 | }
395 |
396 | stxTree := &ast.StmtStmtList{
397 | Stmts: []ast.Vertex{
398 | class,
399 | },
400 | }
401 |
402 | expected := map[ast.Vertex]string{
403 | class: "A",
404 | nameAB: "A\\B",
405 | nameBC: "B\\C",
406 | }
407 |
408 | nsResolver := nsresolver.NewNamespaceResolver()
409 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
410 |
411 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
412 | }
413 |
414 | func TestResolveInterfaceName(t *testing.T) {
415 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
416 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
417 |
418 | interfaceNode := &ast.StmtInterface{
419 | Name: &ast.Identifier{Value: []byte("A")},
420 | Extends: []ast.Vertex{
421 | nameAB,
422 | nameBC,
423 | },
424 | }
425 |
426 | stxTree := &ast.StmtStmtList{
427 | Stmts: []ast.Vertex{
428 | interfaceNode,
429 | },
430 | }
431 |
432 | expected := map[ast.Vertex]string{
433 | interfaceNode: "A",
434 | nameAB: "A\\B",
435 | nameBC: "B\\C",
436 | }
437 |
438 | nsResolver := nsresolver.NewNamespaceResolver()
439 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
440 |
441 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
442 | }
443 |
444 | func TestResolveTraitName(t *testing.T) {
445 | traitNode := &ast.StmtTrait{
446 | Name: &ast.Identifier{Value: []byte("A")},
447 | Stmts: []ast.Vertex{},
448 | }
449 |
450 | stxTree := &ast.StmtStmtList{
451 | Stmts: []ast.Vertex{
452 | traitNode,
453 | },
454 | }
455 |
456 | expected := map[ast.Vertex]string{
457 | traitNode: "A",
458 | }
459 |
460 | nsResolver := nsresolver.NewNamespaceResolver()
461 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
462 |
463 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
464 | }
465 |
466 | func TestResolveFunctionName(t *testing.T) {
467 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
468 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
469 |
470 | functionNode := &ast.StmtFunction{
471 | Name: &ast.Identifier{Value: []byte("A")},
472 | Params: []ast.Vertex{
473 | &ast.Parameter{
474 | Type: nameAB,
475 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
476 | },
477 | },
478 | ReturnType: &ast.Nullable{Expr: nameBC},
479 | Stmts: []ast.Vertex{},
480 | }
481 |
482 | stxTree := &ast.StmtStmtList{
483 | Stmts: []ast.Vertex{
484 | functionNode,
485 | },
486 | }
487 |
488 | expected := map[ast.Vertex]string{
489 | functionNode: "A",
490 | nameAB: "A\\B",
491 | nameBC: "B\\C",
492 | }
493 |
494 | nsResolver := nsresolver.NewNamespaceResolver()
495 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
496 |
497 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
498 | }
499 |
500 | func TestResolveMethodName(t *testing.T) {
501 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
502 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
503 |
504 | methodNode := &ast.StmtClassMethod{
505 | Name: &ast.Identifier{Value: []byte("A")},
506 | Params: []ast.Vertex{
507 | &ast.Parameter{
508 | Type: nameAB,
509 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
510 | },
511 | },
512 | ReturnType: &ast.Nullable{Expr: nameBC},
513 | Stmt: &ast.StmtStmtList{
514 | Stmts: []ast.Vertex{},
515 | },
516 | }
517 |
518 | expected := map[ast.Vertex]string{
519 | nameAB: "A\\B",
520 | nameBC: "B\\C",
521 | }
522 |
523 | nsResolver := nsresolver.NewNamespaceResolver()
524 | traverser.NewTraverser(nsResolver).Traverse(methodNode)
525 |
526 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
527 | }
528 |
529 | func TestResolveClosureName(t *testing.T) {
530 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
531 | nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
532 |
533 | closureNode := &ast.ExprClosure{
534 | Params: []ast.Vertex{
535 | &ast.Parameter{
536 | Type: nameAB,
537 | Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
538 | },
539 | },
540 | ReturnType: &ast.Nullable{Expr: nameBC},
541 | Stmts: []ast.Vertex{},
542 | }
543 |
544 | expected := map[ast.Vertex]string{
545 | nameAB: "A\\B",
546 | nameBC: "B\\C",
547 | }
548 |
549 | nsResolver := nsresolver.NewNamespaceResolver()
550 | traverser.NewTraverser(nsResolver).Traverse(closureNode)
551 |
552 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
553 | }
554 |
555 | func TestResolveConstantsName(t *testing.T) {
556 | nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
557 |
558 | constantB := &ast.StmtConstant{
559 | Name: &ast.Identifier{Value: []byte("B")},
560 | Expr: &ast.ScalarLnumber{Value: []byte("1")},
561 | }
562 | constantC := &ast.StmtConstant{
563 | Name: &ast.Identifier{Value: []byte("C")},
564 | Expr: &ast.ScalarLnumber{Value: []byte("1")},
565 | }
566 |
567 | stxTree := &ast.StmtStmtList{
568 | Stmts: []ast.Vertex{
569 | &ast.StmtNamespace{
570 | Name: nameAB,
571 | },
572 | &ast.StmtConstList{
573 | Consts: []ast.Vertex{
574 | constantB,
575 | constantC,
576 | },
577 | },
578 | },
579 | }
580 |
581 | expected := map[ast.Vertex]string{
582 | constantB: "A\\B\\B",
583 | constantC: "A\\B\\C",
584 | }
585 |
586 | nsResolver := nsresolver.NewNamespaceResolver()
587 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
588 |
589 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
590 | }
591 |
592 | func TestResolveNamespaces(t *testing.T) {
593 | namespaceAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
594 | namespaceCD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("D")}}}
595 |
596 | nameAC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("C")}}}
597 | nameCF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("F")}}}
598 | nameFG := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}, &ast.NamePart{Value: []byte("G")}}}
599 | relativeNameCE := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("E")}}}
600 |
601 | constantB := &ast.StmtConstant{
602 | Name: &ast.Identifier{Value: []byte("B")},
603 | Expr: &ast.ScalarLnumber{Value: []byte("1")},
604 | }
605 | constantC := &ast.StmtConstant{
606 | Name: &ast.Identifier{Value: []byte("C")},
607 | Expr: &ast.ScalarLnumber{Value: []byte("1")},
608 | }
609 |
610 | stxTree := &ast.StmtStmtList{
611 | Stmts: []ast.Vertex{
612 | &ast.StmtNamespace{
613 | Name: namespaceAB,
614 | },
615 | &ast.StmtConstList{
616 | Consts: []ast.Vertex{
617 | constantB,
618 | constantC,
619 | },
620 | },
621 | &ast.ExprStaticCall{
622 | Class: nameFG,
623 | Call: &ast.Identifier{Value: []byte("foo")},
624 | },
625 | &ast.StmtNamespace{
626 | Stmts: []ast.Vertex{},
627 | },
628 | &ast.StmtNamespace{
629 | Name: namespaceCD,
630 | Stmts: []ast.Vertex{
631 | &ast.StmtUseList{
632 | Uses: []ast.Vertex{
633 | &ast.StmtUse{
634 | Use: nameAC,
635 | },
636 | },
637 | },
638 | &ast.ExprStaticCall{
639 | Class: relativeNameCE,
640 | Call: &ast.Identifier{Value: []byte("foo")},
641 | },
642 | &ast.ExprStaticCall{
643 | Class: nameCF,
644 | Call: &ast.Identifier{Value: []byte("foo")},
645 | },
646 | },
647 | },
648 | },
649 | }
650 |
651 | expected := map[ast.Vertex]string{
652 | constantB: "A\\B\\B",
653 | constantC: "A\\B\\C",
654 | nameFG: "A\\B\\F\\G",
655 | relativeNameCE: "C\\D\\C\\E",
656 | nameCF: "A\\C\\F",
657 | }
658 |
659 | nsResolver := nsresolver.NewNamespaceResolver()
660 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
661 |
662 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
663 | }
664 |
665 | func TestResolveStaticCallDinamicClassName(t *testing.T) {
666 | stxTree := &ast.StmtStmtList{
667 | Stmts: []ast.Vertex{
668 | &ast.ExprStaticCall{
669 | Class: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
670 | Call: &ast.Identifier{Value: []byte("foo")},
671 | },
672 | },
673 | }
674 |
675 | expected := map[ast.Vertex]string{}
676 |
677 | nsResolver := nsresolver.NewNamespaceResolver()
678 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
679 |
680 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
681 | }
682 |
683 | func TestDoNotResolveReservedConstants(t *testing.T) {
684 | namespaceName := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("Foo")}}}
685 |
686 | constantTrue := &ast.Name{
687 | Parts: []ast.Vertex{
688 | &ast.NamePart{Value: []byte("True")},
689 | },
690 | }
691 |
692 | constantFalse := &ast.Name{
693 | Parts: []ast.Vertex{
694 | &ast.NamePart{Value: []byte("False")},
695 | },
696 | }
697 |
698 | constantNull := &ast.Name{
699 | Parts: []ast.Vertex{
700 | &ast.NamePart{Value: []byte("NULL")},
701 | },
702 | }
703 |
704 | stxTree := &ast.StmtStmtList{
705 | Stmts: []ast.Vertex{
706 | &ast.StmtNamespace{
707 | Name: namespaceName,
708 | },
709 | &ast.StmtExpression{
710 | Expr: &ast.ExprConstFetch{
711 | Const: constantTrue,
712 | },
713 | },
714 | &ast.StmtExpression{
715 | Expr: &ast.ExprConstFetch{
716 | Const: constantFalse,
717 | },
718 | },
719 | &ast.StmtExpression{
720 | Expr: &ast.ExprConstFetch{
721 | Const: constantNull,
722 | },
723 | },
724 | },
725 | }
726 |
727 | expected := map[ast.Vertex]string{
728 | constantTrue: "true",
729 | constantFalse: "false",
730 | constantNull: "null",
731 | }
732 |
733 | nsResolver := nsresolver.NewNamespaceResolver()
734 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
735 |
736 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
737 | }
738 |
739 | func TestDoNotResolveReservedNames(t *testing.T) {
740 |
741 | nameInt := &ast.Name{
742 | Parts: []ast.Vertex{
743 | &ast.NamePart{Value: []byte("int")},
744 | },
745 | }
746 |
747 | nameFloat := &ast.Name{
748 | Parts: []ast.Vertex{
749 | &ast.NamePart{Value: []byte("float")},
750 | },
751 | }
752 |
753 | nameBool := &ast.Name{
754 | Parts: []ast.Vertex{
755 | &ast.NamePart{Value: []byte("bool")},
756 | },
757 | }
758 |
759 | nameString := &ast.Name{
760 | Parts: []ast.Vertex{
761 | &ast.NamePart{Value: []byte("string")},
762 | },
763 | }
764 |
765 | nameVoid := &ast.Name{
766 | Parts: []ast.Vertex{
767 | &ast.NamePart{Value: []byte("void")},
768 | },
769 | }
770 |
771 | nameIterable := &ast.Name{
772 | Parts: []ast.Vertex{
773 | &ast.NamePart{Value: []byte("iterable")},
774 | },
775 | }
776 |
777 | nameObject := &ast.Name{
778 | Parts: []ast.Vertex{
779 | &ast.NamePart{Value: []byte("object")},
780 | },
781 | }
782 |
783 | function := &ast.StmtFunction{
784 | Name: &ast.Identifier{Value: []byte("bar")},
785 | Params: []ast.Vertex{
786 | &ast.Parameter{
787 | Type: nameInt,
788 | Var: &ast.ExprVariable{
789 | Name: &ast.Identifier{Value: []byte("Int")},
790 | },
791 | },
792 | &ast.Parameter{
793 | Type: nameFloat,
794 | Var: &ast.ExprVariable{
795 | Name: &ast.Identifier{Value: []byte("Float")},
796 | },
797 | },
798 | &ast.Parameter{
799 | Type: nameBool,
800 | Var: &ast.ExprVariable{
801 | Name: &ast.Identifier{Value: []byte("Bool")},
802 | },
803 | },
804 | &ast.Parameter{
805 | Type: nameString,
806 | Var: &ast.ExprVariable{
807 | Name: &ast.Identifier{Value: []byte("String")},
808 | },
809 | },
810 | &ast.Parameter{
811 | Type: nameVoid,
812 | Var: &ast.ExprVariable{
813 | Name: &ast.Identifier{Value: []byte("Void")},
814 | },
815 | },
816 | &ast.Parameter{
817 | Type: nameIterable,
818 | Var: &ast.ExprVariable{
819 | Name: &ast.Identifier{Value: []byte("Iterable")},
820 | },
821 | },
822 | &ast.Parameter{
823 | Type: nameObject,
824 | Var: &ast.ExprVariable{
825 | Name: &ast.Identifier{Value: []byte("Object")},
826 | },
827 | },
828 | },
829 | }
830 |
831 | stxTree := &ast.StmtStmtList{
832 | Stmts: []ast.Vertex{
833 | &ast.StmtNamespace{
834 | Name: &ast.Name{
835 | Parts: []ast.Vertex{
836 | &ast.NamePart{Value: []byte("Foo")},
837 | },
838 | },
839 | },
840 | function,
841 | },
842 | }
843 |
844 | expected := map[ast.Vertex]string{
845 | function: "Foo\\bar",
846 | nameInt: "int",
847 | nameFloat: "float",
848 | nameBool: "bool",
849 | nameString: "string",
850 | nameVoid: "void",
851 | nameIterable: "iterable",
852 | nameObject: "object",
853 | }
854 |
855 | nsResolver := nsresolver.NewNamespaceResolver()
856 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
857 |
858 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
859 | }
860 |
861 | func TestDoNotResolveReservedSpecialNames(t *testing.T) {
862 |
863 | nameSelf := &ast.Name{
864 | Parts: []ast.Vertex{
865 | &ast.NamePart{Value: []byte("Self")},
866 | },
867 | }
868 |
869 | nameStatic := &ast.Name{
870 | Parts: []ast.Vertex{
871 | &ast.NamePart{Value: []byte("Static")},
872 | },
873 | }
874 |
875 | nameParent := &ast.Name{
876 | Parts: []ast.Vertex{
877 | &ast.NamePart{Value: []byte("Parent")},
878 | },
879 | }
880 |
881 | cls := &ast.StmtClass{
882 | Name: &ast.Identifier{Value: []byte("Bar")},
883 | Stmts: []ast.Vertex{
884 | &ast.StmtExpression{
885 | Expr: &ast.ExprStaticCall{
886 | Class: nameSelf,
887 | Call: &ast.Identifier{Value: []byte("func")},
888 | },
889 | },
890 | &ast.StmtExpression{
891 | Expr: &ast.ExprStaticCall{
892 | Class: nameStatic,
893 | Call: &ast.Identifier{Value: []byte("func")},
894 | },
895 | },
896 | &ast.StmtExpression{
897 | Expr: &ast.ExprStaticCall{
898 | Class: nameParent,
899 | Call: &ast.Identifier{Value: []byte("func")},
900 | },
901 | },
902 | },
903 | }
904 |
905 | stxTree := &ast.StmtStmtList{
906 | Stmts: []ast.Vertex{
907 | &ast.StmtNamespace{
908 | Name: &ast.Name{
909 | Parts: []ast.Vertex{
910 | &ast.NamePart{Value: []byte("Foo")},
911 | },
912 | },
913 | },
914 | cls,
915 | },
916 | }
917 |
918 | expected := map[ast.Vertex]string{
919 | cls: "Foo\\Bar",
920 | nameSelf: "self",
921 | nameStatic: "static",
922 | nameParent: "parent",
923 | }
924 |
925 | nsResolver := nsresolver.NewNamespaceResolver()
926 | traverser.NewTraverser(nsResolver).Traverse(stxTree)
927 |
928 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
929 | }
930 | func TestResolvePropertyTypeName(t *testing.T) {
931 | nameSimple := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
932 | nameRelative := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
933 | nameFullyQualified := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
934 |
935 | propertyNodeSimple := &ast.StmtPropertyList{
936 | Type: nameSimple,
937 | }
938 |
939 | propertyNodeRelative := &ast.StmtPropertyList{
940 | Type: nameRelative,
941 | }
942 |
943 | propertyNodeFullyQualified := &ast.StmtPropertyList{
944 | Type: nameFullyQualified,
945 | }
946 |
947 | classNode := &ast.StmtClass{
948 | Name: &ast.Identifier{Value: []byte("Bar")},
949 | Stmts: []ast.Vertex{
950 | propertyNodeSimple,
951 | propertyNodeRelative,
952 | propertyNodeFullyQualified,
953 | },
954 | }
955 |
956 | stmts := &ast.StmtStmtList{
957 | Stmts: []ast.Vertex{
958 | &ast.StmtNamespace{
959 | Name: &ast.Name{
960 | Parts: []ast.Vertex{
961 | &ast.NamePart{Value: []byte("Foo")},
962 | },
963 | },
964 | },
965 | classNode,
966 | },
967 | }
968 |
969 | expected := map[ast.Vertex]string{
970 | nameSimple: "Foo\\A\\B",
971 | nameRelative: "Foo\\A\\B",
972 | nameFullyQualified: "A\\B",
973 | classNode: "Foo\\Bar",
974 | }
975 |
976 | nsResolver := nsresolver.NewNamespaceResolver()
977 | traverser.NewTraverser(nsResolver).Traverse(stmts)
978 |
979 | assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
980 | }
981 |
--------------------------------------------------------------------------------
/pkg/visitor/null.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/ast"
5 | )
6 |
7 | type Null struct {
8 | }
9 |
10 | func (v *Null) Enter(_ string, _ bool) {
11 | // do nothing
12 | }
13 | func (v *Null) Leave(_ string, _ bool) {
14 | // do nothing
15 | }
16 |
17 | func (v *Null) EnterNode(_ ast.Vertex) bool {
18 | return true
19 | }
20 |
21 | func (v *Null) LeaveNode(_ ast.Vertex) {
22 | // do nothing
23 | }
24 |
25 | func (v *Null) Root(_ *ast.Root) {
26 | // do nothing
27 | }
28 |
29 | func (v *Null) Nullable(_ *ast.Nullable) {
30 | // do nothing
31 | }
32 |
33 | func (v *Null) Parameter(_ *ast.Parameter) {
34 | // do nothing
35 | }
36 |
37 | func (v *Null) Identifier(_ *ast.Identifier) {
38 | // do nothing
39 | }
40 |
41 | func (v *Null) Argument(_ *ast.Argument) {
42 | // do nothing
43 | }
44 |
45 | func (v *Null) StmtBreak(_ *ast.StmtBreak) {
46 | // do nothing
47 | }
48 |
49 | func (v *Null) StmtCase(_ *ast.StmtCase) {
50 | // do nothing
51 | }
52 |
53 | func (v *Null) StmtCatch(_ *ast.StmtCatch) {
54 | // do nothing
55 | }
56 |
57 | func (v *Null) StmtClass(_ *ast.StmtClass) {
58 | // do nothing
59 | }
60 |
61 | func (v *Null) StmtClassConstList(_ *ast.StmtClassConstList) {
62 | // do nothing
63 | }
64 |
65 | func (v *Null) StmtClassMethod(_ *ast.StmtClassMethod) {
66 | // do nothing
67 | }
68 |
69 | func (v *Null) StmtConstList(_ *ast.StmtConstList) {
70 | // do nothing
71 | }
72 |
73 | func (v *Null) StmtConstant(_ *ast.StmtConstant) {
74 | // do nothing
75 | }
76 |
77 | func (v *Null) StmtContinue(_ *ast.StmtContinue) {
78 | // do nothing
79 | }
80 |
81 | func (v *Null) StmtDeclare(_ *ast.StmtDeclare) {
82 | // do nothing
83 | }
84 |
85 | func (v *Null) StmtDefault(_ *ast.StmtDefault) {
86 | // do nothing
87 | }
88 |
89 | func (v *Null) StmtDo(_ *ast.StmtDo) {
90 | // do nothing
91 | }
92 |
93 | func (v *Null) StmtEcho(_ *ast.StmtEcho) {
94 | // do nothing
95 | }
96 |
97 | func (v *Null) StmtElse(_ *ast.StmtElse) {
98 | // do nothing
99 | }
100 |
101 | func (v *Null) StmtElseIf(_ *ast.StmtElseIf) {
102 | // do nothing
103 | }
104 |
105 | func (v *Null) StmtExpression(_ *ast.StmtExpression) {
106 | // do nothing
107 | }
108 |
109 | func (v *Null) StmtFinally(_ *ast.StmtFinally) {
110 | // do nothing
111 | }
112 |
113 | func (v *Null) StmtFor(_ *ast.StmtFor) {
114 | // do nothing
115 | }
116 |
117 | func (v *Null) StmtForeach(_ *ast.StmtForeach) {
118 | // do nothing
119 | }
120 |
121 | func (v *Null) StmtFunction(_ *ast.StmtFunction) {
122 | // do nothing
123 | }
124 |
125 | func (v *Null) StmtGlobal(_ *ast.StmtGlobal) {
126 | // do nothing
127 | }
128 |
129 | func (v *Null) StmtGoto(_ *ast.StmtGoto) {
130 | // do nothing
131 | }
132 |
133 | func (v *Null) StmtHaltCompiler(_ *ast.StmtHaltCompiler) {
134 | // do nothing
135 | }
136 |
137 | func (v *Null) StmtIf(_ *ast.StmtIf) {
138 | // do nothing
139 | }
140 |
141 | func (v *Null) StmtInlineHtml(_ *ast.StmtInlineHtml) {
142 | // do nothing
143 | }
144 |
145 | func (v *Null) StmtInterface(_ *ast.StmtInterface) {
146 | // do nothing
147 | }
148 |
149 | func (v *Null) StmtLabel(_ *ast.StmtLabel) {
150 | // do nothing
151 | }
152 |
153 | func (v *Null) StmtNamespace(_ *ast.StmtNamespace) {
154 | // do nothing
155 | }
156 |
157 | func (v *Null) StmtNop(_ *ast.StmtNop) {
158 | // do nothing
159 | }
160 |
161 | func (v *Null) StmtProperty(_ *ast.StmtProperty) {
162 | // do nothing
163 | }
164 |
165 | func (v *Null) StmtPropertyList(_ *ast.StmtPropertyList) {
166 | // do nothing
167 | }
168 |
169 | func (v *Null) StmtReturn(_ *ast.StmtReturn) {
170 | // do nothing
171 | }
172 |
173 | func (v *Null) StmtStatic(_ *ast.StmtStatic) {
174 | // do nothing
175 | }
176 |
177 | func (v *Null) StmtStaticVar(_ *ast.StmtStaticVar) {
178 | // do nothing
179 | }
180 |
181 | func (v *Null) StmtStmtList(_ *ast.StmtStmtList) {
182 | // do nothing
183 | }
184 |
185 | func (v *Null) StmtSwitch(_ *ast.StmtSwitch) {
186 | // do nothing
187 | }
188 |
189 | func (v *Null) StmtThrow(_ *ast.StmtThrow) {
190 | // do nothing
191 | }
192 |
193 | func (v *Null) StmtTrait(_ *ast.StmtTrait) {
194 | // do nothing
195 | }
196 |
197 | func (v *Null) StmtTraitUse(_ *ast.StmtTraitUse) {
198 | // do nothing
199 | }
200 |
201 | func (v *Null) StmtTraitUseAlias(_ *ast.StmtTraitUseAlias) {
202 | // do nothing
203 | }
204 |
205 | func (v *Null) StmtTraitUsePrecedence(_ *ast.StmtTraitUsePrecedence) {
206 | // do nothing
207 | }
208 |
209 | func (v *Null) StmtTry(_ *ast.StmtTry) {
210 | // do nothing
211 | }
212 |
213 | func (v *Null) StmtUnset(_ *ast.StmtUnset) {
214 | // do nothing
215 | }
216 |
217 | func (v *Null) StmtUse(_ *ast.StmtUseList) {
218 | // do nothing
219 | }
220 |
221 | func (v *Null) StmtGroupUse(_ *ast.StmtGroupUseList) {
222 | // do nothing
223 | }
224 |
225 | func (v *Null) StmtUseDeclaration(_ *ast.StmtUse) {
226 | // do nothing
227 | }
228 |
229 | func (v *Null) StmtWhile(_ *ast.StmtWhile) {
230 | // do nothing
231 | }
232 |
233 | func (v *Null) ExprArray(_ *ast.ExprArray) {
234 | // do nothing
235 | }
236 |
237 | func (v *Null) ExprArrayDimFetch(_ *ast.ExprArrayDimFetch) {
238 | // do nothing
239 | }
240 |
241 | func (v *Null) ExprArrayItem(_ *ast.ExprArrayItem) {
242 | // do nothing
243 | }
244 |
245 | func (v *Null) ExprArrowFunction(_ *ast.ExprArrowFunction) {
246 | // do nothing
247 | }
248 |
249 | func (v *Null) ExprBitwiseNot(_ *ast.ExprBitwiseNot) {
250 | // do nothing
251 | }
252 |
253 | func (v *Null) ExprBooleanNot(_ *ast.ExprBooleanNot) {
254 | // do nothing
255 | }
256 |
257 | func (v *Null) ExprBrackets(_ *ast.ExprBrackets) {
258 | // do nothing
259 | }
260 |
261 | func (v *Null) ExprClassConstFetch(_ *ast.ExprClassConstFetch) {
262 | // do nothing
263 | }
264 |
265 | func (v *Null) ExprClone(_ *ast.ExprClone) {
266 | // do nothing
267 | }
268 |
269 | func (v *Null) ExprClosure(_ *ast.ExprClosure) {
270 | // do nothing
271 | }
272 |
273 | func (v *Null) ExprClosureUse(_ *ast.ExprClosureUse) {
274 | // do nothing
275 | }
276 |
277 | func (v *Null) ExprConstFetch(_ *ast.ExprConstFetch) {
278 | // do nothing
279 | }
280 |
281 | func (v *Null) ExprEmpty(_ *ast.ExprEmpty) {
282 | // do nothing
283 | }
284 |
285 | func (v *Null) ExprErrorSuppress(_ *ast.ExprErrorSuppress) {
286 | // do nothing
287 | }
288 |
289 | func (v *Null) ExprEval(_ *ast.ExprEval) {
290 | // do nothing
291 | }
292 |
293 | func (v *Null) ExprExit(_ *ast.ExprExit) {
294 | // do nothing
295 | }
296 |
297 | func (v *Null) ExprFunctionCall(_ *ast.ExprFunctionCall) {
298 | // do nothing
299 | }
300 |
301 | func (v *Null) ExprInclude(_ *ast.ExprInclude) {
302 | // do nothing
303 | }
304 |
305 | func (v *Null) ExprIncludeOnce(_ *ast.ExprIncludeOnce) {
306 | // do nothing
307 | }
308 |
309 | func (v *Null) ExprInstanceOf(_ *ast.ExprInstanceOf) {
310 | // do nothing
311 | }
312 |
313 | func (v *Null) ExprIsset(_ *ast.ExprIsset) {
314 | // do nothing
315 | }
316 |
317 | func (v *Null) ExprList(_ *ast.ExprList) {
318 | // do nothing
319 | }
320 |
321 | func (v *Null) ExprMethodCall(_ *ast.ExprMethodCall) {
322 | // do nothing
323 | }
324 |
325 | func (v *Null) ExprNew(_ *ast.ExprNew) {
326 | // do nothing
327 | }
328 |
329 | func (v *Null) ExprPostDec(_ *ast.ExprPostDec) {
330 | // do nothing
331 | }
332 |
333 | func (v *Null) ExprPostInc(_ *ast.ExprPostInc) {
334 | // do nothing
335 | }
336 |
337 | func (v *Null) ExprPreDec(_ *ast.ExprPreDec) {
338 | // do nothing
339 | }
340 |
341 | func (v *Null) ExprPreInc(_ *ast.ExprPreInc) {
342 | // do nothing
343 | }
344 |
345 | func (v *Null) ExprPrint(_ *ast.ExprPrint) {
346 | // do nothing
347 | }
348 |
349 | func (v *Null) ExprPropertyFetch(_ *ast.ExprPropertyFetch) {
350 | // do nothing
351 | }
352 |
353 | func (v *Null) ExprRequire(_ *ast.ExprRequire) {
354 | // do nothing
355 | }
356 |
357 | func (v *Null) ExprRequireOnce(_ *ast.ExprRequireOnce) {
358 | // do nothing
359 | }
360 |
361 | func (v *Null) ExprShellExec(_ *ast.ExprShellExec) {
362 | // do nothing
363 | }
364 |
365 | func (v *Null) ExprStaticCall(_ *ast.ExprStaticCall) {
366 | // do nothing
367 | }
368 |
369 | func (v *Null) ExprStaticPropertyFetch(_ *ast.ExprStaticPropertyFetch) {
370 | // do nothing
371 | }
372 |
373 | func (v *Null) ExprTernary(_ *ast.ExprTernary) {
374 | // do nothing
375 | }
376 |
377 | func (v *Null) ExprUnaryMinus(_ *ast.ExprUnaryMinus) {
378 | // do nothing
379 | }
380 |
381 | func (v *Null) ExprUnaryPlus(_ *ast.ExprUnaryPlus) {
382 | // do nothing
383 | }
384 |
385 | func (v *Null) ExprVariable(_ *ast.ExprVariable) {
386 | // do nothing
387 | }
388 |
389 | func (v *Null) ExprYield(_ *ast.ExprYield) {
390 | // do nothing
391 | }
392 |
393 | func (v *Null) ExprYieldFrom(_ *ast.ExprYieldFrom) {
394 | // do nothing
395 | }
396 |
397 | func (v *Null) ExprAssign(_ *ast.ExprAssign) {
398 | // do nothing
399 | }
400 |
401 | func (v *Null) ExprAssignReference(_ *ast.ExprAssignReference) {
402 | // do nothing
403 | }
404 |
405 | func (v *Null) ExprAssignBitwiseAnd(_ *ast.ExprAssignBitwiseAnd) {
406 | // do nothing
407 | }
408 |
409 | func (v *Null) ExprAssignBitwiseOr(_ *ast.ExprAssignBitwiseOr) {
410 | // do nothing
411 | }
412 |
413 | func (v *Null) ExprAssignBitwiseXor(_ *ast.ExprAssignBitwiseXor) {
414 | // do nothing
415 | }
416 |
417 | func (v *Null) ExprAssignCoalesce(_ *ast.ExprAssignCoalesce) {
418 | // do nothing
419 | }
420 |
421 | func (v *Null) ExprAssignConcat(_ *ast.ExprAssignConcat) {
422 | // do nothing
423 | }
424 |
425 | func (v *Null) ExprAssignDiv(_ *ast.ExprAssignDiv) {
426 | // do nothing
427 | }
428 |
429 | func (v *Null) ExprAssignMinus(_ *ast.ExprAssignMinus) {
430 | // do nothing
431 | }
432 |
433 | func (v *Null) ExprAssignMod(_ *ast.ExprAssignMod) {
434 | // do nothing
435 | }
436 |
437 | func (v *Null) ExprAssignMul(_ *ast.ExprAssignMul) {
438 | // do nothing
439 | }
440 |
441 | func (v *Null) ExprAssignPlus(_ *ast.ExprAssignPlus) {
442 | // do nothing
443 | }
444 |
445 | func (v *Null) ExprAssignPow(_ *ast.ExprAssignPow) {
446 | // do nothing
447 | }
448 |
449 | func (v *Null) ExprAssignShiftLeft(_ *ast.ExprAssignShiftLeft) {
450 | // do nothing
451 | }
452 |
453 | func (v *Null) ExprAssignShiftRight(_ *ast.ExprAssignShiftRight) {
454 | // do nothing
455 | }
456 |
457 | func (v *Null) ExprBinaryBitwiseAnd(_ *ast.ExprBinaryBitwiseAnd) {
458 | // do nothing
459 | }
460 |
461 | func (v *Null) ExprBinaryBitwiseOr(_ *ast.ExprBinaryBitwiseOr) {
462 | // do nothing
463 | }
464 |
465 | func (v *Null) ExprBinaryBitwiseXor(_ *ast.ExprBinaryBitwiseXor) {
466 | // do nothing
467 | }
468 |
469 | func (v *Null) ExprBinaryBooleanAnd(_ *ast.ExprBinaryBooleanAnd) {
470 | // do nothing
471 | }
472 |
473 | func (v *Null) ExprBinaryBooleanOr(_ *ast.ExprBinaryBooleanOr) {
474 | // do nothing
475 | }
476 |
477 | func (v *Null) ExprBinaryCoalesce(_ *ast.ExprBinaryCoalesce) {
478 | // do nothing
479 | }
480 |
481 | func (v *Null) ExprBinaryConcat(_ *ast.ExprBinaryConcat) {
482 | // do nothing
483 | }
484 |
485 | func (v *Null) ExprBinaryDiv(_ *ast.ExprBinaryDiv) {
486 | // do nothing
487 | }
488 |
489 | func (v *Null) ExprBinaryEqual(_ *ast.ExprBinaryEqual) {
490 | // do nothing
491 | }
492 |
493 | func (v *Null) ExprBinaryGreater(_ *ast.ExprBinaryGreater) {
494 | // do nothing
495 | }
496 |
497 | func (v *Null) ExprBinaryGreaterOrEqual(_ *ast.ExprBinaryGreaterOrEqual) {
498 | // do nothing
499 | }
500 |
501 | func (v *Null) ExprBinaryIdentical(_ *ast.ExprBinaryIdentical) {
502 | // do nothing
503 | }
504 |
505 | func (v *Null) ExprBinaryLogicalAnd(_ *ast.ExprBinaryLogicalAnd) {
506 | // do nothing
507 | }
508 |
509 | func (v *Null) ExprBinaryLogicalOr(_ *ast.ExprBinaryLogicalOr) {
510 | // do nothing
511 | }
512 |
513 | func (v *Null) ExprBinaryLogicalXor(_ *ast.ExprBinaryLogicalXor) {
514 | // do nothing
515 | }
516 |
517 | func (v *Null) ExprBinaryMinus(_ *ast.ExprBinaryMinus) {
518 | // do nothing
519 | }
520 |
521 | func (v *Null) ExprBinaryMod(_ *ast.ExprBinaryMod) {
522 | // do nothing
523 | }
524 |
525 | func (v *Null) ExprBinaryMul(_ *ast.ExprBinaryMul) {
526 | // do nothing
527 | }
528 |
529 | func (v *Null) ExprBinaryNotEqual(_ *ast.ExprBinaryNotEqual) {
530 | // do nothing
531 | }
532 |
533 | func (v *Null) ExprBinaryNotIdentical(_ *ast.ExprBinaryNotIdentical) {
534 | // do nothing
535 | }
536 |
537 | func (v *Null) ExprBinaryPlus(_ *ast.ExprBinaryPlus) {
538 | // do nothing
539 | }
540 |
541 | func (v *Null) ExprBinaryPow(_ *ast.ExprBinaryPow) {
542 | // do nothing
543 | }
544 |
545 | func (v *Null) ExprBinaryShiftLeft(_ *ast.ExprBinaryShiftLeft) {
546 | // do nothing
547 | }
548 |
549 | func (v *Null) ExprBinaryShiftRight(_ *ast.ExprBinaryShiftRight) {
550 | // do nothing
551 | }
552 |
553 | func (v *Null) ExprBinarySmaller(_ *ast.ExprBinarySmaller) {
554 | // do nothing
555 | }
556 |
557 | func (v *Null) ExprBinarySmallerOrEqual(_ *ast.ExprBinarySmallerOrEqual) {
558 | // do nothing
559 | }
560 |
561 | func (v *Null) ExprBinarySpaceship(_ *ast.ExprBinarySpaceship) {
562 | // do nothing
563 | }
564 |
565 | func (v *Null) ExprCastArray(_ *ast.ExprCastArray) {
566 | // do nothing
567 | }
568 |
569 | func (v *Null) ExprCastBool(_ *ast.ExprCastBool) {
570 | // do nothing
571 | }
572 |
573 | func (v *Null) ExprCastDouble(_ *ast.ExprCastDouble) {
574 | // do nothing
575 | }
576 |
577 | func (v *Null) ExprCastInt(_ *ast.ExprCastInt) {
578 | // do nothing
579 | }
580 |
581 | func (v *Null) ExprCastObject(_ *ast.ExprCastObject) {
582 | // do nothing
583 | }
584 |
585 | func (v *Null) ExprCastString(_ *ast.ExprCastString) {
586 | // do nothing
587 | }
588 |
589 | func (v *Null) ExprCastUnset(_ *ast.ExprCastUnset) {
590 | // do nothing
591 | }
592 |
593 | func (v *Null) ScalarDnumber(_ *ast.ScalarDnumber) {
594 | // do nothing
595 | }
596 |
597 | func (v *Null) ScalarEncapsed(_ *ast.ScalarEncapsed) {
598 | // do nothing
599 | }
600 |
601 | func (v *Null) ScalarEncapsedStringPart(_ *ast.ScalarEncapsedStringPart) {
602 | // do nothing
603 | }
604 |
605 | func (v *Null) ScalarEncapsedStringBrackets(_ *ast.ScalarEncapsedStringBrackets) {
606 | // do nothing
607 | }
608 |
609 | func (v *Null) ScalarEncapsedStringVar(_ *ast.ScalarEncapsedStringVar) {
610 | // do nothing
611 | }
612 |
613 | func (v *Null) ScalarHeredoc(_ *ast.ScalarHeredoc) {
614 | // do nothing
615 | }
616 |
617 | func (v *Null) ScalarLnumber(_ *ast.ScalarLnumber) {
618 | // do nothing
619 | }
620 |
621 | func (v *Null) ScalarMagicConstant(_ *ast.ScalarMagicConstant) {
622 | // do nothing
623 | }
624 |
625 | func (v *Null) ScalarString(_ *ast.ScalarString) {
626 | // do nothing
627 | }
628 |
629 | func (v *Null) NameName(_ *ast.Name) {
630 | // do nothing
631 | }
632 |
633 | func (v *Null) NameFullyQualified(_ *ast.NameFullyQualified) {
634 | // do nothing
635 | }
636 |
637 | func (v *Null) NameRelative(_ *ast.NameRelative) {
638 | // do nothing
639 | }
640 |
641 | func (v *Null) NameNamePart(_ *ast.NamePart) {
642 | // do nothing
643 | }
644 |
--------------------------------------------------------------------------------
/pkg/visitor/traverser/traverser.go:
--------------------------------------------------------------------------------
1 | package traverser
2 |
3 | import (
4 | "github.com/z7zmey/php-parser/pkg/ast"
5 | )
6 |
7 | type Traverser struct {
8 | v ast.Visitor
9 | }
10 |
11 | func NewTraverser(v ast.Visitor) *Traverser {
12 | return &Traverser{
13 | v: v,
14 | }
15 | }
16 |
17 | func (t *Traverser) Traverse(n ast.Vertex) {
18 | if n != nil {
19 | n.Accept(t)
20 | }
21 | }
22 |
23 | func (t *Traverser) Root(n *ast.Root) {
24 | n.Accept(t.v)
25 |
26 | for _, nn := range n.Stmts {
27 | nn.Accept(t)
28 | }
29 | }
30 |
31 | func (t *Traverser) Nullable(n *ast.Nullable) {
32 | n.Accept(t.v)
33 |
34 | t.Traverse(n.Expr)
35 | }
36 |
37 | func (t *Traverser) Parameter(n *ast.Parameter) {
38 | n.Accept(t.v)
39 |
40 | t.Traverse(n.Type)
41 | t.Traverse(n.Var)
42 | t.Traverse(n.DefaultValue)
43 | }
44 |
45 | func (t *Traverser) Identifier(n *ast.Identifier) {
46 | n.Accept(t.v)
47 | }
48 |
49 | func (t *Traverser) Argument(n *ast.Argument) {
50 | n.Accept(t.v)
51 |
52 | t.Traverse(n.Expr)
53 | }
54 |
55 | func (t *Traverser) StmtBreak(n *ast.StmtBreak) {
56 | n.Accept(t.v)
57 |
58 | t.Traverse(n.Expr)
59 | }
60 |
61 | func (t *Traverser) StmtCase(n *ast.StmtCase) {
62 | n.Accept(t.v)
63 |
64 | t.Traverse(n.Cond)
65 | for _, nn := range n.Stmts {
66 | nn.Accept(t)
67 | }
68 | }
69 |
70 | func (t *Traverser) StmtCatch(n *ast.StmtCatch) {
71 | n.Accept(t.v)
72 |
73 | for _, nn := range n.Types {
74 | nn.Accept(t)
75 | }
76 | t.Traverse(n.Var)
77 | for _, nn := range n.Stmts {
78 | nn.Accept(t)
79 | }
80 | }
81 |
82 | func (t *Traverser) StmtClass(n *ast.StmtClass) {
83 | n.Accept(t.v)
84 |
85 | for _, nn := range n.Modifiers {
86 | nn.Accept(t)
87 | }
88 | t.Traverse(n.Name)
89 | for _, nn := range n.Args {
90 | nn.Accept(t)
91 | }
92 | t.Traverse(n.Extends)
93 | for _, nn := range n.Implements {
94 | nn.Accept(t)
95 | }
96 | for _, nn := range n.Stmts {
97 | nn.Accept(t)
98 | }
99 | }
100 |
101 | func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) {
102 | n.Accept(t.v)
103 |
104 | for _, nn := range n.Modifiers {
105 | nn.Accept(t)
106 | }
107 | for _, nn := range n.Consts {
108 | nn.Accept(t)
109 | }
110 | }
111 |
112 | func (t *Traverser) StmtClassMethod(n *ast.StmtClassMethod) {
113 | n.Accept(t.v)
114 |
115 | for _, nn := range n.Modifiers {
116 | nn.Accept(t)
117 | }
118 | t.Traverse(n.Name)
119 | for _, nn := range n.Params {
120 | nn.Accept(t)
121 | }
122 | t.Traverse(n.ReturnType)
123 | t.Traverse(n.Stmt)
124 | }
125 |
126 | func (t *Traverser) StmtConstList(n *ast.StmtConstList) {
127 | n.Accept(t.v)
128 |
129 | for _, nn := range n.Consts {
130 | nn.Accept(t)
131 | }
132 | }
133 |
134 | func (t *Traverser) StmtConstant(n *ast.StmtConstant) {
135 | n.Accept(t.v)
136 |
137 | t.Traverse(n.Name)
138 | t.Traverse(n.Expr)
139 | }
140 |
141 | func (t *Traverser) StmtContinue(n *ast.StmtContinue) {
142 | n.Accept(t.v)
143 |
144 | t.Traverse(n.Expr)
145 | }
146 |
147 | func (t *Traverser) StmtDeclare(n *ast.StmtDeclare) {
148 | n.Accept(t.v)
149 |
150 | for _, nn := range n.Consts {
151 | nn.Accept(t)
152 | }
153 | t.Traverse(n.Stmt)
154 | }
155 |
156 | func (t *Traverser) StmtDefault(n *ast.StmtDefault) {
157 | n.Accept(t.v)
158 |
159 | for _, nn := range n.Stmts {
160 | nn.Accept(t)
161 | }
162 | }
163 |
164 | func (t *Traverser) StmtDo(n *ast.StmtDo) {
165 | n.Accept(t.v)
166 |
167 | t.Traverse(n.Stmt)
168 | t.Traverse(n.Cond)
169 | }
170 |
171 | func (t *Traverser) StmtEcho(n *ast.StmtEcho) {
172 | n.Accept(t.v)
173 |
174 | for _, nn := range n.Exprs {
175 | nn.Accept(t)
176 | }
177 | }
178 |
179 | func (t *Traverser) StmtElse(n *ast.StmtElse) {
180 | n.Accept(t.v)
181 |
182 | t.Traverse(n.Stmt)
183 | }
184 |
185 | func (t *Traverser) StmtElseIf(n *ast.StmtElseIf) {
186 | n.Accept(t.v)
187 |
188 | t.Traverse(n.Cond)
189 | t.Traverse(n.Stmt)
190 | }
191 |
192 | func (t *Traverser) StmtExpression(n *ast.StmtExpression) {
193 | n.Accept(t.v)
194 |
195 | t.Traverse(n.Expr)
196 | }
197 |
198 | func (t *Traverser) StmtFinally(n *ast.StmtFinally) {
199 | n.Accept(t.v)
200 |
201 | for _, nn := range n.Stmts {
202 | nn.Accept(t)
203 | }
204 | }
205 |
206 | func (t *Traverser) StmtFor(n *ast.StmtFor) {
207 | n.Accept(t.v)
208 |
209 | for _, nn := range n.Init {
210 | nn.Accept(t)
211 | }
212 | for _, nn := range n.Cond {
213 | nn.Accept(t)
214 | }
215 | for _, nn := range n.Loop {
216 | nn.Accept(t)
217 | }
218 | t.Traverse(n.Stmt)
219 | }
220 |
221 | func (t *Traverser) StmtForeach(n *ast.StmtForeach) {
222 | n.Accept(t.v)
223 |
224 | t.Traverse(n.Expr)
225 | t.Traverse(n.Key)
226 | t.Traverse(n.Var)
227 | t.Traverse(n.Stmt)
228 | }
229 |
230 | func (t *Traverser) StmtFunction(n *ast.StmtFunction) {
231 | n.Accept(t.v)
232 |
233 | t.Traverse(n.Name)
234 | for _, nn := range n.Params {
235 | nn.Accept(t)
236 | }
237 | t.Traverse(n.ReturnType)
238 | for _, nn := range n.Stmts {
239 | nn.Accept(t)
240 | }
241 | }
242 |
243 | func (t *Traverser) StmtGlobal(n *ast.StmtGlobal) {
244 | n.Accept(t.v)
245 |
246 | for _, nn := range n.Vars {
247 | nn.Accept(t)
248 | }
249 | }
250 |
251 | func (t *Traverser) StmtGoto(n *ast.StmtGoto) {
252 | n.Accept(t.v)
253 |
254 | t.Traverse(n.Label)
255 | }
256 |
257 | func (t *Traverser) StmtHaltCompiler(n *ast.StmtHaltCompiler) {
258 | n.Accept(t.v)
259 | }
260 |
261 | func (t *Traverser) StmtIf(n *ast.StmtIf) {
262 | n.Accept(t.v)
263 |
264 | t.Traverse(n.Cond)
265 | t.Traverse(n.Stmt)
266 | for _, nn := range n.ElseIf {
267 | nn.Accept(t)
268 | }
269 | t.Traverse(n.Else)
270 | }
271 |
272 | func (t *Traverser) StmtInlineHtml(n *ast.StmtInlineHtml) {
273 | n.Accept(t.v)
274 | }
275 |
276 | func (t *Traverser) StmtInterface(n *ast.StmtInterface) {
277 | n.Accept(t.v)
278 |
279 | t.Traverse(n.Name)
280 | for _, nn := range n.Extends {
281 | nn.Accept(t)
282 | }
283 | for _, nn := range n.Stmts {
284 | nn.Accept(t)
285 | }
286 | }
287 |
288 | func (t *Traverser) StmtLabel(n *ast.StmtLabel) {
289 | n.Accept(t.v)
290 |
291 | t.Traverse(n.Name)
292 | }
293 |
294 | func (t *Traverser) StmtNamespace(n *ast.StmtNamespace) {
295 | n.Accept(t.v)
296 |
297 | t.Traverse(n.Name)
298 | for _, nn := range n.Stmts {
299 | nn.Accept(t)
300 | }
301 | }
302 |
303 | func (t *Traverser) StmtNop(n *ast.StmtNop) {
304 | n.Accept(t.v)
305 | }
306 |
307 | func (t *Traverser) StmtProperty(n *ast.StmtProperty) {
308 | n.Accept(t.v)
309 |
310 | t.Traverse(n.Var)
311 | t.Traverse(n.Expr)
312 | }
313 |
314 | func (t *Traverser) StmtPropertyList(n *ast.StmtPropertyList) {
315 | n.Accept(t.v)
316 |
317 | for _, nn := range n.Modifiers {
318 | nn.Accept(t)
319 | }
320 | t.Traverse(n.Type)
321 | for _, nn := range n.Props {
322 | nn.Accept(t)
323 | }
324 | }
325 |
326 | func (t *Traverser) StmtReturn(n *ast.StmtReturn) {
327 | n.Accept(t.v)
328 |
329 | t.Traverse(n.Expr)
330 | }
331 |
332 | func (t *Traverser) StmtStatic(n *ast.StmtStatic) {
333 | n.Accept(t.v)
334 |
335 | for _, nn := range n.Vars {
336 | nn.Accept(t)
337 | }
338 | }
339 |
340 | func (t *Traverser) StmtStaticVar(n *ast.StmtStaticVar) {
341 | n.Accept(t.v)
342 |
343 | t.Traverse(n.Var)
344 | t.Traverse(n.Expr)
345 | }
346 |
347 | func (t *Traverser) StmtStmtList(n *ast.StmtStmtList) {
348 | n.Accept(t.v)
349 |
350 | for _, nn := range n.Stmts {
351 | nn.Accept(t)
352 | }
353 | }
354 |
355 | func (t *Traverser) StmtSwitch(n *ast.StmtSwitch) {
356 | n.Accept(t.v)
357 |
358 | t.Traverse(n.Cond)
359 | for _, nn := range n.Cases {
360 | nn.Accept(t)
361 | }
362 | }
363 |
364 | func (t *Traverser) StmtThrow(n *ast.StmtThrow) {
365 | n.Accept(t.v)
366 |
367 | t.Traverse(n.Expr)
368 | }
369 |
370 | func (t *Traverser) StmtTrait(n *ast.StmtTrait) {
371 | n.Accept(t.v)
372 |
373 | t.Traverse(n.Name)
374 | for _, nn := range n.Stmts {
375 | nn.Accept(t)
376 | }
377 | }
378 |
379 | func (t *Traverser) StmtTraitUse(n *ast.StmtTraitUse) {
380 | n.Accept(t.v)
381 |
382 | for _, nn := range n.Traits {
383 | nn.Accept(t)
384 | }
385 | for _, nn := range n.Adaptations {
386 | nn.Accept(t)
387 | }
388 | }
389 |
390 | func (t *Traverser) StmtTraitUseAlias(n *ast.StmtTraitUseAlias) {
391 | n.Accept(t.v)
392 |
393 | t.Traverse(n.Trait)
394 | t.Traverse(n.Method)
395 | t.Traverse(n.Modifier)
396 | t.Traverse(n.Alias)
397 | }
398 |
399 | func (t *Traverser) StmtTraitUsePrecedence(n *ast.StmtTraitUsePrecedence) {
400 | n.Accept(t.v)
401 |
402 | t.Traverse(n.Trait)
403 | t.Traverse(n.Method)
404 | for _, nn := range n.Insteadof {
405 | nn.Accept(t)
406 | }
407 | }
408 |
409 | func (t *Traverser) StmtTry(n *ast.StmtTry) {
410 | n.Accept(t.v)
411 |
412 | for _, nn := range n.Stmts {
413 | nn.Accept(t)
414 | }
415 | for _, nn := range n.Catches {
416 | nn.Accept(t)
417 | }
418 | t.Traverse(n.Finally)
419 | }
420 |
421 | func (t *Traverser) StmtUnset(n *ast.StmtUnset) {
422 | n.Accept(t.v)
423 |
424 | for _, nn := range n.Vars {
425 | nn.Accept(t)
426 | }
427 | }
428 |
429 | func (t *Traverser) StmtUse(n *ast.StmtUseList) {
430 | n.Accept(t.v)
431 |
432 | t.Traverse(n.Type)
433 | for _, nn := range n.Uses {
434 | nn.Accept(t)
435 | }
436 | }
437 |
438 | func (t *Traverser) StmtGroupUse(n *ast.StmtGroupUseList) {
439 | n.Accept(t.v)
440 |
441 | t.Traverse(n.Type)
442 | t.Traverse(n.Prefix)
443 | for _, nn := range n.Uses {
444 | nn.Accept(t)
445 | }
446 | }
447 |
448 | func (t *Traverser) StmtUseDeclaration(n *ast.StmtUse) {
449 | n.Accept(t.v)
450 |
451 | t.Traverse(n.Type)
452 | t.Traverse(n.Use)
453 | t.Traverse(n.Alias)
454 | }
455 |
456 | func (t *Traverser) StmtWhile(n *ast.StmtWhile) {
457 | n.Accept(t.v)
458 |
459 | t.Traverse(n.Cond)
460 | t.Traverse(n.Stmt)
461 | }
462 |
463 | func (t *Traverser) ExprArray(n *ast.ExprArray) {
464 | n.Accept(t.v)
465 |
466 | for _, nn := range n.Items {
467 | nn.Accept(t)
468 | }
469 | }
470 |
471 | func (t *Traverser) ExprArrayDimFetch(n *ast.ExprArrayDimFetch) {
472 | n.Accept(t.v)
473 |
474 | t.Traverse(n.Var)
475 | t.Traverse(n.Dim)
476 | }
477 |
478 | func (t *Traverser) ExprArrayItem(n *ast.ExprArrayItem) {
479 | n.Accept(t.v)
480 |
481 | t.Traverse(n.Key)
482 | t.Traverse(n.Val)
483 | }
484 |
485 | func (t *Traverser) ExprArrowFunction(n *ast.ExprArrowFunction) {
486 | n.Accept(t.v)
487 |
488 | for _, nn := range n.Params {
489 | nn.Accept(t)
490 | }
491 | t.Traverse(n.ReturnType)
492 | t.Traverse(n.Expr)
493 | }
494 |
495 | func (t *Traverser) ExprBitwiseNot(n *ast.ExprBitwiseNot) {
496 | n.Accept(t.v)
497 |
498 | t.Traverse(n.Expr)
499 | }
500 |
501 | func (t *Traverser) ExprBooleanNot(n *ast.ExprBooleanNot) {
502 | n.Accept(t.v)
503 |
504 | t.Traverse(n.Expr)
505 | }
506 |
507 | func (t *Traverser) ExprBrackets(n *ast.ExprBrackets) {
508 | n.Accept(t.v)
509 |
510 | t.Traverse(n.Expr)
511 | }
512 |
513 | func (t *Traverser) ExprClassConstFetch(n *ast.ExprClassConstFetch) {
514 | n.Accept(t.v)
515 |
516 | t.Traverse(n.Class)
517 | t.Traverse(n.Const)
518 | }
519 |
520 | func (t *Traverser) ExprClone(n *ast.ExprClone) {
521 | n.Accept(t.v)
522 |
523 | t.Traverse(n.Expr)
524 | }
525 |
526 | func (t *Traverser) ExprClosure(n *ast.ExprClosure) {
527 | n.Accept(t.v)
528 |
529 | for _, nn := range n.Params {
530 | nn.Accept(t)
531 | }
532 | for _, nn := range n.Uses {
533 | nn.Accept(t)
534 | }
535 | t.Traverse(n.ReturnType)
536 | for _, nn := range n.Stmts {
537 | nn.Accept(t)
538 | }
539 | }
540 |
541 | func (t *Traverser) ExprClosureUse(n *ast.ExprClosureUse) {
542 | n.Accept(t.v)
543 |
544 | t.Traverse(n.Var)
545 | }
546 |
547 | func (t *Traverser) ExprConstFetch(n *ast.ExprConstFetch) {
548 | n.Accept(t.v)
549 |
550 | t.Traverse(n.Const)
551 | }
552 |
553 | func (t *Traverser) ExprEmpty(n *ast.ExprEmpty) {
554 | n.Accept(t.v)
555 |
556 | t.Traverse(n.Expr)
557 | }
558 |
559 | func (t *Traverser) ExprErrorSuppress(n *ast.ExprErrorSuppress) {
560 | n.Accept(t.v)
561 |
562 | t.Traverse(n.Expr)
563 | }
564 |
565 | func (t *Traverser) ExprEval(n *ast.ExprEval) {
566 | n.Accept(t.v)
567 |
568 | t.Traverse(n.Expr)
569 | }
570 |
571 | func (t *Traverser) ExprExit(n *ast.ExprExit) {
572 | n.Accept(t.v)
573 |
574 | t.Traverse(n.Expr)
575 | }
576 |
577 | func (t *Traverser) ExprFunctionCall(n *ast.ExprFunctionCall) {
578 | n.Accept(t.v)
579 |
580 | t.Traverse(n.Function)
581 | for _, nn := range n.Args {
582 | nn.Accept(t)
583 | }
584 | }
585 |
586 | func (t *Traverser) ExprInclude(n *ast.ExprInclude) {
587 | n.Accept(t.v)
588 |
589 | t.Traverse(n.Expr)
590 | }
591 |
592 | func (t *Traverser) ExprIncludeOnce(n *ast.ExprIncludeOnce) {
593 | n.Accept(t.v)
594 |
595 | t.Traverse(n.Expr)
596 | }
597 |
598 | func (t *Traverser) ExprInstanceOf(n *ast.ExprInstanceOf) {
599 | n.Accept(t.v)
600 |
601 | t.Traverse(n.Expr)
602 | t.Traverse(n.Class)
603 | }
604 |
605 | func (t *Traverser) ExprIsset(n *ast.ExprIsset) {
606 | n.Accept(t.v)
607 |
608 | for _, nn := range n.Vars {
609 | nn.Accept(t)
610 | }
611 | }
612 |
613 | func (t *Traverser) ExprList(n *ast.ExprList) {
614 | n.Accept(t.v)
615 |
616 | for _, nn := range n.Items {
617 | nn.Accept(t)
618 | }
619 | }
620 |
621 | func (t *Traverser) ExprMethodCall(n *ast.ExprMethodCall) {
622 | n.Accept(t.v)
623 |
624 | t.Traverse(n.Var)
625 | t.Traverse(n.Method)
626 | for _, nn := range n.Args {
627 | nn.Accept(t)
628 | }
629 | }
630 |
631 | func (t *Traverser) ExprNew(n *ast.ExprNew) {
632 | n.Accept(t.v)
633 |
634 | t.Traverse(n.Class)
635 | for _, nn := range n.Args {
636 | nn.Accept(t)
637 | }
638 | }
639 |
640 | func (t *Traverser) ExprPostDec(n *ast.ExprPostDec) {
641 | n.Accept(t.v)
642 |
643 | t.Traverse(n.Var)
644 | }
645 |
646 | func (t *Traverser) ExprPostInc(n *ast.ExprPostInc) {
647 | n.Accept(t.v)
648 |
649 | t.Traverse(n.Var)
650 | }
651 |
652 | func (t *Traverser) ExprPreDec(n *ast.ExprPreDec) {
653 | n.Accept(t.v)
654 |
655 | t.Traverse(n.Var)
656 | }
657 |
658 | func (t *Traverser) ExprPreInc(n *ast.ExprPreInc) {
659 | n.Accept(t.v)
660 |
661 | t.Traverse(n.Var)
662 | }
663 |
664 | func (t *Traverser) ExprPrint(n *ast.ExprPrint) {
665 | n.Accept(t.v)
666 |
667 | t.Traverse(n.Expr)
668 | }
669 |
670 | func (t *Traverser) ExprPropertyFetch(n *ast.ExprPropertyFetch) {
671 | n.Accept(t.v)
672 |
673 | t.Traverse(n.Var)
674 | t.Traverse(n.Prop)
675 | }
676 |
677 | func (t *Traverser) ExprRequire(n *ast.ExprRequire) {
678 | n.Accept(t.v)
679 |
680 | t.Traverse(n.Expr)
681 | }
682 |
683 | func (t *Traverser) ExprRequireOnce(n *ast.ExprRequireOnce) {
684 | n.Accept(t.v)
685 |
686 | t.Traverse(n.Expr)
687 | }
688 |
689 | func (t *Traverser) ExprShellExec(n *ast.ExprShellExec) {
690 | n.Accept(t.v)
691 |
692 | for _, nn := range n.Parts {
693 | nn.Accept(t)
694 | }
695 | }
696 |
697 | func (t *Traverser) ExprStaticCall(n *ast.ExprStaticCall) {
698 | n.Accept(t.v)
699 |
700 | t.Traverse(n.Class)
701 | t.Traverse(n.Call)
702 | for _, nn := range n.Args {
703 | nn.Accept(t)
704 | }
705 | }
706 |
707 | func (t *Traverser) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) {
708 | n.Accept(t.v)
709 |
710 | t.Traverse(n.Class)
711 | t.Traverse(n.Prop)
712 | }
713 |
714 | func (t *Traverser) ExprTernary(n *ast.ExprTernary) {
715 | n.Accept(t.v)
716 |
717 | t.Traverse(n.Cond)
718 | t.Traverse(n.IfTrue)
719 | t.Traverse(n.IfFalse)
720 | }
721 |
722 | func (t *Traverser) ExprUnaryMinus(n *ast.ExprUnaryMinus) {
723 | n.Accept(t.v)
724 |
725 | t.Traverse(n.Expr)
726 | }
727 |
728 | func (t *Traverser) ExprUnaryPlus(n *ast.ExprUnaryPlus) {
729 | n.Accept(t.v)
730 |
731 | t.Traverse(n.Expr)
732 | }
733 |
734 | func (t *Traverser) ExprVariable(n *ast.ExprVariable) {
735 | n.Accept(t.v)
736 |
737 | t.Traverse(n.Name)
738 | }
739 |
740 | func (t *Traverser) ExprYield(n *ast.ExprYield) {
741 | n.Accept(t.v)
742 |
743 | t.Traverse(n.Key)
744 | t.Traverse(n.Val)
745 | }
746 |
747 | func (t *Traverser) ExprYieldFrom(n *ast.ExprYieldFrom) {
748 | n.Accept(t.v)
749 |
750 | t.Traverse(n.Expr)
751 | }
752 |
753 | func (t *Traverser) ExprAssign(n *ast.ExprAssign) {
754 | n.Accept(t.v)
755 |
756 | t.Traverse(n.Var)
757 | t.Traverse(n.Expr)
758 | }
759 |
760 | func (t *Traverser) ExprAssignReference(n *ast.ExprAssignReference) {
761 | n.Accept(t.v)
762 |
763 | t.Traverse(n.Var)
764 | t.Traverse(n.Expr)
765 | }
766 |
767 | func (t *Traverser) ExprAssignBitwiseAnd(n *ast.ExprAssignBitwiseAnd) {
768 | n.Accept(t.v)
769 |
770 | t.Traverse(n.Var)
771 | t.Traverse(n.Expr)
772 | }
773 |
774 | func (t *Traverser) ExprAssignBitwiseOr(n *ast.ExprAssignBitwiseOr) {
775 | n.Accept(t.v)
776 |
777 | t.Traverse(n.Var)
778 | t.Traverse(n.Expr)
779 | }
780 |
781 | func (t *Traverser) ExprAssignBitwiseXor(n *ast.ExprAssignBitwiseXor) {
782 | n.Accept(t.v)
783 |
784 | t.Traverse(n.Var)
785 | t.Traverse(n.Expr)
786 | }
787 |
788 | func (t *Traverser) ExprAssignCoalesce(n *ast.ExprAssignCoalesce) {
789 | n.Accept(t.v)
790 |
791 | t.Traverse(n.Var)
792 | t.Traverse(n.Expr)
793 | }
794 |
795 | func (t *Traverser) ExprAssignConcat(n *ast.ExprAssignConcat) {
796 | n.Accept(t.v)
797 |
798 | t.Traverse(n.Var)
799 | t.Traverse(n.Expr)
800 | }
801 |
802 | func (t *Traverser) ExprAssignDiv(n *ast.ExprAssignDiv) {
803 | n.Accept(t.v)
804 |
805 | t.Traverse(n.Var)
806 | t.Traverse(n.Expr)
807 | }
808 |
809 | func (t *Traverser) ExprAssignMinus(n *ast.ExprAssignMinus) {
810 | n.Accept(t.v)
811 |
812 | t.Traverse(n.Var)
813 | t.Traverse(n.Expr)
814 | }
815 |
816 | func (t *Traverser) ExprAssignMod(n *ast.ExprAssignMod) {
817 | n.Accept(t.v)
818 |
819 | t.Traverse(n.Var)
820 | t.Traverse(n.Expr)
821 | }
822 |
823 | func (t *Traverser) ExprAssignMul(n *ast.ExprAssignMul) {
824 | n.Accept(t.v)
825 |
826 | t.Traverse(n.Var)
827 | t.Traverse(n.Expr)
828 | }
829 |
830 | func (t *Traverser) ExprAssignPlus(n *ast.ExprAssignPlus) {
831 | n.Accept(t.v)
832 |
833 | t.Traverse(n.Var)
834 | t.Traverse(n.Expr)
835 | }
836 |
837 | func (t *Traverser) ExprAssignPow(n *ast.ExprAssignPow) {
838 | n.Accept(t.v)
839 |
840 | t.Traverse(n.Var)
841 | t.Traverse(n.Expr)
842 | }
843 |
844 | func (t *Traverser) ExprAssignShiftLeft(n *ast.ExprAssignShiftLeft) {
845 | n.Accept(t.v)
846 |
847 | t.Traverse(n.Var)
848 | t.Traverse(n.Expr)
849 | }
850 |
851 | func (t *Traverser) ExprAssignShiftRight(n *ast.ExprAssignShiftRight) {
852 | n.Accept(t.v)
853 |
854 | t.Traverse(n.Var)
855 | t.Traverse(n.Expr)
856 | }
857 |
858 | func (t *Traverser) ExprBinaryBitwiseAnd(n *ast.ExprBinaryBitwiseAnd) {
859 | n.Accept(t.v)
860 |
861 | t.Traverse(n.Left)
862 | t.Traverse(n.Right)
863 | }
864 |
865 | func (t *Traverser) ExprBinaryBitwiseOr(n *ast.ExprBinaryBitwiseOr) {
866 | n.Accept(t.v)
867 |
868 | t.Traverse(n.Left)
869 | t.Traverse(n.Right)
870 | }
871 |
872 | func (t *Traverser) ExprBinaryBitwiseXor(n *ast.ExprBinaryBitwiseXor) {
873 | n.Accept(t.v)
874 |
875 | t.Traverse(n.Left)
876 | t.Traverse(n.Right)
877 | }
878 |
879 | func (t *Traverser) ExprBinaryBooleanAnd(n *ast.ExprBinaryBooleanAnd) {
880 | n.Accept(t.v)
881 |
882 | t.Traverse(n.Left)
883 | t.Traverse(n.Right)
884 | }
885 |
886 | func (t *Traverser) ExprBinaryBooleanOr(n *ast.ExprBinaryBooleanOr) {
887 | n.Accept(t.v)
888 |
889 | t.Traverse(n.Left)
890 | t.Traverse(n.Right)
891 | }
892 |
893 | func (t *Traverser) ExprBinaryCoalesce(n *ast.ExprBinaryCoalesce) {
894 | n.Accept(t.v)
895 |
896 | t.Traverse(n.Left)
897 | t.Traverse(n.Right)
898 | }
899 |
900 | func (t *Traverser) ExprBinaryConcat(n *ast.ExprBinaryConcat) {
901 | n.Accept(t.v)
902 |
903 | t.Traverse(n.Left)
904 | t.Traverse(n.Right)
905 | }
906 |
907 | func (t *Traverser) ExprBinaryDiv(n *ast.ExprBinaryDiv) {
908 | n.Accept(t.v)
909 |
910 | t.Traverse(n.Left)
911 | t.Traverse(n.Right)
912 | }
913 |
914 | func (t *Traverser) ExprBinaryEqual(n *ast.ExprBinaryEqual) {
915 | n.Accept(t.v)
916 |
917 | t.Traverse(n.Left)
918 | t.Traverse(n.Right)
919 | }
920 |
921 | func (t *Traverser) ExprBinaryGreater(n *ast.ExprBinaryGreater) {
922 | n.Accept(t.v)
923 |
924 | t.Traverse(n.Left)
925 | t.Traverse(n.Right)
926 | }
927 |
928 | func (t *Traverser) ExprBinaryGreaterOrEqual(n *ast.ExprBinaryGreaterOrEqual) {
929 | n.Accept(t.v)
930 |
931 | t.Traverse(n.Left)
932 | t.Traverse(n.Right)
933 | }
934 |
935 | func (t *Traverser) ExprBinaryIdentical(n *ast.ExprBinaryIdentical) {
936 | n.Accept(t.v)
937 |
938 | t.Traverse(n.Left)
939 | t.Traverse(n.Right)
940 | }
941 |
942 | func (t *Traverser) ExprBinaryLogicalAnd(n *ast.ExprBinaryLogicalAnd) {
943 | n.Accept(t.v)
944 |
945 | t.Traverse(n.Left)
946 | t.Traverse(n.Right)
947 | }
948 |
949 | func (t *Traverser) ExprBinaryLogicalOr(n *ast.ExprBinaryLogicalOr) {
950 | n.Accept(t.v)
951 |
952 | t.Traverse(n.Left)
953 | t.Traverse(n.Right)
954 | }
955 |
956 | func (t *Traverser) ExprBinaryLogicalXor(n *ast.ExprBinaryLogicalXor) {
957 | n.Accept(t.v)
958 |
959 | t.Traverse(n.Left)
960 | t.Traverse(n.Right)
961 | }
962 |
963 | func (t *Traverser) ExprBinaryMinus(n *ast.ExprBinaryMinus) {
964 | n.Accept(t.v)
965 |
966 | t.Traverse(n.Left)
967 | t.Traverse(n.Right)
968 | }
969 |
970 | func (t *Traverser) ExprBinaryMod(n *ast.ExprBinaryMod) {
971 | n.Accept(t.v)
972 |
973 | t.Traverse(n.Left)
974 | t.Traverse(n.Right)
975 | }
976 |
977 | func (t *Traverser) ExprBinaryMul(n *ast.ExprBinaryMul) {
978 | n.Accept(t.v)
979 |
980 | t.Traverse(n.Left)
981 | t.Traverse(n.Right)
982 | }
983 |
984 | func (t *Traverser) ExprBinaryNotEqual(n *ast.ExprBinaryNotEqual) {
985 | n.Accept(t.v)
986 |
987 | t.Traverse(n.Left)
988 | t.Traverse(n.Right)
989 | }
990 |
991 | func (t *Traverser) ExprBinaryNotIdentical(n *ast.ExprBinaryNotIdentical) {
992 | n.Accept(t.v)
993 |
994 | t.Traverse(n.Left)
995 | t.Traverse(n.Right)
996 | }
997 |
998 | func (t *Traverser) ExprBinaryPlus(n *ast.ExprBinaryPlus) {
999 | n.Accept(t.v)
1000 |
1001 | t.Traverse(n.Left)
1002 | t.Traverse(n.Right)
1003 | }
1004 |
1005 | func (t *Traverser) ExprBinaryPow(n *ast.ExprBinaryPow) {
1006 | n.Accept(t.v)
1007 |
1008 | t.Traverse(n.Left)
1009 | t.Traverse(n.Right)
1010 | }
1011 |
1012 | func (t *Traverser) ExprBinaryShiftLeft(n *ast.ExprBinaryShiftLeft) {
1013 | n.Accept(t.v)
1014 |
1015 | t.Traverse(n.Left)
1016 | t.Traverse(n.Right)
1017 | }
1018 |
1019 | func (t *Traverser) ExprBinaryShiftRight(n *ast.ExprBinaryShiftRight) {
1020 | n.Accept(t.v)
1021 |
1022 | t.Traverse(n.Left)
1023 | t.Traverse(n.Right)
1024 | }
1025 |
1026 | func (t *Traverser) ExprBinarySmaller(n *ast.ExprBinarySmaller) {
1027 | n.Accept(t.v)
1028 |
1029 | t.Traverse(n.Left)
1030 | t.Traverse(n.Right)
1031 | }
1032 |
1033 | func (t *Traverser) ExprBinarySmallerOrEqual(n *ast.ExprBinarySmallerOrEqual) {
1034 | n.Accept(t.v)
1035 |
1036 | t.Traverse(n.Left)
1037 | t.Traverse(n.Right)
1038 | }
1039 |
1040 | func (t *Traverser) ExprBinarySpaceship(n *ast.ExprBinarySpaceship) {
1041 | n.Accept(t.v)
1042 |
1043 | t.Traverse(n.Left)
1044 | t.Traverse(n.Right)
1045 | }
1046 |
1047 | func (t *Traverser) ExprCastArray(n *ast.ExprCastArray) {
1048 | n.Accept(t.v)
1049 |
1050 | t.Traverse(n.Expr)
1051 | }
1052 |
1053 | func (t *Traverser) ExprCastBool(n *ast.ExprCastBool) {
1054 | n.Accept(t.v)
1055 |
1056 | t.Traverse(n.Expr)
1057 | }
1058 |
1059 | func (t *Traverser) ExprCastDouble(n *ast.ExprCastDouble) {
1060 | n.Accept(t.v)
1061 |
1062 | t.Traverse(n.Expr)
1063 | }
1064 |
1065 | func (t *Traverser) ExprCastInt(n *ast.ExprCastInt) {
1066 | n.Accept(t.v)
1067 |
1068 | t.Traverse(n.Expr)
1069 | }
1070 |
1071 | func (t *Traverser) ExprCastObject(n *ast.ExprCastObject) {
1072 | n.Accept(t.v)
1073 |
1074 | t.Traverse(n.Expr)
1075 | }
1076 |
1077 | func (t *Traverser) ExprCastString(n *ast.ExprCastString) {
1078 | n.Accept(t.v)
1079 |
1080 | t.Traverse(n.Expr)
1081 | }
1082 |
1083 | func (t *Traverser) ExprCastUnset(n *ast.ExprCastUnset) {
1084 | n.Accept(t.v)
1085 |
1086 | t.Traverse(n.Expr)
1087 | }
1088 |
1089 | func (t *Traverser) ScalarDnumber(n *ast.ScalarDnumber) {
1090 | n.Accept(t.v)
1091 | }
1092 |
1093 | func (t *Traverser) ScalarEncapsed(n *ast.ScalarEncapsed) {
1094 | n.Accept(t.v)
1095 |
1096 | for _, nn := range n.Parts {
1097 | nn.Accept(t)
1098 | }
1099 | }
1100 |
1101 | func (t *Traverser) ScalarEncapsedStringPart(n *ast.ScalarEncapsedStringPart) {
1102 | n.Accept(t.v)
1103 | }
1104 |
1105 | func (t *Traverser) ScalarEncapsedStringVar(n *ast.ScalarEncapsedStringVar) {
1106 | n.Accept(t.v)
1107 |
1108 | t.Traverse(n.Name)
1109 | t.Traverse(n.Dim)
1110 | }
1111 |
1112 | func (t *Traverser) ScalarEncapsedStringBrackets(n *ast.ScalarEncapsedStringBrackets) {
1113 | n.Accept(t.v)
1114 |
1115 | t.Traverse(n.Var)
1116 | }
1117 |
1118 | func (t *Traverser) ScalarHeredoc(n *ast.ScalarHeredoc) {
1119 | n.Accept(t.v)
1120 |
1121 | for _, nn := range n.Parts {
1122 | nn.Accept(t)
1123 | }
1124 | }
1125 |
1126 | func (t *Traverser) ScalarLnumber(n *ast.ScalarLnumber) {
1127 | n.Accept(t.v)
1128 | }
1129 |
1130 | func (t *Traverser) ScalarMagicConstant(n *ast.ScalarMagicConstant) {
1131 | n.Accept(t.v)
1132 | }
1133 |
1134 | func (t *Traverser) ScalarString(n *ast.ScalarString) {
1135 | n.Accept(t.v)
1136 | }
1137 |
1138 | func (t *Traverser) NameName(n *ast.Name) {
1139 | n.Accept(t.v)
1140 |
1141 | for _, nn := range n.Parts {
1142 | nn.Accept(t)
1143 | }
1144 | }
1145 |
1146 | func (t *Traverser) NameFullyQualified(n *ast.NameFullyQualified) {
1147 | n.Accept(t.v)
1148 |
1149 | for _, nn := range n.Parts {
1150 | nn.Accept(t)
1151 | }
1152 | }
1153 |
1154 | func (t *Traverser) NameRelative(n *ast.NameRelative) {
1155 | n.Accept(t.v)
1156 |
1157 | for _, nn := range n.Parts {
1158 | nn.Accept(t)
1159 | }
1160 | }
1161 |
1162 | func (t *Traverser) NameNamePart(n *ast.NamePart) {
1163 | n.Accept(t.v)
1164 | }
1165 |
--------------------------------------------------------------------------------