refactor php7

This commit is contained in:
Vadym Slizov
2020-05-13 00:16:36 +03:00
parent aab9da03f0
commit 6a84d58ee6
54 changed files with 29034 additions and 28821 deletions

227
internal/php5/parser.go Normal file
View File

@@ -0,0 +1,227 @@
package php5
import (
"github.com/z7zmey/php-parser/freefloating"
"github.com/z7zmey/php-parser/node"
"strings"
"github.com/z7zmey/php-parser/internal/positionbuilder"
"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/scanner"
)
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,
}
}
// Lex proxy to lexer Lex
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.SetWithFreeFloating(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 []node.Node) node.Node {
if len(nn) == 0 {
return nil
}
return nn[len(nn)-1]
}
func firstNode(nn []node.Node) node.Node {
return nn[0]
}
func isDollar(r rune) bool {
return r == '$'
}
func (l *Parser) MoveFreeFloating(src node.Node, dst node.Node) {
if l.Lexer.GetWithFreeFloating() == false {
return
}
if src.GetFreeFloating() == nil {
return
}
l.setFreeFloating(dst, freefloating.Start, (*src.GetFreeFloating())[freefloating.Start])
delete((*src.GetFreeFloating()), freefloating.Start)
}
func (l *Parser) setFreeFloating(dst node.Node, p freefloating.Position, strings []freefloating.String) {
if l.Lexer.GetWithFreeFloating() == false {
return
}
if len(strings) == 0 {
return
}
dstCollection := dst.GetFreeFloating()
if *dstCollection == nil {
*dstCollection = make(freefloating.Collection)
}
(*dstCollection)[p] = strings
}
func (l *Parser) GetFreeFloatingToken(t *scanner.Token) []freefloating.String {
if l.Lexer.GetWithFreeFloating() == false {
return []freefloating.String{}
}
return t.GetFreeFloatingToken()
}
func (l *Parser) addDollarToken(v node.Node) {
if l.Lexer.GetWithFreeFloating() == false {
return
}
l.setFreeFloating(v, freefloating.Dollar, []freefloating.String{
{
StringType: freefloating.TokenType,
Value: "$",
Position: &position.Position{
StartLine: v.GetPosition().StartLine,
EndLine: v.GetPosition().StartLine,
StartPos: v.GetPosition().StartPos,
EndPos: v.GetPosition().StartPos + 1,
},
},
})
}
func (l *Parser) splitSemiColonAndPhpCloseTag(htmlNode node.Node, prevNode node.Node) {
if l.Lexer.GetWithFreeFloating() == false {
return
}
semiColon := (*prevNode.GetFreeFloating())[freefloating.SemiColon]
delete((*prevNode.GetFreeFloating()), freefloating.SemiColon)
if len(semiColon) == 0 {
return
}
p := semiColon[0].Position
if semiColon[0].Value[0] == ';' {
l.setFreeFloating(prevNode, freefloating.SemiColon, []freefloating.String{
{
StringType: freefloating.TokenType,
Value: ";",
Position: &position.Position{
StartLine: p.StartLine,
EndLine: p.StartLine,
StartPos: p.StartPos,
EndPos: p.StartPos + 1,
},
},
})
}
vlen := len(semiColon[0].Value)
tlen := 2
if strings.HasSuffix(semiColon[0].Value, "?>\n") {
tlen = 3
}
phpCloseTag := []freefloating.String{}
if vlen-tlen > 1 {
phpCloseTag = append(phpCloseTag, freefloating.String{
StringType: freefloating.WhiteSpaceType,
Value: semiColon[0].Value[1 : vlen-tlen],
Position: &position.Position{
StartLine: p.StartLine,
EndLine: p.EndLine,
StartPos: p.StartPos + 1,
EndPos: p.EndPos - tlen,
},
})
}
phpCloseTag = append(phpCloseTag, freefloating.String{
StringType: freefloating.WhiteSpaceType,
Value: semiColon[0].Value[vlen-tlen:],
Position: &position.Position{
StartLine: p.EndLine,
EndLine: p.EndLine,
StartPos: p.EndPos - tlen,
EndPos: p.EndPos,
},
})
l.setFreeFloating(htmlNode, freefloating.Start, append(phpCloseTag, (*htmlNode.GetFreeFloating())[freefloating.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
}

9823
internal/php5/php5.go Normal file

File diff suppressed because it is too large Load Diff

7205
internal/php5/php5.y Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
package php5_test
import (
"testing"
"github.com/z7zmey/php-parser/php5"
)
func BenchmarkPhp5(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);
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[1234567890123456789012345678901234567890]";
"test $var[bar]";
"test $var[$bar]";
"$foo $bar";
"test $foo->bar()";
"test ${foo}";
"test ${foo[0]}";
"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{ const FOO = 1, BAR = 2; }
class foo{ function bar() {} }
class foo{ public static function &bar() {} }
class foo{ final private function bar() {} protected function baz() {} }
abstract class foo{ abstract public function bar(); }
final class foo extends bar { }
final class foo implements bar { }
final class 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, strict_types=1) {}
declare(ticks=1): enddeclare;
do {} while(1);
echo $a, 1;
echo($a);
for($i = 0; $i < 10; $i++, $i++) {}
for(; $i < 10; $i++) : endfor;
foreach ($a as $v) {}
foreach ([] as $v) {}
foreach ($a as $v) : endforeach;
foreach ($a as $k => $v) {}
foreach ([] as $k => $v) {}
foreach ($a as $k => &$v) {}
foreach ($a as $k => list($v)) {}
function foo() {}
function foo() {
__halt_compiler();
function bar() {}
class Baz {}
return $a;
}
function foo(array $a, callable $b) {return;}
function &foo() {return 1;}
function &foo() {}
global $a, $b, $$c, ${foo()};
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\Bar {}
namespace {}
class foo {var $a;}
class foo {public static $a, $b = 1;}
class foo {public static $a = 1, $b;}
static $a, $b = 1;
static $a = 1, $b;
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 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 $e) {} catch (RuntimeException $e) {}
try {} catch (Exception $e) {} catch (\RuntimeException $e) {} catch (namespace\AdditionException $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;
$a[1];
$a[1][2];
array();
array(1);
array(1=>1, &$b,);
array(3 =>&$b);
array(&$b, 1=>1, 1, 3 =>&$b);
~$a;
!$a;
Foo::Bar;
clone($a);
clone $a;
function(){};
function($a, $b) use ($c, &$d) {};
function($a, $b) use (&$c, $d) {};
function() {};
foo;
namespace\foo;
\foo;
empty($a);
empty(Foo);
@$a;
eval($a);
exit;
exit($a);
die();
die($a);
foo();
namespace\foo(&$a);
\foo([]);
$foo(yield $a);
$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);
isset(Foo);
list() = $b;
list($a, $b) = $b;
list($a[]) = $b;
list(list($a)) = $b;
$a->foo();
new Foo;
new namespace\Foo();
new \Foo();
print($a);
$a->foo;
$a->foo[1];
$a->foo->bar->baz()->quux[0];
$a->foo()[1][1];
` + "`cmd $a`;" + `
` + "`cmd`;" + `
` + "``;" + `
[];
[1];
[1=>1, &$b,];
Foo::bar();
namespace\Foo::bar();
\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;
$$$a;
yield;
yield $a;
yield $a => $b;
yield Foo::class;
yield $a => Foo::class;
(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 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 =& new Foo;
$a =& new Foo($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;
(new \Foo());
(new \Foo())->bar()->baz;
(new \Foo())[0][0];
(new \Foo())[0]->bar();
array([0])[0][0];
"foo"[0];
foo[0];
static::foo;
new $foo;
new $foo::$bar;
new $a->b[0];
new $a->b{$b ?: null}->$c->d[0];static $a = [1][0];
static $a = !1;
static $a = ~1;
static $a = +1;
static $a = -1;
static $a = (1);
static $a = 1 ?: 2;
static $a = 1 ? 2 : 3;
static $a = 1 & 2;
static $a = 1 | 2;
static $a = 1 ^ 2;
static $a = 1 && 2;
static $a = 1 || 2;
static $a = 1 . 2;
static $a = 1 / 2;
static $a = 1 == 2;
static $a = 1 >= 2;
static $a = 1 > 2;
static $a = 1 === 2;
static $a = 1 and 2;
static $a = 1 or 2;
static $a = 1 xor 2;
static $a = 1 - 2;
static $a = 1 % 2;
static $a = 1 * 2;
static $a = 1 != 2;
static $a = 1 !== 2;
static $a = 1 + 2;
static $a = 1 ** 2;
static $a = 1 << 2;
static $a = 1 >> 2;
static $a = 1 <= 2;
static $a = 1 < 2;
static $a = Foo::bar;
static $a = Foo::class;
static $a = __CLASS__;
static $a = Foo;
static $a = namespace\Foo;
static $a = \Foo;
static $a = array();
static $a = array(1 => 1, 2);
static $a = [1, 2 => 2][0];
if (yield 1) {}
Foo::$$bar;
$foo();
$foo()[0][0];
$a{$b};
${$a};
$foo::{$bar}();
$foo::bar;
`
for n := 0; n < b.N; n++ {
php5parser := php5.NewParser([]byte(src), "5.6")
php5parser.Parse()
}
}

18733
internal/php5/php5_test.go Normal file

File diff suppressed because it is too large Load Diff