From: Sebastian Brix Date: Wed, 16 Feb 2022 05:33:57 +0000 (+0100) Subject: - added type annotations X-Git-Tag: v0.1.0 X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=63af394b4069b89b58db5434777a63ef7fb0285e;p=graphit%2Fparser.git - added type annotations - checked code with psalm, still a lot todo --- diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..3240886 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/src/BaseParser.php b/src/BaseParser.php index fe8a71a..33804b8 100644 --- a/src/BaseParser.php +++ b/src/BaseParser.php @@ -4,14 +4,22 @@ namespace Graphit\Parser; abstract class BaseParser { + /** @var array */ protected $internals; + /** @var callable */ protected $generator; + /** @var string */ protected $description; - protected $acceptsEmpty; + /** @var bool */ + protected $acceptsEmpty = false; + /** + * @param array $internals + * @param callable $generator + */ public function __construct(array $internals = array(), $generator = null) { if ( !$this->description) { @@ -35,16 +43,24 @@ abstract class BaseParser } } + /** @return array */ public function getInternals() { return $this->internals; } + /** @return string */ public function getDescription() { return $this->description; } + /** + * @param string $string + * @param int $p the position + * + * @return array{ r: mixed, p: int }|false + */ protected function parse($string, $p = 0) { if ( !$r = $this->accept($string, $p)) { @@ -56,10 +72,18 @@ abstract class BaseParser ); } + /** + * @param string $string + * @param int $p the position + * + * @return array{ r: mixed, p: int }|false + */ protected abstract function accept($string, $p); + /** @return bool */ protected abstract function evalAcceptsEmpty(); + /** @return BaseParser[] */ protected abstract function firstSet(); public function __toString() @@ -67,6 +91,10 @@ abstract class BaseParser return $this->description; } + /** + * @param mixed $result + * @return array{ t: string, r: mixed } + */ protected function defaultGenerator($result) { return array( @@ -75,6 +103,10 @@ abstract class BaseParser ); } + /** + * @param array $internals + * @return string + */ protected function serializeInternals(array $internals) { $chunks = array(); diff --git a/src/ConcatParser.php b/src/ConcatParser.php index df9f80a..92d6e4f 100644 --- a/src/ConcatParser.php +++ b/src/ConcatParser.php @@ -4,6 +4,10 @@ namespace Graphit\Parser; class ConcatParser extends BaseParser { + /** + * @param array $internals + * @param callable $generator + */ public function __construct(array $internals, $generator = null) { $this->description = 'new '.get_class().'('.$this->serializeInternals($internals).')'; @@ -12,8 +16,17 @@ class ConcatParser extends BaseParser protected function accept($string, $p) { - $r = array(); + $internals = []; foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + $r = array(); + foreach ($internals as $internal) { if ($i = $internal->parse($string, $p)) { $r[] = $i['r']; $p = $i['p']; @@ -31,7 +44,16 @@ class ConcatParser extends BaseParser protected function evalAcceptsEmpty() { + $internals = []; foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + foreach ($internals as $internal) { if ( !$internal->acceptsEmpty) { return false; } @@ -41,8 +63,17 @@ class ConcatParser extends BaseParser protected function firstSet() { - $firstSet = array(); + $internals = []; foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + $firstSet = array(); + foreach ($internals as $internal) { $firstSet[] = $internal; if ( !$internal->acceptsEmpty) { diff --git a/src/EBNFGenerator.php b/src/EBNFGenerator.php index 000565a..14690d5 100644 --- a/src/EBNFGenerator.php +++ b/src/EBNFGenerator.php @@ -4,6 +4,7 @@ namespace Graphit\Parser; class EBNFGenerator { + /** @var EBNFParser */ protected $parser; public function __construct() @@ -11,6 +12,13 @@ class EBNFGenerator $this->parser = new EBNFParser(); } + /** + * @param string $ebnf + * @param string $start + * @param string $class + * + * @return string + */ public function generate($ebnf, $start, $class) { if ( !$ast = $this->parser->ast($ebnf)) { @@ -21,6 +29,7 @@ class EBNFGenerator $namespace = substr($class, 0, $pos); $classname = substr($class, $pos + 1); } else { + $namespace = null; $classname = $class; } @@ -59,6 +68,11 @@ class EBNFGenerator return $code; } + /** + * @param string $generator + * + * @return string + */ protected function genParser(array $object, $generator = null) { $method = 'gen'.ucfirst($object['t']).'Parser'; @@ -69,7 +83,12 @@ class EBNFGenerator throw new \Exception("Unknow t '{$object['t']}' or missing method $method"); } - protected function genSyntaxParser($object, $generator) + /** + * @param string $generator + * + * @return string + */ + protected function genSyntaxParser(array $object, $generator) { $code = "\$internals = array(\n"; foreach ($object['r'][1]['r'] as $rule) { @@ -79,7 +98,8 @@ class EBNFGenerator return $code; } - protected function genRuleParser($object) + /** @return string */ + protected function genRuleParser(array $object) { $name = $object['r'][0]['r'][0]['r'][0]; $generator = $this->genGenerator($name); @@ -89,7 +109,12 @@ class EBNFGenerator return $code; } - protected function genAltParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genAltParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\LazyAltParser(\n"; $code.= " array(\n"; @@ -102,7 +127,12 @@ class EBNFGenerator return $code; } - protected function genConcParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genConcParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\ConcatParser(\n"; $code.= " array(\n"; @@ -115,13 +145,21 @@ class EBNFGenerator return $code; } - protected function genBarewordParser($object) + /** + * @return string + */ + protected function genBarewordParser(array $object) { $code = var_export($object['r'][0]['r'][0], true); return $code; } - protected function genSqParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genSqParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\StringParser("; $code.= $generator? "\n ": ''; @@ -132,7 +170,12 @@ class EBNFGenerator return $code; } - protected function genDqParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genDqParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\StringParser("; $code.= $generator? "\n ": ''; @@ -143,7 +186,12 @@ class EBNFGenerator return $code; } - protected function genRegexParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genRegexParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\RegexParser("; $code.= $generator? "\n ": ''; @@ -153,12 +201,22 @@ class EBNFGenerator return $code; } - protected function genGroupParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genGroupParser(array $object, $generator = null) { return $this->genParser($object['r'][2], $generator); } - protected function genRepetitionParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genRepetitionParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\GreedyMultiParser(\n"; $code.= $this->indent($this->genParser($object['r'][2]), 2); @@ -168,7 +226,12 @@ class EBNFGenerator return $code; } - protected function genOptionalParser($object, $generator = null) + /** + * @param string $generator + * + * @return string + */ + protected function genOptionalParser(array $object, $generator = null) { $code = "new \\Graphit\\Parser\\GreedyMultiParser(\n"; $code.= $this->indent($this->genParser($object['r'][2]), 2); @@ -178,11 +241,22 @@ class EBNFGenerator return $code; } + /** + * @param string $source + * + * @return string + */ protected function genGenerator($source) { return "{$source}Generator"; } + /** + * @param string|null $generator + * @param bool $break + * + * @return string + */ protected function genGeneratorCall($generator, $break = false) { if ( !$generator) { @@ -195,6 +269,13 @@ class EBNFGenerator return $code; } + /** + * @param string $lines + * @param int $size + * @param int $from + * + * @return string + */ protected function indent($lines, $size = 2, $from = 0) { $pad = str_repeat(' ', $size); @@ -202,7 +283,7 @@ class EBNFGenerator $lines = explode("\n", $lines); for ($i = 0; $i < $from; ++$i) { - if (($line = array_shift($lines)) === false) { + if ( !($line = array_shift($lines))) { break; } $code .= "{$line}\n"; diff --git a/src/EmptyParser.php b/src/EmptyParser.php index 47a4601..ffdd253 100644 --- a/src/EmptyParser.php +++ b/src/EmptyParser.php @@ -4,6 +4,9 @@ namespace Graphit\Parser; class EmptyParser extends BaseParser { + /** + * @param callable $generator + */ public function __construct($generator = null) { $this->description = 'new '.get_class().'()'; diff --git a/src/GrammerParser.php b/src/GrammerParser.php index d1a8b04..640a9c2 100644 --- a/src/GrammerParser.php +++ b/src/GrammerParser.php @@ -4,8 +4,14 @@ namespace Graphit\Parser; class GrammerParser extends BaseParser { + /** @var string */ protected $s; + /** + * @param string $s + * @param array $internals + * @param callable $generator + */ public function __construct($s, array $internals, $generator = null) { $this->description = 'new '.get_class().'('.$this->serializeInternals($internals).')'; @@ -23,7 +29,9 @@ class GrammerParser extends BaseParser $this->testInifinitGreedy(); foreach ($this->internals as $internal) { + /** @var BaseParser[] */ $done = array(); + /** @var BaseParser[] */ $todo = array($internal); while ($current = array_shift($todo)) { $done[] = $current; @@ -43,10 +51,14 @@ class GrammerParser extends BaseParser } } + /** @return void */ protected function resolveParserNames() { + /** @var BaseParser[] */ $done = array(); + /** @var BaseParser[] */ $todo = array($this); + while ($current = array_shift($todo)) { $done[] = $current; @@ -67,18 +79,30 @@ class GrammerParser extends BaseParser } } + /** @return void */ protected function floodAcceptsEmpty() { $change = true; while ($change) { $change = false; + /** @var BaseParser[] */ $done = array(); + /** @var BaseParser[] */ $todo = array($this); while ($current = array_shift($todo)) { $done[] = $current; + $internals = []; foreach ($current->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + foreach ($internals as $internal) { if ($internal->acceptsEmpty) { continue; } @@ -101,11 +125,16 @@ class GrammerParser extends BaseParser } } + + /** @return void */ protected function testInifinitGreedy() { $done = array(); $todo = $this->internals; while ($current = array_shift($todo)) { + if ( !($current instanceof BaseParser)) { + throw new \Exception('Expected a BaseParser!'); + } $done[] = $current; foreach ($current->internals as $internal) { @@ -120,12 +149,20 @@ class GrammerParser extends BaseParser if ($current->getOptional() !== null) { continue; } + + if ( !($current->internals[0] instanceof BaseParser)) { + throw new \Exception('Expected a BaseParser!'); + } if ($current->internals[0]->acceptsEmpty) { throw new GrammerException("{$current} will cause infinite loops, because the internal parser accepts empty!"); } } } + /** + * @param string $code + * @return mixed + */ public function ast($code) { if ($r = $this->parse($code, 0)) { @@ -140,21 +177,29 @@ class GrammerParser extends BaseParser protected function accept($string, $p) { - if ($r = $this->internals[$this->s]->parse($string, $p)) { - return $r; + if ($this->internals[$this->s] instanceof BaseParser) { + if ($r = $this->internals[$this->s]->parse($string, $p)) { + return $r; + } + return false; } - - return false; + throw new \Exception('BaseParser expected'); } protected function evalAcceptsEmpty() { - return $this->internals[$this->s]->acceptsEmpty; + if ($this->internals[$this->s] instanceof BaseParser) { + return $this->internals[$this->s]->acceptsEmpty; + } + throw new \Exception('BaseParser expected'); } protected function firstSet() { - return array($this->internals[$this->s]); + if ($this->internals[$this->s] instanceof BaseParser) { + return array($this->internals[$this->s]); + } + throw new \Exception('BaseParser expected'); } } diff --git a/src/GreedyMultiParser.php b/src/GreedyMultiParser.php index 1e4aa73..b117663 100644 --- a/src/GreedyMultiParser.php +++ b/src/GreedyMultiParser.php @@ -4,10 +4,18 @@ namespace Graphit\Parser; class GreedyMultiParser extends BaseParser { + /** @var int */ protected $lower; + /** @var int|null */ protected $optional; + /** + * @param BaseParser|string $internal + * @param int $lower + * @param int|null $optional + * @param callable $generator + */ public function __construct($internal, $lower = 0, $optional = null, $generator = null) { $this->lower = $lower; @@ -17,11 +25,13 @@ class GreedyMultiParser extends BaseParser parent::__construct(array($internal), $generator); } + /** @return int */ public function getLower() { return $this->lower; } + /** @return int|null */ public function getOptional() { return $this->optional; @@ -29,6 +39,9 @@ class GreedyMultiParser extends BaseParser protected function accept($string, $p) { + if ( !($this->internals[0] instanceof BaseParser)) { + throw new \Exception('Expected a BaseParser!'); + } $r = array(); for ($j = 0; $j < $this->lower; $j++) { if ($i = $this->internals[0]->parse($string, $p)) { @@ -58,11 +71,17 @@ class GreedyMultiParser extends BaseParser protected function evalAcceptsEmpty() { - return $this->lower == 0 || $this->internals[0]->acceptsEmpty; + if ($this->internals[0] instanceof BaseParser) { + return $this->lower == 0 || $this->internals[0]->acceptsEmpty; + } + throw new \Exception('Expected a BaseParser!'); } protected function firstSet() { - return $this->internals; + if ($this->internals[0] instanceof BaseParser) { + return array($this->internals[0]); + } + throw new \Exception('Expected a BaseParser!'); } } diff --git a/src/LazyAltParser.php b/src/LazyAltParser.php index 7c2efcf..7e8fd50 100644 --- a/src/LazyAltParser.php +++ b/src/LazyAltParser.php @@ -4,6 +4,10 @@ namespace Graphit\Parser; class LazyAltParser extends BaseParser { + /** + * @param array $internals + * @param callable $generator + */ public function __construct(array $internals, $generator = null) { if ( !$internals) { @@ -16,7 +20,16 @@ class LazyAltParser extends BaseParser protected function accept($string, $p) { + $internals = []; foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + foreach ($internals as $internal) { if ($i = $internal->parse($string, $p)) { return array( 'r' => $i['r'], @@ -29,7 +42,16 @@ class LazyAltParser extends BaseParser protected function evalAcceptsEmpty() { + $internals = []; foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + foreach ($internals as $internal) { if ($internal->acceptsEmpty) { return true; } @@ -39,6 +61,15 @@ class LazyAltParser extends BaseParser protected function firstSet() { - return $this->internals; + $internals = []; + foreach ($this->internals as $internal) { + if ($internal instanceof BaseParser) { + $internals[] = $internal; + } else { + throw new \Exception('Expected a BaseParser!'); + } + } + + return $internals; } } diff --git a/src/RegexParser.php b/src/RegexParser.php index 43aae7b..ac36e94 100644 --- a/src/RegexParser.php +++ b/src/RegexParser.php @@ -4,8 +4,13 @@ namespace Graphit\Parser; class RegexParser extends BaseParser { + /** @var string */ protected $pattern; + /** + * @param string $pattern + * @param callable $generator + */ public function __construct($pattern, $generator = null) { $this->pattern = (string)$pattern; @@ -17,11 +22,18 @@ class RegexParser extends BaseParser parent::__construct(array(), $generator); } + /** @return string */ public function getPattern() { return $this->pattern; } + /** + * @param string $string + * @param int $p the position + * + * @return array{ r: mixed, p: int }|false + */ protected function accept($string, $p) { $matches = array(); @@ -35,6 +47,7 @@ class RegexParser extends BaseParser ); } + /** @return bool */ protected function evalAcceptsEmpty() { return preg_match($this->pattern, '') === 1; diff --git a/src/StringParser.php b/src/StringParser.php index d63e862..d07eead 100644 --- a/src/StringParser.php +++ b/src/StringParser.php @@ -4,8 +4,13 @@ namespace Graphit\Parser; class StringParser extends BaseParser { + /** @var string */ protected $needle; + /** + * @param string $needle + * @param callable $generator + */ public function __construct($needle, $generator = null) { $this->needle = (string)$needle; @@ -14,11 +19,18 @@ class StringParser extends BaseParser parent::__construct(array(), $generator); } + /** @return string */ public function getNeedle() { return $this->needle; } + /** + * @param string $string + * @param int $p + * + * @return array{ r: mixed ,p: int }|false + */ protected function accept($string, $p) { if ($this->needle !== '' && strpos($string, $this->needle, $p) !== $p) { @@ -31,6 +43,7 @@ class StringParser extends BaseParser ); } + /** @return bool */ protected function evalAcceptsEmpty() { return $this->needle === '';