+<?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