refactor php7
This commit is contained in:
190
internal/php7/parser.go
Normal file
190
internal/php7/parser.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package php7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/z7zmey/php-parser/internal/positionbuilder"
|
||||
"github.com/z7zmey/php-parser/internal/scanner"
|
||||
"github.com/z7zmey/php-parser/pkg/ast"
|
||||
"github.com/z7zmey/php-parser/pkg/errors"
|
||||
"github.com/z7zmey/php-parser/pkg/position"
|
||||
"github.com/z7zmey/php-parser/pkg/token"
|
||||
)
|
||||
|
||||
func (lval *yySymType) Token(t *scanner.Token) {
|
||||
lval.token = t
|
||||
}
|
||||
|
||||
// Parser structure
|
||||
type Parser struct {
|
||||
Lexer scanner.Scanner
|
||||
currentToken *scanner.Token
|
||||
positionBuilder *positionbuilder.PositionBuilder
|
||||
rootNode ast.Vertex
|
||||
}
|
||||
|
||||
// NewParser creates and returns new Parser
|
||||
func NewParser(src []byte, v string) *Parser {
|
||||
lexer := scanner.NewLexer(src)
|
||||
lexer.PHPVersion = v
|
||||
|
||||
return &Parser{
|
||||
lexer,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Parser) Lex(lval *yySymType) int {
|
||||
t := l.Lexer.Lex(lval)
|
||||
l.currentToken = lval.token
|
||||
return t
|
||||
}
|
||||
|
||||
func (l *Parser) Error(msg string) {
|
||||
pos := &position.Position{
|
||||
StartLine: l.currentToken.StartLine,
|
||||
EndLine: l.currentToken.EndLine,
|
||||
StartPos: l.currentToken.StartPos,
|
||||
EndPos: l.currentToken.EndPos,
|
||||
}
|
||||
|
||||
l.Lexer.AddError(errors.NewError(msg, pos))
|
||||
}
|
||||
|
||||
func (l *Parser) WithTokens() {
|
||||
l.Lexer.SetWithTokens(true)
|
||||
}
|
||||
|
||||
// Parse the php7 Parser entrypoint
|
||||
func (l *Parser) Parse() int {
|
||||
// init
|
||||
l.Lexer.SetErrors(nil)
|
||||
l.rootNode = nil
|
||||
l.positionBuilder = &positionbuilder.PositionBuilder{}
|
||||
|
||||
// parse
|
||||
|
||||
return yyParse(l)
|
||||
}
|
||||
|
||||
// GetRootNode returns root node
|
||||
func (l *Parser) GetRootNode() ast.Vertex {
|
||||
return l.rootNode
|
||||
}
|
||||
|
||||
// GetErrors returns errors list
|
||||
func (l *Parser) GetErrors() []*errors.Error {
|
||||
return l.Lexer.GetErrors()
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
func lastNode(nn []ast.Vertex) ast.Vertex {
|
||||
if len(nn) == 0 {
|
||||
return nil
|
||||
}
|
||||
return nn[len(nn)-1]
|
||||
}
|
||||
|
||||
func firstNode(nn []ast.Vertex) ast.Vertex {
|
||||
return nn[0]
|
||||
}
|
||||
|
||||
func isDollar(r rune) bool {
|
||||
return r == '$'
|
||||
}
|
||||
|
||||
func (l *Parser) MoveFreeFloating(src ast.Vertex, dst ast.Vertex) {
|
||||
if l.Lexer.GetWithFreeFloating() == false {
|
||||
return
|
||||
}
|
||||
|
||||
if src.GetNode().Tokens == nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.setFreeFloating(dst, token.Start, src.GetNode().Tokens[token.Start])
|
||||
delete(src.GetNode().Tokens, token.Start)
|
||||
}
|
||||
|
||||
func (l *Parser) setFreeFloating(dst ast.Vertex, p token.Position, strings []token.Token) {
|
||||
if l.Lexer.GetWithFreeFloating() == false {
|
||||
return
|
||||
}
|
||||
|
||||
if len(strings) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dstCollection := &dst.GetNode().Tokens
|
||||
if *dstCollection == nil {
|
||||
*dstCollection = make(token.Collection)
|
||||
}
|
||||
|
||||
(*dstCollection)[p] = strings
|
||||
}
|
||||
|
||||
func (l *Parser) GetFreeFloatingToken(t *scanner.Token) []token.Token {
|
||||
if l.Lexer.GetWithFreeFloating() == false {
|
||||
return []token.Token{}
|
||||
}
|
||||
|
||||
tokens := make([]token.Token, len(t.Tokens))
|
||||
copy(tokens, t.Tokens)
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
func (l *Parser) splitSemiColonAndPhpCloseTag(htmlNode ast.Vertex, prevNode ast.Vertex) {
|
||||
if l.Lexer.GetWithFreeFloating() == false {
|
||||
return
|
||||
}
|
||||
|
||||
semiColon := prevNode.GetNode().Tokens[token.SemiColon]
|
||||
delete(prevNode.GetNode().Tokens, token.SemiColon)
|
||||
if len(semiColon) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if semiColon[0].Value[0] == ';' {
|
||||
l.setFreeFloating(prevNode, token.SemiColon, []token.Token{
|
||||
{
|
||||
ID: token.ID(';'),
|
||||
Value: semiColon[0].Value[0:1],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
vlen := len(semiColon[0].Value)
|
||||
tlen := 2
|
||||
if bytes.HasSuffix(semiColon[0].Value, []byte("?>\n")) {
|
||||
tlen = 3
|
||||
}
|
||||
|
||||
phpCloseTag := []token.Token{}
|
||||
if vlen-tlen > 1 {
|
||||
phpCloseTag = append(phpCloseTag, token.Token{
|
||||
ID: token.T_WHITESPACE,
|
||||
Value: semiColon[0].Value[1 : vlen-tlen],
|
||||
})
|
||||
}
|
||||
|
||||
phpCloseTag = append(phpCloseTag, token.Token{
|
||||
ID: T_CLOSE_TAG,
|
||||
Value: semiColon[0].Value[vlen-tlen:],
|
||||
})
|
||||
|
||||
l.setFreeFloating(htmlNode, token.Start, append(phpCloseTag, htmlNode.GetNode().Tokens[token.Start]...))
|
||||
}
|
||||
|
||||
func (p *Parser) returnTokenToPool(yyDollar []yySymType, yyVAL *yySymType) {
|
||||
for i := 1; i < len(yyDollar); i++ {
|
||||
if yyDollar[i].token != nil {
|
||||
p.Lexer.ReturnTokenToPool(yyDollar[i].token)
|
||||
}
|
||||
yyDollar[i].token = nil
|
||||
}
|
||||
yyVAL.token = nil
|
||||
}
|
||||
8382
internal/php7/php7.go
Normal file
8382
internal/php7/php7.go
Normal file
File diff suppressed because it is too large
Load Diff
5655
internal/php7/php7.y
Normal file
5655
internal/php7/php7.y
Normal file
File diff suppressed because it is too large
Load Diff
387
internal/php7/php7_bench_test.go
Normal file
387
internal/php7/php7_bench_test.go
Normal file
@@ -0,0 +1,387 @@
|
||||
package php7_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/z7zmey/php-parser/internal/php7"
|
||||
)
|
||||
|
||||
func BenchmarkPhp7(b *testing.B) {
|
||||
src := `<?
|
||||
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) {};
|
||||
|
||||
"test";
|
||||
"\$test";
|
||||
"
|
||||
test
|
||||
";
|
||||
'$test';
|
||||
'
|
||||
$test
|
||||
';
|
||||
<<<CAD
|
||||
CAD;
|
||||
<<<CAD
|
||||
hello
|
||||
CAD;
|
||||
<<<"CAD"
|
||||
hello
|
||||
CAD;
|
||||
<<<"CAD"
|
||||
hello $world
|
||||
CAD;
|
||||
<<<'CAD'
|
||||
hello $world
|
||||
CAD;
|
||||
|
||||
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;
|
||||
__halt_compiler();
|
||||
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) {
|
||||
__halt_compiler();
|
||||
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)];
|
||||
`
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
php7parser := php7.NewParser([]byte(src), "7.4")
|
||||
php7parser.Parse()
|
||||
}
|
||||
}
|
||||
20023
internal/php7/php7_test.go
Normal file
20023
internal/php7/php7_test.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user