PHP 8 (#1)
PHP 8 Update
- nullsafe operator (?->)
- Remove (real) cast
- Named arguments
- Remove (unset) cast
- Remove {} access
- match expression
- Union types in type hints and static typehint
- Block catch without variable
- Trailing comma in parameter lists
- throw can be used as an expression
- Concatenation precedence
- Declaring properties in the constructor
- Attributes
- Names in the namespace are treated as a single token
- Trailing comma in closure use list
- Check that ::class on object works
- Deferencable changes and arbitrary expressions in new/instanceof
			
			
This commit is contained in:
		
							parent
							
								
									367eff9de6
								
							
						
					
					
						commit
						049ce7ddc6
					
				
							
								
								
									
										21
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								Makefile
									
									
									
									
									
								
							| @ -19,12 +19,15 @@ bench: | |||||||
| 	go test -benchmem -bench=. ./internal/php5 | 	go test -benchmem -bench=. ./internal/php5 | ||||||
| 	go test -benchmem -bench=. ./internal/php7 | 	go test -benchmem -bench=. ./internal/php7 | ||||||
| 
 | 
 | ||||||
| compile: ./internal/php5/php5.go ./internal/php7/php7.go ./internal/scanner/scanner.go | compile: ./internal/php5/php5.go ./internal/php7/php7.go ./internal/php8/php8.go ./internal/php8/scanner.go ./internal/scanner/scanner.go | ||||||
| 	sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php7/php7.go | 	sed -i -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php5/php5.go | ||||||
| 	sed -i '' -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php5/php5.go | 	sed -i -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php7/php7.go | ||||||
| 	sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php5/php5.go | 	sed -i -e 's/yyErrorVerbose = false/yyErrorVerbose = true/g' ./internal/php8/php8.go | ||||||
| 	sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/php7/php7.go | 	sed -i -e 's/\/\/line/\/\/ line/g' ./internal/php5/php5.go | ||||||
| 	sed -i '' -e 's/\/\/line/\/\/ line/g' ./internal/scanner/scanner.go | 	sed -i -e 's/\/\/line/\/\/ line/g' ./internal/php7/php7.go | ||||||
|  | 	sed -i -e 's/\/\/line/\/\/ line/g' ./internal/php8/php8.go | ||||||
|  | 	sed -i -e 's/\/\/line/\/\/ line/g' ./internal/scanner/scanner.go | ||||||
|  | 	sed -i -e 's/\/\/line/\/\/ line/g' ./internal/php8/scanner.go | ||||||
| 	rm -f y.output | 	rm -f y.output | ||||||
| 
 | 
 | ||||||
| ./internal/scanner/scanner.go: ./internal/scanner/scanner.rl | ./internal/scanner/scanner.go: ./internal/scanner/scanner.rl | ||||||
| @ -36,6 +39,12 @@ compile: ./internal/php5/php5.go ./internal/php7/php7.go ./internal/scanner/scan | |||||||
| ./internal/php7/php7.go: ./internal/php7/php7.y | ./internal/php7/php7.go: ./internal/php7/php7.y | ||||||
| 	goyacc -o $@ $< | 	goyacc -o $@ $< | ||||||
| 
 | 
 | ||||||
|  | ./internal/php8/php8.go: ./internal/php8/php8.y | ||||||
|  | 	goyacc -o $@ $< | ||||||
|  | 
 | ||||||
|  | ./internal/php8/scanner.go: ./internal/php8/scanner.rl | ||||||
|  | 	ragel -Z -G2 -o $@ $< | ||||||
|  | 
 | ||||||
| cpu_pprof: | cpu_pprof: | ||||||
| 	go test -cpuprofile cpu.pprof -bench=. -benchtime=20s ./internal/php7 | 	go test -cpuprofile cpu.pprof -bench=. -benchtime=20s ./internal/php7 | ||||||
| 	go tool pprof ./php7.test cpu.pprof | 	go tool pprof ./php7.test cpu.pprof | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								README.md
									
									
									
									
									
								
							| @ -1,3 +1,5 @@ | |||||||
|  | > This is a fork of the [z7zmey](https://github.com/z7zmey) [parser](https://github.com/z7zmey/php-parser) that adds PHP 8 support. | ||||||
|  | 
 | ||||||
| PHP Parser written in Go | PHP Parser written in Go | ||||||
| ======================== | ======================== | ||||||
| 
 | 
 | ||||||
| @ -6,29 +8,25 @@ PHP Parser written in Go | |||||||
| [](https://godoc.org/github.com/z7zmey/php-parser) | [](https://godoc.org/github.com/z7zmey/php-parser) | ||||||
| [](https://travis-ci.org/z7zmey/php-parser) | [](https://travis-ci.org/z7zmey/php-parser) | ||||||
| [](https://goreportcard.com/report/github.com/z7zmey/php-parser) | [](https://goreportcard.com/report/github.com/z7zmey/php-parser) | ||||||
| [](https://codeclimate.com/github/z7zmey/php-parser/maintainability) |  | ||||||
| [](https://codeclimate.com/github/z7zmey/php-parser/test_coverage) |  | ||||||
| 
 | 
 | ||||||
| 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. | 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. | ||||||
| 
 | 
 | ||||||
| #### Try it online: [demo](https://php-parser.com) | Features | ||||||
| 
 |  | ||||||
| Features: |  | ||||||
| --------- | --------- | ||||||
| 
 | 
 | ||||||
| - Fully support PHP 5 and PHP 7 syntax | - Fully support PHP 5, PHP 7 and PHP 8.0 syntax | ||||||
| - Abstract syntax tree (AST) representation | - Abstract syntax tree (AST) representation | ||||||
| - Traversing AST | - Traversing AST | ||||||
| - Resolving namespaced names | - Resolving namespace names | ||||||
| - Parsing syntax-invalid PHP files | - Parsing syntax-invalid PHP files | ||||||
| - Saving and printing free-floating comments and whitespaces | - Saving and printing free-floating comments and whitespaces | ||||||
| 
 | 
 | ||||||
| Who Uses | Who Uses | ||||||
| -------- | -------- | ||||||
| 
 | 
 | ||||||
| [VKCOM/noverify](https://github.com/VKCOM/noverify) - NoVerify is a pretty fast linter for PHP | - [VKCOM/noverify](https://github.com/VKCOM/noverify) — pretty fast linter for PHP | ||||||
| 
 | - [VKCOM/nocolor](https://github.com/VKCOM/nocolor) — architecture validation tool for PHP based on the [*concept of colored functions*](https://github.com/VKCOM/nocolor/blob/master/docs/introducing_colors.md) | ||||||
| [quasilyte/phpgrep](https://github.com/quasilyte/phpgrep) - phpgrep is a tool for syntax-aware PHP code search | - [quasilyte/phpgrep](https://github.com/quasilyte/phpgrep) — tool for syntax-aware PHP code search | ||||||
| 
 | 
 | ||||||
| Usage example | Usage example | ||||||
| ------- | ------- | ||||||
| @ -48,7 +46,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
| 	src := []byte(`<? echo "Hello world";`) | 	src := []byte(`<?php echo "Hello world";`) | ||||||
| 
 | 
 | ||||||
| 	// Error handler | 	// Error handler | ||||||
| 
 | 
 | ||||||
| @ -60,7 +58,7 @@ func main() { | |||||||
| 	// Parse | 	// Parse | ||||||
| 
 | 
 | ||||||
| 	rootNode, err := parser.Parse(src, cfg.Config{ | 	rootNode, err := parser.Parse(src, cfg.Config{ | ||||||
| 		Version:          &version.Version{Major: 5, Minor: 6}, | 		Version:          &version.Version{Major: 8, Minor: 0}, | ||||||
| 		ErrorHandlerFunc: errorHandler, | 		ErrorHandlerFunc: errorHandler, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| @ -78,12 +76,6 @@ func main() { | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Roadmap |  | ||||||
| ------- |  | ||||||
| 
 |  | ||||||
| - Control Flow Graph (CFG) |  | ||||||
| - PHP8 |  | ||||||
| 
 |  | ||||||
| Install | Install | ||||||
| ------- | ------- | ||||||
| 
 | 
 | ||||||
| @ -98,14 +90,14 @@ CLI | |||||||
| php-parser [flags] <path> ... | php-parser [flags] <path> ... | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| | flag    | type   | description                       | | | flag       | type     | description                         | | ||||||
| | ------- | ------ | --------------------------------- | | | ---------- | -------- | ----------------------------------- | | ||||||
| | -p      | bool   | print filepath                    | | | `--p`      | `bool`   | Print file paths                    | | ||||||
| | -e      | bool   | print errors                      | | | `--e`      | `bool`   | Print errors                        | | ||||||
| | -d      | bool   | dump in golang format             | | | `--d`      | `bool`   | Dump AST in Golang format           | | ||||||
| | -r      | bool   | resolve names                     | | | `--r`      | `bool`   | Resolve names                       | | ||||||
| | -prof   | string | start profiler: [cpu, mem, trace] | | | `--prof`   | `string` | Start profiler: `[cpu, mem, trace]` | | ||||||
| | -phpver | string | php version (default: 7.4)        | | | `--phpver` | `string` | PHP version (default: 8.0)          | | ||||||
| 
 | 
 | ||||||
| Namespace resolver | Namespace resolver | ||||||
| ------------------ | ------------------ | ||||||
| @ -113,4 +105,4 @@ Namespace resolver | |||||||
| Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[node.Node]string` structure | Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[node.Node]string` structure | ||||||
| 
 | 
 | ||||||
| - For `Class`, `Interface`, `Trait`, `Function`, `Constant` nodes it saves name with current namespace. | - For `Class`, `Interface`, `Trait`, `Function`, `Constant` nodes it saves name with current namespace. | ||||||
| - For `Name`, `Relative`, `FullyQualified` nodes it resolves `use` aliases and saves a fully qualified name. | - For `Name`, `Relative`, `FullyQualified` nodes it resolves `use` aliases and saves a fully qualified name. | ||||||
							
								
								
									
										19
									
								
								internal/php5/php5.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								internal/php5/php5.go
									
									
									
										generated
									
									
									
								
							| @ -333,7 +333,6 @@ var yyToknames = [...]string{ | |||||||
| 	"'>'", | 	"'>'", | ||||||
| 	"'.'", | 	"'.'", | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyStatenames = [...]string{} | var yyStatenames = [...]string{} | ||||||
| 
 | 
 | ||||||
| const yyEofCode = 1 | const yyEofCode = 1 | ||||||
| @ -589,6 +588,7 @@ const yyPrivate = 57344 | |||||||
| const yyLast = 8973 | const yyLast = 8973 | ||||||
| 
 | 
 | ||||||
| var yyAct = [...]int{ | var yyAct = [...]int{ | ||||||
|  | 
 | ||||||
| 	102, 571, 1007, 565, 420, 966, 844, 715, 808, 607, | 	102, 571, 1007, 565, 420, 966, 844, 715, 808, 607, | ||||||
| 	123, 131, 919, 200, 827, 735, 567, 684, 452, 594, | 	123, 131, 919, 200, 827, 735, 567, 684, 452, 594, | ||||||
| 	461, 816, 474, 592, 61, 579, 421, 319, 390, 142, | 	461, 816, 474, 592, 61, 579, 421, 319, 390, 142, | ||||||
| @ -1488,8 +1488,8 @@ var yyAct = [...]int{ | |||||||
| 	0, 0, 0, 536, 0, 525, 0, 0, 0, 535, | 	0, 0, 0, 536, 0, 525, 0, 0, 0, 535, | ||||||
| 	534, 532, 533, | 	534, 532, 533, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyPact = [...]int{ | var yyPact = [...]int{ | ||||||
|  | 
 | ||||||
| 	-1000, -1000, 2243, -1000, -1000, -1000, -1000, -1000, 321, 547, | 	-1000, -1000, 2243, -1000, -1000, -1000, -1000, -1000, 321, 547, | ||||||
| 	755, 70, -1000, 286, -1000, -1000, 946, -1000, 242, 242, | 	755, 70, -1000, 286, -1000, -1000, 946, -1000, 242, 242, | ||||||
| 	5391, 319, 242, 6739, 6627, 6515, 383, 172, 795, 6851, | 	5391, 319, 242, 6739, 6627, 6515, 383, 172, 795, 6851, | ||||||
| @ -1594,8 +1594,8 @@ var yyPact = [...]int{ | |||||||
| 	-1000, -1000, -1000, -1000, 718, 2559, 807, -1000, 36, 396, | 	-1000, -1000, -1000, -1000, 718, 2559, 807, -1000, 36, 396, | ||||||
| 	-1000, 2401, -1000, | 	-1000, 2401, -1000, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyPgo = [...]int{ | var yyPgo = [...]int{ | ||||||
|  | 
 | ||||||
| 	0, 29, 1177, 22, 8, 13, 1175, 1167, 40, 33, | 	0, 29, 1177, 22, 8, 13, 1175, 1167, 40, 33, | ||||||
| 	27, 940, 1165, 1164, 36, 217, 1179, 91, 1036, 72, | 	27, 940, 1165, 1164, 36, 217, 1179, 91, 1036, 72, | ||||||
| 	99, 59, 810, 200, 1163, 31, 1162, 1154, 1152, 42, | 	99, 59, 810, 200, 1163, 31, 1162, 1154, 1152, 42, | ||||||
| @ -1613,8 +1613,8 @@ var yyPgo = [...]int{ | |||||||
| 	23, 968, 19, 9, 965, 37, 964, 963, 962, 960, | 	23, 968, 19, 9, 965, 37, 964, 963, 962, 960, | ||||||
| 	182, 923, | 	182, 923, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyR1 = [...]int{ | var yyR1 = [...]int{ | ||||||
|  | 
 | ||||||
| 	0, 151, 122, 122, 115, 115, 7, 7, 7, 7, | 	0, 151, 122, 122, 115, 115, 7, 7, 7, 7, | ||||||
| 	7, 7, 7, 7, 7, 7, 7, 7, 117, 117, | 	7, 7, 7, 7, 7, 7, 7, 7, 117, 117, | ||||||
| 	8, 8, 8, 8, 118, 118, 9, 9, 9, 9, | 	8, 8, 8, 8, 118, 118, 9, 9, 9, 9, | ||||||
| @ -1669,8 +1669,8 @@ var yyR1 = [...]int{ | |||||||
| 	41, 41, 41, 107, 107, 40, 40, 34, 34, 35, | 	41, 41, 41, 107, 107, 40, 40, 34, 34, 35, | ||||||
| 	36, | 	36, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyR2 = [...]int{ | var yyR2 = [...]int{ | ||||||
|  | 
 | ||||||
| 	0, 1, 2, 0, 1, 3, 1, 1, 1, 1, | 	0, 1, 2, 0, 1, 3, 1, 1, 1, 1, | ||||||
| 	4, 3, 5, 4, 3, 4, 4, 2, 3, 1, | 	4, 3, 5, 4, 3, 4, 4, 2, 3, 1, | ||||||
| 	1, 3, 2, 4, 3, 1, 1, 3, 2, 4, | 	1, 3, 2, 4, 3, 1, 1, 3, 2, 4, | ||||||
| @ -1725,8 +1725,8 @@ var yyR2 = [...]int{ | |||||||
| 	4, 2, 2, 1, 3, 1, 1, 3, 3, 3, | 	4, 2, 2, 1, 3, 1, 1, 3, 3, 3, | ||||||
| 	3, | 	3, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyChk = [...]int{ | var yyChk = [...]int{ | ||||||
|  | 
 | ||||||
| 	-1000, -151, -122, -7, 2, -29, -55, -56, 52, 80, | 	-1000, -151, -122, -7, 2, -29, -55, -56, 52, 80, | ||||||
| 	45, -57, -30, 10, -66, -67, 39, 144, 7, 21, | 	45, -57, -30, 10, -66, -67, 39, 144, 7, 21, | ||||||
| 	20, 23, 30, 34, 35, 40, -54, 47, 99, 19, | 	20, 23, 30, 34, 35, 40, -54, 47, 99, 19, | ||||||
| @ -1831,8 +1831,8 @@ var yyChk = [...]int{ | |||||||
| 	146, 144, -49, -65, 148, -123, -49, 145, 12, 149, | 	146, 144, -49, -65, 148, -123, -49, 145, 12, 149, | ||||||
| 	144, -123, 145, | 	144, -123, 145, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyDef = [...]int{ | var yyDef = [...]int{ | ||||||
|  | 
 | ||||||
| 	3, -2, -2, 2, 6, 7, 8, 9, 0, 0, | 	3, -2, -2, 2, 6, 7, 8, 9, 0, 0, | ||||||
| 	0, 0, 45, 4, 87, 88, 0, 39, 0, 0, | 	0, 0, 45, 4, 87, 88, 0, 39, 0, 0, | ||||||
| 	0, 0, 0, 0, 0, 0, 0, 0, 331, 0, | 	0, 0, 0, 0, 0, 0, 0, 0, 331, 0, | ||||||
| @ -1937,8 +1937,8 @@ var yyDef = [...]int{ | |||||||
| 	196, 39, 188, 82, 0, 0, 0, 197, 0, 0, | 	196, 39, 188, 82, 0, 0, 0, 197, 0, 0, | ||||||
| 	39, 0, 83, | 	39, 0, 83, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyTok1 = [...]int{ | var yyTok1 = [...]int{ | ||||||
|  | 
 | ||||||
| 	1, 3, 3, 3, 3, 3, 3, 3, 3, 3, | 	1, 3, 3, 3, 3, 3, 3, 3, 3, 3, | ||||||
| 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | ||||||
| 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | ||||||
| @ -1953,8 +1953,8 @@ var yyTok1 = [...]int{ | |||||||
| 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | ||||||
| 	3, 3, 3, 144, 161, 145, 157, | 	3, 3, 3, 144, 161, 145, 157, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyTok2 = [...]int{ | var yyTok2 = [...]int{ | ||||||
|  | 
 | ||||||
| 	2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 	2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | ||||||
| 	12, 13, 14, 15, 16, 17, 18, 19, 20, 21, | 	12, 13, 14, 15, 16, 17, 18, 19, 20, 21, | ||||||
| 	22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | 	22, 23, 24, 25, 26, 27, 28, 29, 30, 31, | ||||||
| @ -1970,7 +1970,6 @@ var yyTok2 = [...]int{ | |||||||
| 	122, 123, 124, 125, 126, 127, 128, 129, 130, 131, | 	122, 123, 124, 125, 126, 127, 128, 129, 130, 131, | ||||||
| 	132, 133, 134, 135, 136, 137, 138, 139, 140, 141, | 	132, 133, 134, 135, 136, 137, 138, 139, 140, 141, | ||||||
| } | } | ||||||
| 
 |  | ||||||
| var yyTok3 = [...]int{ | var yyTok3 = [...]int{ | ||||||
| 	0, | 	0, | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										421
									
								
								internal/php7/php7.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										421
									
								
								internal/php7/php7.go
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1431
									
								
								internal/php8/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1431
									
								
								internal/php8/builder.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										251
									
								
								internal/php8/lexer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								internal/php8/lexer.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,251 @@ | |||||||
|  | package php8 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/errors" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/position" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Lexer struct { | ||||||
|  | 	data           []byte | ||||||
|  | 	phpVersion     *version.Version | ||||||
|  | 	errHandlerFunc func(*errors.Error) | ||||||
|  | 
 | ||||||
|  | 	p, pe, cs   int | ||||||
|  | 	ts, te, act int | ||||||
|  | 	stack       []int | ||||||
|  | 	top         int | ||||||
|  | 
 | ||||||
|  | 	heredocLabel []byte | ||||||
|  | 	tokenPool    *token.Pool | ||||||
|  | 	positionPool *position.Pool | ||||||
|  | 	newLines     NewLines | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewLexer(data []byte, config conf.Config) *Lexer { | ||||||
|  | 	lex := &Lexer{ | ||||||
|  | 		data:           data, | ||||||
|  | 		phpVersion:     config.Version, | ||||||
|  | 		errHandlerFunc: config.ErrorHandlerFunc, | ||||||
|  | 
 | ||||||
|  | 		pe:    len(data), | ||||||
|  | 		stack: make([]int, 0), | ||||||
|  | 
 | ||||||
|  | 		tokenPool:    token.NewPool(position.DefaultBlockSize), | ||||||
|  | 		positionPool: position.NewPool(token.DefaultBlockSize), | ||||||
|  | 		newLines:     NewLines{make([]int, 0, 128)}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	initLexer(lex) | ||||||
|  | 
 | ||||||
|  | 	return lex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) setTokenPosition(token *token.Token) { | ||||||
|  | 	pos := lex.positionPool.Get() | ||||||
|  | 
 | ||||||
|  | 	pos.StartLine = lex.newLines.GetLine(lex.ts) | ||||||
|  | 	pos.EndLine = lex.newLines.GetLine(lex.te - 1) | ||||||
|  | 	pos.StartPos = lex.ts | ||||||
|  | 	pos.EndPos = lex.te | ||||||
|  | 
 | ||||||
|  | 	token.Position = pos | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) addFreeFloatingToken(t *token.Token, id token.ID, ps, pe int) { | ||||||
|  | 	skippedTkn := lex.tokenPool.Get() | ||||||
|  | 	skippedTkn.ID = id | ||||||
|  | 	skippedTkn.Value = lex.data[ps:pe] | ||||||
|  | 
 | ||||||
|  | 	lex.setTokenPosition(skippedTkn) | ||||||
|  | 
 | ||||||
|  | 	if t.FreeFloating == nil { | ||||||
|  | 		t.FreeFloating = make([]*token.Token, 0, 2) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t.FreeFloating = append(t.FreeFloating, skippedTkn) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isNotStringVar() bool { | ||||||
|  | 	p := lex.p | ||||||
|  | 	if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(lex.data) < p+1 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if lex.data[p] == '$' && (lex.data[p+1] == '{' || isValidVarNameStart(lex.data[p+1])) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if lex.data[p] == '{' && lex.data[p+1] == '$' { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isNotStringEnd(s byte) bool { | ||||||
|  | 	p := lex.p | ||||||
|  | 	if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return !(lex.data[p] == s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isHeredocEnd(p int) bool { | ||||||
|  | 	o, err := version.New("7.3") | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if lex.phpVersion.GreaterOrEqual(o) { | ||||||
|  | 		return lex.isHeredocEndSince73(p) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return lex.isHeredocEndBefore73(p) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isHeredocEndBefore73(p int) bool { | ||||||
|  | 	if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l := len(lex.heredocLabel) | ||||||
|  | 	if len(lex.data) < p+l { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(lex.data) > p+l && lex.data[p+l] != ';' && lex.data[p+l] != '\r' && lex.data[p+l] != '\n' { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(lex.data) > p+l+1 && lex.data[p+l] == ';' && lex.data[p+l+1] != '\r' && lex.data[p+l+1] != '\n' { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isHeredocEndSince73(p int) bool { | ||||||
|  | 	if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if p == len(lex.data) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for lex.data[p] == ' ' || lex.data[p] == '\t' { | ||||||
|  | 		p++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l := len(lex.heredocLabel) | ||||||
|  | 	if len(lex.data) < p+l { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(lex.data) > p+l && isValidVarName(lex.data[p+l]) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	a := string(lex.heredocLabel) | ||||||
|  | 	b := string(lex.data[p : p+l]) | ||||||
|  | 
 | ||||||
|  | 	_, _ = a, b | ||||||
|  | 
 | ||||||
|  | 	if bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) { | ||||||
|  | 		lex.p = p | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isNotHeredocEnd(p int) bool { | ||||||
|  | 	return !lex.isHeredocEnd(p) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) growCallStack() { | ||||||
|  | 	if lex.top == len(lex.stack) { | ||||||
|  | 		lex.stack = append(lex.stack, 0) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isNotPhpCloseToken() bool { | ||||||
|  | 	if lex.p+1 == len(lex.data) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return lex.data[lex.p] != '?' || lex.data[lex.p+1] != '>' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) isNotNewLine() bool { | ||||||
|  | 	if lex.data[lex.p] == '\n' && lex.data[lex.p-1] == '\r' { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return lex.data[lex.p-1] != '\n' && lex.data[lex.p-1] != '\r' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) call(state int, fnext int) { | ||||||
|  | 	lex.growCallStack() | ||||||
|  | 
 | ||||||
|  | 	lex.stack[lex.top] = state | ||||||
|  | 	lex.top++ | ||||||
|  | 
 | ||||||
|  | 	lex.p++ | ||||||
|  | 	lex.cs = fnext | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) ret(n int) { | ||||||
|  | 	lex.top = lex.top - n | ||||||
|  | 	if lex.top < 0 { | ||||||
|  | 		lex.top = 0 | ||||||
|  | 	} | ||||||
|  | 	lex.cs = lex.stack[lex.top] | ||||||
|  | 	lex.p++ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) ungetStr(s string) { | ||||||
|  | 	tokenStr := string(lex.data[lex.ts:lex.te]) | ||||||
|  | 	if strings.HasSuffix(tokenStr, s) { | ||||||
|  | 		lex.ungetCnt(len(s)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) ungetCnt(n int) { | ||||||
|  | 	lex.p = lex.p - n | ||||||
|  | 	lex.te = lex.te - n | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) error(msg string) { | ||||||
|  | 	if lex.errHandlerFunc == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pos := position.NewPosition( | ||||||
|  | 		lex.newLines.GetLine(lex.ts), | ||||||
|  | 		lex.newLines.GetLine(lex.te-1), | ||||||
|  | 		lex.ts, | ||||||
|  | 		lex.te, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	lex.errHandlerFunc(errors.NewError(msg, pos)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isValidVarNameStart(r byte) bool { | ||||||
|  | 	return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || r >= 0x80 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isValidVarName(r byte) bool { | ||||||
|  | 	return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r >= 0x80 | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								internal/php8/newline.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								internal/php8/newline.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | package php8 | ||||||
|  | 
 | ||||||
|  | type NewLines struct { | ||||||
|  | 	data []int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (nl *NewLines) Append(p int) { | ||||||
|  | 	if len(nl.data) == 0 || nl.data[len(nl.data)-1] < p { | ||||||
|  | 		nl.data = append(nl.data, p) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (nl *NewLines) GetLine(p int) int { | ||||||
|  | 	line := len(nl.data) + 1 | ||||||
|  | 
 | ||||||
|  | 	for i := len(nl.data) - 1; i >= 0; i-- { | ||||||
|  | 		if p < nl.data[i] { | ||||||
|  | 			line = i + 1 | ||||||
|  | 		} else { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return line | ||||||
|  | } | ||||||
							
								
								
									
										99
									
								
								internal/php8/node.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								internal/php8/node.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | package php8 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/position" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ParserBrackets struct { | ||||||
|  | 	Position        *position.Position | ||||||
|  | 	OpenBracketTkn  *token.Token | ||||||
|  | 	Child           ast.Vertex | ||||||
|  | 	CloseBracketTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ParserBrackets) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ParserBrackets) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ParserSeparatedList struct { | ||||||
|  | 	Position      *position.Position | ||||||
|  | 	Items         []ast.Vertex | ||||||
|  | 	SeparatorTkns []*token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ParserSeparatedList) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ParserSeparatedList) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TraitAdaptationList node | ||||||
|  | type TraitAdaptationList struct { | ||||||
|  | 	Position             *position.Position | ||||||
|  | 	OpenCurlyBracketTkn  *token.Token | ||||||
|  | 	Adaptations          []ast.Vertex | ||||||
|  | 	CloseCurlyBracketTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *TraitAdaptationList) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *TraitAdaptationList) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ArgumentList node | ||||||
|  | type ArgumentList struct { | ||||||
|  | 	Position            *position.Position | ||||||
|  | 	OpenParenthesisTkn  *token.Token | ||||||
|  | 	Arguments           []ast.Vertex | ||||||
|  | 	SeparatorTkns       []*token.Token | ||||||
|  | 	CloseParenthesisTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ArgumentList) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ArgumentList) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ReturnType struct { | ||||||
|  | 	Position *position.Position | ||||||
|  | 	ColonTkn *token.Token | ||||||
|  | 	Type     ast.Vertex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ReturnType) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ReturnType) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TraitMethodRef node | ||||||
|  | type TraitMethodRef struct { | ||||||
|  | 	Position       *position.Position | ||||||
|  | 	Trait          ast.Vertex | ||||||
|  | 	DoubleColonTkn *token.Token | ||||||
|  | 	Method         ast.Vertex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *TraitMethodRef) Accept(v ast.Visitor) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *TraitMethodRef) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								internal/php8/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								internal/php8/parser.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | package php8 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/position" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/errors" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Parser structure | ||||||
|  | type Parser struct { | ||||||
|  | 	Lexer          *Lexer | ||||||
|  | 	currentToken   *token.Token | ||||||
|  | 	rootNode       ast.Vertex | ||||||
|  | 	errHandlerFunc func(*errors.Error) | ||||||
|  | 	builder        *Builder | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewParser creates and returns new Parser | ||||||
|  | func NewParser(lexer *Lexer, config conf.Config) *Parser { | ||||||
|  | 	p := &Parser{ | ||||||
|  | 		Lexer:          lexer, | ||||||
|  | 		errHandlerFunc: config.ErrorHandlerFunc, | ||||||
|  | 	} | ||||||
|  | 	p.builder = NewBuilder(position.NewBuilder(), p) | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Parser) Lex(lval *yySymType) int { | ||||||
|  | 	t := p.Lexer.Lex() | ||||||
|  | 
 | ||||||
|  | 	p.currentToken = t | ||||||
|  | 	lval.token = t | ||||||
|  | 
 | ||||||
|  | 	return int(t.ID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Parser) Error(msg string) { | ||||||
|  | 	if p.errHandlerFunc == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.errHandlerFunc(errors.NewError(msg, p.currentToken.Position)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse the php7 Parser entrypoint | ||||||
|  | func (p *Parser) Parse() int { | ||||||
|  | 	p.rootNode = nil | ||||||
|  | 
 | ||||||
|  | 	return yyParse(p) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetRootNode returns root node | ||||||
|  | func (p *Parser) GetRootNode() ast.Vertex { | ||||||
|  | 	return p.rootNode | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // helpers | ||||||
|  | 
 | ||||||
|  | func lastNode(nn []ast.Vertex) ast.Vertex { | ||||||
|  | 	if len(nn) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return nn[len(nn)-1] | ||||||
|  | } | ||||||
							
								
								
									
										4896
									
								
								internal/php8/parser_php8_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4896
									
								
								internal/php8/parser_php8_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										56137
									
								
								internal/php8/parser_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56137
									
								
								internal/php8/parser_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										7192
									
								
								internal/php8/php8.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7192
									
								
								internal/php8/php8.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3423
									
								
								internal/php8/php8.y
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3423
									
								
								internal/php8/php8.y
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										30
									
								
								internal/php8/php8_bench_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/php8/php8_bench_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | package php8_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func BenchmarkPhp8(b *testing.B) { | ||||||
|  | 	src, err := ioutil.ReadFile("test.php") | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		b.Fatal("can not read test.php: " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for n := 0; n < b.N; n++ { | ||||||
|  | 		config := conf.Config{ | ||||||
|  | 			Version: &version.Version{ | ||||||
|  | 				Major: 8, | ||||||
|  | 				Minor: 8, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		lexer := php8.NewLexer(src, config) | ||||||
|  | 		php8parser := php8.NewParser(lexer, config) | ||||||
|  | 		php8parser.Parse() | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										28084
									
								
								internal/php8/scanner.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28084
									
								
								internal/php8/scanner.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										516
									
								
								internal/php8/scanner.rl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								internal/php8/scanner.rl
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,516 @@ | |||||||
|  | package php8 | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  |     "fmt" | ||||||
|  |     "strconv" | ||||||
|  |     "strings" | ||||||
|  | 
 | ||||||
|  |     "github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | %%{  | ||||||
|  |     machine lexer; | ||||||
|  |     write data; | ||||||
|  |     access lex.; | ||||||
|  |     variable p lex.p; | ||||||
|  |     variable pe lex.pe; | ||||||
|  | }%% | ||||||
|  | 
 | ||||||
|  | func initLexer(lex *Lexer)  { | ||||||
|  |     %% write init; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (lex *Lexer) Lex() *token.Token { | ||||||
|  |     eof := lex.pe | ||||||
|  |     var tok token.ID | ||||||
|  | 
 | ||||||
|  |     tkn := lex.tokenPool.Get() | ||||||
|  | 
 | ||||||
|  |     lblStart := 0 | ||||||
|  |     lblEnd   := 0 | ||||||
|  | 
 | ||||||
|  |     _, _ = lblStart, lblEnd | ||||||
|  | 
 | ||||||
|  |     %%{  | ||||||
|  |         action heredoc_lbl_start {lblStart = lex.p} | ||||||
|  |         action heredoc_lbl_end   {lblEnd = lex.p} | ||||||
|  | 
 | ||||||
|  |         action new_line   { | ||||||
|  |             if lex.data[lex.p] == '\n' { | ||||||
|  |                 lex.newLines.Append(lex.p+1) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if lex.data[lex.p] == '\r' && lex.data[lex.p+1] != '\n' { | ||||||
|  |                 lex.newLines.Append(lex.p+1) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         action is_not_heredoc_end { lex.isNotHeredocEnd(lex.p) } | ||||||
|  |         action is_not_comment_end { lex.isNotPhpCloseToken() && lex.isNotNewLine()  } | ||||||
|  |         action is_not_heredoc_end_or_var { lex.isNotHeredocEnd(lex.p) && lex.isNotStringVar() } | ||||||
|  |         action is_not_string_end_or_var { lex.isNotStringEnd('"') && lex.isNotStringVar() } | ||||||
|  |         action is_not_backqoute_end_or_var { lex.isNotStringEnd('`') && lex.isNotStringVar() } | ||||||
|  | 
 | ||||||
|  |         newline = ('\r\n' >(nl, 1) | '\r' >(nl, 0) | '\n' >(nl, 0)) $new_line %{}; | ||||||
|  |         any_line = any | newline; | ||||||
|  |         whitespace = [\t\v\f ]; | ||||||
|  |         whitespace_line = [\t\v\f ] | newline; | ||||||
|  | 
 | ||||||
|  |         lnum = [0-9]+('_'[0-9]+)*; | ||||||
|  |         dnum = (lnum?"." lnum)|(lnum"."lnum?); | ||||||
|  |         hnum = '0x'[0-9a-fA-F]+('_'[0-9a-fA-F]+)*; | ||||||
|  |         bnum = '0b'[01]+('_'[01]+)*; | ||||||
|  | 
 | ||||||
|  |         exponent_dnum = (lnum | dnum) ('e'|'E') ('+'|'-')? lnum; | ||||||
|  |         varname_first = [a-zA-Z_] | (0x0080..0x00FF); | ||||||
|  |         varname_second = varname_first | [0-9]; | ||||||
|  |         varname       = varname_first (varname_second)*; | ||||||
|  |         heredoc_label = varname >heredoc_lbl_start %heredoc_lbl_end; | ||||||
|  |         operators     = ';'|':'|','|'.'|'['|']'|'('|')'|'|'|'/'|'^'|'&'|'+'|'-'|'*'|'='|'%'|'!'|'~'|'$'|'<'|'>'|'?'|'@'; | ||||||
|  | 
 | ||||||
|  |         prepush { lex.growCallStack(); } | ||||||
|  | 
 | ||||||
|  |         constant_string = | ||||||
|  |             start: ( | ||||||
|  |                 "'"         -> qoute | ||||||
|  |                 | "b"i? '"' -> double_qoute | ||||||
|  |             ), | ||||||
|  | 
 | ||||||
|  |             # single qoute string | ||||||
|  | 
 | ||||||
|  |             qoute: ( | ||||||
|  |                 (any - [\\'\r\n]) -> qoute | ||||||
|  |                 | "\r" @new_line  -> qoute | ||||||
|  |                 | "\n" @new_line  -> qoute | ||||||
|  |                 | "\\"            -> qoute_any | ||||||
|  |                 | "'"             -> final | ||||||
|  |             ), | ||||||
|  |             qoute_any: ( | ||||||
|  |                 (any - [\r\n])   -> qoute | ||||||
|  |                 | "\r" @new_line -> qoute | ||||||
|  |                 | "\n" @new_line -> qoute | ||||||
|  |             ), | ||||||
|  | 
 | ||||||
|  |             # double qoute string | ||||||
|  | 
 | ||||||
|  |             double_qoute: ( | ||||||
|  |                 (any - [\\"${\r\n]) -> double_qoute | ||||||
|  |                 | "\r" @new_line    -> double_qoute | ||||||
|  |                 | "\n" @new_line    -> double_qoute | ||||||
|  |                 | "\\"              -> double_qoute_any | ||||||
|  |                 | '"'               -> final | ||||||
|  |                 | '$'               -> double_qoute_nonvarname | ||||||
|  |                 | '{'               -> double_qoute_nondollar | ||||||
|  |             ), | ||||||
|  |             double_qoute_any: ( | ||||||
|  |                 (any - [\r\n])     -> double_qoute | ||||||
|  |                 | "\r" @new_line   -> double_qoute | ||||||
|  |                 | "\n" @new_line   -> double_qoute | ||||||
|  |             ), | ||||||
|  |             double_qoute_nondollar: ( | ||||||
|  |                 (any - [\\$"\r\n]) -> double_qoute | ||||||
|  |                 | "\r" @new_line   -> double_qoute | ||||||
|  |                 | "\n" @new_line   -> double_qoute | ||||||
|  |                 | "\\"             -> double_qoute_any | ||||||
|  |                 | '"'              -> final | ||||||
|  |             ), | ||||||
|  |             double_qoute_nonvarname: ( | ||||||
|  |                 (any - [\\${"\r\n] - varname_first) -> double_qoute | ||||||
|  |                 | "\r" @new_line                    -> double_qoute | ||||||
|  |                 | "\n" @new_line                    -> double_qoute | ||||||
|  |                 | "\\"                              -> double_qoute_any | ||||||
|  |                 | '$'                               -> double_qoute_nonvarname | ||||||
|  |                 | '"'                               -> final | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |         main := |* | ||||||
|  |             "#!" any* :>> newline => { | ||||||
|  |                 lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) | ||||||
|  |             }; | ||||||
|  |             any => { | ||||||
|  |                 fnext html; | ||||||
|  |                 lex.ungetCnt(1) | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         html := |* | ||||||
|  |             any_line+ -- '<?' => { | ||||||
|  |                 lex.ungetStr("<") | ||||||
|  |                 lex.setTokenPosition(tkn) | ||||||
|  |                 tok = token.T_INLINE_HTML; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |             '<?' => { | ||||||
|  |                 lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.te) | ||||||
|  |                 fnext php; | ||||||
|  |             }; | ||||||
|  |             '<?php'i ( [ \t] | newline ) => { | ||||||
|  |                 lex.ungetCnt(lex.te - lex.ts - 5) | ||||||
|  |                 lex.addFreeFloatingToken(tkn, token.T_OPEN_TAG, lex.ts, lex.ts+5) | ||||||
|  |                 fnext php; | ||||||
|  |             }; | ||||||
|  |             '<?='i => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_ECHO; | ||||||
|  |                 fnext php; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         php := |* | ||||||
|  |             whitespace_line*                   => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; | ||||||
|  |             '?>' newline?                      => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;}; | ||||||
|  |             ';' whitespace_line* '?>' newline? => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext html; fbreak;}; | ||||||
|  | 
 | ||||||
|  |             (dnum | exponent_dnum)          => {lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak;}; | ||||||
|  |             bnum => { | ||||||
|  |                 s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1) | ||||||
|  |                 _, err := strconv.ParseInt(s, 2, 0) | ||||||
|  | 
 | ||||||
|  |                 if err == nil { | ||||||
|  |                     lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; | ||||||
|  |             }; | ||||||
|  |             lnum => { | ||||||
|  |                 base := 10 | ||||||
|  |                 if lex.data[lex.ts] == '0' { | ||||||
|  |                     base = 8 | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 s := strings.Replace(string(lex.data[lex.ts:lex.te]), "_", "", -1) | ||||||
|  |                 _, err := strconv.ParseInt(s, base, 0) | ||||||
|  | 
 | ||||||
|  |                 if err == nil { | ||||||
|  |                     lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; | ||||||
|  |             }; | ||||||
|  |             hnum => { | ||||||
|  |                 s := strings.Replace(string(lex.data[lex.ts+2:lex.te]), "_", "", -1) | ||||||
|  |                 _, err := strconv.ParseInt(s, 16, 0) | ||||||
|  | 
 | ||||||
|  |                 if err == nil { | ||||||
|  |                     lex.setTokenPosition(tkn); tok = token.T_LNUMBER; fbreak; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 lex.setTokenPosition(tkn); tok = token.T_DNUMBER; fbreak; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             'namespace'i ('\\' varname)+      => {lex.setTokenPosition(tkn); tok = token.T_NAME_RELATIVE; fbreak;}; | ||||||
|  |             varname ('\\' varname)+           => {lex.setTokenPosition(tkn); tok = token.T_NAME_QUALIFIED; fbreak;}; | ||||||
|  |             '\\' varname ('\\' varname)*      => {lex.setTokenPosition(tkn); tok = token.T_NAME_FULLY_QUALIFIED; fbreak;}; | ||||||
|  |             '\\'                              => {lex.setTokenPosition(tkn); tok = token.T_NS_SEPARATOR; fbreak;}; | ||||||
|  | 
 | ||||||
|  |             'abstract'i                       => {lex.setTokenPosition(tkn); tok = token.T_ABSTRACT; fbreak;}; | ||||||
|  |             'array'i                          => {lex.setTokenPosition(tkn); tok = token.T_ARRAY; fbreak;}; | ||||||
|  |             'as'i                             => {lex.setTokenPosition(tkn); tok = token.T_AS; fbreak;}; | ||||||
|  |             'break'i                          => {lex.setTokenPosition(tkn); tok = token.T_BREAK; fbreak;}; | ||||||
|  |             'callable'i                       => {lex.setTokenPosition(tkn); tok = token.T_CALLABLE; fbreak;}; | ||||||
|  |             'case'i                           => {lex.setTokenPosition(tkn); tok = token.T_CASE; fbreak;}; | ||||||
|  |             'catch'i                          => {lex.setTokenPosition(tkn); tok = token.T_CATCH; fbreak;}; | ||||||
|  |             'class'i                          => {lex.setTokenPosition(tkn); tok = token.T_CLASS; fbreak;}; | ||||||
|  |             'clone'i                          => {lex.setTokenPosition(tkn); tok = token.T_CLONE; fbreak;}; | ||||||
|  |             'const'i                          => {lex.setTokenPosition(tkn); tok = token.T_CONST; fbreak;}; | ||||||
|  |             'continue'i                       => {lex.setTokenPosition(tkn); tok = token.T_CONTINUE; fbreak;}; | ||||||
|  |             'declare'i                        => {lex.setTokenPosition(tkn); tok = token.T_DECLARE; fbreak;}; | ||||||
|  |             'default'i                        => {lex.setTokenPosition(tkn); tok = token.T_DEFAULT; fbreak;}; | ||||||
|  |             'do'i                             => {lex.setTokenPosition(tkn); tok = token.T_DO; fbreak;}; | ||||||
|  |             'echo'i                           => {lex.setTokenPosition(tkn); tok = token.T_ECHO; fbreak;}; | ||||||
|  |             'else'i                           => {lex.setTokenPosition(tkn); tok = token.T_ELSE; fbreak;}; | ||||||
|  |             'elseif'i                         => {lex.setTokenPosition(tkn); tok = token.T_ELSEIF; fbreak;}; | ||||||
|  |             'empty'i                          => {lex.setTokenPosition(tkn); tok = token.T_EMPTY; fbreak;}; | ||||||
|  |             'enddeclare'i                     => {lex.setTokenPosition(tkn); tok = token.T_ENDDECLARE; fbreak;}; | ||||||
|  |             'endfor'i                         => {lex.setTokenPosition(tkn); tok = token.T_ENDFOR; fbreak;}; | ||||||
|  |             'endforeach'i                     => {lex.setTokenPosition(tkn); tok = token.T_ENDFOREACH; fbreak;}; | ||||||
|  |             'endif'i                          => {lex.setTokenPosition(tkn); tok = token.T_ENDIF; fbreak;}; | ||||||
|  |             'endswitch'i                      => {lex.setTokenPosition(tkn); tok = token.T_ENDSWITCH; fbreak;}; | ||||||
|  |             'endwhile'i                       => {lex.setTokenPosition(tkn); tok = token.T_ENDWHILE; fbreak;}; | ||||||
|  |             'eval'i                           => {lex.setTokenPosition(tkn); tok = token.T_EVAL; fbreak;}; | ||||||
|  |             'exit'i | 'die'i                  => {lex.setTokenPosition(tkn); tok = token.T_EXIT; fbreak;}; | ||||||
|  |             'extends'i                        => {lex.setTokenPosition(tkn); tok = token.T_EXTENDS; fbreak;}; | ||||||
|  |             'final'i                          => {lex.setTokenPosition(tkn); tok = token.T_FINAL; fbreak;}; | ||||||
|  |             'finally'i                        => {lex.setTokenPosition(tkn); tok = token.T_FINALLY; fbreak;}; | ||||||
|  |             'for'i                            => {lex.setTokenPosition(tkn); tok = token.T_FOR; fbreak;}; | ||||||
|  |             'foreach'i                        => {lex.setTokenPosition(tkn); tok = token.T_FOREACH; fbreak;}; | ||||||
|  |             'function'i | 'cfunction'i        => {lex.setTokenPosition(tkn); tok = token.T_FUNCTION; fbreak;}; | ||||||
|  |             'fn'i                             => {lex.setTokenPosition(tkn); tok = token.T_FN; fbreak;}; | ||||||
|  |             'global'i                         => {lex.setTokenPosition(tkn); tok = token.T_GLOBAL; fbreak;}; | ||||||
|  |             'goto'i                           => {lex.setTokenPosition(tkn); tok = token.T_GOTO; fbreak;}; | ||||||
|  |             'if'i                             => {lex.setTokenPosition(tkn); tok = token.T_IF; fbreak;}; | ||||||
|  |             'isset'i                          => {lex.setTokenPosition(tkn); tok = token.T_ISSET; fbreak;}; | ||||||
|  |             'implements'i                     => {lex.setTokenPosition(tkn); tok = token.T_IMPLEMENTS; fbreak;}; | ||||||
|  |             'instanceof'i                     => {lex.setTokenPosition(tkn); tok = token.T_INSTANCEOF; fbreak;}; | ||||||
|  |             'insteadof'i                      => {lex.setTokenPosition(tkn); tok = token.T_INSTEADOF; fbreak;}; | ||||||
|  |             'interface'i                      => {lex.setTokenPosition(tkn); tok = token.T_INTERFACE; fbreak;}; | ||||||
|  |             'list'i                           => {lex.setTokenPosition(tkn); tok = token.T_LIST; fbreak;}; | ||||||
|  |             'namespace'i                      => {lex.setTokenPosition(tkn); tok = token.T_NAMESPACE; fbreak;}; | ||||||
|  |             'private'i                        => {lex.setTokenPosition(tkn); tok = token.T_PRIVATE; fbreak;}; | ||||||
|  |             'public'i                         => {lex.setTokenPosition(tkn); tok = token.T_PUBLIC; fbreak;}; | ||||||
|  |             'print'i                          => {lex.setTokenPosition(tkn); tok = token.T_PRINT; fbreak;}; | ||||||
|  |             'protected'i                      => {lex.setTokenPosition(tkn); tok = token.T_PROTECTED; fbreak;}; | ||||||
|  |             'return'i                         => {lex.setTokenPosition(tkn); tok = token.T_RETURN; fbreak;}; | ||||||
|  |             'static'i                         => {lex.setTokenPosition(tkn); tok = token.T_STATIC; fbreak;}; | ||||||
|  |             'switch'i                         => {lex.setTokenPosition(tkn); tok = token.T_SWITCH; fbreak;}; | ||||||
|  |             'match'i                          => {lex.setTokenPosition(tkn); tok = token.T_MATCH; fbreak;}; | ||||||
|  |             'throw'i                          => {lex.setTokenPosition(tkn); tok = token.T_THROW; fbreak;}; | ||||||
|  |             'trait'i                          => {lex.setTokenPosition(tkn); tok = token.T_TRAIT; fbreak;}; | ||||||
|  |             'try'i                            => {lex.setTokenPosition(tkn); tok = token.T_TRY; fbreak;}; | ||||||
|  |             'unset'i                          => {lex.setTokenPosition(tkn); tok = token.T_UNSET; fbreak;}; | ||||||
|  |             'use'i                            => {lex.setTokenPosition(tkn); tok = token.T_USE; fbreak;}; | ||||||
|  |             'var'i                            => {lex.setTokenPosition(tkn); tok = token.T_VAR; fbreak;}; | ||||||
|  |             'while'i                          => {lex.setTokenPosition(tkn); tok = token.T_WHILE; fbreak;}; | ||||||
|  |             'yield'i whitespace_line+ 'from'i => {lex.setTokenPosition(tkn); tok = token.T_YIELD_FROM; fbreak;}; | ||||||
|  |             'yield'i                          => {lex.setTokenPosition(tkn); tok = token.T_YIELD; fbreak;}; | ||||||
|  |             'include'i                        => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE; fbreak;}; | ||||||
|  |             'include_once'i                   => {lex.setTokenPosition(tkn); tok = token.T_INCLUDE_ONCE; fbreak;}; | ||||||
|  |             'require'i                        => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE; fbreak;}; | ||||||
|  |             'require_once'i                   => {lex.setTokenPosition(tkn); tok = token.T_REQUIRE_ONCE; fbreak;}; | ||||||
|  |             '__CLASS__'i                      => {lex.setTokenPosition(tkn); tok = token.T_CLASS_C; fbreak;}; | ||||||
|  |             '__DIR__'i                        => {lex.setTokenPosition(tkn); tok = token.T_DIR; fbreak;}; | ||||||
|  |             '__FILE__'i                       => {lex.setTokenPosition(tkn); tok = token.T_FILE; fbreak;}; | ||||||
|  |             '__FUNCTION__'i                   => {lex.setTokenPosition(tkn); tok = token.T_FUNC_C; fbreak;}; | ||||||
|  |             '__LINE__'i                       => {lex.setTokenPosition(tkn); tok = token.T_LINE; fbreak;}; | ||||||
|  |             '__NAMESPACE__'i                  => {lex.setTokenPosition(tkn); tok = token.T_NS_C; fbreak;}; | ||||||
|  |             '__METHOD__'i                     => {lex.setTokenPosition(tkn); tok = token.T_METHOD_C; fbreak;}; | ||||||
|  |             '__TRAIT__'i                      => {lex.setTokenPosition(tkn); tok = token.T_TRAIT_C; fbreak;}; | ||||||
|  |             '__halt_compiler'i                => {lex.setTokenPosition(tkn); tok = token.T_HALT_COMPILER; fnext halt_compiller_open_parenthesis; fbreak;}; | ||||||
|  |             'new'i                            => {lex.setTokenPosition(tkn); tok = token.T_NEW; fbreak;}; | ||||||
|  |             'and'i                            => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_AND; fbreak;}; | ||||||
|  |             'or'i                             => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_OR; fbreak;}; | ||||||
|  |             'xor'i                            => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_XOR; fbreak;}; | ||||||
|  |             '#['                              => {lex.setTokenPosition(tkn); tok = token.T_ATTRIBUTE; fbreak;}; | ||||||
|  |             '...'                             => {lex.setTokenPosition(tkn); tok = token.T_ELLIPSIS; fbreak;}; | ||||||
|  |             '::'                              => {lex.setTokenPosition(tkn); tok = token.T_PAAMAYIM_NEKUDOTAYIM; fbreak;}; | ||||||
|  |             '&&'                              => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_AND; fbreak;}; | ||||||
|  |             '||'                              => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_OR; fbreak;}; | ||||||
|  |             '&='                              => {lex.setTokenPosition(tkn); tok = token.T_AND_EQUAL; fbreak;}; | ||||||
|  |             '|='                              => {lex.setTokenPosition(tkn); tok = token.T_OR_EQUAL; fbreak;}; | ||||||
|  |             '.='                              => {lex.setTokenPosition(tkn); tok = token.T_CONCAT_EQUAL; fbreak;}; | ||||||
|  |             '*='                              => {lex.setTokenPosition(tkn); tok = token.T_MUL_EQUAL; fbreak;}; | ||||||
|  |             '**='                             => {lex.setTokenPosition(tkn); tok = token.T_POW_EQUAL; fbreak;}; | ||||||
|  |             '/='                              => {lex.setTokenPosition(tkn); tok = token.T_DIV_EQUAL; fbreak;}; | ||||||
|  |             '+='                              => {lex.setTokenPosition(tkn); tok = token.T_PLUS_EQUAL; fbreak;}; | ||||||
|  |             '-='                              => {lex.setTokenPosition(tkn); tok = token.T_MINUS_EQUAL; fbreak;}; | ||||||
|  |             '^='                              => {lex.setTokenPosition(tkn); tok = token.T_XOR_EQUAL; fbreak;}; | ||||||
|  |             '%='                              => {lex.setTokenPosition(tkn); tok = token.T_MOD_EQUAL; fbreak;}; | ||||||
|  |             '--'                              => {lex.setTokenPosition(tkn); tok = token.T_DEC; fbreak;}; | ||||||
|  |             '++'                              => {lex.setTokenPosition(tkn); tok = token.T_INC; fbreak;}; | ||||||
|  |             '=>'                              => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_ARROW; fbreak;}; | ||||||
|  |             '<=>'                             => {lex.setTokenPosition(tkn); tok = token.T_SPACESHIP; fbreak;}; | ||||||
|  |             '!=' | '<>'                       => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_EQUAL; fbreak;}; | ||||||
|  |             '!=='                             => {lex.setTokenPosition(tkn); tok = token.T_IS_NOT_IDENTICAL; fbreak;}; | ||||||
|  |             '=='                              => {lex.setTokenPosition(tkn); tok = token.T_IS_EQUAL; fbreak;}; | ||||||
|  |             '==='                             => {lex.setTokenPosition(tkn); tok = token.T_IS_IDENTICAL; fbreak;}; | ||||||
|  |             '<<='                             => {lex.setTokenPosition(tkn); tok = token.T_SL_EQUAL; fbreak;}; | ||||||
|  |             '>>='                             => {lex.setTokenPosition(tkn); tok = token.T_SR_EQUAL; fbreak;}; | ||||||
|  |             '>='                              => {lex.setTokenPosition(tkn); tok = token.T_IS_GREATER_OR_EQUAL; fbreak;}; | ||||||
|  |             '<='                              => {lex.setTokenPosition(tkn); tok = token.T_IS_SMALLER_OR_EQUAL; fbreak;}; | ||||||
|  |             '**'                              => {lex.setTokenPosition(tkn); tok = token.T_POW; fbreak;}; | ||||||
|  |             '<<'                              => {lex.setTokenPosition(tkn); tok = token.T_SL; fbreak;}; | ||||||
|  |             '>>'                              => {lex.setTokenPosition(tkn); tok = token.T_SR; fbreak;}; | ||||||
|  |             '??'                              => {lex.setTokenPosition(tkn); tok = token.T_COALESCE; fbreak;}; | ||||||
|  |             '??='                             => {lex.setTokenPosition(tkn); tok = token.T_COALESCE_EQUAL; fbreak;}; | ||||||
|  | 
 | ||||||
|  |             '(' whitespace* 'array'i whitespace* ')'                     => {lex.setTokenPosition(tkn); tok = token.T_ARRAY_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* ('bool'i|'boolean'i) whitespace* ')'         => {lex.setTokenPosition(tkn); tok = token.T_BOOL_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* ('real'i) whitespace* ')'                    => {lex.error(fmt.Sprintf("The (real) cast has been removed, use (float) instead")); fbreak;}; | ||||||
|  |             '(' whitespace* ('double'i|'float'i) whitespace* ')'         => {lex.setTokenPosition(tkn); tok = token.T_DOUBLE_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* ('int'i|'integer'i) whitespace* ')'          => {lex.setTokenPosition(tkn); tok = token.T_INT_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* 'object'i whitespace* ')'                    => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* ('string'i|'binary'i) whitespace* ')'        => {lex.setTokenPosition(tkn); tok = token.T_STRING_CAST; fbreak;}; | ||||||
|  |             '(' whitespace* 'unset'i whitespace* ')'                     => {lex.error(fmt.Sprintf("The (unset) cast is no longer supported")); fbreak;}; | ||||||
|  | 
 | ||||||
|  |             (('#' ^'[') | '//') any_line* when is_not_comment_end => { | ||||||
|  |                 lex.ungetStr("?>") | ||||||
|  |                 lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) | ||||||
|  |             }; | ||||||
|  |             '#' => { | ||||||
|  |                 lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) | ||||||
|  |             }; | ||||||
|  |             '/*' any_line* :>> '*/' { | ||||||
|  |                 isDocComment := false; | ||||||
|  |                 if lex.te - lex.ts > 4 && string(lex.data[lex.ts:lex.ts+3]) == "/**" { | ||||||
|  |                     isDocComment = true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if isDocComment { | ||||||
|  |                     lex.addFreeFloatingToken(tkn, token.T_DOC_COMMENT, lex.ts, lex.te) | ||||||
|  |                 } else { | ||||||
|  |                     lex.addFreeFloatingToken(tkn, token.T_COMMENT, lex.ts, lex.te) | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             operators => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.ID(int(lex.data[lex.ts])); | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             "{"          => { lex.setTokenPosition(tkn); tok = token.ID(int('{')); lex.call(ftargs, fentry(php)); goto _out; }; | ||||||
|  |             "}"          => { lex.setTokenPosition(tkn); tok = token.ID(int('}')); lex.ret(1); goto _out;}; | ||||||
|  |             "$" varname  => { lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak; }; | ||||||
|  |             varname      => { lex.setTokenPosition(tkn); tok = token.T_STRING;   fbreak; }; | ||||||
|  | 
 | ||||||
|  |             "->"         => { lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fnext property; fbreak; }; | ||||||
|  |             "?->"        => { lex.setTokenPosition(tkn); tok = token.T_NULLSAFE_OBJECT_OPERATOR; fnext property; fbreak; }; | ||||||
|  | 
 | ||||||
|  |             constant_string => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_CONSTANT_ENCAPSED_STRING; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             "b"i? "<<<" [ \t]* ( heredoc_label | ("'" heredoc_label "'") | ('"' heredoc_label '"') ) newline  => { | ||||||
|  |                 lex.heredocLabel = lex.data[lblStart:lblEnd] | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_START_HEREDOC; | ||||||
|  | 
 | ||||||
|  |                 if lex.isHeredocEnd(lex.p+1) { | ||||||
|  |                     fnext heredoc_end; | ||||||
|  |                 } else if lex.data[lblStart-1] == '\'' { | ||||||
|  |                     fnext nowdoc; | ||||||
|  |                 } else { | ||||||
|  |                     fnext heredoc; | ||||||
|  |                 } | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |             "`" => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext backqote; fbreak;}; | ||||||
|  |             '"' => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext template_string; fbreak;}; | ||||||
|  | 
 | ||||||
|  |             any_line => { | ||||||
|  |                 c := lex.data[lex.p] | ||||||
|  |                 lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c)); | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         property := |* | ||||||
|  |             whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; | ||||||
|  |             "->"             => {lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;}; | ||||||
|  |             "?->"            => {lex.setTokenPosition(tkn); tok = token.T_NULLSAFE_OBJECT_OPERATOR; fbreak;}; | ||||||
|  |             varname          => {lex.setTokenPosition(tkn); tok = token.T_STRING; fnext php; fbreak;}; | ||||||
|  |             any              => {lex.ungetCnt(1); fgoto php;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         nowdoc := |* | ||||||
|  |             any_line* when is_not_heredoc_end => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_ENCAPSED_AND_WHITESPACE; | ||||||
|  |                 fnext heredoc_end; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         heredoc := |* | ||||||
|  |             "{$" => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; | ||||||
|  |             "${" => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; | ||||||
|  |             "$"  => {lex.ungetCnt(1); fcall string_var;}; | ||||||
|  |             any_line* when is_not_heredoc_end_or_var => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_ENCAPSED_AND_WHITESPACE; | ||||||
|  | 
 | ||||||
|  |                 if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' { | ||||||
|  |                     fnext heredoc_end; | ||||||
|  |                 } | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         backqote := |* | ||||||
|  |             "{$"              => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; | ||||||
|  |             "${"              => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; | ||||||
|  |             "$" varname_first => {lex.ungetCnt(2); fcall string_var;}; | ||||||
|  |             '`'               => {lex.setTokenPosition(tkn); tok = token.ID(int('`')); fnext php; fbreak;}; | ||||||
|  |             any_line* when is_not_backqoute_end_or_var => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_ENCAPSED_AND_WHITESPACE; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         template_string := |* | ||||||
|  |             "{$"               => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_CURLY_OPEN; lex.call(ftargs, fentry(php)); goto _out;}; | ||||||
|  |             "${"               => {lex.setTokenPosition(tkn); tok = token.T_DOLLAR_OPEN_CURLY_BRACES; lex.call(ftargs, fentry(string_var_name)); goto _out;}; | ||||||
|  |             "$" varname_first  => {lex.ungetCnt(2); fcall string_var;}; | ||||||
|  |             '"'                => {lex.setTokenPosition(tkn); tok = token.ID(int('"')); fnext php; fbreak;}; | ||||||
|  |             any_line* when is_not_string_end_or_var => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_ENCAPSED_AND_WHITESPACE; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         heredoc_end := |* | ||||||
|  |             varname -- ";" => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_END_HEREDOC; | ||||||
|  |                 fnext php; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |             varname => { | ||||||
|  |                 lex.setTokenPosition(tkn); | ||||||
|  |                 tok = token.T_END_HEREDOC; | ||||||
|  |                 fnext php; | ||||||
|  |                 fbreak; | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         string_var := |* | ||||||
|  |             '$' varname         => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;}; | ||||||
|  |             '->' varname_first  => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_OBJECT_OPERATOR; fbreak;}; | ||||||
|  |             '?->' varname_first => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_NULLSAFE_OBJECT_OPERATOR; fbreak;}; | ||||||
|  |             varname             => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;}; | ||||||
|  |             '['                 => {lex.setTokenPosition(tkn); tok = token.ID(int('[')); lex.call(ftargs, fentry(string_var_index)); goto _out;}; | ||||||
|  |             any                 => {lex.ungetCnt(1); fret;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         string_var_index := |* | ||||||
|  |             lnum | hnum | bnum       => {lex.setTokenPosition(tkn); tok = token.T_NUM_STRING; fbreak;}; | ||||||
|  |             '$' varname              => {lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak;}; | ||||||
|  |             varname                  => {lex.setTokenPosition(tkn); tok = token.T_STRING; fbreak;}; | ||||||
|  |             whitespace_line | [\\'#] => {lex.setTokenPosition(tkn); tok = token.T_ENCAPSED_AND_WHITESPACE; lex.ret(2); goto _out;}; | ||||||
|  |             operators > (svi, 1)     => {lex.setTokenPosition(tkn); tok = token.ID(int(lex.data[lex.ts])); fbreak;}; | ||||||
|  |             ']'       > (svi, 2)     => {lex.setTokenPosition(tkn); tok = token.ID(int(']')); lex.ret(2); goto _out;}; | ||||||
|  |             any_line => { | ||||||
|  |                 c := lex.data[lex.p] | ||||||
|  |                 lex.error(fmt.Sprintf("WARNING: Unexpected character in input: '%c' (ASCII=%d)", c, c)); | ||||||
|  |             }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         string_var_name := |* | ||||||
|  |             varname ("[" | "}") => {lex.ungetCnt(1); lex.setTokenPosition(tkn); tok = token.T_STRING_VARNAME; fnext php; fbreak;}; | ||||||
|  |             any                 => {lex.ungetCnt(1); fnext php;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         halt_compiller_open_parenthesis := |* | ||||||
|  |             whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; | ||||||
|  |             "("              => {lex.setTokenPosition(tkn); tok = token.ID(int('(')); fnext halt_compiller_close_parenthesis; fbreak;}; | ||||||
|  |             any              => {lex.ungetCnt(1); fnext php;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         halt_compiller_close_parenthesis := |* | ||||||
|  |             whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; | ||||||
|  |             ")"              => {lex.setTokenPosition(tkn); tok = token.ID(int(')')); fnext halt_compiller_close_semicolon; fbreak;}; | ||||||
|  |             any              => {lex.ungetCnt(1); fnext php;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         halt_compiller_close_semicolon := |* | ||||||
|  |             whitespace_line* => {lex.addFreeFloatingToken(tkn, token.T_WHITESPACE, lex.ts, lex.te)}; | ||||||
|  |             ";"              => {lex.setTokenPosition(tkn); tok = token.ID(int(';')); fnext halt_compiller_end; fbreak;}; | ||||||
|  |             any              => {lex.ungetCnt(1); fnext php;}; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         halt_compiller_end := |* | ||||||
|  |             any_line* => { lex.addFreeFloatingToken(tkn, token.T_HALT_COMPILER, lex.ts, lex.te); }; | ||||||
|  |         *|; | ||||||
|  | 
 | ||||||
|  |         write exec; | ||||||
|  |     }%% | ||||||
|  | 
 | ||||||
|  |     tkn.Value = lex.data[lex.ts:lex.te] | ||||||
|  |     tkn.ID = token.ID(tok) | ||||||
|  | 
 | ||||||
|  |     return tkn | ||||||
|  | } | ||||||
							
								
								
									
										346
									
								
								internal/php8/scanner_php8_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								internal/php8/scanner_php8_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,346 @@ | |||||||
|  | package php8_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/tester" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestNullsafeMethodCallTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php $a?->foo();" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"$a", | ||||||
|  | 		"?->", | ||||||
|  | 		"foo", | ||||||
|  | 		"(", | ||||||
|  | 		")", | ||||||
|  | 		";", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNullsafePropertyFetchTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php $a?->prop;" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"$a", | ||||||
|  | 		"?->", | ||||||
|  | 		"prop", | ||||||
|  | 		";", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNullsafePropertyFetchInStringTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php \"$a?->prop\";" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"\"", | ||||||
|  | 		"$a", | ||||||
|  | 		"?->", | ||||||
|  | 		"prop", | ||||||
|  | 		"\"", | ||||||
|  | 		";", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNullsafeMethodCallTokensFreeFloating(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenFreeFloatingTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = `<?php | ||||||
|  | 	$a ?-> bar ( '' ) ;` | ||||||
|  | 
 | ||||||
|  | 	suite.Expected = [][]*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_OPEN_TAG, | ||||||
|  | 				Value: []byte("<?php"), | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte("\n\t"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			{ | ||||||
|  | 				ID:    token.T_WHITESPACE, | ||||||
|  | 				Value: []byte(" "), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMatchStringTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php match($a) {}" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"match", | ||||||
|  | 		"(", | ||||||
|  | 		"$a", | ||||||
|  | 		")", | ||||||
|  | 		"{", | ||||||
|  | 		"}", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMatchWithConditionStringTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php match($a) { 10 => 100 }" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"match", | ||||||
|  | 		"(", | ||||||
|  | 		"$a", | ||||||
|  | 		")", | ||||||
|  | 		"{", | ||||||
|  | 		"10", | ||||||
|  | 		"=>", | ||||||
|  | 		"100", | ||||||
|  | 		"}", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMatchWithDefaultStringTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php match($a) { default => 10 }" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"match", | ||||||
|  | 		"(", | ||||||
|  | 		"$a", | ||||||
|  | 		")", | ||||||
|  | 		"{", | ||||||
|  | 		"default", | ||||||
|  | 		"=>", | ||||||
|  | 		"10", | ||||||
|  | 		"}", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestAttributeTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStringTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = "<?php #[ FooAttribute]" | ||||||
|  | 	suite.Expected = []string{ | ||||||
|  | 		"#[", | ||||||
|  | 		"FooAttribute", | ||||||
|  | 		"]", | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCommentEnd2(t *testing.T) { | ||||||
|  | 	src := `<?php //` | ||||||
|  | 
 | ||||||
|  | 	expected := []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_OPEN_TAG, | ||||||
|  | 			Value: []byte("<?php"), | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_WHITESPACE, | ||||||
|  | 			Value: []byte(" "), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_COMMENT, | ||||||
|  | 			Value: []byte("//"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	lexer := php8.NewLexer([]byte(src), conf.Config{}) | ||||||
|  | 
 | ||||||
|  | 	tkn := lexer.Lex() | ||||||
|  | 
 | ||||||
|  | 	actual := tkn.FreeFloating | ||||||
|  | 	for _, v := range actual { | ||||||
|  | 		v.Position = nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert.DeepEqual(t, expected, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCommentEnd3(t *testing.T) { | ||||||
|  | 	src := `<?php #` | ||||||
|  | 
 | ||||||
|  | 	expected := []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_OPEN_TAG, | ||||||
|  | 			Value: []byte("<?php"), | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_WHITESPACE, | ||||||
|  | 			Value: []byte(" "), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_COMMENT, | ||||||
|  | 			Value: []byte("#"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	lexer := php8.NewLexer([]byte(src), conf.Config{}) | ||||||
|  | 
 | ||||||
|  | 	tkn := lexer.Lex() | ||||||
|  | 
 | ||||||
|  | 	actual := tkn.FreeFloating | ||||||
|  | 	for _, v := range actual { | ||||||
|  | 		v.Position = nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert.DeepEqual(t, expected, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestAttribute(t *testing.T) { | ||||||
|  | 	src := `<?php #[ FooAttribute]` | ||||||
|  | 
 | ||||||
|  | 	expected := []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_OPEN_TAG, | ||||||
|  | 			Value: []byte("<?php"), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    token.T_WHITESPACE, | ||||||
|  | 			Value: []byte(" "), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	lexer := php8.NewLexer([]byte(src), conf.Config{}) | ||||||
|  | 
 | ||||||
|  | 	tkn := lexer.Lex() | ||||||
|  | 
 | ||||||
|  | 	actual := tkn.FreeFloating | ||||||
|  | 	for _, v := range actual { | ||||||
|  | 		v.Position = nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert.DeepEqual(t, expected, actual) | ||||||
|  | 
 | ||||||
|  | 	assert.DeepEqual(t, token.T_ATTRIBUTE, tkn.ID) | ||||||
|  | 	assert.DeepEqual(t, "#[", string(tkn.Value)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNamespaceFullyQualifiedTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStructTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = `<?php use \Foo;` | ||||||
|  | 	suite.Expected = []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_USE, | ||||||
|  | 			Value: []byte("use"), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_NAME_FULLY_QUALIFIED, | ||||||
|  | 			Value: []byte(`\Foo`), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    ';', | ||||||
|  | 			Value: []byte(";"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNamespaceFullyQualifiedWithKeywordsTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStructTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = `<?php use \Foo\match\fn;` | ||||||
|  | 	suite.Expected = []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_USE, | ||||||
|  | 			Value: []byte("use"), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_NAME_FULLY_QUALIFIED, | ||||||
|  | 			Value: []byte(`\Foo\match\fn`), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    ';', | ||||||
|  | 			Value: []byte(";"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNamespaceQualifiedTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStructTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = `<?php namespace Boo\Foo;` | ||||||
|  | 	suite.Expected = []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_NAMESPACE, | ||||||
|  | 			Value: []byte("namespace"), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_NAME_QUALIFIED, | ||||||
|  | 			Value: []byte(`Boo\Foo`), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    ';', | ||||||
|  | 			Value: []byte(";"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNamespaceRelativeTokens(t *testing.T) { | ||||||
|  | 	suite := tester.NewLexerTokenStructTestSuite(t) | ||||||
|  | 	suite.UsePHP8() | ||||||
|  | 	suite.Code = `<?php namespace\match;` | ||||||
|  | 	suite.Expected = []*token.Token{ | ||||||
|  | 		{ | ||||||
|  | 			ID:    php8.T_NAME_RELATIVE, | ||||||
|  | 			Value: []byte(`namespace\match`), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			ID:    ';', | ||||||
|  | 			Value: []byte(";"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	suite.Run() | ||||||
|  | } | ||||||
							
								
								
									
										1897
									
								
								internal/php8/scanner_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1897
									
								
								internal/php8/scanner_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										356
									
								
								internal/php8/test.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								internal/php8/test.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,356 @@ | |||||||
|  | <? | ||||||
|  | foo($a, ...$b); | ||||||
|  | $foo($a, ...$b); | ||||||
|  | $foo->bar($a, ...$b); | ||||||
|  | foo::bar($a, ...$b); | ||||||
|  | $foo::bar($a, ...$b); | ||||||
|  | new foo($a, ...$b); | ||||||
|  | /** anonymous class */ | ||||||
|  | new class ($a, ...$b) {}; | ||||||
|  | new class {}; | ||||||
|  | new $foo; | ||||||
|  | new $foo[1]; | ||||||
|  | new $foo{$bar}; | ||||||
|  | new $foo->bar; | ||||||
|  | new $foo::$bar; | ||||||
|  | new static::$bar; | ||||||
|  | 
 | ||||||
|  | function foo(?bar $bar=null, baz &...$baz) {} | ||||||
|  | class foo {public function foo(?bar $bar=null, baz &...$baz) {}} | ||||||
|  | function(?bar $bar=null, baz &...$baz) {}; | ||||||
|  | static function(?bar $bar=null, baz &...$baz) {}; | ||||||
|  | 
 | ||||||
|  | 1234567890123456789; | ||||||
|  | 12345678901234567890; | ||||||
|  | 0.; | ||||||
|  | 0b0111111111111111111111111111111111111111111111111111111111111111; | ||||||
|  | 0b1111111111111111111111111111111111111111111111111111111111111111; | ||||||
|  | 0x007111111111111111; | ||||||
|  | 0x8111111111111111; | ||||||
|  | __CLASS__; | ||||||
|  | __DIR__; | ||||||
|  | __FILE__; | ||||||
|  | __FUNCTION__; | ||||||
|  | __LINE__; | ||||||
|  | __NAMESPACE__; | ||||||
|  | __METHOD__; | ||||||
|  | __TRAIT__; | ||||||
|  | 
 | ||||||
|  | "test $var"; | ||||||
|  | "test $var[1]"; | ||||||
|  | "test $var[-1]"; | ||||||
|  | "test $var[1234567890123456789012345678901234567890]"; | ||||||
|  | "test $var[-1234567890123456789012345678901234567890]"; | ||||||
|  | "test $var[bar]"; | ||||||
|  | "test $var[$bar]"; | ||||||
|  | "$foo $bar"; | ||||||
|  | "test $foo->bar()"; | ||||||
|  | "test ${foo}"; | ||||||
|  | "test ${foo[0]}"; | ||||||
|  | "test ${$foo}"; | ||||||
|  | "test {$foo->bar()}"; | ||||||
|  | 
 | ||||||
|  | if ($a) : | ||||||
|  | endif; | ||||||
|  | if ($a) : | ||||||
|  | elseif ($b): | ||||||
|  | endif; | ||||||
|  | if ($a) : | ||||||
|  | else: | ||||||
|  | endif; | ||||||
|  | if ($a) : | ||||||
|  | elseif ($b): | ||||||
|  | elseif ($c): | ||||||
|  | else: | ||||||
|  | endif; | ||||||
|  | 
 | ||||||
|  | while (1) { break; } | ||||||
|  | while (1) { break 2; } | ||||||
|  | while (1) : break(3); endwhile; | ||||||
|  | class foo{ public const FOO = 1, BAR = 2; } | ||||||
|  | class foo{ const FOO = 1, BAR = 2; } | ||||||
|  | class foo{ function bar() {} } | ||||||
|  | class foo{ public static function &bar() {} } | ||||||
|  | class foo{ public static function &bar(): void {} } | ||||||
|  | abstract class foo{ } | ||||||
|  | final class foo extends bar { } | ||||||
|  | final class foo implements bar { } | ||||||
|  | final class foo implements bar, baz { } | ||||||
|  | new class() extends foo implements bar, baz { }; | ||||||
|  | 
 | ||||||
|  | const FOO = 1, BAR = 2; | ||||||
|  | while (1) { continue; } | ||||||
|  | while (1) { continue 2; } | ||||||
|  | while (1) { continue(3); } | ||||||
|  | declare(ticks=1); | ||||||
|  | declare(ticks=1) {} | ||||||
|  | declare(ticks=1): enddeclare; | ||||||
|  | do {} while(1); | ||||||
|  | echo $a, 1; | ||||||
|  | echo($a); | ||||||
|  | for($i = 0; $i < 10; $i++, $i++) {} | ||||||
|  | for(; $i < 10; $i++, $i++) : endfor; | ||||||
|  | foreach ($a as $v) {} | ||||||
|  | foreach ($a as $v) : endforeach; | ||||||
|  | foreach ($a as $k => $v) {} | ||||||
|  | foreach ($a as $k => &$v) {} | ||||||
|  | foreach ($a as $k => list($v)) {} | ||||||
|  | foreach ($a as $k => [$v]) {} | ||||||
|  | function foo() {} | ||||||
|  | function foo() {return;} | ||||||
|  | function &foo() {return 1;} | ||||||
|  | function &foo(): void {} | ||||||
|  | global $a, $b; | ||||||
|  | a: | ||||||
|  | goto a; | ||||||
|  | if ($a) {} | ||||||
|  | if ($a) {} elseif ($b) {} | ||||||
|  | if ($a) {} else {} | ||||||
|  | if ($a) {} elseif ($b) {} elseif ($c) {} else {} | ||||||
|  | if ($a) {} elseif ($b) {} else if ($c) {} else {} | ||||||
|  | ?> <div></div> <?
 | ||||||
|  | interface Foo {} | ||||||
|  | interface Foo extends Bar {} | ||||||
|  | interface Foo extends Bar, Baz {} | ||||||
|  | namespace Foo; | ||||||
|  | namespace Foo {} | ||||||
|  | namespace {} | ||||||
|  | class foo {var $a;} | ||||||
|  | class foo {public static $a, $b = 1;} | ||||||
|  | static $a, $b = 1; | ||||||
|  | 
 | ||||||
|  | switch (1) : | ||||||
|  |     case 1: | ||||||
|  |     default: | ||||||
|  |     case 2: | ||||||
|  | endswitch; | ||||||
|  | 
 | ||||||
|  | switch (1) :; | ||||||
|  |     case 1; | ||||||
|  |     case 2; | ||||||
|  | endswitch; | ||||||
|  | 
 | ||||||
|  | switch (1) { | ||||||
|  |     case 1: break; | ||||||
|  |     case 2: break; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | switch (1) {; | ||||||
|  |     case 1; break; | ||||||
|  |     case 2; break; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | throw $e; | ||||||
|  | 
 | ||||||
|  | trait Foo {} | ||||||
|  | class Foo { use Bar; } | ||||||
|  | class Foo { use Bar, Baz {} } | ||||||
|  | class Foo { use Bar, Baz { one as include; } } | ||||||
|  | class Foo { use Bar, Baz { one as public; } } | ||||||
|  | class Foo { use Bar, Baz { one as public two; } } | ||||||
|  | class Foo { use Bar, Baz { Bar::one insteadof Baz, Quux; Baz::one as two; } } | ||||||
|  | 
 | ||||||
|  | try {} | ||||||
|  | try {} catch (Exception $e) {} | ||||||
|  | try {} catch (Exception|RuntimeException $e) {} | ||||||
|  | try {} catch (Exception $e) {} catch (RuntimeException $e) {} | ||||||
|  | try {} catch (Exception $e) {} finally {} | ||||||
|  | 
 | ||||||
|  | unset($a, $b,); | ||||||
|  | 
 | ||||||
|  | use Foo; | ||||||
|  | use \Foo; | ||||||
|  | use \Foo as Bar; | ||||||
|  | use Foo, Bar; | ||||||
|  | use Foo, Bar as Baz; | ||||||
|  | use function Foo, \Bar; | ||||||
|  | use function Foo as foo, \Bar as bar; | ||||||
|  | use const Foo, \Bar; | ||||||
|  | use const Foo as foo, \Bar as bar; | ||||||
|  | 
 | ||||||
|  | use \Foo\{Bar, Baz}; | ||||||
|  | use Foo\{Bar, Baz as quux}; | ||||||
|  | use function Foo\{Bar, Baz}; | ||||||
|  | use const \Foo\{Bar, Baz}; | ||||||
|  | use Foo\{const Bar, function Baz}; | ||||||
|  | 
 | ||||||
|  | $a[1]; | ||||||
|  | $a[1][2]; | ||||||
|  | array(); | ||||||
|  | array(1); | ||||||
|  | array(1=>1, &$b,); | ||||||
|  | ~$a; | ||||||
|  | !$a; | ||||||
|  | 
 | ||||||
|  | Foo::Bar; | ||||||
|  | $foo::Bar; | ||||||
|  | clone($a); | ||||||
|  | clone $a; | ||||||
|  | function(){}; | ||||||
|  | function($a, $b) use ($c, &$d) {}; | ||||||
|  | function(): void {}; | ||||||
|  | foo; | ||||||
|  | namespace\foo; | ||||||
|  | \foo; | ||||||
|  | 
 | ||||||
|  | empty($a); | ||||||
|  | @$a; | ||||||
|  | eval($a); | ||||||
|  | exit; | ||||||
|  | exit($a); | ||||||
|  | die; | ||||||
|  | die($a); | ||||||
|  | foo(); | ||||||
|  | namespace\foo(); | ||||||
|  | \foo(); | ||||||
|  | $foo(); | ||||||
|  | 
 | ||||||
|  | $a--; | ||||||
|  | $a++; | ||||||
|  | --$a; | ||||||
|  | ++$a; | ||||||
|  | 
 | ||||||
|  | include $a; | ||||||
|  | include_once $a; | ||||||
|  | require $a; | ||||||
|  | require_once $a; | ||||||
|  | 
 | ||||||
|  | $a instanceof Foo; | ||||||
|  | $a instanceof namespace\Foo; | ||||||
|  | $a instanceof \Foo; | ||||||
|  | 
 | ||||||
|  | isset($a, $b); | ||||||
|  | list($a) = $b; | ||||||
|  | list($a[]) = $b; | ||||||
|  | list(list($a)) = $b; | ||||||
|  | 
 | ||||||
|  | $a->foo(); | ||||||
|  | new Foo(); | ||||||
|  | new namespace\Foo(); | ||||||
|  | new \Foo(); | ||||||
|  | new class ($a, ...$b) {}; | ||||||
|  | print($a); | ||||||
|  | $a->foo; | ||||||
|  | `cmd $a`; | ||||||
|  | `cmd`; | ||||||
|  | ``; | ||||||
|  | []; | ||||||
|  | [1]; | ||||||
|  | [1=>1, &$b,]; | ||||||
|  | 
 | ||||||
|  | [$a] = $b; | ||||||
|  | [$a[]] = $b; | ||||||
|  | [list($a)] = $b; | ||||||
|  | Foo::bar(); | ||||||
|  | namespace\Foo::bar(); | ||||||
|  | \Foo::bar(); | ||||||
|  | Foo::$bar; | ||||||
|  | $foo::$bar; | ||||||
|  | namespace\Foo::$bar; | ||||||
|  | \Foo::$bar; | ||||||
|  | $a ? $b : $c; | ||||||
|  | $a ? : $c; | ||||||
|  | $a ? $b ? $c : $d : $e; | ||||||
|  | $a ? $b : $c ? $d : $e; | ||||||
|  | -$a; | ||||||
|  | +$a; | ||||||
|  | $$a; | ||||||
|  | yield; | ||||||
|  | yield $a; | ||||||
|  | yield $a => $b; | ||||||
|  | yield from $a; | ||||||
|  | 
 | ||||||
|  | (array)$a; | ||||||
|  | (boolean)$a; | ||||||
|  | (bool)$a; | ||||||
|  | (double)$a; | ||||||
|  | (float)$a; | ||||||
|  | (integer)$a; | ||||||
|  | (int)$a; | ||||||
|  | (object)$a; | ||||||
|  | (string)$a; | ||||||
|  | (unset)$a; | ||||||
|  | 
 | ||||||
|  | $a & $b; | ||||||
|  | $a | $b; | ||||||
|  | $a ^ $b; | ||||||
|  | $a && $b; | ||||||
|  | $a || $b; | ||||||
|  | $a ?? $b; | ||||||
|  | $a . $b; | ||||||
|  | $a / $b; | ||||||
|  | $a == $b; | ||||||
|  | $a >= $b; | ||||||
|  | $a > $b; | ||||||
|  | $a === $b; | ||||||
|  | $a and $b; | ||||||
|  | $a or $b; | ||||||
|  | $a xor $b; | ||||||
|  | $a - $b; | ||||||
|  | $a % $b; | ||||||
|  | $a * $b; | ||||||
|  | $a != $b; | ||||||
|  | $a !== $b; | ||||||
|  | $a + $b; | ||||||
|  | $a ** $b; | ||||||
|  | $a << $b; | ||||||
|  | $a >> $b; | ||||||
|  | $a <= $b; | ||||||
|  | $a < $b; | ||||||
|  | $a <=> $b; | ||||||
|  | 
 | ||||||
|  | $a =& $b; | ||||||
|  | $a = $b; | ||||||
|  | $a &= $b; | ||||||
|  | $a |= $b; | ||||||
|  | $a ^= $b; | ||||||
|  | $a .= $b; | ||||||
|  | $a /= $b; | ||||||
|  | $a -= $b; | ||||||
|  | $a %= $b; | ||||||
|  | $a *= $b; | ||||||
|  | $a += $b; | ||||||
|  | $a **= $b; | ||||||
|  | $a <<= $b; | ||||||
|  | $a >>= $b; | ||||||
|  | 
 | ||||||
|  | class foo {public function class() {} } | ||||||
|  | \foo\bar(); | ||||||
|  | 
 | ||||||
|  | function foo(&$a, ...$b) { | ||||||
|  | 
 | ||||||
|  |     function bar() {} | ||||||
|  |     class Baz {} | ||||||
|  |     trait Quux{} | ||||||
|  |     interface Quuux {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function foo(&$a = 1, ...$b = 1, $c = 1) {} | ||||||
|  | function foo(array $a, callable $b) {} | ||||||
|  | abstract final class foo { abstract protected static function bar(); final private function baz() {} } | ||||||
|  | 
 | ||||||
|  | (new Foo)->bar; | ||||||
|  | (new Foo)(); | ||||||
|  | [$foo][0](); | ||||||
|  | foo[1](); | ||||||
|  | "foo"(); | ||||||
|  | [1]{$foo}(); | ||||||
|  | ${foo()}; | ||||||
|  | 
 | ||||||
|  | Foo::$bar(); | ||||||
|  | Foo::{$bar[0]}(); | ||||||
|  | 
 | ||||||
|  | $foo->$bar; | ||||||
|  | $foo->{$bar[0]}; | ||||||
|  | 
 | ||||||
|  | [1=>&$a, 2=>list($b)]; | ||||||
|  | 
 | ||||||
|  | __halt_compiler(); | ||||||
|  | 
 | ||||||
|  | (new Foo)?->bar; | ||||||
|  | (new Foo)?->bar(); | ||||||
|  | $foo?->$bar; | ||||||
|  | $foo?->{$bar[0]}; | ||||||
|  | "{$foo?->{$bar[0]}}"; | ||||||
|  | 
 | ||||||
|  | parsing process must be terminated | ||||||
							
								
								
									
										62
									
								
								internal/tester/lexer_token_freefloating.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								internal/tester/lexer_token_freefloating.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/scanner" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Lexer interface { | ||||||
|  | 	Lex() *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type LexerTokenFreeFloatingTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected [][]*token.Token | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewLexerTokenFreeFloatingTestSuite(t *testing.T) *LexerTokenFreeFloatingTestSuite { | ||||||
|  | 	return &LexerTokenFreeFloatingTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenFreeFloatingTestSuite) UsePHP8() { | ||||||
|  | 	l.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenFreeFloatingTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &l.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var lexer Lexer | ||||||
|  | 
 | ||||||
|  | 	if l.Version.Less(&version.Version{Major: 8, Minor: 0}) { | ||||||
|  | 		lexer = scanner.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} else { | ||||||
|  | 		lexer = php8.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, expected := range l.Expected { | ||||||
|  | 		tkn := lexer.Lex() | ||||||
|  | 		actual := tkn.FreeFloating | ||||||
|  | 		for _, v := range actual { | ||||||
|  | 			v.Position = nil | ||||||
|  | 		} | ||||||
|  | 		assert.DeepEqual(l.t, expected, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								internal/tester/lexer_token_string.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								internal/tester/lexer_token_string.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/scanner" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type LexerTokenStringTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected []string | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewLexerTokenStringTestSuite(t *testing.T) *LexerTokenStringTestSuite { | ||||||
|  | 	return &LexerTokenStringTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenStringTestSuite) UsePHP8() { | ||||||
|  | 	l.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenStringTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &l.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var lexer Lexer | ||||||
|  | 
 | ||||||
|  | 	if l.Version.Less(&version.Version{Major: 8, Minor: 0}) { | ||||||
|  | 		lexer = scanner.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} else { | ||||||
|  | 		lexer = php8.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, expected := range l.Expected { | ||||||
|  | 		tkn := lexer.Lex() | ||||||
|  | 		actual := string(tkn.Value) | ||||||
|  | 		assert.DeepEqual(l.t, expected, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								internal/tester/lexer_token_struct.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/tester/lexer_token_struct.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/scanner" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type LexerTokenStructTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected []*token.Token | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewLexerTokenStructTestSuite(t *testing.T) *LexerTokenStructTestSuite { | ||||||
|  | 	return &LexerTokenStructTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenStructTestSuite) UsePHP8() { | ||||||
|  | 	l.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *LexerTokenStructTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &l.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var lexer Lexer | ||||||
|  | 
 | ||||||
|  | 	if l.Version.Less(&version.Version{Major: 8, Minor: 0}) { | ||||||
|  | 		lexer = scanner.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} else { | ||||||
|  | 		lexer = php8.NewLexer([]byte(l.Code), config) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, expected := range l.Expected { | ||||||
|  | 		actual := lexer.Lex() | ||||||
|  | 		actual.Position = nil | ||||||
|  | 		actual.FreeFloating = nil | ||||||
|  | 		assert.DeepEqual(l.t, expected, actual) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								internal/tester/parse_print.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/tester/parse_print.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/parser" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/visitor/printer" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ParserPrintTestSuite struct { | ||||||
|  | 	t       *testing.T | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewParserPrintTestSuite(t *testing.T) *ParserPrintTestSuite { | ||||||
|  | 	return &ParserPrintTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserPrintTestSuite) UsePHP8() *ParserPrintTestSuite { | ||||||
|  | 	p.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserPrintTestSuite) Run(code string) { | ||||||
|  | 	actual := p.print(p.parse(code)) | ||||||
|  | 	assert.DeepEqual(p.t, code, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserPrintTestSuite) parse(src string) ast.Vertex { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &p.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	root, err := parser.Parse([]byte(src), config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		p.t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return root | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserPrintTestSuite) print(n ast.Vertex) string { | ||||||
|  | 	o := bytes.NewBufferString("") | ||||||
|  | 
 | ||||||
|  | 	pr := printer.NewPrinter(o) | ||||||
|  | 	n.Accept(pr) | ||||||
|  | 
 | ||||||
|  | 	return o.String() | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								internal/tester/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								internal/tester/parser.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/parser" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ParserTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected ast.Vertex | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewParserTestSuite(t *testing.T) *ParserTestSuite { | ||||||
|  | 	return &ParserTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserTestSuite) UsePHP8() { | ||||||
|  | 	p.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &p.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	actual, err := parser.Parse([]byte(p.Code), config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		p.t.Fatalf("Error parse: %v", err) | ||||||
|  | 	} | ||||||
|  | 	assert.DeepEqual(p.t, p.Expected, actual) | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								internal/tester/parser_dump.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								internal/tester/parser_dump.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/parser" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/visitor/dumper" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ParserDumpTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected string | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | 
 | ||||||
|  | 	actualDump *bytes.Buffer | ||||||
|  | 	dumper     *dumper.Dumper | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewParserDumpTestSuite(t *testing.T) *ParserDumpTestSuite { | ||||||
|  | 	actualDump := bytes.NewBuffer(nil) | ||||||
|  | 	return &ParserDumpTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 		actualDump: actualDump, | ||||||
|  | 		dumper:     dumper.NewDumper(actualDump), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserDumpTestSuite) WithTokens() { | ||||||
|  | 	p.dumper = p.dumper.WithTokens() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserDumpTestSuite) WithPositions() { | ||||||
|  | 	p.dumper = p.dumper.WithPositions() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserDumpTestSuite) UsePHP8() { | ||||||
|  | 	p.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserDumpTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &p.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	actual, err := parser.Parse([]byte(p.Code), config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		p.t.Fatalf("Error parse: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.dumper.Dump(actual) | ||||||
|  | 
 | ||||||
|  | 	assert.DeepEqual(p.t, p.Expected+"\n", p.actualDump.String()) | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								internal/tester/parser_error.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								internal/tester/parser_error.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | package tester | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/errors" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/parser" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ParserErrorTestSuite struct { | ||||||
|  | 	t *testing.T | ||||||
|  | 
 | ||||||
|  | 	Code     string | ||||||
|  | 	Expected []*errors.Error | ||||||
|  | 
 | ||||||
|  | 	Version version.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewParserErrorTestSuite(t *testing.T) *ParserErrorTestSuite { | ||||||
|  | 	return &ParserErrorTestSuite{ | ||||||
|  | 		t: t, | ||||||
|  | 		Version: version.Version{ | ||||||
|  | 			Major: 7, | ||||||
|  | 			Minor: 4, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserErrorTestSuite) UsePHP8() { | ||||||
|  | 	p.Version = version.Version{Major: 8, Minor: 0} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ParserErrorTestSuite) Run() { | ||||||
|  | 	config := conf.Config{ | ||||||
|  | 		Version: &p.Version, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var errs []*errors.Error | ||||||
|  | 
 | ||||||
|  | 	config.ErrorHandlerFunc = func(e *errors.Error) { | ||||||
|  | 		errs = append(errs, e) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, err := parser.Parse([]byte(p.Code), config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		p.t.Fatalf("Error parse: %v", err) | ||||||
|  | 	} | ||||||
|  | 	assert.DeepEqual(p.t, p.Expected, errs) | ||||||
|  | } | ||||||
| @ -13,6 +13,10 @@ type Visitor interface { | |||||||
| 	Parameter(n *Parameter) | 	Parameter(n *Parameter) | ||||||
| 	Identifier(n *Identifier) | 	Identifier(n *Identifier) | ||||||
| 	Argument(n *Argument) | 	Argument(n *Argument) | ||||||
|  | 	MatchArm(n *MatchArm) | ||||||
|  | 	Union(n *Union) | ||||||
|  | 	Attribute(n *Attribute) | ||||||
|  | 	AttributeGroup(n *AttributeGroup) | ||||||
| 
 | 
 | ||||||
| 	StmtBreak(n *StmtBreak) | 	StmtBreak(n *StmtBreak) | ||||||
| 	StmtCase(n *StmtCase) | 	StmtCase(n *StmtCase) | ||||||
| @ -85,6 +89,8 @@ type Visitor interface { | |||||||
| 	ExprIsset(n *ExprIsset) | 	ExprIsset(n *ExprIsset) | ||||||
| 	ExprList(n *ExprList) | 	ExprList(n *ExprList) | ||||||
| 	ExprMethodCall(n *ExprMethodCall) | 	ExprMethodCall(n *ExprMethodCall) | ||||||
|  | 	ExprNullsafeMethodCall(n *ExprNullsafeMethodCall) | ||||||
|  | 	ExprMatch(n *ExprMatch) | ||||||
| 	ExprNew(n *ExprNew) | 	ExprNew(n *ExprNew) | ||||||
| 	ExprPostDec(n *ExprPostDec) | 	ExprPostDec(n *ExprPostDec) | ||||||
| 	ExprPostInc(n *ExprPostInc) | 	ExprPostInc(n *ExprPostInc) | ||||||
| @ -92,12 +98,14 @@ type Visitor interface { | |||||||
| 	ExprPreInc(n *ExprPreInc) | 	ExprPreInc(n *ExprPreInc) | ||||||
| 	ExprPrint(n *ExprPrint) | 	ExprPrint(n *ExprPrint) | ||||||
| 	ExprPropertyFetch(n *ExprPropertyFetch) | 	ExprPropertyFetch(n *ExprPropertyFetch) | ||||||
|  | 	ExprNullsafePropertyFetch(n *ExprNullsafePropertyFetch) | ||||||
| 	ExprRequire(n *ExprRequire) | 	ExprRequire(n *ExprRequire) | ||||||
| 	ExprRequireOnce(n *ExprRequireOnce) | 	ExprRequireOnce(n *ExprRequireOnce) | ||||||
| 	ExprShellExec(n *ExprShellExec) | 	ExprShellExec(n *ExprShellExec) | ||||||
| 	ExprStaticCall(n *ExprStaticCall) | 	ExprStaticCall(n *ExprStaticCall) | ||||||
| 	ExprStaticPropertyFetch(n *ExprStaticPropertyFetch) | 	ExprStaticPropertyFetch(n *ExprStaticPropertyFetch) | ||||||
| 	ExprTernary(n *ExprTernary) | 	ExprTernary(n *ExprTernary) | ||||||
|  | 	ExprThrow(n *ExprThrow) | ||||||
| 	ExprUnaryMinus(n *ExprUnaryMinus) | 	ExprUnaryMinus(n *ExprUnaryMinus) | ||||||
| 	ExprUnaryPlus(n *ExprUnaryPlus) | 	ExprUnaryPlus(n *ExprUnaryPlus) | ||||||
| 	ExprVariable(n *ExprVariable) | 	ExprVariable(n *ExprVariable) | ||||||
|  | |||||||
							
								
								
									
										161
									
								
								pkg/ast/node.go
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								pkg/ast/node.go
									
									
									
									
									
								
							| @ -20,7 +20,7 @@ func (n *Root) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Nullable node | // Nullable node is ?Expr | ||||||
| type Nullable struct { | type Nullable struct { | ||||||
| 	Position    *position.Position | 	Position    *position.Position | ||||||
| 	QuestionTkn *token.Token | 	QuestionTkn *token.Token | ||||||
| @ -35,9 +35,26 @@ func (n *Nullable) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Union node is Expr|Expr1|... | ||||||
|  | type Union struct { | ||||||
|  | 	Position      *position.Position | ||||||
|  | 	Types         []Vertex | ||||||
|  | 	SeparatorTkns []*token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *Union) Accept(v Visitor) { | ||||||
|  | 	v.Union(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *Union) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Parameter node | // Parameter node | ||||||
| type Parameter struct { | type Parameter struct { | ||||||
| 	Position     *position.Position | 	Position     *position.Position | ||||||
|  | 	AttrGroups   []Vertex | ||||||
|  | 	Visibility   Vertex | ||||||
| 	Type         Vertex | 	Type         Vertex | ||||||
| 	AmpersandTkn *token.Token | 	AmpersandTkn *token.Token | ||||||
| 	VariadicTkn  *token.Token | 	VariadicTkn  *token.Token | ||||||
| @ -72,6 +89,8 @@ func (n *Identifier) GetPosition() *position.Position { | |||||||
| // Argument node | // Argument node | ||||||
| type Argument struct { | type Argument struct { | ||||||
| 	Position     *position.Position | 	Position     *position.Position | ||||||
|  | 	Name         Vertex | ||||||
|  | 	ColonTkn     *token.Token | ||||||
| 	VariadicTkn  *token.Token | 	VariadicTkn  *token.Token | ||||||
| 	AmpersandTkn *token.Token | 	AmpersandTkn *token.Token | ||||||
| 	Expr         Vertex | 	Expr         Vertex | ||||||
| @ -85,6 +104,41 @@ func (n *Argument) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Attribute node | ||||||
|  | type Attribute struct { | ||||||
|  | 	Position            *position.Position | ||||||
|  | 	Name                Vertex | ||||||
|  | 	OpenParenthesisTkn  *token.Token | ||||||
|  | 	Args                []Vertex | ||||||
|  | 	SeparatorTkns       []*token.Token | ||||||
|  | 	CloseParenthesisTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *Attribute) Accept(v Visitor) { | ||||||
|  | 	v.Attribute(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *Attribute) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AttributeGroup node | ||||||
|  | type AttributeGroup struct { | ||||||
|  | 	Position          *position.Position | ||||||
|  | 	OpenAttributeTkn  *token.Token | ||||||
|  | 	Attrs             []Vertex | ||||||
|  | 	SeparatorTkns     []*token.Token | ||||||
|  | 	CloseAttributeTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *AttributeGroup) Accept(v Visitor) { | ||||||
|  | 	v.AttributeGroup(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *AttributeGroup) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ScalarDnumber node | // ScalarDnumber node | ||||||
| type ScalarDnumber struct { | type ScalarDnumber struct { | ||||||
| 	Position  *position.Position | 	Position  *position.Position | ||||||
| @ -286,6 +340,7 @@ func (n *StmtCatch) GetPosition() *position.Position { | |||||||
| // StmtClass node | // StmtClass node | ||||||
| type StmtClass struct { | type StmtClass struct { | ||||||
| 	Position                *position.Position | 	Position                *position.Position | ||||||
|  | 	AttrGroups              []Vertex | ||||||
| 	Modifiers               []Vertex | 	Modifiers               []Vertex | ||||||
| 	ClassTkn                *token.Token | 	ClassTkn                *token.Token | ||||||
| 	Name                    Vertex | 	Name                    Vertex | ||||||
| @ -314,6 +369,7 @@ func (n *StmtClass) GetPosition() *position.Position { | |||||||
| // StmtClassConstList node | // StmtClassConstList node | ||||||
| type StmtClassConstList struct { | type StmtClassConstList struct { | ||||||
| 	Position      *position.Position | 	Position      *position.Position | ||||||
|  | 	AttrGroups    []Vertex | ||||||
| 	Modifiers     []Vertex | 	Modifiers     []Vertex | ||||||
| 	ConstTkn      *token.Token | 	ConstTkn      *token.Token | ||||||
| 	Consts        []Vertex | 	Consts        []Vertex | ||||||
| @ -332,6 +388,7 @@ func (n *StmtClassConstList) GetPosition() *position.Position { | |||||||
| // StmtClassMethod node | // StmtClassMethod node | ||||||
| type StmtClassMethod struct { | type StmtClassMethod struct { | ||||||
| 	Position            *position.Position | 	Position            *position.Position | ||||||
|  | 	AttrGroups          []Vertex | ||||||
| 	Modifiers           []Vertex | 	Modifiers           []Vertex | ||||||
| 	FunctionTkn         *token.Token | 	FunctionTkn         *token.Token | ||||||
| 	AmpersandTkn        *token.Token | 	AmpersandTkn        *token.Token | ||||||
| @ -601,6 +658,7 @@ func (n *StmtForeach) GetPosition() *position.Position { | |||||||
| // StmtFunction node | // StmtFunction node | ||||||
| type StmtFunction struct { | type StmtFunction struct { | ||||||
| 	Position             *position.Position | 	Position             *position.Position | ||||||
|  | 	AttrGroups           []Vertex | ||||||
| 	FunctionTkn          *token.Token | 	FunctionTkn          *token.Token | ||||||
| 	AmpersandTkn         *token.Token | 	AmpersandTkn         *token.Token | ||||||
| 	Name                 Vertex | 	Name                 Vertex | ||||||
| @ -714,6 +772,7 @@ func (n *StmtInlineHtml) GetPosition() *position.Position { | |||||||
| // StmtInterface node | // StmtInterface node | ||||||
| type StmtInterface struct { | type StmtInterface struct { | ||||||
| 	Position             *position.Position | 	Position             *position.Position | ||||||
|  | 	AttrGroups           []Vertex | ||||||
| 	InterfaceTkn         *token.Token | 	InterfaceTkn         *token.Token | ||||||
| 	Name                 Vertex | 	Name                 Vertex | ||||||
| 	ExtendsTkn           *token.Token | 	ExtendsTkn           *token.Token | ||||||
| @ -799,6 +858,7 @@ func (n *StmtProperty) GetPosition() *position.Position { | |||||||
| // StmtPropertyList node | // StmtPropertyList node | ||||||
| type StmtPropertyList struct { | type StmtPropertyList struct { | ||||||
| 	Position      *position.Position | 	Position      *position.Position | ||||||
|  | 	AttrGroups    []Vertex | ||||||
| 	Modifiers     []Vertex | 	Modifiers     []Vertex | ||||||
| 	Type          Vertex | 	Type          Vertex | ||||||
| 	Props         []Vertex | 	Props         []Vertex | ||||||
| @ -922,6 +982,7 @@ func (n *StmtThrow) GetPosition() *position.Position { | |||||||
| // StmtTrait node | // StmtTrait node | ||||||
| type StmtTrait struct { | type StmtTrait struct { | ||||||
| 	Position             *position.Position | 	Position             *position.Position | ||||||
|  | 	AttrGroups           []Vertex | ||||||
| 	TraitTkn             *token.Token | 	TraitTkn             *token.Token | ||||||
| 	Name                 Vertex | 	Name                 Vertex | ||||||
| 	OpenCurlyBracketTkn  *token.Token | 	OpenCurlyBracketTkn  *token.Token | ||||||
| @ -1171,6 +1232,7 @@ func (n *ExprArrayItem) GetPosition() *position.Position { | |||||||
| // ExprArrowFunction node | // ExprArrowFunction node | ||||||
| type ExprArrowFunction struct { | type ExprArrowFunction struct { | ||||||
| 	Position            *position.Position | 	Position            *position.Position | ||||||
|  | 	AttrGroups          []Vertex | ||||||
| 	StaticTkn           *token.Token | 	StaticTkn           *token.Token | ||||||
| 	FnTkn               *token.Token | 	FnTkn               *token.Token | ||||||
| 	AmpersandTkn        *token.Token | 	AmpersandTkn        *token.Token | ||||||
| @ -1271,6 +1333,7 @@ func (n *ExprClone) GetPosition() *position.Position { | |||||||
| // ExprClosure node | // ExprClosure node | ||||||
| type ExprClosure struct { | type ExprClosure struct { | ||||||
| 	Position               *position.Position | 	Position               *position.Position | ||||||
|  | 	AttrGroups             []Vertex | ||||||
| 	StaticTkn              *token.Token | 	StaticTkn              *token.Token | ||||||
| 	FunctionTkn            *token.Token | 	FunctionTkn            *token.Token | ||||||
| 	AmpersandTkn           *token.Token | 	AmpersandTkn           *token.Token | ||||||
| @ -1515,6 +1578,28 @@ func (n *ExprMethodCall) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ExprNullsafeMethodCall node is $a?->methodName() | ||||||
|  | type ExprNullsafeMethodCall struct { | ||||||
|  | 	Position             *position.Position | ||||||
|  | 	Var                  Vertex | ||||||
|  | 	ObjectOperatorTkn    *token.Token | ||||||
|  | 	OpenCurlyBracketTkn  *token.Token | ||||||
|  | 	Method               Vertex | ||||||
|  | 	CloseCurlyBracketTkn *token.Token | ||||||
|  | 	OpenParenthesisTkn   *token.Token | ||||||
|  | 	Args                 []Vertex | ||||||
|  | 	SeparatorTkns        []*token.Token | ||||||
|  | 	CloseParenthesisTkn  *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprNullsafeMethodCall) Accept(v Visitor) { | ||||||
|  | 	v.ExprNullsafeMethodCall(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprNullsafeMethodCall) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ExprNew node | // ExprNew node | ||||||
| type ExprNew struct { | type ExprNew struct { | ||||||
| 	Position            *position.Position | 	Position            *position.Position | ||||||
| @ -1627,6 +1712,24 @@ func (n *ExprPropertyFetch) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ExprNullsafePropertyFetch node | ||||||
|  | type ExprNullsafePropertyFetch struct { | ||||||
|  | 	Position             *position.Position | ||||||
|  | 	Var                  Vertex | ||||||
|  | 	ObjectOperatorTkn    *token.Token | ||||||
|  | 	OpenCurlyBracketTkn  *token.Token | ||||||
|  | 	Prop                 Vertex | ||||||
|  | 	CloseCurlyBracketTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprNullsafePropertyFetch) Accept(v Visitor) { | ||||||
|  | 	v.ExprNullsafePropertyFetch(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprNullsafePropertyFetch) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ExprRequire node | // ExprRequire node | ||||||
| type ExprRequire struct { | type ExprRequire struct { | ||||||
| 	Position   *position.Position | 	Position   *position.Position | ||||||
| @ -2586,6 +2689,62 @@ func (n *ExprBinarySpaceship) GetPosition() *position.Position { | |||||||
| 	return n.Position | 	return n.Position | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ExprMatch node is match(expr) { list<MatchArm> } | ||||||
|  | type ExprMatch struct { | ||||||
|  | 	Position             *position.Position | ||||||
|  | 	MatchTkn             *token.Token | ||||||
|  | 	OpenParenthesisTkn   *token.Token | ||||||
|  | 	Expr                 Vertex | ||||||
|  | 	CloseParenthesisTkn  *token.Token | ||||||
|  | 	OpenCurlyBracketTkn  *token.Token | ||||||
|  | 	Arms                 []Vertex | ||||||
|  | 	SeparatorTkns        []*token.Token | ||||||
|  | 	CloseCurlyBracketTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprMatch) Accept(v Visitor) { | ||||||
|  | 	v.ExprMatch(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprMatch) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ExprThrow node is 'throw Expr' | ||||||
|  | type ExprThrow struct { | ||||||
|  | 	Position     *position.Position | ||||||
|  | 	ThrowTkn     *token.Token | ||||||
|  | 	Expr         Vertex | ||||||
|  | 	SemiColonTkn *token.Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprThrow) Accept(v Visitor) { | ||||||
|  | 	v.ExprThrow(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *ExprThrow) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MatchArm node is [expr, expr1, ...]|default => return_expr | ||||||
|  | type MatchArm struct { | ||||||
|  | 	Position        *position.Position | ||||||
|  | 	DefaultTkn      *token.Token | ||||||
|  | 	DefaultCommaTkn *token.Token | ||||||
|  | 	Exprs           []Vertex | ||||||
|  | 	SeparatorTkns   []*token.Token | ||||||
|  | 	DoubleArrowTkn  *token.Token | ||||||
|  | 	ReturnExpr      Vertex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *MatchArm) Accept(v Visitor) { | ||||||
|  | 	v.MatchArm(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (n *MatchArm) GetPosition() *position.Position { | ||||||
|  | 	return n.Position | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Name struct { | type Name struct { | ||||||
| 	Position      *position.Position | 	Position      *position.Position | ||||||
| 	Parts         []Vertex | 	Parts         []Vertex | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/z7zmey/php-parser/internal/php5" | 	"github.com/z7zmey/php-parser/internal/php5" | ||||||
| 	"github.com/z7zmey/php-parser/internal/php7" | 	"github.com/z7zmey/php-parser/internal/php7" | ||||||
|  | 	"github.com/z7zmey/php-parser/internal/php8" | ||||||
| 	"github.com/z7zmey/php-parser/internal/scanner" | 	"github.com/z7zmey/php-parser/internal/scanner" | ||||||
| 	"github.com/z7zmey/php-parser/pkg/ast" | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
| 	"github.com/z7zmey/php-parser/pkg/conf" | 	"github.com/z7zmey/php-parser/pkg/conf" | ||||||
| @ -20,6 +21,9 @@ var ( | |||||||
| 
 | 
 | ||||||
| 	php7RangeStart = &version.Version{Major: 7} | 	php7RangeStart = &version.Version{Major: 7} | ||||||
| 	php7RangeEnd   = &version.Version{Major: 7, Minor: 4} | 	php7RangeEnd   = &version.Version{Major: 7, Minor: 4} | ||||||
|  | 
 | ||||||
|  | 	php8RangeStart = &version.Version{Major: 8} | ||||||
|  | 	php8RangeEnd   = &version.Version{Major: 8, Minor: 1} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Parser interface | // Parser interface | ||||||
| @ -49,5 +53,12 @@ func Parse(src []byte, config conf.Config) (ast.Vertex, error) { | |||||||
| 		return parser.GetRootNode(), nil | 		return parser.GetRootNode(), nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if config.Version.InRange(php8RangeStart, php8RangeEnd) { | ||||||
|  | 		lexer := php8.NewLexer(src, config) | ||||||
|  | 		parser = php8.NewParser(lexer, config) | ||||||
|  | 		parser.Parse() | ||||||
|  | 		return parser.GetRootNode(), nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return nil, ErrVersionOutOfRange | 	return nil, ErrVersionOutOfRange | ||||||
| } | } | ||||||
|  | |||||||
| @ -144,6 +144,12 @@ const ( | |||||||
| 	T_IS_NOT_EQUAL | 	T_IS_NOT_EQUAL | ||||||
| 	T_IS_SMALLER_OR_EQUAL | 	T_IS_SMALLER_OR_EQUAL | ||||||
| 	T_IS_GREATER_OR_EQUAL | 	T_IS_GREATER_OR_EQUAL | ||||||
|  | 	T_NULLSAFE_OBJECT_OPERATOR | ||||||
|  | 	T_MATCH | ||||||
|  | 	T_ATTRIBUTE | ||||||
|  | 	T_NAME_RELATIVE | ||||||
|  | 	T_NAME_QUALIFIED | ||||||
|  | 	T_NAME_FULLY_QUALIFIED | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Token struct { | type Token struct { | ||||||
|  | |||||||
| @ -146,11 +146,17 @@ func _() { | |||||||
| 	_ = x[T_IS_NOT_EQUAL-57481] | 	_ = x[T_IS_NOT_EQUAL-57481] | ||||||
| 	_ = x[T_IS_SMALLER_OR_EQUAL-57482] | 	_ = x[T_IS_SMALLER_OR_EQUAL-57482] | ||||||
| 	_ = x[T_IS_GREATER_OR_EQUAL-57483] | 	_ = x[T_IS_GREATER_OR_EQUAL-57483] | ||||||
|  | 	_ = x[T_NULLSAFE_OBJECT_OPERATOR-57484] | ||||||
|  | 	_ = x[T_MATCH-57485] | ||||||
|  | 	_ = x[T_ATTRIBUTE-57486] | ||||||
|  | 	_ = x[T_NAME_RELATIVE-57487] | ||||||
|  | 	_ = x[T_NAME_QUALIFIED-57488] | ||||||
|  | 	_ = x[T_NAME_FULLY_QUALIFIED-57489] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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" | 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_EQUALT_NULLSAFE_OBJECT_OPERATORT_MATCHT_ATTRIBUTET_NAME_RELATIVET_NAME_QUALIFIEDT_NAME_FULLY_QUALIFIED" | ||||||
| 
 | 
 | ||||||
| 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} | 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, 1436, 1443, 1454, 1469, 1485, 1507} | ||||||
| 
 | 
 | ||||||
| func (i ID) String() string { | func (i ID) String() string { | ||||||
| 	i -= 57346 | 	i -= 57346 | ||||||
|  | |||||||
| @ -22,6 +22,9 @@ var ( | |||||||
| 
 | 
 | ||||||
| 	php7RangeStart = &Version{Major: 7} | 	php7RangeStart = &Version{Major: 7} | ||||||
| 	php7RangeEnd   = &Version{Major: 7, Minor: 4} | 	php7RangeEnd   = &Version{Major: 7, Minor: 4} | ||||||
|  | 
 | ||||||
|  | 	php8RangeStart = &Version{Major: 8} | ||||||
|  | 	php8RangeEnd   = &Version{Major: 8, Minor: 1} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func New(v string) (*Version, error) { | func New(v string) (*Version, error) { | ||||||
| @ -48,7 +51,9 @@ func New(v string) (*Version, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (v *Version) Validate() error { | func (v *Version) Validate() error { | ||||||
| 	if !v.InRange(php5RangeStart, php5RangeEnd) && !v.InRange(php7RangeStart, php7RangeEnd) { | 	if !v.InRange(php5RangeStart, php5RangeEnd) && | ||||||
|  | 		!v.InRange(php7RangeStart, php7RangeEnd) && | ||||||
|  | 		!v.InRange(php8RangeStart, php8RangeEnd) { | ||||||
| 		return ErrUnsupportedVer | 		return ErrUnsupportedVer | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -75,7 +80,7 @@ func (v *Version) GreaterOrEqual(o *Version) bool { | |||||||
| 	return v.Compare(o) >= 0 | 	return v.Compare(o) >= 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GreaterOrEqual tests if one version is greater than another one or equal | // InRange tests if version is in range in another one | ||||||
| func (v *Version) InRange(s, e *Version) bool { | func (v *Version) InRange(s, e *Version) bool { | ||||||
| 	return v.Compare(s) >= 0 && v.Compare(e) <= 0 | 	return v.Compare(s) >= 0 && v.Compare(e) <= 0 | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| package version_test | package version_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"gotest.tools/assert" |  | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"gotest.tools/assert" | ||||||
|  | 
 | ||||||
| 	"github.com/z7zmey/php-parser/pkg/version" | 	"github.com/z7zmey/php-parser/pkg/version" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -46,3 +47,19 @@ func TestInRange(t *testing.T) { | |||||||
| 	assert.NilError(t, err) | 	assert.NilError(t, err) | ||||||
| 	assert.Assert(t, ver.InRange(s, e)) | 	assert.Assert(t, ver.InRange(s, e)) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestInRangePHP8(t *testing.T) { | ||||||
|  | 	s, err := version.New("8.0") | ||||||
|  | 	assert.NilError(t, err) | ||||||
|  | 
 | ||||||
|  | 	e, err := version.New("8.1") | ||||||
|  | 	assert.NilError(t, err) | ||||||
|  | 
 | ||||||
|  | 	ver, err := version.New("8.0") | ||||||
|  | 	assert.NilError(t, err) | ||||||
|  | 	assert.Assert(t, ver.InRange(s, e)) | ||||||
|  | 
 | ||||||
|  | 	ver, err = version.New("8.1") | ||||||
|  | 	assert.NilError(t, err) | ||||||
|  | 	assert.Assert(t, ver.InRange(s, e)) | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,12 +1,13 @@ | |||||||
| package dumper | package dumper | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/z7zmey/php-parser/pkg/position" |  | ||||||
| 	"github.com/z7zmey/php-parser/pkg/token" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/position" | ||||||
|  | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
|  | 
 | ||||||
| 	"github.com/z7zmey/php-parser/pkg/ast" | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -192,6 +193,8 @@ func (v *Dumper) Parameter(n *ast.Parameter) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
|  | 	v.dumpVertex("Visibility", n.Visibility) | ||||||
| 	v.dumpVertex("Type", n.Type) | 	v.dumpVertex("Type", n.Type) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
| 	v.dumpToken("VariadicTkn", n.VariadicTkn) | 	v.dumpToken("VariadicTkn", n.VariadicTkn) | ||||||
| @ -221,6 +224,8 @@ func (v *Dumper) Argument(n *ast.Argument) { | |||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
|  | 	v.dumpVertex("Name", n.Name) | ||||||
|  | 	v.dumpToken("ColonTkn", n.ColonTkn) | ||||||
| 	v.dumpToken("VariadicTkn", n.VariadicTkn) | 	v.dumpToken("VariadicTkn", n.VariadicTkn) | ||||||
| 	v.dumpVertex("Expr", n.Expr) | 	v.dumpVertex("Expr", n.Expr) | ||||||
| 
 | 
 | ||||||
| @ -228,6 +233,63 @@ func (v *Dumper) Argument(n *ast.Argument) { | |||||||
| 	v.print(v.indent, "},\n") | 	v.print(v.indent, "},\n") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Dumper) MatchArm(n *ast.MatchArm) { | ||||||
|  | 	v.print(0, "&ast.MatchArm{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpToken("DefaultTkn", n.DefaultTkn) | ||||||
|  | 	v.dumpToken("DefaultCommaTkn", n.DefaultCommaTkn) | ||||||
|  | 	v.dumpVertexList("Exprs", n.Exprs) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 	v.dumpToken("DoubleArrowTkn", n.DoubleArrowTkn) | ||||||
|  | 	v.dumpVertex("ReturnExpr", n.ReturnExpr) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Dumper) Union(n *ast.Union) { | ||||||
|  | 	v.print(0, "&ast.Union{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("Types", n.Types) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Dumper) Attribute(n *ast.Attribute) { | ||||||
|  | 	v.print(0, "&ast.Attribute{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertex("Name", n.Name) | ||||||
|  | 	v.dumpToken("OpenParenthesisTkn", n.OpenParenthesisTkn) | ||||||
|  | 	v.dumpVertexList("Args", n.Args) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 	v.dumpToken("CloseParenthesisTkn", n.CloseParenthesisTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Dumper) AttributeGroup(n *ast.AttributeGroup) { | ||||||
|  | 	v.print(0, "&ast.AttributeGroup{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpToken("OpenAttributeTkn", n.OpenAttributeTkn) | ||||||
|  | 	v.dumpVertexList("Attrs", n.Attrs) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 	v.dumpToken("CloseAttributeTkn", n.CloseAttributeTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Dumper) StmtBreak(n *ast.StmtBreak) { | func (v *Dumper) StmtBreak(n *ast.StmtBreak) { | ||||||
| 	v.print(0, "&ast.StmtBreak{\n") | 	v.print(0, "&ast.StmtBreak{\n") | ||||||
| 	v.indent++ | 	v.indent++ | ||||||
| @ -279,6 +341,7 @@ func (v *Dumper) StmtClass(n *ast.StmtClass) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpVertexList("Modifiers", n.Modifiers) | 	v.dumpVertexList("Modifiers", n.Modifiers) | ||||||
| 	v.dumpToken("ClassTkn", n.ClassTkn) | 	v.dumpToken("ClassTkn", n.ClassTkn) | ||||||
| 	v.dumpVertex("Name", n.Name) | 	v.dumpVertex("Name", n.Name) | ||||||
| @ -304,6 +367,7 @@ func (v *Dumper) StmtClassConstList(n *ast.StmtClassConstList) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpVertexList("Modifiers", n.Modifiers) | 	v.dumpVertexList("Modifiers", n.Modifiers) | ||||||
| 	v.dumpToken("ConstTkn", n.ConstTkn) | 	v.dumpToken("ConstTkn", n.ConstTkn) | ||||||
| 	v.dumpVertexList("Consts", n.Consts) | 	v.dumpVertexList("Consts", n.Consts) | ||||||
| @ -319,6 +383,7 @@ func (v *Dumper) StmtClassMethod(n *ast.StmtClassMethod) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpVertexList("Modifiers", n.Modifiers) | 	v.dumpVertexList("Modifiers", n.Modifiers) | ||||||
| 	v.dumpToken("FunctionTkn", n.FunctionTkn) | 	v.dumpToken("FunctionTkn", n.FunctionTkn) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
| @ -547,6 +612,7 @@ func (v *Dumper) StmtFunction(n *ast.StmtFunction) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpToken("FunctionTkn", n.FunctionTkn) | 	v.dumpToken("FunctionTkn", n.FunctionTkn) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
| 	v.dumpVertex("Name", n.Name) | 	v.dumpVertex("Name", n.Name) | ||||||
| @ -642,6 +708,7 @@ func (v *Dumper) StmtInterface(n *ast.StmtInterface) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpToken("InterfaceTkn", n.InterfaceTkn) | 	v.dumpToken("InterfaceTkn", n.InterfaceTkn) | ||||||
| 	v.dumpVertex("Name", n.Name) | 	v.dumpVertex("Name", n.Name) | ||||||
| 	v.dumpToken("ExtendsTkn", n.ExtendsTkn) | 	v.dumpToken("ExtendsTkn", n.ExtendsTkn) | ||||||
| @ -712,6 +779,7 @@ func (v *Dumper) StmtPropertyList(n *ast.StmtPropertyList) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpVertexList("Modifiers", n.Modifiers) | 	v.dumpVertexList("Modifiers", n.Modifiers) | ||||||
| 	v.dumpVertex("Type", n.Type) | 	v.dumpVertex("Type", n.Type) | ||||||
| 	v.dumpVertexList("Props", n.Props) | 	v.dumpVertexList("Props", n.Props) | ||||||
| @ -814,6 +882,7 @@ func (v *Dumper) StmtTrait(n *ast.StmtTrait) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpToken("TraitTkn", n.TraitTkn) | 	v.dumpToken("TraitTkn", n.TraitTkn) | ||||||
| 	v.dumpVertex("Name", n.Name) | 	v.dumpVertex("Name", n.Name) | ||||||
| 	v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) | 	v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) | ||||||
| @ -1024,6 +1093,7 @@ func (v *Dumper) ExprArrowFunction(n *ast.ExprArrowFunction) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpToken("StaticTkn", n.StaticTkn) | 	v.dumpToken("StaticTkn", n.StaticTkn) | ||||||
| 	v.dumpToken("FnTkn", n.FnTkn) | 	v.dumpToken("FnTkn", n.FnTkn) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
| @ -1107,6 +1177,7 @@ func (v *Dumper) ExprClosure(n *ast.ExprClosure) { | |||||||
| 	v.indent++ | 	v.indent++ | ||||||
| 
 | 
 | ||||||
| 	v.dumpPosition(n.Position) | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertexList("AttrGroups", n.AttrGroups) | ||||||
| 	v.dumpToken("StaticTkn", n.StaticTkn) | 	v.dumpToken("StaticTkn", n.StaticTkn) | ||||||
| 	v.dumpToken("FunctionTkn", n.FunctionTkn) | 	v.dumpToken("FunctionTkn", n.FunctionTkn) | ||||||
| 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | 	v.dumpToken("AmpersandTkn", n.AmpersandTkn) | ||||||
| @ -1307,6 +1378,25 @@ func (v *Dumper) ExprMethodCall(n *ast.ExprMethodCall) { | |||||||
| 	v.print(v.indent, "},\n") | 	v.print(v.indent, "},\n") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Dumper) ExprNullsafeMethodCall(n *ast.ExprNullsafeMethodCall) { | ||||||
|  | 	v.print(0, "&ast.ExprNullsafeMethodCall{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertex("Var", n.Var) | ||||||
|  | 	v.dumpToken("ObjectOperatorTkn", n.ObjectOperatorTkn) | ||||||
|  | 	v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) | ||||||
|  | 	v.dumpVertex("Method", n.Method) | ||||||
|  | 	v.dumpToken("CloseCurlyBracketTkn", n.CloseCurlyBracketTkn) | ||||||
|  | 	v.dumpToken("OpenParenthesisTkn", n.OpenParenthesisTkn) | ||||||
|  | 	v.dumpVertexList("Args", n.Args) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 	v.dumpToken("CloseParenthesisTkn", n.CloseParenthesisTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Dumper) ExprNew(n *ast.ExprNew) { | func (v *Dumper) ExprNew(n *ast.ExprNew) { | ||||||
| 	v.print(0, "&ast.ExprNew{\n") | 	v.print(0, "&ast.ExprNew{\n") | ||||||
| 	v.indent++ | 	v.indent++ | ||||||
| @ -1398,6 +1488,21 @@ func (v *Dumper) ExprPropertyFetch(n *ast.ExprPropertyFetch) { | |||||||
| 	v.print(v.indent, "},\n") | 	v.print(v.indent, "},\n") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Dumper) ExprNullsafePropertyFetch(n *ast.ExprNullsafePropertyFetch) { | ||||||
|  | 	v.print(0, "&ast.ExprNullsafePropertyFetch{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpVertex("Var", n.Var) | ||||||
|  | 	v.dumpToken("ObjectOperatorTkn", n.ObjectOperatorTkn) | ||||||
|  | 	v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) | ||||||
|  | 	v.dumpVertex("Prop", n.Prop) | ||||||
|  | 	v.dumpToken("CloseCurlyBracketTkn", n.CloseCurlyBracketTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Dumper) ExprRequire(n *ast.ExprRequire) { | func (v *Dumper) ExprRequire(n *ast.ExprRequire) { | ||||||
| 	v.print(0, "&ast.ExprRequire{\n") | 	v.print(0, "&ast.ExprRequire{\n") | ||||||
| 	v.indent++ | 	v.indent++ | ||||||
| @ -2177,6 +2282,37 @@ func (v *Dumper) ExprCastUnset(n *ast.ExprCastUnset) { | |||||||
| 	v.print(v.indent, "},\n") | 	v.print(v.indent, "},\n") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Dumper) ExprMatch(n *ast.ExprMatch) { | ||||||
|  | 	v.print(0, "&ast.ExprMatch{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpToken("MatchTkn", n.MatchTkn) | ||||||
|  | 	v.dumpToken("OpenParenthesisTkn", n.OpenParenthesisTkn) | ||||||
|  | 	v.dumpVertex("Expr", n.Expr) | ||||||
|  | 	v.dumpToken("CloseParenthesisTkn", n.CloseParenthesisTkn) | ||||||
|  | 	v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) | ||||||
|  | 	v.dumpVertexList("Arms", n.Arms) | ||||||
|  | 	v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) | ||||||
|  | 	v.dumpToken("CloseCurlyBracketTkn", n.CloseCurlyBracketTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Dumper) ExprThrow(n *ast.ExprThrow) { | ||||||
|  | 	v.print(0, "&ast.ExprThrow{\n") | ||||||
|  | 	v.indent++ | ||||||
|  | 
 | ||||||
|  | 	v.dumpPosition(n.Position) | ||||||
|  | 	v.dumpToken("ThrowTkn", n.ThrowTkn) | ||||||
|  | 	v.dumpVertex("Expr", n.Expr) | ||||||
|  | 	v.dumpToken("SemiColonTkn", n.SemiColonTkn) | ||||||
|  | 
 | ||||||
|  | 	v.indent-- | ||||||
|  | 	v.print(v.indent, "},\n") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Dumper) ScalarDnumber(n *ast.ScalarDnumber) { | func (v *Dumper) ScalarDnumber(n *ast.ScalarDnumber) { | ||||||
| 	v.print(0, "&ast.ScalarDnumber{\n") | 	v.print(0, "&ast.ScalarDnumber{\n") | ||||||
| 	v.indent++ | 	v.indent++ | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ package formatter | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 
 | ||||||
| 	"github.com/z7zmey/php-parser/pkg/ast" | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
| 	"github.com/z7zmey/php-parser/pkg/token" | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
| ) | ) | ||||||
| @ -153,6 +154,18 @@ func (f *formatter) Nullable(n *ast.Nullable) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) Parameter(n *ast.Parameter) { | func (f *formatter) Parameter(n *ast.Parameter) { | ||||||
|  | 	if n.AttrGroups != nil { | ||||||
|  | 		for _, group := range n.AttrGroups { | ||||||
|  | 			group.Accept(f) | ||||||
|  | 		} | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if n.Visibility != nil { | ||||||
|  | 		n.Visibility.Accept(f) | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if n.Type != nil { | 	if n.Type != nil { | ||||||
| 		n.Type.Accept(f) | 		n.Type.Accept(f) | ||||||
| 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -185,6 +198,14 @@ func (f *formatter) Identifier(n *ast.Identifier) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) Argument(n *ast.Argument) { | func (f *formatter) Argument(n *ast.Argument) { | ||||||
|  | 	if n.Name != nil { | ||||||
|  | 		n.Name.Accept(f) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if n.ColonTkn != nil { | ||||||
|  | 		n.ColonTkn = f.newToken(':', []byte(":")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if n.VariadicTkn != nil { | 	if n.VariadicTkn != nil { | ||||||
| 		n.VariadicTkn = f.newToken(token.T_ELLIPSIS, []byte("...")) | 		n.VariadicTkn = f.newToken(token.T_ELLIPSIS, []byte("...")) | ||||||
| 	} | 	} | ||||||
| @ -196,6 +217,45 @@ func (f *formatter) Argument(n *ast.Argument) { | |||||||
| 	n.Expr.Accept(f) | 	n.Expr.Accept(f) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f *formatter) MatchArm(n *ast.MatchArm) { | ||||||
|  | 	if n.DefaultTkn != nil { | ||||||
|  | 		n.DefaultTkn = f.newToken(token.T_DEFAULT, []byte("default")) | ||||||
|  | 	} | ||||||
|  | 	if n.DefaultCommaTkn != nil { | ||||||
|  | 		n.DefaultCommaTkn = f.newToken(',', []byte(",")) | ||||||
|  | 	} | ||||||
|  | 	n.SeparatorTkns = nil | ||||||
|  | 	if len(n.Exprs) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Exprs, ',') | ||||||
|  | 	} | ||||||
|  | 	n.DoubleArrowTkn = f.newToken(token.T_DOUBLE_ARROW, []byte("=>")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *formatter) Union(n *ast.Union) { | ||||||
|  | 	if len(n.Types) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Types, '|') | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *formatter) Attribute(n *ast.Attribute) { | ||||||
|  | 	n.Name.Accept(f) | ||||||
|  | 	n.OpenParenthesisTkn = f.newToken('(', []byte("(")) | ||||||
|  | 	n.SeparatorTkns = nil | ||||||
|  | 	if len(n.Args) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Args, ',') | ||||||
|  | 	} | ||||||
|  | 	n.CloseParenthesisTkn = f.newToken(')', []byte(")")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *formatter) AttributeGroup(n *ast.AttributeGroup) { | ||||||
|  | 	n.OpenAttributeTkn = f.newToken(token.T_ATTRIBUTE, []byte("#[")) | ||||||
|  | 	n.SeparatorTkns = nil | ||||||
|  | 	if len(n.Attrs) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Attrs, ',') | ||||||
|  | 	} | ||||||
|  | 	n.CloseAttributeTkn = f.newToken(']', []byte("]")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (f *formatter) StmtBreak(n *ast.StmtBreak) { | func (f *formatter) StmtBreak(n *ast.StmtBreak) { | ||||||
| 	n.BreakTkn = f.newToken(token.T_BREAK, []byte("break")) | 	n.BreakTkn = f.newToken(token.T_BREAK, []byte("break")) | ||||||
| 
 | 
 | ||||||
| @ -307,6 +367,10 @@ func (f *formatter) StmtClass(n *ast.StmtClass) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) StmtClassConstList(n *ast.StmtClassConstList) { | func (f *formatter) StmtClassConstList(n *ast.StmtClassConstList) { | ||||||
|  | 	for _, m := range n.AttrGroups { | ||||||
|  | 		m.Accept(f) | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte("\n")) | ||||||
|  | 	} | ||||||
| 	for _, m := range n.Modifiers { | 	for _, m := range n.Modifiers { | ||||||
| 		m.Accept(f) | 		m.Accept(f) | ||||||
| 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -736,6 +800,10 @@ func (f *formatter) StmtProperty(n *ast.StmtProperty) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) StmtPropertyList(n *ast.StmtPropertyList) { | func (f *formatter) StmtPropertyList(n *ast.StmtPropertyList) { | ||||||
|  | 	for _, m := range n.AttrGroups { | ||||||
|  | 		m.Accept(f) | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte("\n")) | ||||||
|  | 	} | ||||||
| 	for _, m := range n.Modifiers { | 	for _, m := range n.Modifiers { | ||||||
| 		m.Accept(f) | 		m.Accept(f) | ||||||
| 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -1061,6 +1129,13 @@ func (f *formatter) ExprArrayItem(n *ast.ExprArrayItem) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) ExprArrowFunction(n *ast.ExprArrowFunction) { | func (f *formatter) ExprArrowFunction(n *ast.ExprArrowFunction) { | ||||||
|  | 	if n.AttrGroups != nil { | ||||||
|  | 		for _, group := range n.AttrGroups { | ||||||
|  | 			group.Accept(f) | ||||||
|  | 		} | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if n.StaticTkn != nil { | 	if n.StaticTkn != nil { | ||||||
| 		n.StaticTkn = f.newToken(token.T_STATIC, []byte("static")) | 		n.StaticTkn = f.newToken(token.T_STATIC, []byte("static")) | ||||||
| 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -1123,6 +1198,13 @@ func (f *formatter) ExprClone(n *ast.ExprClone) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (f *formatter) ExprClosure(n *ast.ExprClosure) { | func (f *formatter) ExprClosure(n *ast.ExprClosure) { | ||||||
|  | 	if n.AttrGroups != nil { | ||||||
|  | 		for _, group := range n.AttrGroups { | ||||||
|  | 			group.Accept(f) | ||||||
|  | 		} | ||||||
|  | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if n.StaticTkn != nil { | 	if n.StaticTkn != nil { | ||||||
| 		n.StaticTkn = f.newToken(token.T_STATIC, []byte("static")) | 		n.StaticTkn = f.newToken(token.T_STATIC, []byte("static")) | ||||||
| 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 		f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -1287,6 +1369,30 @@ func (f *formatter) ExprMethodCall(n *ast.ExprMethodCall) { | |||||||
| 	n.CloseParenthesisTkn = f.newToken(')', []byte(")")) | 	n.CloseParenthesisTkn = f.newToken(')', []byte(")")) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f *formatter) ExprNullsafeMethodCall(n *ast.ExprNullsafeMethodCall) { | ||||||
|  | 	n.Var.Accept(f) | ||||||
|  | 	n.ObjectOperatorTkn = f.newToken(token.T_NULLSAFE_OBJECT_OPERATOR, []byte("?->")) | ||||||
|  | 
 | ||||||
|  | 	n.OpenCurlyBracketTkn = nil | ||||||
|  | 	n.CloseCurlyBracketTkn = nil | ||||||
|  | 	switch n.Method.(type) { | ||||||
|  | 	case *ast.Identifier: | ||||||
|  | 	case *ast.ExprVariable: | ||||||
|  | 	default: | ||||||
|  | 		n.OpenCurlyBracketTkn = f.newToken('{', []byte("{")) | ||||||
|  | 		n.CloseCurlyBracketTkn = f.newToken('}', []byte("}")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	n.Method.Accept(f) | ||||||
|  | 
 | ||||||
|  | 	n.OpenParenthesisTkn = f.newToken('(', []byte("(")) | ||||||
|  | 	n.SeparatorTkns = nil | ||||||
|  | 	if len(n.Args) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Args, ',') | ||||||
|  | 	} | ||||||
|  | 	n.CloseParenthesisTkn = f.newToken(')', []byte(")")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (f *formatter) ExprNew(n *ast.ExprNew) { | func (f *formatter) ExprNew(n *ast.ExprNew) { | ||||||
| 	n.NewTkn = f.newToken(token.T_NEW, []byte("new")) | 	n.NewTkn = f.newToken(token.T_NEW, []byte("new")) | ||||||
| 	f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 	f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -1347,6 +1453,23 @@ func (f *formatter) ExprPropertyFetch(n *ast.ExprPropertyFetch) { | |||||||
| 	n.Prop.Accept(f) | 	n.Prop.Accept(f) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f *formatter) ExprNullsafePropertyFetch(n *ast.ExprNullsafePropertyFetch) { | ||||||
|  | 	n.Var.Accept(f) | ||||||
|  | 	n.ObjectOperatorTkn = f.newToken(token.T_NULLSAFE_OBJECT_OPERATOR, []byte("?->")) | ||||||
|  | 
 | ||||||
|  | 	n.OpenCurlyBracketTkn = nil | ||||||
|  | 	n.CloseCurlyBracketTkn = nil | ||||||
|  | 	switch n.Prop.(type) { | ||||||
|  | 	case *ast.Identifier: | ||||||
|  | 	case *ast.ExprVariable: | ||||||
|  | 	default: | ||||||
|  | 		n.OpenCurlyBracketTkn = f.newToken('{', []byte("{")) | ||||||
|  | 		n.CloseCurlyBracketTkn = f.newToken('}', []byte("}")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	n.Prop.Accept(f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (f *formatter) ExprRequire(n *ast.ExprRequire) { | func (f *formatter) ExprRequire(n *ast.ExprRequire) { | ||||||
| 	n.RequireTkn = f.newToken(token.T_REQUIRE, []byte("require")) | 	n.RequireTkn = f.newToken(token.T_REQUIRE, []byte("require")) | ||||||
| 	f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | 	f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
| @ -1916,6 +2039,27 @@ func (f *formatter) ExprCastUnset(n *ast.ExprCastUnset) { | |||||||
| 	n.Expr.Accept(f) | 	n.Expr.Accept(f) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f *formatter) ExprMatch(n *ast.ExprMatch) { | ||||||
|  | 	n.MatchTkn = f.newToken(token.T_MATCH, []byte("match")) | ||||||
|  | 	n.OpenParenthesisTkn = f.newToken('(', []byte("(")) | ||||||
|  | 	n.CloseParenthesisTkn = f.newToken(')', []byte(")")) | ||||||
|  | 	n.OpenCurlyBracketTkn = f.newToken('}', []byte("}")) | ||||||
|  | 	n.SeparatorTkns = nil | ||||||
|  | 	if len(n.Arms) > 0 { | ||||||
|  | 		n.SeparatorTkns = f.formatList(n.Arms, ',') | ||||||
|  | 	} | ||||||
|  | 	n.CloseCurlyBracketTkn = f.newToken('{', []byte("{")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f *formatter) ExprThrow(n *ast.ExprThrow) { | ||||||
|  | 	n.ThrowTkn = f.newToken(token.T_THROW, []byte("throw")) | ||||||
|  | 	f.addFreeFloating(token.T_WHITESPACE, []byte(" ")) | ||||||
|  | 
 | ||||||
|  | 	n.Expr.Accept(f) | ||||||
|  | 
 | ||||||
|  | 	n.SemiColonTkn = f.newSemicolonTkn() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (f *formatter) ScalarDnumber(n *ast.ScalarDnumber) { | func (f *formatter) ScalarDnumber(n *ast.ScalarDnumber) { | ||||||
| 	if n.NumberTkn == nil { | 	if n.NumberTkn == nil { | ||||||
| 		n.NumberTkn = f.newToken(token.T_STRING, n.Value) | 		n.NumberTkn = f.newToken(token.T_STRING, n.Value) | ||||||
|  | |||||||
| @ -3,9 +3,10 @@ package nsresolver | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
| 	"github.com/z7zmey/php-parser/pkg/ast" | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
| 	"github.com/z7zmey/php-parser/pkg/visitor" | 	"github.com/z7zmey/php-parser/pkg/visitor" | ||||||
| 	"strings" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // NamespaceResolver visitor | // NamespaceResolver visitor | ||||||
|  | |||||||
| @ -42,6 +42,22 @@ func (v *Null) Argument(_ *ast.Argument) { | |||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Null) MatchArm(_ *ast.MatchArm) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Null) Union(_ *ast.Union) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Null) Attribute(_ *ast.Attribute) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Null) AttributeGroup(_ *ast.AttributeGroup) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Null) StmtBreak(_ *ast.StmtBreak) { | func (v *Null) StmtBreak(_ *ast.StmtBreak) { | ||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| @ -322,6 +338,10 @@ func (v *Null) ExprMethodCall(_ *ast.ExprMethodCall) { | |||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Null) ExprNullsafeMethodCall(_ *ast.ExprNullsafeMethodCall) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Null) ExprNew(_ *ast.ExprNew) { | func (v *Null) ExprNew(_ *ast.ExprNew) { | ||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| @ -350,6 +370,10 @@ func (v *Null) ExprPropertyFetch(_ *ast.ExprPropertyFetch) { | |||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Null) ExprNullsafePropertyFetch(_ *ast.ExprNullsafePropertyFetch) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Null) ExprRequire(_ *ast.ExprRequire) { | func (v *Null) ExprRequire(_ *ast.ExprRequire) { | ||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| @ -590,6 +614,14 @@ func (v *Null) ExprCastUnset(_ *ast.ExprCastUnset) { | |||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (v *Null) ExprMatch(_ *ast.ExprMatch) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (v *Null) ExprThrow(_ *ast.ExprThrow) { | ||||||
|  | 	// do nothing | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Null) ScalarDnumber(_ *ast.ScalarDnumber) { | func (v *Null) ScalarDnumber(_ *ast.ScalarDnumber) { | ||||||
| 	// do nothing | 	// do nothing | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,9 +2,10 @@ package printer | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 	"io" | ||||||
|  | 
 | ||||||
| 	"github.com/z7zmey/php-parser/pkg/ast" | 	"github.com/z7zmey/php-parser/pkg/ast" | ||||||
| 	"github.com/z7zmey/php-parser/pkg/token" | 	"github.com/z7zmey/php-parser/pkg/token" | ||||||
| 	"io" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type printerState int | type printerState int | ||||||
| @ -145,6 +146,8 @@ func (p *printer) Nullable(n *ast.Nullable) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) Parameter(n *ast.Parameter) { | func (p *printer) Parameter(n *ast.Parameter) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
|  | 	p.printNode(n.Visibility) | ||||||
| 	p.printNode(n.Type) | 	p.printNode(n.Type) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| 	p.printToken(n.VariadicTkn, nil) | 	p.printToken(n.VariadicTkn, nil) | ||||||
| @ -158,11 +161,38 @@ func (p *printer) Identifier(n *ast.Identifier) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) Argument(n *ast.Argument) { | func (p *printer) Argument(n *ast.Argument) { | ||||||
|  | 	p.printNode(n.Name) | ||||||
|  | 	p.printToken(n.ColonTkn, nil) | ||||||
| 	p.printToken(n.VariadicTkn, nil) | 	p.printToken(n.VariadicTkn, nil) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| 	p.printNode(n.Expr) | 	p.printNode(n.Expr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *printer) MatchArm(n *ast.MatchArm) { | ||||||
|  | 	p.printToken(n.DefaultTkn, nil) | ||||||
|  | 	p.printToken(n.DefaultCommaTkn, nil) | ||||||
|  | 	p.printSeparatedList(n.Exprs, n.SeparatorTkns, []byte(",")) | ||||||
|  | 	p.printToken(n.DoubleArrowTkn, []byte("=>")) | ||||||
|  | 	p.printNode(n.ReturnExpr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *printer) Union(n *ast.Union) { | ||||||
|  | 	p.printSeparatedList(n.Types, n.SeparatorTkns, []byte("|")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *printer) Attribute(n *ast.Attribute) { | ||||||
|  | 	p.printNode(n.Name) | ||||||
|  | 	p.printToken(n.OpenParenthesisTkn, p.ifNodeList(n.Args, []byte("("))) | ||||||
|  | 	p.printSeparatedList(n.Args, n.SeparatorTkns, []byte(",")) | ||||||
|  | 	p.printToken(n.CloseParenthesisTkn, p.ifNodeList(n.Args, []byte(")"))) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *printer) AttributeGroup(n *ast.AttributeGroup) { | ||||||
|  | 	p.printToken(n.OpenAttributeTkn, []byte("#[")) | ||||||
|  | 	p.printSeparatedList(n.Attrs, n.SeparatorTkns, []byte(",")) | ||||||
|  | 	p.printToken(n.CloseAttributeTkn, []byte("]")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p *printer) StmtBreak(n *ast.StmtBreak) { | func (p *printer) StmtBreak(n *ast.StmtBreak) { | ||||||
| 	p.printToken(n.BreakTkn, []byte("break")) | 	p.printToken(n.BreakTkn, []byte("break")) | ||||||
| 	p.printNode(n.Expr) | 	p.printNode(n.Expr) | ||||||
| @ -188,6 +218,7 @@ func (p *printer) StmtCatch(n *ast.StmtCatch) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtClass(n *ast.StmtClass) { | func (p *printer) StmtClass(n *ast.StmtClass) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printList(n.Modifiers) | 	p.printList(n.Modifiers) | ||||||
| 	p.printToken(n.ClassTkn, []byte("class")) | 	p.printToken(n.ClassTkn, []byte("class")) | ||||||
| 	p.printNode(n.Name) | 	p.printNode(n.Name) | ||||||
| @ -204,6 +235,7 @@ func (p *printer) StmtClass(n *ast.StmtClass) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtClassConstList(n *ast.StmtClassConstList) { | func (p *printer) StmtClassConstList(n *ast.StmtClassConstList) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printList(n.Modifiers) | 	p.printList(n.Modifiers) | ||||||
| 	p.printToken(n.ConstTkn, []byte("const")) | 	p.printToken(n.ConstTkn, []byte("const")) | ||||||
| 	p.printSeparatedList(n.Consts, n.SeparatorTkns, []byte(",")) | 	p.printSeparatedList(n.Consts, n.SeparatorTkns, []byte(",")) | ||||||
| @ -211,6 +243,7 @@ func (p *printer) StmtClassConstList(n *ast.StmtClassConstList) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtClassMethod(n *ast.StmtClassMethod) { | func (p *printer) StmtClassMethod(n *ast.StmtClassMethod) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printList(n.Modifiers) | 	p.printList(n.Modifiers) | ||||||
| 	p.printToken(n.FunctionTkn, []byte("function")) | 	p.printToken(n.FunctionTkn, []byte("function")) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| @ -364,6 +397,7 @@ func (p *printer) StmtForeach(n *ast.StmtForeach) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtFunction(n *ast.StmtFunction) { | func (p *printer) StmtFunction(n *ast.StmtFunction) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printToken(n.FunctionTkn, []byte("function")) | 	p.printToken(n.FunctionTkn, []byte("function")) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| 	p.printNode(n.Name) | 	p.printNode(n.Name) | ||||||
| @ -426,6 +460,7 @@ func (p *printer) StmtInlineHtml(n *ast.StmtInlineHtml) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtInterface(n *ast.StmtInterface) { | func (p *printer) StmtInterface(n *ast.StmtInterface) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printToken(n.InterfaceTkn, []byte("interface")) | 	p.printToken(n.InterfaceTkn, []byte("interface")) | ||||||
| 	p.printNode(n.Name) | 	p.printNode(n.Name) | ||||||
| 	p.printToken(n.ExtendsTkn, p.ifNodeList(n.Extends, []byte("extends"))) | 	p.printToken(n.ExtendsTkn, p.ifNodeList(n.Extends, []byte("extends"))) | ||||||
| @ -460,6 +495,7 @@ func (p *printer) StmtProperty(n *ast.StmtProperty) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtPropertyList(n *ast.StmtPropertyList) { | func (p *printer) StmtPropertyList(n *ast.StmtPropertyList) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printList(n.Modifiers) | 	p.printList(n.Modifiers) | ||||||
| 	p.printNode(n.Type) | 	p.printNode(n.Type) | ||||||
| 	p.printSeparatedList(n.Props, n.SeparatorTkns, []byte(",")) | 	p.printSeparatedList(n.Props, n.SeparatorTkns, []byte(",")) | ||||||
| @ -511,6 +547,7 @@ func (p *printer) StmtThrow(n *ast.StmtThrow) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) StmtTrait(n *ast.StmtTrait) { | func (p *printer) StmtTrait(n *ast.StmtTrait) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printToken(n.TraitTkn, []byte("trait")) | 	p.printToken(n.TraitTkn, []byte("trait")) | ||||||
| 	p.printNode(n.Name) | 	p.printNode(n.Name) | ||||||
| 	p.printToken(n.OpenCurlyBracketTkn, []byte("{")) | 	p.printToken(n.OpenCurlyBracketTkn, []byte("{")) | ||||||
| @ -630,6 +667,7 @@ func (p *printer) ExprArrayItem(n *ast.ExprArrayItem) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) ExprArrowFunction(n *ast.ExprArrowFunction) { | func (p *printer) ExprArrowFunction(n *ast.ExprArrowFunction) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printToken(n.StaticTkn, nil) | 	p.printToken(n.StaticTkn, nil) | ||||||
| 	p.printToken(n.FnTkn, []byte("fn")) | 	p.printToken(n.FnTkn, []byte("fn")) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| @ -670,6 +708,7 @@ func (p *printer) ExprClone(n *ast.ExprClone) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *printer) ExprClosure(n *ast.ExprClosure) { | func (p *printer) ExprClosure(n *ast.ExprClosure) { | ||||||
|  | 	p.printList(n.AttrGroups) | ||||||
| 	p.printToken(n.StaticTkn, nil) | 	p.printToken(n.StaticTkn, nil) | ||||||
| 	p.printToken(n.FunctionTkn, []byte("function")) | 	p.printToken(n.FunctionTkn, []byte("function")) | ||||||
| 	p.printToken(n.AmpersandTkn, nil) | 	p.printToken(n.AmpersandTkn, nil) | ||||||
| @ -770,6 +809,17 @@ func (p *printer) ExprMethodCall(n *ast.ExprMethodCall) { | |||||||
| 	p.printToken(n.CloseParenthesisTkn, []byte(")")) | 	p.printToken(n.CloseParenthesisTkn, []byte(")")) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *printer) ExprNullsafeMethodCall(n *ast.ExprNullsafeMethodCall) { | ||||||
|  | 	p.printNode(n.Var) | ||||||
|  | 	p.printToken(n.ObjectOperatorTkn, []byte("?->")) | ||||||
|  | 	p.printToken(n.OpenCurlyBracketTkn, nil) | ||||||
|  | 	p.printNode(n.Method) | ||||||
|  | 	p.printToken(n.CloseCurlyBracketTkn, nil) | ||||||
|  | 	p.printToken(n.OpenParenthesisTkn, []byte("(")) | ||||||
|  | 	p.printSeparatedList(n.Args, n.SeparatorTkns, []byte(",")) | ||||||
|  | 	p.printToken(n.CloseParenthesisTkn, []byte(")")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p *printer) ExprNew(n *ast.ExprNew) { | func (p *printer) ExprNew(n *ast.ExprNew) { | ||||||
| 	p.printToken(n.NewTkn, []byte("new")) | 	p.printToken(n.NewTkn, []byte("new")) | ||||||
| 	p.printNode(n.Class) | 	p.printNode(n.Class) | ||||||
| @ -811,6 +861,14 @@ func (p *printer) ExprPropertyFetch(n *ast.ExprPropertyFetch) { | |||||||
| 	p.printToken(n.CloseCurlyBracketTkn, nil) | 	p.printToken(n.CloseCurlyBracketTkn, nil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *printer) ExprNullsafePropertyFetch(n *ast.ExprNullsafePropertyFetch) { | ||||||
|  | 	p.printNode(n.Var) | ||||||
|  | 	p.printToken(n.ObjectOperatorTkn, []byte("?->")) | ||||||
|  | 	p.printToken(n.OpenCurlyBracketTkn, nil) | ||||||
|  | 	p.printNode(n.Prop) | ||||||
|  | 	p.printToken(n.CloseCurlyBracketTkn, nil) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p *printer) ExprRequire(n *ast.ExprRequire) { | func (p *printer) ExprRequire(n *ast.ExprRequire) { | ||||||
| 	p.printToken(n.RequireTkn, []byte("require")) | 	p.printToken(n.RequireTkn, []byte("require")) | ||||||
| 	p.printNode(n.Expr) | 	p.printNode(n.Expr) | ||||||
| @ -1169,6 +1227,21 @@ func (p *printer) ExprCastUnset(n *ast.ExprCastUnset) { | |||||||
| 	p.printNode(n.Expr) | 	p.printNode(n.Expr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *printer) ExprMatch(n *ast.ExprMatch) { | ||||||
|  | 	p.printToken(n.MatchTkn, []byte("match")) | ||||||
|  | 	p.printToken(n.OpenParenthesisTkn, []byte("(")) | ||||||
|  | 	p.printNode(n.Expr) | ||||||
|  | 	p.printToken(n.CloseParenthesisTkn, []byte(")")) | ||||||
|  | 	p.printToken(n.OpenCurlyBracketTkn, []byte("{")) | ||||||
|  | 	p.printSeparatedList(n.Arms, n.SeparatorTkns, []byte(",")) | ||||||
|  | 	p.printToken(n.CloseCurlyBracketTkn, []byte("}")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *printer) ExprThrow(n *ast.ExprThrow) { | ||||||
|  | 	p.printToken(n.ThrowTkn, []byte("throw")) | ||||||
|  | 	p.printNode(n.Expr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p *printer) ScalarDnumber(n *ast.ScalarDnumber) { | func (p *printer) ScalarDnumber(n *ast.ScalarDnumber) { | ||||||
| 	p.printToken(n.NumberTkn, n.Value) | 	p.printToken(n.NumberTkn, n.Value) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										2651
									
								
								pkg/visitor/printer/printer_php8_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2651
									
								
								pkg/visitor/printer/printer_php8_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -37,6 +37,11 @@ func (t *Traverser) Nullable(n *ast.Nullable) { | |||||||
| func (t *Traverser) Parameter(n *ast.Parameter) { | func (t *Traverser) Parameter(n *ast.Parameter) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Visibility) | ||||||
| 	t.Traverse(n.Type) | 	t.Traverse(n.Type) | ||||||
| 	t.Traverse(n.Var) | 	t.Traverse(n.Var) | ||||||
| 	t.Traverse(n.DefaultValue) | 	t.Traverse(n.DefaultValue) | ||||||
| @ -49,9 +54,44 @@ func (t *Traverser) Identifier(n *ast.Identifier) { | |||||||
| func (t *Traverser) Argument(n *ast.Argument) { | func (t *Traverser) Argument(n *ast.Argument) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	t.Traverse(n.Name) | ||||||
| 	t.Traverse(n.Expr) | 	t.Traverse(n.Expr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (t *Traverser) MatchArm(n *ast.MatchArm) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	for _, nn := range n.Exprs { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | 	t.Traverse(n.ReturnExpr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Traverser) Union(n *ast.Union) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	for _, nn := range n.Types { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Traverser) Attribute(n *ast.Attribute) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Name) | ||||||
|  | 	for _, nn := range n.Args { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Traverser) AttributeGroup(n *ast.AttributeGroup) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	for _, nn := range n.Attrs { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (t *Traverser) StmtBreak(n *ast.StmtBreak) { | func (t *Traverser) StmtBreak(n *ast.StmtBreak) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
| @ -82,6 +122,9 @@ func (t *Traverser) StmtCatch(n *ast.StmtCatch) { | |||||||
| func (t *Traverser) StmtClass(n *ast.StmtClass) { | func (t *Traverser) StmtClass(n *ast.StmtClass) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Modifiers { | 	for _, nn := range n.Modifiers { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -101,6 +144,9 @@ func (t *Traverser) StmtClass(n *ast.StmtClass) { | |||||||
| func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) { | func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Modifiers { | 	for _, nn := range n.Modifiers { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -112,6 +158,9 @@ func (t *Traverser) StmtClassConstList(n *ast.StmtClassConstList) { | |||||||
| func (t *Traverser) StmtClassMethod(n *ast.StmtClassMethod) { | func (t *Traverser) StmtClassMethod(n *ast.StmtClassMethod) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Modifiers { | 	for _, nn := range n.Modifiers { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -230,6 +279,9 @@ func (t *Traverser) StmtForeach(n *ast.StmtForeach) { | |||||||
| func (t *Traverser) StmtFunction(n *ast.StmtFunction) { | func (t *Traverser) StmtFunction(n *ast.StmtFunction) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	t.Traverse(n.Name) | 	t.Traverse(n.Name) | ||||||
| 	for _, nn := range n.Params { | 	for _, nn := range n.Params { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| @ -276,6 +328,9 @@ func (t *Traverser) StmtInlineHtml(n *ast.StmtInlineHtml) { | |||||||
| func (t *Traverser) StmtInterface(n *ast.StmtInterface) { | func (t *Traverser) StmtInterface(n *ast.StmtInterface) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	t.Traverse(n.Name) | 	t.Traverse(n.Name) | ||||||
| 	for _, nn := range n.Extends { | 	for _, nn := range n.Extends { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| @ -314,6 +369,9 @@ func (t *Traverser) StmtProperty(n *ast.StmtProperty) { | |||||||
| func (t *Traverser) StmtPropertyList(n *ast.StmtPropertyList) { | func (t *Traverser) StmtPropertyList(n *ast.StmtPropertyList) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Modifiers { | 	for _, nn := range n.Modifiers { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -370,6 +428,9 @@ func (t *Traverser) StmtThrow(n *ast.StmtThrow) { | |||||||
| func (t *Traverser) StmtTrait(n *ast.StmtTrait) { | func (t *Traverser) StmtTrait(n *ast.StmtTrait) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	t.Traverse(n.Name) | 	t.Traverse(n.Name) | ||||||
| 	for _, nn := range n.Stmts { | 	for _, nn := range n.Stmts { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| @ -485,6 +546,9 @@ func (t *Traverser) ExprArrayItem(n *ast.ExprArrayItem) { | |||||||
| func (t *Traverser) ExprArrowFunction(n *ast.ExprArrowFunction) { | func (t *Traverser) ExprArrowFunction(n *ast.ExprArrowFunction) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Params { | 	for _, nn := range n.Params { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -526,6 +590,9 @@ func (t *Traverser) ExprClone(n *ast.ExprClone) { | |||||||
| func (t *Traverser) ExprClosure(n *ast.ExprClosure) { | func (t *Traverser) ExprClosure(n *ast.ExprClosure) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
|  | 	for _, nn := range n.AttrGroups { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
| 	for _, nn := range n.Params { | 	for _, nn := range n.Params { | ||||||
| 		nn.Accept(t) | 		nn.Accept(t) | ||||||
| 	} | 	} | ||||||
| @ -628,6 +695,16 @@ func (t *Traverser) ExprMethodCall(n *ast.ExprMethodCall) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (t *Traverser) ExprNullsafeMethodCall(n *ast.ExprNullsafeMethodCall) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Var) | ||||||
|  | 	t.Traverse(n.Method) | ||||||
|  | 	for _, nn := range n.Args { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (t *Traverser) ExprNew(n *ast.ExprNew) { | func (t *Traverser) ExprNew(n *ast.ExprNew) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
| @ -674,6 +751,13 @@ func (t *Traverser) ExprPropertyFetch(n *ast.ExprPropertyFetch) { | |||||||
| 	t.Traverse(n.Prop) | 	t.Traverse(n.Prop) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (t *Traverser) ExprNullsafePropertyFetch(n *ast.ExprNullsafePropertyFetch) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Var) | ||||||
|  | 	t.Traverse(n.Prop) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (t *Traverser) ExprRequire(n *ast.ExprRequire) { | func (t *Traverser) ExprRequire(n *ast.ExprRequire) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| 
 | 
 | ||||||
| @ -1086,6 +1170,21 @@ func (t *Traverser) ExprCastUnset(n *ast.ExprCastUnset) { | |||||||
| 	t.Traverse(n.Expr) | 	t.Traverse(n.Expr) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (t *Traverser) ExprMatch(n *ast.ExprMatch) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Expr) | ||||||
|  | 	for _, nn := range n.Arms { | ||||||
|  | 		nn.Accept(t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *Traverser) ExprThrow(n *ast.ExprThrow) { | ||||||
|  | 	n.Accept(t.v) | ||||||
|  | 
 | ||||||
|  | 	t.Traverse(n.Expr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (t *Traverser) ScalarDnumber(n *ast.ScalarDnumber) { | func (t *Traverser) ScalarDnumber(n *ast.ScalarDnumber) { | ||||||
| 	n.Accept(t.v) | 	n.Accept(t.v) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user