--- /dev/null
+#!/usr/bin/env php
+<?php
+
+$options = getopt("e:r:c:p:");
+
+if (
+ !is_array($options) ||
+ !isset($options['e'], $options['r'], $options['c'], $options['p']) ||
+ !is_string($options['e']) ||
+ !is_string($options['r']) ||
+ !is_string($options['c']) ||
+ !is_string($options['p'])
+) {
+ echo "Parameters:", PHP_EOL;
+ echo " -e: ebnf file", PHP_EOL;
+ echo " -r: root rule", PHP_EOL;
+ echo " -c: classname", PHP_EOL;
+ echo " -p: path for generated files", PHP_EOL;
+ echo PHP_EOL , 'Examples:';
+ echo PHP_EOL , $argv[0].' -e "./src/ebnf.ebnf" -r "syntax" -c "Graphit\Parser\EBNF\EBNFParser" -p "./src/EBNF_new"';
+ echo PHP_EOL , $argv[0].' -e "./src/attribute.ebnf" -r "attribute" -c "Graphit\Graph\DSLParser\Attribute\AttributeParser" -p "./src/Attribute/"';
+ echo PHP_EOL , $argv[0].' -e "./src/reihenfolge.ebnf" -r "reihenfolge" -c "Graphit\Graph\DSLParser\Reihenfolge\ReihenfolgeParser" -p "./src/Reihenfolge/"';
+ echo PHP_EOL , $argv[0].' -e "./src/eingrenzung.ebnf" -r "eingrenzung" -c "Graphit\Graph\DSLParser\Eingrenzung\EingrenzungParser" -p "./src/Eingrenzung/"';
+ echo PHP_EOL;
+ exit(1);
+};
+
+$className = $options['c'];
+$path = $options['p'];
+$EBNFFile = $options['e'];
+$topMostFuncName = $options['r'];
+
+$pos = strrpos($className, '\\');
+if ($pos === false) {
+ echo PHP_EOL;
+ echo $className . ' does not contain namespace!' . PHP_EOL;
+ exit(1);
+}
+
+$namespace = substr($className, 0, $pos);
+$className = substr($className, $pos + 1);
+
+if ( !is_file($EBNFFile)) {
+ echo PHP_EOL;
+ echo $EBNFFile . ' does not exists!' . PHP_EOL;
+ exit(1);
+}
+
+require_once __DIR__.'/../vendor/autoload.php';
+
+use Graphit\Parser\EBNF\EBNFParser;
+use Graphit\Parser\EBNFTransform;
+use Graphit\Parser\EBNFResultGenerator;
+use Graphit\Parser\EBNFParserGenerator;
+
+$EBNFFileContent = file_get_contents($EBNFFile);
+$result = EBNFParser::parse($EBNFFileContent);
+if ($result === null) {
+ echo PHP_EOL;
+ echo 'EBNFParser::parse returns NULL' . PHP_EOL;
+ exit(1);
+}
+
+function folderHasContents(string $folder): bool {
+ if ( !is_dir($folder)) {
+ return false;
+ }
+
+ $files = scandir($folder);
+ return count($files) > 2;
+}
+
+if (folderHasContents($path)) {
+ echo PHP_EOL;
+ echo 'Folder exists and contains files or sub-folders: ' . $path . PHP_EOL;
+ exit(1);
+}
+
+$transformResult = EBNFTransform::transformSyntax($result);
+
+EBNFResultGenerator::generate($transformResult, $namespace, $path);
+
+EBNFParserGenerator::generate($transformResult, $namespace, $className, $path, $topMostFuncName);
+
}
],
"require": {
- "php": ">=5.6.0"
+ "php": ">=8.1.0"
},
"autoload": {
"psr-4": {
"Graphit\\Parser\\": "src/"
}
- }
+ },
+ "bin": [ "bin/generate_parser.php" ]
}
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "hash": "58c59c1998c08b0e41abf3c52e03cf24",
- "content-hash": "885de769d17478ea8942f54d26e6a439",
+ "content-hash": "b6e7241a4a8086d50eb538b9dc2583f6",
"packages": [],
"packages-dev": [],
"aliases": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=5.6.0"
+ "php": ">=8.1.0"
},
- "platform-dev": []
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
}
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
+ <directory name="bin" />
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-abstract class BaseParser
-{
- /** @var array<BaseParser|string> */
- protected $internals;
-
- /** @var callable */
- protected $generator;
-
- /** @var string */
- protected $description;
-
- /** @var bool */
- protected $acceptsEmpty = false;
-
- /**
- * @param array<BaseParser|string> $internals
- * @param callable $generator
- */
- public function __construct(array $internals = array(), $generator = null)
- {
- if ( !$this->description) {
- throw new \Exception("No description prepared!");
- }
-
- foreach ($internals as $internal) {
- if ( !is_string($internal) && !$internal instanceof BaseParser) {
- throw new \Exception(var_export($internal, true).' is not a string and not a BaseParser');
- }
- }
- $this->internals = $internals;
-
- if ($generator) {
- if ( !is_callable($generator)) {
- throw new \Exception(var_export($generator, true).' is not callable');
- }
- $this->generator = $generator;
- } else {
- $this->generator = array($this, 'defaultGenerator');
- }
- }
-
- /** @return array<string, BaseParser|string> */
- 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)) {
- return false;
- }
- return array(
- 'r' => call_user_func($this->generator, $r['r']),
- 'p' => $r['p'],
- );
- }
-
- /**
- * @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()
- {
- return $this->description;
- }
-
- /**
- * @param mixed $result
- * @return array{ t: string, r: mixed }
- */
- protected function defaultGenerator($result)
- {
- return array(
- 't' => (string)$this,
- 'r' => $result,
- );
- }
-
- /**
- * @param array<BaseParser|string> $internals
- * @return string
- */
- protected function serializeInternals(array $internals)
- {
- $chunks = array();
- foreach ($internals as $key => $internal) {
- $chunk = var_export($key, true).' => ';
- if (is_string($internal)) {
- $chunk .= var_export($internal, true).' => ';
- } else {
- $chunk .= (string)$internal;
- }
- $chunks[] = $chunk;
- }
- return 'array('.implode(', ', $chunks).')';
- }
-}
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class ConcatParser extends BaseParser
-{
- /**
- * @param array<BaseParser|string> $internals
- * @param callable $generator
- */
- public function __construct(array $internals, $generator = null)
- {
- $this->description = 'new '.get_class().'('.$this->serializeInternals($internals).')';
- parent::__construct($internals, $generator);
- }
-
- protected function accept($string, $p)
- {
- $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'];
-
- continue;
- }
- return false;
- }
-
- return array(
- 'r' => $r,
- 'p' => $p,
- );
- }
-
- 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;
- }
- }
- return true;
- }
-
- protected function firstSet()
- {
- $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) {
- break;
- }
- }
- return $firstSet;
- }
-}
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class AltResult
+{
+ /**
+ * @param list{ConcResult, PipeconclistResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous11Result
+{
+ /**
+ * @param ',' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous14Result
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous16Result
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous18Result
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous20Result
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous22Result
+{
+ /**
+ * @param '(' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous23Result
+{
+ /**
+ * @param ')' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous25Result
+{
+ /**
+ * @param '{' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous26Result
+{
+ /**
+ * @param '}' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous28Result
+{
+ /**
+ * @param '[' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous29Result
+{
+ /**
+ * @param ']' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous2Result
+{
+ /**
+ * @param '=' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous31Result
+{
+ /**
+ * @param WhitespaceResult|CommentResult $result
+ */
+ public function __construct(
+ public readonly WhitespaceResult|CommentResult $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous3Result
+{
+ /**
+ * @param ';' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class Anonymous7Result
+{
+ /**
+ * @param '|' $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class BarewordResult
+{
+ /**
+ * @param list{Anonymous14Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class CommatermResult
+{
+ /**
+ * @param list{Anonymous11Result, SpaceResult, TermResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class CommatermlistResult
+{
+ /**
+ * @param list<CommatermResult> $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class CommentResult
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class ConcResult
+{
+ /**
+ * @param list{TermResult, CommatermlistResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class DqResult
+{
+ /**
+ * @param list{Anonymous18Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class EBNFParser
+{
+ public static function parse(string $code): ?SyntaxResult
+ {
+ $result = self::syntax($code, 0);
+ if ($result && $result->position != strlen($code)) {
+ return null;
+ }
+
+ return $result;
+ }
+
+ private static function anonymous14(string $code, int $position): ?Anonymous14Result
+ {
+ $regexp = '/^([a-z][a-z ]*[a-z]|[a-z])/';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new Anonymous14Result($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function anonymous16(string $code, int $position): ?Anonymous16Result
+ {
+ $regexp = '/^\'([^\']*)\'/';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new Anonymous16Result($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function anonymous18(string $code, int $position): ?Anonymous18Result
+ {
+ $regexp = '/^"([^"]*)"/';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new Anonymous18Result($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function anonymous20(string $code, int $position): ?Anonymous20Result
+ {
+ $regexp = '/^\\/\\^([^\\/\\\\]*(\\\\\\/|\\\\[^\\/])?)*\\//';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new Anonymous20Result($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function whitespace(string $code, int $position): ?WhitespaceResult
+ {
+ $regexp = '/^[ \\t\\r\\n]+/';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new WhitespaceResult($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function comment(string $code, int $position): ?CommentResult
+ {
+ $regexp = '/^(\\(\\*\\s+[^*]*\\s+\\*\\)|\\(\\* \\*\\)|\\(\\*\\*\\))/';
+ if (preg_match($regexp, substr($code, $position), $matches) === 1) {
+ return new CommentResult($matches[0], $position + strlen($matches[0]));
+ }
+
+ return null;
+ }
+
+ private static function anonymous2(string $code, int $position): ?Anonymous2Result
+ {
+ if (substr($code, $position, 1) === '=') {
+ return new Anonymous2Result('=', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous3(string $code, int $position): ?Anonymous3Result
+ {
+ if (substr($code, $position, 1) === ';') {
+ return new Anonymous3Result(';', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous7(string $code, int $position): ?Anonymous7Result
+ {
+ if (substr($code, $position, 1) === '|') {
+ return new Anonymous7Result('|', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous11(string $code, int $position): ?Anonymous11Result
+ {
+ if (substr($code, $position, 1) === ',') {
+ return new Anonymous11Result(',', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous22(string $code, int $position): ?Anonymous22Result
+ {
+ if (substr($code, $position, 1) === '(') {
+ return new Anonymous22Result('(', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous23(string $code, int $position): ?Anonymous23Result
+ {
+ if (substr($code, $position, 1) === ')') {
+ return new Anonymous23Result(')', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous25(string $code, int $position): ?Anonymous25Result
+ {
+ if (substr($code, $position, 1) === '{') {
+ return new Anonymous25Result('{', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous26(string $code, int $position): ?Anonymous26Result
+ {
+ if (substr($code, $position, 1) === '}') {
+ return new Anonymous26Result('}', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous28(string $code, int $position): ?Anonymous28Result
+ {
+ if (substr($code, $position, 1) === '[') {
+ return new Anonymous28Result('[', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function anonymous29(string $code, int $position): ?Anonymous29Result
+ {
+ if (substr($code, $position, 1) === ']') {
+ return new Anonymous29Result(']', $position + 1);
+ }
+
+ return null;
+ }
+
+ private static function term(string $code, int $position): ?TermResult
+ {
+ if ($result = self::bareword($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::sq($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::dq($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::regex($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::group($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::repetition($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+ if ($result = self::optional($code, $position)) {
+ return new TermResult($result, $result->position);
+ }
+
+ return null;
+ }
+
+ private static function anonymous31(string $code, int $position): ?Anonymous31Result
+ {
+ if ($result = self::whitespace($code, $position)) {
+ return new Anonymous31Result($result, $result->position);
+ }
+ if ($result = self::comment($code, $position)) {
+ return new Anonymous31Result($result, $result->position);
+ }
+
+ return null;
+ }
+
+ private static function syntax(string $code, int $position): ?SyntaxResult
+ {
+ $results = [];
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::rules($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new SyntaxResult($results, $position);
+ }
+
+ private static function rule(string $code, int $position): ?RuleResult
+ {
+ $results = [];
+ if ($result = self::bareword($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::anonymous2($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::alt($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::anonymous3($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new RuleResult($results, $position);
+ }
+
+ private static function alt(string $code, int $position): ?AltResult
+ {
+ $results = [];
+ if ($result = self::conc($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::pipeconclist($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new AltResult($results, $position);
+ }
+
+ private static function pipeconc(string $code, int $position): ?PipeconcResult
+ {
+ $results = [];
+ if ($result = self::anonymous7($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::conc($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new PipeconcResult($results, $position);
+ }
+
+ private static function conc(string $code, int $position): ?ConcResult
+ {
+ $results = [];
+ if ($result = self::term($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::commatermlist($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new ConcResult($results, $position);
+ }
+
+ private static function commaterm(string $code, int $position): ?CommatermResult
+ {
+ $results = [];
+ if ($result = self::anonymous11($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::term($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new CommatermResult($results, $position);
+ }
+
+ private static function bareword(string $code, int $position): ?BarewordResult
+ {
+ $results = [];
+ if ($result = self::anonymous14($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new BarewordResult($results, $position);
+ }
+
+ private static function sq(string $code, int $position): ?SqResult
+ {
+ $results = [];
+ if ($result = self::anonymous16($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new SqResult($results, $position);
+ }
+
+ private static function dq(string $code, int $position): ?DqResult
+ {
+ $results = [];
+ if ($result = self::anonymous18($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new DqResult($results, $position);
+ }
+
+ private static function regex(string $code, int $position): ?RegexResult
+ {
+ $results = [];
+ if ($result = self::anonymous20($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new RegexResult($results, $position);
+ }
+
+ private static function group(string $code, int $position): ?GroupResult
+ {
+ $results = [];
+ if ($result = self::anonymous22($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::alt($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::anonymous23($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new GroupResult($results, $position);
+ }
+
+ private static function repetition(string $code, int $position): ?RepetitionResult
+ {
+ $results = [];
+ if ($result = self::anonymous25($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::alt($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::anonymous26($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new RepetitionResult($results, $position);
+ }
+
+ private static function optional(string $code, int $position): ?OptionalResult
+ {
+ $results = [];
+ if ($result = self::anonymous28($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::alt($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::anonymous29($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+ if ($result = self::space($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ } else {
+ return null;
+ }
+
+ return new OptionalResult($results, $position);
+ }
+
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function rules(string $code, int $position): ?RulesResult
+ {
+ $results = [];
+ while ($result = self::rule($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ }
+
+ return new RulesResult($results, $position);
+ }
+
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function pipeconclist(string $code, int $position): ?PipeconclistResult
+ {
+ $results = [];
+ while ($result = self::pipeconc($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ }
+
+ return new PipeconclistResult($results, $position);
+ }
+
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function commatermlist(string $code, int $position): ?CommatermlistResult
+ {
+ $results = [];
+ while ($result = self::commaterm($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ }
+
+ return new CommatermlistResult($results, $position);
+ }
+
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function space(string $code, int $position): ?SpaceResult
+ {
+ $results = [];
+ while ($result = self::anonymous31($code, $position)) {
+ $position = $result->position;
+ $results[] = $result;
+ }
+
+ return new SpaceResult($results, $position);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class GroupResult
+{
+ /**
+ * @param list{Anonymous22Result, SpaceResult, AltResult, Anonymous23Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class OptionalResult
+{
+ /**
+ * @param list{Anonymous28Result, SpaceResult, AltResult, Anonymous29Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class PipeconcResult
+{
+ /**
+ * @param list{Anonymous7Result, SpaceResult, ConcResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class PipeconclistResult
+{
+ /**
+ * @param list<PipeconcResult> $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class RegexResult
+{
+ /**
+ * @param list{Anonymous20Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class RepetitionResult
+{
+ /**
+ * @param list{Anonymous25Result, SpaceResult, AltResult, Anonymous26Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class RuleResult
+{
+ /**
+ * @param list{BarewordResult, SpaceResult, Anonymous2Result, SpaceResult, AltResult, Anonymous3Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class RulesResult
+{
+ /**
+ * @param list<RuleResult> $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class SpaceResult
+{
+ /**
+ * @param list<Anonymous31Result> $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class SqResult
+{
+ /**
+ * @param list{Anonymous16Result, SpaceResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class SyntaxResult
+{
+ /**
+ * @param list{SpaceResult, RulesResult} $result
+ */
+ public function __construct(
+ public readonly array $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class TermResult
+{
+ /**
+ * @param BarewordResult|SqResult|DqResult|RegexResult|GroupResult|RepetitionResult|OptionalResult $result
+ */
+ public function __construct(
+ public readonly BarewordResult|SqResult|DqResult|RegexResult|GroupResult|RepetitionResult|OptionalResult $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace Graphit\Parser\EBNF;
+
+class WhitespaceResult
+{
+ /**
+ * @param string $result
+ */
+ public function __construct(
+ public readonly string $result,
+ public readonly int $position
+ ) {}
+}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class EBNFGenerator
-{
- /** @var EBNFParser */
- protected $parser;
-
- public function __construct()
- {
- $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)) {
- throw new \Exception('Unable to parse given EBNF');
- }
-
- if ($pos = strrpos($class, '\\')){
- $namespace = substr($class, 0, $pos);
- $classname = substr($class, $pos + 1);
- } else {
- $namespace = null;
- $classname = $class;
- }
-
- $code = "<?php\n\n";
- if ($namespace) {
- $code.= "namespace {$namespace};\n\n";
- }
- $code.= "class {$classname} extends \Graphit\Parser\GrammerParser\n{\n";
-
- $code.= " public function __construct()\n";
- $code.= " {\n";
- $code.= " \$s = ".var_export($start, true).";\n";
-
- $code.= $this->indent($this->genParser($ast), 4)."\n";
-
- $code.= " parent::__construct(\$s, \$internals);\n";
- $code.= " }\n";
-
- foreach ($ast['r'][1]['r'] as $rule) {
- $name = $rule['r'][0]['r'][0]['r'][0];
-
- $code.= "\n";
- $code.= " protected function ";
- $code.= $this->genGenerator($name);
- $code.= "(\$result)\n";
- $code.= " {\n";
- $code.= " \$result = \$this->internals[".var_export($name, true);
- $code.= "]->defaultGenerator(\$result);\n";
- $code.= " \$result['t'] = ".var_export($name, true).";\n";
- $code.= " return \$result;\n";
- $code.= " }\n";
- }
-
- $code.="}\n";
-
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genParser(array $object, $generator = null)
- {
- $method = 'gen'.ucfirst($object['t']).'Parser';
- if (method_exists($this, $method)) {
- return $this->$method($object, $generator);
- }
-
- throw new \Exception("Unknow t '{$object['t']}' or missing method $method");
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genSyntaxParser(array $object, $generator)
- {
- $code = "\$internals = array(\n";
- foreach ($object['r'][1]['r'] as $rule) {
- $code.= $this->indent($this->genParser($rule), 2).",\n";
- }
- $code.= ");";
- return $code;
- }
-
- /** @return string */
- protected function genRuleParser(array $object)
- {
- $name = $object['r'][0]['r'][0]['r'][0];
- $generator = $this->genGenerator($name);
-
- $code = var_export($name, true)." => ";
- $code.= $this->genParser($object['r'][4], $generator);
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genAltParser(array $object, $generator = null)
- {
- $code = "new \\Graphit\\Parser\\LazyAltParser(\n";
- $code.= " array(\n";
- foreach ($object['r'] as $internal) {
- $code.= $this->indent($this->genParser($internal), 4).",\n";
- }
- $code.= " )";
- $code.= $this->genGeneratorCall($generator, true);
- $code.= ")";
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genConcParser(array $object, $generator = null)
- {
- $code = "new \\Graphit\\Parser\\ConcatParser(\n";
- $code.= " array(\n";
- foreach ($object['r'] as $internal) {
- $code.= $this->indent($this->genParser($internal), 4).",\n";
- }
- $code.= " )";
- $code.= $this->genGeneratorCall($generator, true);
- $code.= ")";
- return $code;
- }
-
- /**
- * @return string
- */
- protected function genBarewordParser(array $object)
- {
- $code = var_export($object['r'][0]['r'][0], true);
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genSqParser(array $object, $generator = null)
- {
- $code = "new \\Graphit\\Parser\\StringParser(";
- $code.= $generator? "\n ": '';
- //$code.= var_export($object['string'], true);
- $code.= "'".$object['r'][0]['r'][1]."'";
- $code.= $this->genGeneratorCall($generator, $generator? true: false);
- $code.= ")";
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genDqParser(array $object, $generator = null)
- {
- $code = "new \\Graphit\\Parser\\StringParser(";
- $code.= $generator? "\n ": '';
- //$code.= var_export($object['string'], true);
- $code.= '"'.$object['r'][0]['r'][1].'"';
- $code.= $this->genGeneratorCall($generator, $generator? true: false);
- $code.= ")";
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genRegexParser(array $object, $generator = null)
- {
- $code = "new \\Graphit\\Parser\\RegexParser(";
- $code.= $generator? "\n ": '';
- $code.= var_export($object['r'][0]['r'][0], true);
- $code.= $this->genGeneratorCall($generator, $generator? true: false);
- $code.= ")";
- return $code;
- }
-
- /**
- * @param string $generator
- *
- * @return string
- */
- protected function genGroupParser(array $object, $generator = null)
- {
- return $this->genParser($object['r'][2], $generator);
- }
-
- /**
- * @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);
- $code.= ", 0, null";
- $code.= $this->genGeneratorCall($generator, true);
- $code.= ")";
- return $code;
- }
-
- /**
- * @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);
- $code.= ", 0, 1";
- $code.= $this->genGeneratorCall($generator, true);
- $code.= ")";
- 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) {
- return $break ? "\n" : '';
- }
-
- $code = "," . ($break ? "\n " : '');
- $code.= "array(\$this, ".var_export($generator, true).")";
- $code.= ($break ? "\n" : '');
- 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);
- $code = '';
-
- $lines = explode("\n", $lines);
- for ($i = 0; $i < $from; ++$i) {
- if ( !($line = array_shift($lines))) {
- break;
- }
- $code .= "{$line}\n";
- }
-
- foreach ($lines as $line) {
- $code .= "{$pad}{$line}\n";
- }
-
- return substr($code, 0, -1);
- }
-}
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class EBNFParser extends EBNFParserBase
-{
- public function __construct()
- {
- parent::__construct();
- }
-
- protected function altGenerator($results)
- {
- if ($results[1]) {
- array_unshift($results[1], $results[0]);
- return array(
- 't' => 'alt',
- 'r' => $results[1],
- );
- } else {
- return $results[0];
- }
- }
-
- protected function pipeconclistGenerator($results)
- {
- return $results;
- }
-
- protected function pipeconcGenerator($results)
- {
- return $results[2];
- }
-
- protected function concGenerator($results)
- {
- if ($results[1]) {
- array_unshift($results[1], $results[0]['r']);
- return array(
- 't' => 'conc',
- 'r' => $results[1],
- );
- } else {
- return $results[0]['r'];
- }
- }
-
- protected function commatermlistGenerator($results)
- {
- return $results;
- }
-
- protected function commatermGenerator($results)
- {
- return $results[2]['r'];
- }
-}
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class EBNFParserBase extends \Graphit\Parser\GrammerParser
-{
- public function __construct()
- {
- $s = 'syntax';
- $internals = array(
- 'syntax' => new \Graphit\Parser\ConcatParser(
- array(
- 'space',
- 'rules',
- ),
- array($this, 'syntaxGenerator')
- ),
- 'rules' => new \Graphit\Parser\GreedyMultiParser(
- 'rule', 0, null,
- array($this, 'rulesGenerator')
- ),
- 'rule' => new \Graphit\Parser\ConcatParser(
- array(
- 'bareword',
- 'space',
- new \Graphit\Parser\StringParser("="),
- 'space',
- 'alt',
- new \Graphit\Parser\StringParser(";"),
- 'space',
- ),
- array($this, 'ruleGenerator')
- ),
- 'alt' => new \Graphit\Parser\ConcatParser(
- array(
- 'conc',
- 'pipeconclist',
- ),
- array($this, 'altGenerator')
- ),
- 'pipeconclist' => new \Graphit\Parser\GreedyMultiParser(
- 'pipeconc', 0, null,
- array($this, 'pipeconclistGenerator')
- ),
- 'pipeconc' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\StringParser("|"),
- 'space',
- 'conc',
- ),
- array($this, 'pipeconcGenerator')
- ),
- 'conc' => new \Graphit\Parser\ConcatParser(
- array(
- 'term',
- 'commatermlist',
- ),
- array($this, 'concGenerator')
- ),
- 'commatermlist' => new \Graphit\Parser\GreedyMultiParser(
- 'commaterm', 0, null,
- array($this, 'commatermlistGenerator')
- ),
- 'commaterm' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\StringParser(","),
- 'space',
- 'term',
- ),
- array($this, 'commatermGenerator')
- ),
- 'term' => new \Graphit\Parser\LazyAltParser(
- array(
- 'bareword',
- 'sq',
- 'dq',
- 'regex',
- 'group',
- 'repetition',
- 'optional',
- ),
- array($this, 'termGenerator')
- ),
- 'bareword' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\RegexParser('/^([a-z][a-z ]*[a-z]|[a-z])/'),
- 'space',
- ),
- array($this, 'barewordGenerator')
- ),
- 'sq' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\RegexParser('/^\'([^\']*)\'/'),
- 'space',
- ),
- array($this, 'sqGenerator')
- ),
- 'dq' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\RegexParser('/^"([^"]*)"/'),
- 'space',
- ),
- array($this, 'dqGenerator')
- ),
- 'regex' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\RegexParser('/^\\/\\^([^\\/\\\\]*(\\\\\\/|\\\\[^\\/])?)*\\//'),
- 'space',
- ),
- array($this, 'regexGenerator')
- ),
- 'group' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\StringParser("("),
- 'space',
- 'alt',
- new \Graphit\Parser\StringParser(")"),
- 'space',
- ),
- array($this, 'groupGenerator')
- ),
- 'repetition' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\StringParser("{"),
- 'space',
- 'alt',
- new \Graphit\Parser\StringParser("}"),
- 'space',
- ),
- array($this, 'repetitionGenerator')
- ),
- 'optional' => new \Graphit\Parser\ConcatParser(
- array(
- new \Graphit\Parser\StringParser("["),
- 'space',
- 'alt',
- new \Graphit\Parser\StringParser("]"),
- 'space',
- ),
- array($this, 'optionalGenerator')
- ),
- 'space' => new \Graphit\Parser\GreedyMultiParser(
- new \Graphit\Parser\LazyAltParser(
- array(
- 'whitespace',
- 'comment',
- )
- ), 0, null,
- array($this, 'spaceGenerator')
- ),
- 'whitespace' => new \Graphit\Parser\RegexParser(
- '/^[ \\t\\r\\n]+/',
- array($this, 'whitespaceGenerator')
- ),
- 'comment' => new \Graphit\Parser\RegexParser(
- '/^(\\(\\*\\s+[^*]*\\s+\\*\\)|\\(\\* \\*\\)|\\(\\*\\*\\))/',
- array($this, 'commentGenerator')
- ),
- );
- parent::__construct($s, $internals);
- }
-
- protected function syntaxGenerator($result)
- {
- $result = $this->internals['syntax']->defaultGenerator($result);
- $result['t'] = 'syntax';
- return $result;
- }
-
- protected function rulesGenerator($result)
- {
- $result = $this->internals['rules']->defaultGenerator($result);
- $result['t'] = 'rules';
- return $result;
- }
-
- protected function ruleGenerator($result)
- {
- $result = $this->internals['rule']->defaultGenerator($result);
- $result['t'] = 'rule';
- return $result;
- }
-
- protected function altGenerator($result)
- {
- $result = $this->internals['alt']->defaultGenerator($result);
- $result['t'] = 'alt';
- return $result;
- }
-
- protected function pipeconclistGenerator($result)
- {
- $result = $this->internals['pipeconclist']->defaultGenerator($result);
- $result['t'] = 'pipeconclist';
- return $result;
- }
-
- protected function pipeconcGenerator($result)
- {
- $result = $this->internals['pipeconc']->defaultGenerator($result);
- $result['t'] = 'pipeconc';
- return $result;
- }
-
- protected function concGenerator($result)
- {
- $result = $this->internals['conc']->defaultGenerator($result);
- $result['t'] = 'conc';
- return $result;
- }
-
- protected function commatermlistGenerator($result)
- {
- $result = $this->internals['commatermlist']->defaultGenerator($result);
- $result['t'] = 'commatermlist';
- return $result;
- }
-
- protected function commatermGenerator($result)
- {
- $result = $this->internals['commaterm']->defaultGenerator($result);
- $result['t'] = 'commaterm';
- return $result;
- }
-
- protected function termGenerator($result)
- {
- $result = $this->internals['term']->defaultGenerator($result);
- $result['t'] = 'term';
- return $result;
- }
-
- protected function barewordGenerator($result)
- {
- $result = $this->internals['bareword']->defaultGenerator($result);
- $result['t'] = 'bareword';
- return $result;
- }
-
- protected function sqGenerator($result)
- {
- $result = $this->internals['sq']->defaultGenerator($result);
- $result['t'] = 'sq';
- return $result;
- }
-
- protected function dqGenerator($result)
- {
- $result = $this->internals['dq']->defaultGenerator($result);
- $result['t'] = 'dq';
- return $result;
- }
-
- protected function regexGenerator($result)
- {
- $result = $this->internals['regex']->defaultGenerator($result);
- $result['t'] = 'regex';
- return $result;
- }
-
- protected function groupGenerator($result)
- {
- $result = $this->internals['group']->defaultGenerator($result);
- $result['t'] = 'group';
- return $result;
- }
-
- protected function repetitionGenerator($result)
- {
- $result = $this->internals['repetition']->defaultGenerator($result);
- $result['t'] = 'repetition';
- return $result;
- }
-
- protected function optionalGenerator($result)
- {
- $result = $this->internals['optional']->defaultGenerator($result);
- $result['t'] = 'optional';
- return $result;
- }
-
- protected function spaceGenerator($result)
- {
- $result = $this->internals['space']->defaultGenerator($result);
- $result['t'] = 'space';
- return $result;
- }
-
- protected function whitespaceGenerator($result)
- {
- $result = $this->internals['whitespace']->defaultGenerator($result);
- $result['t'] = 'whitespace';
- return $result;
- }
-
- protected function commentGenerator($result)
- {
- $result = $this->internals['comment']->defaultGenerator($result);
- $result['t'] = 'comment';
- return $result;
- }
-}
--- /dev/null
+<?php
+
+namespace Graphit\Parser;
+
+use Graphit\Parser\ParserType;
+
+class EBNFParserGenerator
+{
+ /**
+ * @param array<string, ParserType> $transformResults
+ */
+ public static function generate(array $transformResults, string $nameSpace, string $parserClassName, string $path, string $topMostFuncName): void
+ {
+ $regexFuncs = '';
+ $stringFuncs = '';
+ $unionFuncs = '';
+ $concFuncs = '';
+ $repetitionFuncs = '';
+ $optionalType = '';
+ $anonType = '';
+ foreach ($transformResults as $ruleName => $types) {
+
+ $className = ucfirst($ruleName) . 'Result';
+
+ if ($types instanceof RegexpType) {
+ // $regexFuncs .= 'RegexpType';
+ $regexFuncs .= str_repeat(PHP_EOL, 2);
+ $regexFuncs .= self::regexpTypeTemplate($className, $ruleName, $types->regexp);
+ }
+
+ if ($types instanceof StringType) {
+ $stringFuncs .= str_repeat(PHP_EOL, 2);
+ $stringFuncs .= self::stringTypeTemplate($className, $ruleName, $types->string);
+ }
+
+ if ($types instanceof UnionType) {
+ $unionFuncs .= str_repeat(PHP_EOL, 2);
+ $unionFuncs .= self::unionTypeTemplate($className, $ruleName, $types);
+ }
+
+ if ($types instanceof ConcType) {
+ $concFuncs .= str_repeat(PHP_EOL, 2);
+ $concFuncs .= self::concTypeTemplate($className, $ruleName, $types);
+ }
+
+ if ($types instanceof RepetitionType) {
+ $repetitionFuncs .= str_repeat(PHP_EOL, 2);
+ $repetitionFuncs .= self::repetitionTypeTemplate($className, $ruleName, $types);
+ }
+
+ if ($types instanceof OptionalType) {
+ $optionalType .= str_repeat(PHP_EOL, 2);
+ $optionalType .= self::optionalTypeTemplate($className, $ruleName, $types);
+ }
+
+ if ($types instanceof AnonType) {
+ $anonType .= str_repeat(PHP_EOL, 2);
+ $anonType .= self::anonTypeTemplate($className, $ruleName, $types);
+ }
+ }
+
+ $parserFunc = self::parserFuncTemplate($topMostFuncName);
+ //$parserFunc .= str_repeat(PHP_EOL, 2);
+
+ $functions = $parserFunc . $regexFuncs . $stringFuncs . $unionFuncs . $concFuncs . $repetitionFuncs . $optionalType . $anonType;
+
+ $content = self::classTemplate($nameSpace, $parserClassName, $functions);
+ self::writeFile($path, $parserClassName . '.php', $content);
+ }
+
+ private static function classTemplate(string $nameSpace, string $className, string $functions): string
+ {
+ $template = <<<TEMPLATE
+ <?php
+ namespace <NAME_SPACE>;
+
+ class <CLASS_NAME>
+ {
+ <FUNCTIONS>
+ }
+ TEMPLATE;
+
+ return strtr($template, [
+ '<NAME_SPACE>' => $nameSpace,
+ '<CLASS_NAME>' => $className,
+ '<FUNCTIONS>' => $functions
+ ]);
+ }
+
+ private static function writeFile(string $path, string $fileName, string $content): void
+ {
+ if (!is_dir($path) && mkdir($path, 0755, true) === false) {
+ throw new \Exception("Path: {$path} does not exist and could not be created!");
+ }
+
+ if (substr($path, -1) !== '/') {
+ $path .= '/';
+ }
+
+ if (file_put_contents("{$path}{$fileName}", $content) === false) {
+ throw new \Exception("File: {$path}{$fileName} could not be written!");
+ }
+ }
+
+ private static function parserFuncTemplate(string $topMostFunctionName): string
+ {
+ $regexpFuncTemplate = <<<TEMPLATE
+ public static function parse(string \$code): ?<TOPMOST_CLASS_NAME>
+ {
+ \$result = self::<TOPMOST_FUNC_NAME>(\$code, 0);
+ if (\$result && \$result->position != strlen(\$code)) {
+ return null;
+ }
+
+ return \$result;
+ }
+ TEMPLATE;
+
+ return strtr($regexpFuncTemplate, [
+ '<TOPMOST_CLASS_NAME>' => ucfirst($topMostFunctionName) . 'Result',
+ '<TOPMOST_FUNC_NAME>' => $topMostFunctionName,
+ ]);
+ }
+
+ private static function regexpTypeTemplate(string $className, string $functionName, string $regexp): string
+ {
+ $regexpFuncTemplate = <<<TEMPLATE
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ \$regexp = <REGEX_PATTERN>;
+ if (preg_match(\$regexp, substr(\$code, \$position), \$matches) === 1) {
+ return new <CLASS_NAME>(\$matches[0], \$position + strlen(\$matches[0]));
+ }
+
+ return null;
+ }
+ TEMPLATE;
+
+ return strtr($regexpFuncTemplate, [
+ '<REGEX_PATTERN>' => var_export($regexp, true),
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ ]);
+ }
+
+ private static function stringTypeTemplate(string $className, string $functionName, string $string): string
+ {
+ $stringFuncTemplate = <<<TEMPLATE
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ if (substr(\$code, \$position, <LENGTH>) === <STRING>) {
+ return new <CLASS_NAME>(<STRING_PARAM>, \$position + <LENGTH>);
+ }
+
+ return null;
+ }
+ TEMPLATE;
+
+ return strtr($stringFuncTemplate, [
+ '<STRING>' => var_export($string, true),
+ '<STRING_PARAM>' => var_export($string, true),
+ // '<STRING>' => $string,
+ // '<STRING_PARAM>' => $string,
+ '<LENGTH>' => strlen($string),
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ ]);
+ }
+
+ private static function unionTypeTemplate(string $className, string $functionName, UnionType $unionType): string
+ {
+ $contentTemplate = <<<TEMPLATE
+ if (\$result = self::<FUNC_NAME>(\$code, \$position)) {
+ return new <CLASS_NAME>(\$result, \$result->position);
+ }
+ TEMPLATE;
+
+ $content = '';
+ foreach($unionType->types as $type) {
+ $content .= strtr($contentTemplate, [
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => isset($type->name) ? $type->name : throw new \Exception(),
+ ]);
+ $content .= str_repeat(PHP_EOL, 1);
+ }
+
+ $unionFuncTemplate = <<<TEMPLATE
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ <CONTENT>
+ return null;
+ }
+ TEMPLATE;
+
+ return strtr($unionFuncTemplate, [
+ '<CONTENT>' => $content,
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ ]);
+ }
+
+ private static function concTypeTemplate(string $className, string $functionName, ConcType $concType): string
+ {
+ $contentTemplate = <<<TEMPLATE
+ if (\$result = self::<FUNC_NAME>(\$code, \$position)) {
+ \$position = \$result->position;
+ \$results[] = \$result;
+ } else {
+ return null;
+ }
+ TEMPLATE;
+
+ $content = '';
+ foreach($concType->types as $type) {
+ $content .= strtr($contentTemplate, [
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => isset($type->name) ? $type->name : throw new \Exception(),
+ ]);
+ $content .= str_repeat(PHP_EOL, 1);
+ }
+
+ $concFuncTemplate = <<<TEMPLATE
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ \$results = [];
+ <CONTENT>
+ return new <CLASS_NAME>(\$results, \$position);
+ }
+ TEMPLATE;
+
+ return strtr($concFuncTemplate, [
+ '<CONTENT>' => $content,
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ ]);
+ }
+
+ private static function repetitionTypeTemplate(string $className, string $functionName, RepetitionType $repetitionType): string
+ {
+ $repetitionFuncTemplate = <<<TEMPLATE
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ \$results = [];
+ while (\$result = self::<CALLED_FUNC>(\$code, \$position)) {
+ \$position = \$result->position;
+ \$results[] = \$result;
+ }
+
+ return new <CLASS_NAME>(\$results, \$position);
+ }
+ TEMPLATE;
+
+ return strtr($repetitionFuncTemplate, [
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ '<CALLED_FUNC>' => isset($repetitionType->type->name) ? $repetitionType->type->name : throw new \Exception()
+ ]);
+ }
+
+ private static function optionalTypeTemplate(string $className, string $functionName, OptionalType $optionalType): string
+ {
+ $optionalFuncTemplate = <<<TEMPLATE
+ /** @psalm-suppress LessSpecificReturnType */
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ \$result = self::<CALLED_FUNC>(\$code, \$position);
+
+ if (!is_null(\$result)) {
+ \$position = \$result->position;
+ }
+
+ return new <CLASS_NAME>(\$result, \$position);
+ }
+ TEMPLATE;
+
+ return strtr($optionalFuncTemplate, [
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ '<CALLED_FUNC>' => isset($optionalType->type->name) ? $optionalType->type->name : throw new \Exception()
+ ]);
+ }
+
+ private static function anonTypeTemplate(string $className, string $functionName, AnonType $anonType): string
+ {
+ $anonFuncTemplate = <<<TEMPLATE
+ private static function <FUNC_NAME>(string \$code, int \$position): ?<CLASS_NAME>
+ {
+ \$result = self::<CALLED_FUNC>(\$code, \$position);
+
+ if (is_null(\$result)) {
+ return null;
+ }
+
+ return new <CLASS_NAME>(\$result, \$result->position);
+ }
+ TEMPLATE;
+
+ return strtr($anonFuncTemplate, [
+ '<CLASS_NAME>' => $className,
+ '<FUNC_NAME>' => $functionName,
+ '<CALLED_FUNC>' => $anonType->name
+ ]);
+ }
+
+}
+
+
+
+
--- /dev/null
+<?php
+
+namespace Graphit\Parser;
+
+use Graphit\Parser\ParserType;
+
+class EBNFResultGenerator
+{
+ /**
+ * @param array<string, ParserType> $transformResults
+ */
+ public static function generate(array $transformResults, string $nameSpace, string $path): void
+ {
+ $nsTemplate = self::nameSpaceTemplate($nameSpace);
+
+ foreach ($transformResults as $ruleName => $types) {
+ $docTYpe = str_replace('"', "", $types->asDocType());
+
+ $className = ucfirst($ruleName) . 'Result';
+ $classContent = self::classTemplate($className, $types->asPHPType(), $docTYpe);
+
+ self::writeFile($path, $className . '.php', $nsTemplate . $classContent);
+ }
+ }
+
+ private static function nameSpaceTemplate(string $nameSpace): string
+ {
+ $nsTemplate = <<<TEMPLATE
+ <?php
+ namespace <NAME_SPACE>;
+ TEMPLATE;
+
+ $nsTemplate .= str_repeat(PHP_EOL, 2);
+
+ return strtr($nsTemplate, [
+ '<NAME_SPACE>' => $nameSpace
+ ]);
+ }
+
+ private static function classTemplate(string $className, string $phpType, ?string $psalmType = null): string
+ {
+ $template = <<<TEMPLATE
+ class <CLASS_NAME>
+ {<PSALM_TYPE>
+ public function __construct(
+ public readonly <PHP_TYPE> \$result,
+ public readonly int \$position
+ ) {}
+ }
+ TEMPLATE;
+
+ $psalmTemplate = <<<PSALM
+
+ /**
+ * @param <PSALM_TYPE> \$result
+ */
+ PSALM;
+
+ if ($psalmType) {
+ $psalmTemplate = strtr($psalmTemplate, [
+ '<PSALM_TYPE>' => $psalmType
+ ]);
+
+ $psalmType = $psalmTemplate;
+ }
+
+ return strtr($template, [
+ '<CLASS_NAME>' => $className,
+ '<PHP_TYPE>' => $phpType,
+ '<PSALM_TYPE>' => $psalmType
+ ]);
+ }
+
+ private static function writeFile(string $path, string $fileName, string $content): void
+ {
+ if (!is_dir($path) && mkdir($path, 0755, true) === false) {
+ throw new \Exception("Path: {$path} does not exist and could not be created!");
+ }
+
+ if (substr($path, -1) !== '/') {
+ $path .= '/';
+ }
+
+ if (file_put_contents("{$path}{$fileName}", $content) === false) {
+ throw new \Exception("File: {$path}{$fileName} could not be written!");
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+namespace Graphit\Parser;
+
+use Graphit\Parser\EBNF\{
+ AltResult,
+ BarewordResult,
+ CommatermlistResult,
+ CommatermResult,
+ ConcResult,
+ DqResult,
+ GroupResult,
+ OptionalResult,
+ PipeconclistResult,
+ PipeconcResult,
+ RegexResult,
+ RepetitionResult,
+ RuleResult,
+ RulesResult,
+ SqResult,
+ SyntaxResult,
+ TermResult
+};
+
+abstract class ParserType {
+
+ public abstract function asPHPType(): string;
+
+ public abstract function asDocType(): string;
+
+ public abstract function needsDocType(): bool;
+}
+
+class RuleType extends ParserType {
+ private function __construct(
+ public readonly string $name
+ ) {}
+
+ public function asPHPType(): string {
+ return ucfirst($this->name).'Result';
+ }
+
+ public function asDocType(): string {
+ return $this->asPHPType();
+ }
+
+ public function needsDocType(): bool {
+ return false;
+ }
+
+ public static function create(string $name): ParserType {
+ return new RuleType($name);
+ }
+}
+
+class AnonType extends ParserType {
+ private function __construct(
+ public readonly string $name
+ ) {}
+
+ public function asPHPType(): string {
+ return ucfirst($this->name).'Result';
+ }
+
+ public function asDocType(): string {
+ return $this->asPHPType();
+ }
+
+ public function needsDocType(): bool {
+ return false;
+ }
+
+ public static function create(string $name): ParserType {
+ return new AnonType($name);
+ }
+}
+
+class StringType extends ParserType {
+ private function __construct(
+ public readonly string $string
+ ) {}
+
+ public function asPHPType(): string {
+ return 'string';
+ }
+
+ public function asDocType(): string {
+ return var_export($this->string, true);
+ }
+
+ public function needsDocType(): bool {
+ return true;
+ }
+
+ public static function create(string $string): ParserType {
+ if (strlen($string) < 2) throw new \Exception();
+ return new StringType(substr($string, 1, -1));
+ }
+}
+
+class RegexpType extends ParserType {
+ private function __construct(
+ public readonly string $regexp
+ ) {}
+
+ public function asPHPType(): string {
+ return 'string';
+ }
+
+ public function asDocType(): string {
+ return $this->asPHPType();
+ }
+
+ public function needsDocType(): bool {
+ return false;
+ }
+
+ public static function create(string $regexp): ParserType {
+ return new RegexpType($regexp);
+ }
+}
+
+class ConcType extends ParserType {
+
+ /** @param non-empty-list<ParserType> $types */
+ private function __construct(
+ public readonly array $types
+ ) { }
+
+ public function asPHPType(): string {
+ return 'array';
+ }
+
+ public function asDocType(): string {
+ $types = [];
+ foreach($this->types as $type) {
+ $types[] = $type->asDocType();
+ }
+ return 'list{'.implode(', ', $types).'}';
+ }
+
+ public function needsDocType(): bool {
+ return true;
+ }
+
+ public static function create(ParserType ...$others): ParserType {
+ if (count($others) < 2) throw new \Exception();
+
+ $types = [];
+ foreach($others as $other) {
+ if ($other instanceof ConcType) {
+ $types = array_merge($types, $other->types);
+ } else {
+ $types[] = $other;
+ }
+ }
+ return new ConcType($types);
+ }
+}
+
+class UnionType extends ParserType {
+
+ /** @param non-empty-array<string, ParserType> $types */
+ private function __construct(
+ public readonly array $types
+ ) {}
+
+ public function asPHPType(): string {
+ if ($this->needsDocType()) {
+ return 'mixed';
+ }
+ return $this->asDocType();
+ }
+
+ public function asDocType(): string {
+ $types = [];
+ foreach($this->types as $type) {
+ $types[] = $type->asDocType();
+ }
+ return implode('|', $types);
+ }
+
+ public function needsDocType(): bool {
+ foreach($this->types as $type) {
+ if ($type->needsDocType()) return true;
+ }
+ return false;
+ }
+
+ public static function create(ParserType ...$others): ParserType {
+ if (count($others) < 2) throw new \Exception();
+
+ $types = [];
+ $optionals = [];
+ foreach ($others as $other) {
+ if ($other instanceof UnionType) {
+ foreach ($other->types as $type) {
+ if ($type instanceof OptionalType) {
+ $inner = $type->type->asDocType();
+ $optionals[$inner] = true;
+ unset($types[$inner]);
+
+ $types += [$type->asDocType() => $type];
+ } else {
+ $docType = $type->asDocType();
+ if ( !isset($optionals[$docType])) {
+ $types += [$docType => $type];
+ }
+ }
+ }
+ }
+ else if ($other instanceof OptionalType) {
+ $inner = $other->type->asDocType();
+ $optionals[$inner] = true;
+ unset($types[$inner]);
+
+ $types += [$other->asDocType() => $other];
+ } else {
+ $docType = $other->asDocType();
+ if ( !isset($optionals[$docType])) {
+ $types += [$docType => $other];
+ }
+ }
+ }
+ if ( !$types) throw new \Exception();
+ if (count($types) === 1) {
+ return reset($types);
+ }
+ return new UnionType($types);
+ }
+}
+
+class OptionalType extends ParserType {
+ private function __construct(
+ public readonly ParserType $type
+ ) {}
+
+ public function asPHPType(): string {
+ if ($this->needsDocType()) {
+ return 'mixed';
+ }
+ return $this->asDocType();
+ }
+
+ public function asDocType(): string {
+ return '?'.$this->type->asDocType();
+ }
+
+ public function needsDocType(): bool {
+ return $this->type->needsDocType();
+ }
+
+ public static function create(ParserType $type): ParserType {
+ if ($type instanceof OptionalType) return $type;
+ return new OptionalType($type);
+ }
+}
+
+class RepetitionType extends ParserType {
+ private function __construct(
+ public readonly ParserType $type
+ ) {}
+
+ public function asPHPType(): string {
+ return 'array';
+ }
+
+ public function asDocType(): string {
+ return 'list<'.$this->type->asDocType().'>';
+ }
+
+ public function needsDocType(): bool {
+ return true;
+ }
+
+ public static function create(ParserType $type): ParserType {
+ if ($type instanceof RepetitionType) return $type;
+ return new RepetitionType($type);
+ }
+}
+
+class TypeInfos {
+
+ /** @param array<string, ParserType> $named */
+ private function __construct(
+ public readonly ParserType $type,
+ public readonly array $named = []
+ ) {}
+
+ public function conc(TypeInfos $other): TypeInfos {
+ return new TypeInfos(
+ ConcType::create($this->type, $other->type),
+ array_merge($this->named, $other->named)
+ );
+ }
+
+ public function union(TypeInfos $other): TypeInfos {
+ return new TypeInfos(
+ UnionType::create($this->type, $other->type),
+ array_merge($this->named, $other->named)
+ );
+ }
+
+ public function optional(): TypeInfos {
+ return new TypeInfos(
+ OptionalType::create($this->type),
+ $this->named
+ );
+ }
+
+ public function repetition(): TypeInfos {
+ return new TypeInfos(
+ RepetitionType::create($this->type),
+ $this->named
+ );
+ }
+
+ public function generateName(): TypeInfos {
+ $name = TypeInfos::newName();
+ return new TypeInfos(
+ AnonType::create($name),
+ array_merge($this->named, [$name => $this->type])
+ );
+ }
+
+ public static function rule(string $rule): TypeInfos {
+ return new TypeInfos(
+ RuleType::create($rule),
+ );
+ }
+
+ public static function string(string $string): TypeInfos {
+ return new TypeInfos(
+ StringType::create($string)
+ );
+ }
+
+ public static function regexp(string $regexp): TypeInfos {
+ return new TypeInfos(
+ RegexpType::create($regexp)
+ );
+ }
+
+ private static int $counter = 0;
+
+ public static function newName(): string {
+ $counter = static::$counter++;
+ return "anonymous{$counter}";
+ }
+}
+
+class EBNFTransform
+{
+ public static function toRuleName(BarewordResult $result): string {
+ return $result->result[0]->result;
+ }
+
+ /** @return array<string, ParserType> */
+ public static function transformSyntax(SyntaxResult $result): array {
+ return static::transformRules($result->result[1]);
+ }
+
+ /** @return array<string, ParserType> */
+ public static function transformRules(RulesResult $result): array {
+ $named = [];
+ foreach($result->result as $ruleResult) {
+ $named = array_merge($named, static::transformRule($ruleResult));
+ }
+ return $named;
+ }
+
+ /** @return array<string, ParserType> */
+ public static function transformRule(RuleResult $result): array {
+ $ruleName = static::toRuleName($result->result[0]);
+ $typeInfo = static::transformAlt($result->result[4]);
+
+ if ( $typeInfo->type instanceof RuleType) {
+ throw new \RuntimeException("stupid rule!");
+ }
+ if ( !$typeInfo->type instanceof AnonType) {
+ throw new \Exception('bug!');
+ }
+ $name = $typeInfo->type->name;
+
+ $named = $typeInfo->named;
+ $named[$ruleName] = $named[$name];
+ unset($named[$name]);
+
+ return $named;
+ }
+
+ public static function transformAlt(AltResult $result): TypeInfos {
+ $concTypeInfo = static::transformConc($result->result[0]);
+ $pipeconclistTypeInfo = static::transformPipeconclist($result->result[1]);
+ if ($pipeconclistTypeInfo) {
+ $temp = $concTypeInfo->union($pipeconclistTypeInfo);
+ if ($temp->type instanceof UnionType) {
+ return $temp->generateName();
+ } else {
+ return $temp;
+ }
+ }
+ return $concTypeInfo;
+ }
+
+ public static function transformPipeconclist(PipeconclistResult $result): ?TypeInfos {
+ $typeInfos = null;
+ foreach ($result->result as $aResult) {
+ if ($typeInfos instanceof TypeInfos) {
+ $typeInfos = $typeInfos->union(static::transformPipeconc($aResult));
+ } else {
+ $typeInfos = static::transformPipeconc($aResult);
+ }
+ }
+ return $typeInfos;
+ }
+
+ public static function transformPipeconc(PipeconcResult $result): TypeInfos {
+ return static::transformConc($result->result[2]);
+ }
+
+ public static function transformConc(ConcResult $result): TypeInfos {
+ $termTypeInfos = static::transformTerm($result->result[0]);
+ $commatermlistTypeInfos = static::transformCommatermlist($result->result[1]);
+ if ($commatermlistTypeInfos) {
+ return $termTypeInfos->conc($commatermlistTypeInfos)->generateName();
+ }
+ return $termTypeInfos;
+ }
+
+ public static function transformCommatermlist(CommatermlistResult $result): ?TypeInfos {
+ $typeInfos = null;
+ foreach ($result->result as $aResult) {
+ if ($typeInfos instanceof TypeInfos) {
+ $typeInfos = $typeInfos->conc(static::transformCommaterm($aResult));
+ } else {
+ $typeInfos = static::transformCommaterm($aResult);
+ }
+ }
+ return $typeInfos;
+ }
+
+ public static function transformCommaterm(CommatermResult $result): TypeInfos {
+ return static::transformTerm($result->result[2]);
+ }
+
+ // expr = num, { ('+'|'-'), num }
+ // conc <expr>
+ // bare <num>
+ // rep <>
+ // conc <>
+ // pipe <>
+ // sq <>
+ // sq <>
+ // bare <num>
+ //
+ // term = bareword | sq | dq | regex | group | repetition | optional
+ public static function transformTerm(TermResult $result): TypeInfos {
+ if ($result->result instanceof BarewordResult) {
+ return TypeInfos::rule(static::toRuleName($result->result));
+ }
+ if ($result->result instanceof SqResult) {
+ return TypeInfos::string($result->result->result[0]->result)->generateName();
+ }
+ if ($result->result instanceof DqResult) {
+ return TypeInfos::string($result->result->result[0]->result)->generateName();
+ }
+ if ($result->result instanceof RegexResult) {
+ return TypeInfos::regexp($result->result->result[0]->result)->generateName();
+ }
+ if ($result->result instanceof GroupResult) {
+ return static::transformGroup($result->result)->generateName();
+ }
+ if ($result->result instanceof OptionalResult) {
+ return static::transformOptional($result->result)->generateName();
+ }
+ return static::transformRepetition($result->result)->generateName();
+ }
+
+ // group = "(", space, alt, ")", space ;
+ public static function transformGroup(GroupResult $result): TypeInfos {
+ return static::transformAlt($result->result[2]);
+ }
+ public static function transformOptional(OptionalResult $result): TypeInfos {
+ return static::transformAlt($result->result[2])->optional();
+ }
+ public static function transformRepetition(RepetitionResult $result): TypeInfos {
+ return static::transformAlt($result->result[2])->repetition();
+ }
+}
+
+
+
+
+
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class EmptyParser extends BaseParser
-{
- /**
- * @param callable $generator
- */
- public function __construct($generator = null)
- {
- $this->description = 'new '.get_class().'()';
-
- parent::__construct(array(), $generator);
- }
-
- protected function accept($string, $p)
- {
- return array(
- 'r' => null,
- 'p' => $p,
- );
- }
-
- protected function evalAcceptsEmpty()
- {
- return true;
- }
-
- protected function firstSet()
- {
- return array();
- }
-}
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class GrammerException extends \Exception { }
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class GrammerParser extends BaseParser
-{
- /** @var string */
- protected $s;
-
- /**
- * @param string $s
- * @param array<string, BaseParser|string> $internals
- * @param callable $generator
- */
- public function __construct($s, array $internals, $generator = null)
- {
- $this->description = 'new '.get_class().'('.$this->serializeInternals($internals).')';
- parent::__construct($internals, $generator);
-
- $this->s = (string)$s;
- if ( !isset($this->internals[$this->s])) {
- throw new GrammerException("No parser given for start rule {$this->s}!");
- }
-
- $this->resolveParserNames();
-
- $this->floodAcceptsEmpty();
-
- $this->testInifinitGreedy();
-
- foreach ($this->internals as $internal) {
- /** @var BaseParser[] */
- $done = array();
- /** @var BaseParser[] */
- $todo = array($internal);
- while ($current = array_shift($todo)) {
- $done[] = $current;
-
- foreach ($current->firstSet() as $next) {
- if ($next === $current) {
- throw new GrammerException("Grammer is left recursive in {$internal}!");
- }
-
- if (in_array($next, $done, true)) {
- continue;
- }
-
- $todo[] = $next;
- }
- }
- }
- }
-
- /** @return void */
- protected function resolveParserNames()
- {
- /** @var BaseParser[] */
- $done = array();
- /** @var BaseParser[] */
- $todo = array($this);
-
- while ($current = array_shift($todo)) {
- $done[] = $current;
-
- foreach ($current->internals as $key => $internal) {
- if ($internal instanceof BaseParser) {
- if ( !in_array($internal, $done, true) && !in_array($internal, $todo, true)) {
- $todo[] = $internal;
- }
- continue;
- }
-
- if ( !isset($this->internals[$internal])) {
- throw new GrammerException("No parser given for rule {$internal} used by {$current}!");
- }
-
- $current->internals[$key] = $this->internals[$internal];
- }
- }
- }
-
- /** @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;
- }
- if ( !in_array($internal, $done, true) && !in_array($internal, $todo, true)) {
- $todo[] = $internal;
- }
- if ( !$internal->evalAcceptsEmpty()) {
- continue;
- }
-
- $internal->acceptsEmpty = true;
- $change = true;
- break;
- }
-
- if ($change) {
- break;
- }
- }
- }
- }
-
-
- /** @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) {
- if ( !in_array($internal, $done, true) && !in_array($internal, $todo, true)) {
- $todo[] = $internal;
- }
- }
-
- if ( !$current instanceof GreedyMultiParser) {
- continue;
- }
- 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)) {
- if ($r['p'] !== strlen($code)) {
- return false;
- }
-
- return $r['r']['r'];
- }
- return false;
- }
-
- protected function accept($string, $p)
- {
- if ($this->internals[$this->s] instanceof BaseParser) {
- if ($r = $this->internals[$this->s]->parse($string, $p)) {
- return $r;
- }
- return false;
- }
- throw new \Exception('BaseParser expected');
- }
-
- protected function evalAcceptsEmpty()
- {
- if ($this->internals[$this->s] instanceof BaseParser) {
- return $this->internals[$this->s]->acceptsEmpty;
- }
- throw new \Exception('BaseParser expected');
- }
-
- protected function firstSet()
- {
- if ($this->internals[$this->s] instanceof BaseParser) {
- return array($this->internals[$this->s]);
- }
- throw new \Exception('BaseParser expected');
- }
-}
-
+++ /dev/null
-<?php
-
-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;
- $this->optional = $optional;
-
- $this->description = 'new '.get_class()."({$internal}, {$lower}, {$optional})";
- parent::__construct(array($internal), $generator);
- }
-
- /** @return int */
- public function getLower()
- {
- return $this->lower;
- }
-
- /** @return int|null */
- public function getOptional()
- {
- return $this->optional;
- }
-
- 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)) {
- $r[] = $i['r'];
- $p = $i['p'];
-
- continue;
- }
- return false;
- }
-
- for ($j = 0; $this->optional === null || $j < $this->optional; $j++) {
- if ($i = $this->internals[0]->parse($string, $p)) {
- $r[] = $i['r'];
- $p = $i['p'];
-
- continue;
- }
- break;
- }
-
- return array(
- 'r' => $r,
- 'p' => $p,
- );
- }
-
- protected function evalAcceptsEmpty()
- {
- if ($this->internals[0] instanceof BaseParser) {
- return $this->lower == 0 || $this->internals[0]->acceptsEmpty;
- }
- throw new \Exception('Expected a BaseParser!');
- }
-
- protected function firstSet()
- {
- if ($this->internals[0] instanceof BaseParser) {
- return array($this->internals[0]);
- }
- throw new \Exception('Expected a BaseParser!');
- }
-}
+++ /dev/null
-<?php
-
-namespace Graphit\Parser;
-
-class LazyAltParser extends BaseParser
-{
- /**
- * @param array<BaseParser|string> $internals
- * @param callable $generator
- */
- public function __construct(array $internals, $generator = null)
- {
- if ( !$internals) {
- throw new GrammerException('At least one internal parser is needed!');
- }
-
- $this->description = 'new '.get_class().'('.$this->serializeInternals($internals).')';
- parent::__construct($internals, $generator);
- }
-
- 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'],
- 'p' => $i['p'],
- );
- }
- }
- return false;
- }
-
- 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;
- }
- }
- return false;
- }
-
- protected function firstSet()
- {
- $internals = [];
- foreach ($this->internals as $internal) {
- if ($internal instanceof BaseParser) {
- $internals[] = $internal;
- } else {
- throw new \Exception('Expected a BaseParser!');
- }
- }
-
- return $internals;
- }
-}
+++ /dev/null
-<?php
-
-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;
- if (substr($this->pattern, 1, 1) !== '^') {
- throw new GrammerException('Pattern {$this->pattern} must anchor at the beginning of the string!');
- }
-
- $this->description = 'new '.get_class().'('.var_export($this->pattern, true).')';
- 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();
- if (preg_match($this->pattern, substr($string, $p), $matches) !== 1) {
- return false;
- }
-
- return array(
- 'r' => $matches,
- 'p' => $p + strlen($matches[0]),
- );
- }
-
- /** @return bool */
- protected function evalAcceptsEmpty()
- {
- return preg_match($this->pattern, '') === 1;
- }
-
- protected function firstSet()
- {
- return array();
- }
-}
-
+++ /dev/null
-<?php
-
-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;
-
- $this->description = 'new '.get_class().'('.var_export($this->needle, true).')';
- 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) {
- return false;
- }
-
- return array(
- 'r' => $this->needle,
- 'p' => $p + strlen($this->needle),
- );
- }
-
- /** @return bool */
- protected function evalAcceptsEmpty()
- {
- return $this->needle === '';
- }
-
- protected function firstSet()
- {
- return array();
- }
-}
-