This commit is contained in:
Sara Itani 2016-10-15 13:02:41 -07:00
Родитель a578447080
Коммит 8b625589c3
20 изменённых файлов: 467 добавлений и 32 удалений

13
Node/ClassMembersNode.php Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class ClassMembersNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::ClassMembersNode);
}
}

13
Node/ClassNode.php Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class ClassNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::ClassNode);
}
}

27
Node/DelimitedList.php Normal file
Просмотреть файл

@ -0,0 +1,27 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class DelimitedList extends Node {
/** @var Node[] */
public $tokens;
public function __construct() {
parent::__construct(NodeKind::DelimitedList);
}
public function getValues() {
$i = 0;
foreach($this->tokens as $value) {
if ($i++ % 2 == 1) {
yield $value;
}
}
}
protected function addToken(Token $token) {
array_push($this->tokens, $token);
}
}

Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class EmptyStatementNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::EmptyStatement);
}
}

12
Node/Expression.php Normal file
Просмотреть файл

@ -0,0 +1,12 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
class Expression extends Node {
public function __construct() {
parent::__construct(NodeKind::Expression);
}
}

26
Node/Function_.php Normal file
Просмотреть файл

@ -0,0 +1,26 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class Function_ extends Node {
/** @var Token */
public $functionKeyword;
/** @var Token */
public $byRefToken;
/** @var null | Name */
public $name;
/** @var Token */
public $openParen;
/** @var Parameter[] */
public $parameters;
/** @var Token */
public $closeParen;
/** @var null | Name */
public $returnTypeOpt;
public function __construct() {
parent::__construct(NodeKind::FunctionNode);
}
}

13
Node/MethodBlockNode.php Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class MethodBlockNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::MethodBlockNode);
}
}

13
Node/MethodNode.php Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class MethodNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::MethodNode);
}
}

12
Node/Name.php Normal file
Просмотреть файл

@ -0,0 +1,12 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
class Name extends Node {
public function __construct() {
parent::__construct(NodeKind::Name);
}
}

Просмотреть файл

@ -1,11 +1,14 @@
<?php
namespace PhpParser;
namespace PhpParser\Node;
class Node implements \JsonSerializable {
//require_once (__DIR__ . "/../NodeKind.php");
use PhpParser\Token;
class Node implements \JsonSerializable {
public $kind;
public $parent;
public $children;
public function __construct(int $kind) {
$this->kind = $kind;
@ -14,7 +17,7 @@ class Node implements \JsonSerializable {
public function getLength() {
$length = 0;
foreach ($this->children as $child) {
foreach ($this->getChildren() as $child) {
if ($child instanceof Node) {
$length += $child->getLength();
} else if ($child instanceof Token) {
@ -27,7 +30,7 @@ class Node implements \JsonSerializable {
public function getAllChildren() {
$allChildren = array();
foreach ($this->children as $child) {
foreach ($this->getChildren() as $child) {
if ($child instanceof Node) {
array_push($allChildren, $child);
foreach ($child->getAllChildren() as $subChild) {
@ -40,8 +43,25 @@ class Node implements \JsonSerializable {
return $allChildren;
}
private function getChildren() {
$result = array();
foreach (call_user_func('get_object_vars', $this) as $i=>$val) {
if ($i === "parent" || $i == "kind") {
continue;
}
if (is_array($val)) {
foreach ($val as $child) {
array_push($result, $child);
}
continue;
}
array_push($result, $val);
}
return $result;
}
public function getStart() {
$child = $this->children[0];
$child = $this->getChildren()[0];
if ($child instanceof Node) {
return $child->getStart();
} else if ($child instanceof Token) {
@ -59,18 +79,6 @@ class Node implements \JsonSerializable {
}
}
return ["$kindName" => $this->children];
return ["$kindName" => $this->getChildren()];
}
}
class NodeKind {
const SourceFileNode = 0;
const ClassNode = 1;
const BlockNode = 2;
const MethodBlockNode = 3;
const MethodNode = 4;
const StatementNode = 5;
const ClassMembersNode = 6;
const Count = 7;
const TemplateExpression = 8;
}

27
Node/Parameter.php Normal file
Просмотреть файл

@ -0,0 +1,27 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class Parameter extends Node {
/** @var Token */
public $typeOpt;
/** @var Token */
public $byRefToken;
/** @var Token */
public $variableName;
/** @var null | Expression */
public $default;
public function __construct() {
parent::__construct(NodeKind::ParameterNode);
}
public function isVariadic() {
}
}

13
Node/SourceFile.php Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
class SourceFile extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::SourceFileNode);
}
}

Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class TemplateExpressionNode extends Node {
public $children;
public function __construct() {
parent::__construct(NodeKind::TemplateExpression);
}
}

26
Node/Type.php Normal file
Просмотреть файл

@ -0,0 +1,26 @@
<?php
namespace PhpParser\Node;
use PhpParser\NodeKind;
use PhpParser\Token;
class Parameter extends Node {
/** @var Token */
public $type;
/** @var Token */
public $byRefToken;
/** @var Token */
public $variableName;
/** @var Token */
public $openParen;
/** @var DelimitedList */
public $parameters;
/** @var Token */
public $closeParen;
/** @var Type */
public $returnTypeOpt;
public function __construct(int $a = 3) {
parent::__construct(NodeKind::FunctionNode);
}
}

21
NodeKind.php Normal file
Просмотреть файл

@ -0,0 +1,21 @@
<?php
namespace PhpParser;
class NodeKind {
const SourceFileNode = 0;
const ClassNode = 1;
const BlockNode = 2;
const MethodBlockNode = 3;
const MethodNode = 4;
const StatementNode = 5;
const ClassMembersNode = 6;
const Count = 7;
const TemplateExpression = 8;
const EmptyStatement = 9;
const FunctionNode = 10;
const DelimitedList = 11;
const Expression = 12;
const Name = 13;
const ParameterNode = 14;
}

Просмотреть файл

@ -59,6 +59,8 @@ class TokenKind {
const SkippedToken = 4;
const MissingToken = 5;
const QualifiedName = 6;
const AbstractKeyword = 101;
const AndKeyword = 102;
@ -160,7 +162,7 @@ class TokenKind {
const CaretToken = 231;
const BarToken = 232;
const AmpersandToken = 233;
const ApersandAmpersandToken = 234;
const AmpersandAmpersandToken = 234;
const BarBarToken = 235;
const QuestionToken = 235;
const ColonToken = 236;

Просмотреть файл

@ -0,0 +1,34 @@
<?php
/*
* PURPOSE:
* During the course of lexing a file, we will be indexing into strings quite liberally.
* This experiment compares the effects of indexing into a string and indexing into an associative array.
*
* RESULTS:
* Memory by indexing into string: 31457280
* Memory by indexing into character array: 0
*
* CONCLUSION:
* We create new zvals every time we index into a string, whereas we are merely incrementing the refcount
* every time we index into the unpacked character array. Therefore, iterating through a character array is
* preferable to decrease memory footprint.
*/
$count = 1000000;
$str = str_repeat("a", $count);
$unpackedStr = unpack('C*', $str);
$char = new SplFixedArray($count+1);
$memory = memory_get_peak_usage(true);
for ($i = 1; $i<$count; $i++) {
$char[$i] = $str[$i];
}
echo "Memory by indexing into string: ", (memory_get_peak_usage(true) - $memory), PHP_EOL;
$memory = memory_get_peak_usage(true);
for ($i = 1; $i<$count+1; $i++) {
$char[$i] = $unpackedStr[$i];
}
echo "Memory by indexing into character array: ", (memory_get_peak_usage(true) - $memory), PHP_EOL;

Просмотреть файл

@ -794,7 +794,7 @@ const OPERATORS_AND_PUNCTUATORS = array(
"^" => TokenKind::CaretToken,
"|" => TokenKind::BarToken,
"&" => TokenKind::AmpersandToken,
"&&" => TokenKind::ApersandAmpersandToken,
"&&" => TokenKind::AmpersandAmpersandToken,
"||" => TokenKind::BarBarToken,
"?" => TokenKind::QuestionToken,
":" => TokenKind::ColonToken,

Просмотреть файл

@ -1,8 +1,26 @@
<?php
namespace PhpParser;
require_once('Node.php');
// TODO make this less hacky
spl_autoload_register(function ($class) {
if (file_exists($filepath = __DIR__ . "/Node/" . basename($class) . ".php")) {
require_once $filepath;
} else if (file_exists($filepath = __DIR__ . "/" . basename($class) . ".php")) {
require_once $filepath;
}
});
use PhpParser\Node\ClassMembersNode;
use PhpParser\Node\ClassNode;
use PhpParser\Node\EmptyStatementNode;
use PhpParser\Node\MethodBlockNode;
use PhpParser\Node\MethodNode;
use PhpParser\Node\Node;
use PhpParser\Node\SourceFile;
use PhpParser\Node\TemplateExpressionNode;
class Parser {
@ -18,7 +36,7 @@ class Parser {
public function parseSourceFile() : Node {
$this->reset();
$sourceFile = new Node(NodeKind::SourceFileNode);
$sourceFile = new SourceFile();
$this->sourceFile = & $sourceFile;
$sourceFile->children = $this->parseList($sourceFile, ParseContext::SourceElements);
array_push($sourceFile->children, $this->getCurrentToken());
@ -206,10 +224,12 @@ class Parser {
return $this->parseClassDeclaration($parentNode);
case TokenKind::TemplateStringStart:
return $this->parseTemplateString($parentNode);
case TokenKind::SemicolonToken:
return $this->parseEmptyStatement($parentNode);
default:
$token = $this->getCurrentToken();
$this->advanceToken();
return $token;
return $this->parsePrimaryExpression($parentNode);
}
};
}
@ -239,7 +259,7 @@ class Parser {
}
function parseClassDeclaration($parentNode) : Node {
$node = new Node(NodeKind::ClassNode);
$node = new ClassNode();
$node->children = array();
array_push($node->children, $this->eat(TokenKind::ClassKeyword));
array_push($node->children, $this->eat(TokenKind::Name));
@ -249,7 +269,7 @@ class Parser {
}
function parseClassMembers($parentNode) : Node {
$node = new Node(NodeKind::ClassMembersNode);
$node = new ClassMembersNode();
$node->children = array();
array_push($node->children, $this->eat(TokenKind::OpenBraceToken));
$this->array_push_list($node->children, $this->parseList($node, ParseContext::ClassMembers));
@ -259,7 +279,7 @@ class Parser {
}
function parseMethodDeclaration($parentNode) {
$node = new Node(NodeKind::MethodNode);
$node = new MethodNode();
$node->children = array();
array_push($node->children, $this->eat(TokenKind::FunctionKeyword));
array_push($node->children, $this->eat(TokenKind::Name));
@ -272,7 +292,7 @@ class Parser {
}
function parseMethodBlock($parentNode) {
$node = new Node(NodeKind::MethodBlockNode);
$node = new MethodBlockNode();
$node->children = array();
array_push($node->children, $this->eat(TokenKind::OpenBraceToken));
$this->array_push_list($node->children, $this->parseList($node, ParseContext::BlockStatements));
@ -397,7 +417,7 @@ class Parser {
}
private function parseTemplateString($parentNode) {
$templateNode = new Node(NodeKind::TemplateExpression);
$templateNode = new TemplateExpressionNode();
$templateNode->parent = $parentNode;
$templateNode->children = array();
do {
@ -417,6 +437,135 @@ class Parser {
array_push($templateNode->children, $this->eat(TokenKind::TemplateStringEnd));
return $templateNode;
}
private function isPrimaryExpressionStart() {
switch ($this->getCurrentToken()) {
// variable-name
case TokenKind::VariableName: // TODO special case $this
// qualified-name
case TokenKind::QualifiedName: // TODO Qualified name
// literal
case TokenKind::DecimalLiteralToken: // TODO merge dec, oct, hex, bin, float -> NumericLiteral
case TokenKind::OctalLiteralToken:
case TokenKind::HexadecimalLiteralToken:
case TokenKind::BinaryLiteralToken:
case TokenKind::FloatingLiteralToken:
case TokenKind::InvalidOctalLiteralToken:
case TokenKind::InvalidHexadecimalLiteral:
case TokenKind::InvalidBinaryLiteral:
case TokenKind::StringLiteralToken: // TODO merge unterminated
case TokenKind::UnterminatedStringLiteralToken:
case TokenKind::NoSubstitutionTemplateLiteral:
case TokenKind::UnterminatedNoSubstitutionTemplateLiteral:
case TokenKind::TemplateStringStart: //TODO - parse this as an expression
// TODO constant-expression
// intrinsic-construct
case TokenKind::EchoKeyword:
case TokenKind::ListKeyword:
case TokenKind::UnsetKeyword:
// intrinsic-operator
case TokenKind::ArrayKeyword:
case TokenKind::EmptyKeyword:
case TokenKind::EvalKeyword:
case TokenKind::ExitKeyword:
case TokenKind::DieKeyword:
case TokenKind::IsSetKeyword:
case TokenKind::PrintKeyword:
// anonymous-function-creation-expression
case TokenKind::StaticKeyword:
case TokenKind::FunctionKeyword:
// ( expression )
case TokenKind::OpenParenToken:
return true; // TODO
}
return false;
}
private function parsePrimaryExpression($parentNode) {
switch ($this->getCurrentToken()) {
/* // variable-name
case TokenKind::VariableName: // TODO special case $this
return $this->parseVariableNameExpression($parentNode);
// qualified-name
case TokenKind::QualifiedName: // TODO Qualified name
return $this->parseQualifiedNameExpression($parentNode);
// literal
case TokenKind::DecimalLiteralToken: // TODO merge dec, oct, hex, bin, float -> NumericLiteral
case TokenKind::OctalLiteralToken:
case TokenKind::HexadecimalLiteralToken:
case TokenKind::BinaryLiteralToken:
case TokenKind::FloatingLiteralToken:
case TokenKind::InvalidOctalLiteralToken:
case TokenKind::InvalidHexadecimalLiteral:
case TokenKind::InvalidBinaryLiteral:
case TokenKind::StringLiteralToken: // TODO merge unterminated
case TokenKind::UnterminatedStringLiteralToken:
case TokenKind::NoSubstitutionTemplateLiteral:
case TokenKind::UnterminatedNoSubstitutionTemplateLiteral:
return $this->parseLiteralExpression($parentNode);
case TokenKind::TemplateStringStart:
return $this->parseTemplateString($parentNode);
// TODO constant-expression
// intrinsic-construct
case TokenKind::EchoKeyword:
case TokenKind::ListKeyword:
case TokenKind::UnsetKeyword:
return $this->parseIntrinsicConstructExpression($parentNode);
// intrinsic-operator
case TokenKind::ArrayKeyword:
case TokenKind::EmptyKeyword:
case TokenKind::EvalKeyword:
case TokenKind::ExitKeyword:
case TokenKind::DieKeyword:
case TokenKind::IsSetKeyword:
case TokenKind::PrintKeyword:
// return $this->
// anonymous-function-creation-expression
case TokenKind::StaticKeyword:
case TokenKind::FunctionKeyword:
// ( expression )
case TokenKind::OpenParenToken:
return true;*/
default:
// TODO
$token = $this->getCurrentToken();
$this->advanceToken();
return $token;
}
}
private function parseEmptyStatement($parentNode) {
$node = new EmptyStatementNode();
$node->children = array();
array_push($node->children, $this->eat(TokenKind::SemicolonToken));
$node->parent = $parentNode;
return $node;
}
private function parseLiteralExpression($parentNode) {
}
}
class ParseContext {

Просмотреть файл

@ -5,9 +5,9 @@ require_once(__DIR__ . "/../parser.php");
require_once(__DIR__ . "/../Token.php");
require_once(__DIR__ . "/LexerInvariantsTest.php");
use PhpParser\Node\Node;
use PHPUnit\Framework\TestCase;
use PhpParser\TokenKind;
use PhpParser\Node;
class ParserInvariantsTest extends LexerInvariantsTest {
const FILENAMES = array (