<?php
/*
 * Generated by peggy 1.0.0 with phpeggy plugin
 *
 * https://peggyjs.org/
 */

namespace MediaWiki\Extension\Math\WikiTexVC;

/* Useful functions: */

/* chr_unicode - get unicode character from its char code */
if (!function_exists("MediaWiki\\Extension\\Math\\WikiTexVC\\chr_unicode")) {
    function chr_unicode($code) {
        return html_entity_decode("&#$code;", ENT_QUOTES, "UTF-8");
    }
}
/* ord_unicode - get unicode char code from string */
if (!function_exists("MediaWiki\\Extension\\Math\\WikiTexVC\\ord_unicode")) {
    function ord_unicode($character) {
        if (strlen($character) === 1) {
            return ord($character);
        }
        $json = json_encode($character);
        $utf16_1 = hexdec(substr($json, 3, 4));
        if (substr($json, 7, 2) === "\u") {
            $utf16_2 = hexdec(substr($json, 9, 4));
            return 0x10000 + (($utf16_1 & 0x3ff) << 10) + ($utf16_2 & 0x3ff);
        } else {
            return $utf16_1;
        }
    }
}
/* peg_regex_test - multibyte regex test */
if (!function_exists("MediaWiki\\Extension\\Math\\WikiTexVC\\peg_regex_test")) {
    function peg_regex_test($pattern, $string) {
        if (substr($pattern, -1) == "i") {
            return mb_eregi(substr($pattern, 1, -2), $string);
        } else {
            return mb_ereg(substr($pattern, 1, -1), $string);
        }
    }
}

class ParserIntent {
    private $peg_currPos          = 0;
    private $peg_reportedPos      = 0;
    private $peg_cachedPos        = 0;
    private $peg_cachedPosDetails = array('line' => 1, 'column' => 1, 'seenCR' => false );
    private $peg_maxFailPos       = 0;
    private $peg_maxFailExpected  = array();
    private $peg_silentFails      = 0;
    private $input                = array();
    private $input_length         = 0;
    public $peg_cache = array();

    private function cleanup_state() {
      $this->peg_currPos          = 0;
      $this->peg_reportedPos      = 0;
      $this->peg_cachedPos        = 0;
      $this->peg_cachedPosDetails = array('line' => 1, 'column' => 1, 'seenCR' => false );
      $this->peg_maxFailPos       = 0;
      $this->peg_maxFailExpected  = array();
      $this->peg_silentFails      = 0;
      $this->input                = array();
      $this->input_length         = 0;
      $this->peg_cache = array();
    }

    private function input_substr($start, $length) {
      if ($length === 1 && $start < $this->input_length) {
        return $this->input[$start];
      }
      $substr = '';
      $max = min($start + $length, $this->input_length);
      for ($i = $start; $i < $max; $i++) {
        $substr .= $this->input[$i];
      }
      return $substr;
    }


    private function text() {
      return $this->input_substr($this->peg_reportedPos, $this->peg_currPos - $this->peg_reportedPos);
    }

    private function offset() {
      return $this->peg_reportedPos;
    }

    private function line() {
      $compute_pd = $this->peg_computePosDetails($this->peg_reportedPos);
      return $compute_pd["line"];
    }

    private function column() {
      $compute_pd = $this->peg_computePosDetails($this->peg_reportedPos);
      return $compute_pd["column"];
    }

    private function expected($description) {
      throw $this->peg_buildException(
        null,
        array(array("type" => "other", "description" => $description )),
        $this->peg_reportedPos
      );
    }

    private function error($message) {
      throw $this->peg_buildException($message, null, $this->peg_reportedPos);
    }

    private function peg_advancePos(&$details, $startPos, $endPos) {
      for ($p = $startPos; $p < $endPos; $p++) {
        $ch = $this->input_substr($p, 1);
        if ($ch === "\n") {
          if (!$details["seenCR"]) { $details["line"]++; }
          $details["column"] = 1;
          $details["seenCR"] = false;
        } else if ($ch === "\r" || $ch === "\u2028" || $ch === "\u2029") {
          $details["line"]++;
          $details["column"] = 1;
          $details["seenCR"] = true;
        } else {
          $details["column"]++;
          $details["seenCR"] = false;
        }
      }
    }

    private function peg_computePosDetails($pos) {
      if ($this->peg_cachedPos !== $pos) {
        if ($this->peg_cachedPos > $pos) {
          $this->peg_cachedPos = 0;
          $this->peg_cachedPosDetails = array( "line" => 1, "column" => 1, "seenCR" => false );
        }
        $this->peg_advancePos($this->peg_cachedPosDetails, $this->peg_cachedPos, $pos);
        $this->peg_cachedPos = $pos;
      }

      return $this->peg_cachedPosDetails;
    }

    private function peg_fail($expected) {
      if ($this->peg_currPos < $this->peg_maxFailPos) { return; }

      if ($this->peg_currPos > $this->peg_maxFailPos) {
        $this->peg_maxFailPos = $this->peg_currPos;
        $this->peg_maxFailExpected = array();
      }

      $this->peg_maxFailExpected[] = $expected;
    }

    private function peg_buildException_expectedComparator($a, $b) {
      if ($a["description"] < $b["description"]) {
        return -1;
      } else if ($a["description"] > $b["description"]) {
        return 1;
      } else {
        return 0;
      }
    }

    private function peg_buildException($message, $expected, $pos) {
      $posDetails = $this->peg_computePosDetails($pos);
      $found      = $pos < $this->input_length ? $this->input[$pos] : null;

      if ($expected !== null) {
        usort($expected, array($this, "peg_buildException_expectedComparator"));
        $i = 1;
        while ($i < count($expected)) {
          if ($expected[$i - 1] === $expected[$i]) {
            array_splice($expected, $i, 1);
          } else {
            $i++;
          }
        }
      }

      if ($message === null) {
        $expectedDescs = array_fill(0, count($expected), null);

        for ($i = 0; $i < count($expected); $i++) {
          $expectedDescs[$i] = $expected[$i]["description"];
        }

        $expectedDesc = count($expected) > 1
          ? join(", ", array_slice($expectedDescs, 0, -1))
              . " or "
              . $expectedDescs[count($expected) - 1]
          : $expectedDescs[0];

        $foundDesc = $found ? json_encode($found) : "end of input";

        $message = "Expected " . $expectedDesc . " but " . $foundDesc . " found.";
      }

      return new SyntaxError(
        $message,
        $expected,
        $found,
        $pos,
        $posDetails["line"],
        $posDetails["column"]
      );
    }

    private $peg_FAILED;
    private $peg_c0;
    private $peg_c1;
    private $peg_c2;
    private $peg_c3;
    private $peg_c4;
    private $peg_c5;
    private $peg_c6;
    private $peg_c7;
    private $peg_c8;
    private $peg_c9;
    private $peg_c10;
    private $peg_c11;
    private $peg_c12;
    private $peg_c13;
    private $peg_c14;
    private $peg_c15;
    private $peg_c16;
    private $peg_c17;
    private $peg_c18;
    private $peg_c19;
    private $peg_c20;
    private $peg_c21;
    private $peg_c22;
    private $peg_c23;
    private $peg_c24;
    private $peg_c25;
    private $peg_c26;
    private $peg_c27;
    private $peg_c28;
    private $peg_c29;
    private $peg_c30;
    private $peg_c31;
    private $peg_c32;
    private $peg_c33;
    private $peg_c34;
    private $peg_c35;
    private $peg_c36;
    private $peg_c37;
    private $peg_c38;
    private $peg_c39;
    private $peg_c40;
    private $peg_c41;
    private $peg_c42;
    private $peg_c43;
    private $peg_c44;
    private $peg_c45;
    private $peg_c46;
    private $peg_c47;
    private $peg_c48;
    private $peg_c49;
    private $peg_c50;
    private $peg_c51;
    private $peg_c52;
    private $peg_c53;

    private function peg_f0() {
        return $this->text();
    }
    private function peg_f1($sign, $intPart, $fracPart, $hint) {
                $intPartS = implode($intPart ?? []);
                $fracPartS = implode($fracPart[1] ?? []);
                $value = $sign ?? "";
                $value .= $intPartS;
                $value .= isset($fracPart) ? ("." . $fracPartS ) : "";
                return ["type" => "number", "value" => $value, "hint"=>$hint];
            }
    private function peg_f2() {
    	return substr($this->text(),1);
    }
    private function peg_f3($intent, $hint, $args) {
        $argsF = [];
        foreach ($args as $arg) {
           $argsF[] = $arg[2];
        }
       $returnObj =  [
             "type" => "application",
             "intent" => $intent,
             "hint" => isset($hint) ? $hint : null,
             "arguments" => $argsF
         ];
       return $returnObj;
    }
    private function peg_f4($first, $rest) {
          $args = [$first];
          foreach ($rest as $arg) {
              $args[] = $arg[2];
          }
          return $args;
    }
    private function peg_f5($hintType) {
        return $hintType;
    }
    private function peg_f6() { return $this->text(); }
    private function peg_f7() {return " "; }

    private function peg_parsestart() {

      $key = $this->peg_currPos * 13 + 0;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_parseapplication();
      if ($s0 === $this->peg_FAILED) {
        $s0 = $this->peg_parseintent();
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parseintent() {

      $key = $this->peg_currPos * 13 + 1;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_parseconcept_or_literal();
      if ($s0 === $this->peg_FAILED) {
        $s0 = $this->peg_parsenumber();
        if ($s0 === $this->peg_FAILED) {
          $s0 = $this->peg_parsereference();
          if ($s0 === $this->peg_FAILED) {
            $s0 = $this->peg_parsestructure();
          }
        }
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parseconcept_or_literal() {

      $key = $this->peg_currPos * 13 + 2;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      $s1 = $this->peg_parseNCName();
      if ($s1 !== $this->peg_FAILED) {
        $this->peg_reportedPos = $s0;
        $s1 = $this->peg_f0();
      }
      $s0 = $s1;

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsenumber() {

      $key = $this->peg_currPos * 13 + 3;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c0) {
        $s1 = $this->peg_c0;
        $this->peg_currPos++;
      } else {
        $s1 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c1);
        }
      }
      if ($s1 === $this->peg_FAILED) {
        $s1 = null;
      }
      if ($s1 !== $this->peg_FAILED) {
        $s2 = $this->peg_parsedigits();
        if ($s2 !== $this->peg_FAILED) {
          $s3 = $this->peg_currPos;
          if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c2) {
            $s4 = $this->peg_c2;
            $this->peg_currPos++;
          } else {
            $s4 = $this->peg_FAILED;
            if ($this->peg_silentFails === 0) {
                $this->peg_fail($this->peg_c3);
            }
          }
          if ($s4 !== $this->peg_FAILED) {
            $s5 = $this->peg_parsedigits();
            if ($s5 !== $this->peg_FAILED) {
              $s4 = array($s4, $s5);
              $s3 = $s4;
            } else {
              $this->peg_currPos = $s3;
              $s3 = $this->peg_FAILED;
            }
          } else {
            $this->peg_currPos = $s3;
            $s3 = $this->peg_FAILED;
          }
          if ($s3 === $this->peg_FAILED) {
            $s3 = null;
          }
          if ($s3 !== $this->peg_FAILED) {
            $s4 = $this->peg_parsehint();
            if ($s4 === $this->peg_FAILED) {
              $s4 = null;
            }
            if ($s4 !== $this->peg_FAILED) {
              $this->peg_reportedPos = $s0;
              $s1 = $this->peg_f1($s1, $s2, $s3, $s4);
              $s0 = $s1;
            } else {
              $this->peg_currPos = $s0;
              $s0 = $this->peg_FAILED;
            }
          } else {
            $this->peg_currPos = $s0;
            $s0 = $this->peg_FAILED;
          }
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsedigits() {

      $key = $this->peg_currPos * 13 + 4;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = array();
      $s1 = $this->peg_parsedigit();
      if ($s1 !== $this->peg_FAILED) {
        while ($s1 !== $this->peg_FAILED) {
          $s0[] = $s1;
          $s1 = $this->peg_parsedigit();
        }
      } else {
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsedigit() {

      $key = $this->peg_currPos * 13 + 5;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      if (peg_regex_test($this->peg_c4, $this->input_substr($this->peg_currPos, 1))) {
        $s0 = $this->input_substr($this->peg_currPos, 1);
        $this->peg_currPos++;
      } else {
        $s0 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c5);
        }
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsereference() {

      $key = $this->peg_currPos * 13 + 6;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c6) {
        $s1 = $this->peg_c6;
        $this->peg_currPos++;
      } else {
        $s1 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c7);
        }
      }
      if ($s1 !== $this->peg_FAILED) {
        $s2 = $this->peg_parseNCName();
        if ($s2 !== $this->peg_FAILED) {
          $this->peg_reportedPos = $s0;
          $s1 = $this->peg_f2();
          $s0 = $s1;
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsestructure() {

      $key = $this->peg_currPos * 13 + 7;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c8) {
        $s1 = $this->peg_c8;
        $this->peg_currPos++;
      } else {
        $s1 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c9);
        }
      }
      if ($s1 !== $this->peg_FAILED) {
        if ($this->input_substr($this->peg_currPos, 6) === $this->peg_c10) {
          $s2 = $this->peg_c10;
          $this->peg_currPos += 6;
        } else {
          $s2 = $this->peg_FAILED;
          if ($this->peg_silentFails === 0) {
              $this->peg_fail($this->peg_c11);
          }
        }
        if ($s2 === $this->peg_FAILED) {
          if ($this->input_substr($this->peg_currPos, 9) === $this->peg_c12) {
            $s2 = $this->peg_c12;
            $this->peg_currPos += 9;
          } else {
            $s2 = $this->peg_FAILED;
            if ($this->peg_silentFails === 0) {
                $this->peg_fail($this->peg_c13);
            }
          }
          if ($s2 === $this->peg_FAILED) {
            if ($this->input_substr($this->peg_currPos, 9) === $this->peg_c14) {
              $s2 = $this->peg_c14;
              $this->peg_currPos += 9;
            } else {
              $s2 = $this->peg_FAILED;
              if ($this->peg_silentFails === 0) {
                  $this->peg_fail($this->peg_c15);
              }
            }
            if ($s2 === $this->peg_FAILED) {
              if ($this->input_substr($this->peg_currPos, 6) === $this->peg_c16) {
                $s2 = $this->peg_c16;
                $this->peg_currPos += 6;
              } else {
                $s2 = $this->peg_FAILED;
                if ($this->peg_silentFails === 0) {
                    $this->peg_fail($this->peg_c17);
                }
              }
              if ($s2 === $this->peg_FAILED) {
                if ($this->input_substr($this->peg_currPos, 9) === $this->peg_c18) {
                  $s2 = $this->peg_c18;
                  $this->peg_currPos += 9;
                } else {
                  $s2 = $this->peg_FAILED;
                  if ($this->peg_silentFails === 0) {
                      $this->peg_fail($this->peg_c19);
                  }
                }
                if ($s2 === $this->peg_FAILED) {
                  if ($this->input_substr($this->peg_currPos, 16) === $this->peg_c20) {
                    $s2 = $this->peg_c20;
                    $this->peg_currPos += 16;
                  } else {
                    $s2 = $this->peg_FAILED;
                    if ($this->peg_silentFails === 0) {
                        $this->peg_fail($this->peg_c21);
                    }
                  }
                  if ($s2 === $this->peg_FAILED) {
                    if ($this->input_substr($this->peg_currPos, 16) === $this->peg_c22) {
                      $s2 = $this->peg_c22;
                      $this->peg_currPos += 16;
                    } else {
                      $s2 = $this->peg_FAILED;
                      if ($this->peg_silentFails === 0) {
                          $this->peg_fail($this->peg_c23);
                      }
                    }
                    if ($s2 === $this->peg_FAILED) {
                      if ($this->input_substr($this->peg_currPos, 17) === $this->peg_c24) {
                        $s2 = $this->peg_c24;
                        $this->peg_currPos += 17;
                      } else {
                        $s2 = $this->peg_FAILED;
                        if ($this->peg_silentFails === 0) {
                            $this->peg_fail($this->peg_c25);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        if ($s2 !== $this->peg_FAILED) {
          $this->peg_reportedPos = $s0;
          $s1 = $this->peg_f2();
          $s0 = $s1;
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parseapplication() {

      $key = $this->peg_currPos * 13 + 8;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      $s1 = $this->peg_parseintent();
      if ($s1 !== $this->peg_FAILED) {
        $s2 = $this->peg_parsehint();
        if ($s2 === $this->peg_FAILED) {
          $s2 = null;
        }
        if ($s2 !== $this->peg_FAILED) {
          $s3 = array();
          $s4 = $this->peg_currPos;
          if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c26) {
            $s5 = $this->peg_c26;
            $this->peg_currPos++;
          } else {
            $s5 = $this->peg_FAILED;
            if ($this->peg_silentFails === 0) {
                $this->peg_fail($this->peg_c27);
            }
          }
          if ($s5 !== $this->peg_FAILED) {
            $s6 = $this->peg_parseWS();
            if ($s6 === $this->peg_FAILED) {
              $s6 = null;
            }
            if ($s6 !== $this->peg_FAILED) {
              $s7 = $this->peg_parsearguments();
              if ($s7 === $this->peg_FAILED) {
                $s7 = null;
              }
              if ($s7 !== $this->peg_FAILED) {
                $s8 = $this->peg_parseWS();
                if ($s8 === $this->peg_FAILED) {
                  $s8 = null;
                }
                if ($s8 !== $this->peg_FAILED) {
                  if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c28) {
                    $s9 = $this->peg_c28;
                    $this->peg_currPos++;
                  } else {
                    $s9 = $this->peg_FAILED;
                    if ($this->peg_silentFails === 0) {
                        $this->peg_fail($this->peg_c29);
                    }
                  }
                  if ($s9 !== $this->peg_FAILED) {
                    $s5 = array($s5, $s6, $s7, $s8, $s9);
                    $s4 = $s5;
                  } else {
                    $this->peg_currPos = $s4;
                    $s4 = $this->peg_FAILED;
                  }
                } else {
                  $this->peg_currPos = $s4;
                  $s4 = $this->peg_FAILED;
                }
              } else {
                $this->peg_currPos = $s4;
                $s4 = $this->peg_FAILED;
              }
            } else {
              $this->peg_currPos = $s4;
              $s4 = $this->peg_FAILED;
            }
          } else {
            $this->peg_currPos = $s4;
            $s4 = $this->peg_FAILED;
          }
          if ($s4 !== $this->peg_FAILED) {
            while ($s4 !== $this->peg_FAILED) {
              $s3[] = $s4;
              $s4 = $this->peg_currPos;
              if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c26) {
                $s5 = $this->peg_c26;
                $this->peg_currPos++;
              } else {
                $s5 = $this->peg_FAILED;
                if ($this->peg_silentFails === 0) {
                    $this->peg_fail($this->peg_c27);
                }
              }
              if ($s5 !== $this->peg_FAILED) {
                $s6 = $this->peg_parseWS();
                if ($s6 === $this->peg_FAILED) {
                  $s6 = null;
                }
                if ($s6 !== $this->peg_FAILED) {
                  $s7 = $this->peg_parsearguments();
                  if ($s7 === $this->peg_FAILED) {
                    $s7 = null;
                  }
                  if ($s7 !== $this->peg_FAILED) {
                    $s8 = $this->peg_parseWS();
                    if ($s8 === $this->peg_FAILED) {
                      $s8 = null;
                    }
                    if ($s8 !== $this->peg_FAILED) {
                      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c28) {
                        $s9 = $this->peg_c28;
                        $this->peg_currPos++;
                      } else {
                        $s9 = $this->peg_FAILED;
                        if ($this->peg_silentFails === 0) {
                            $this->peg_fail($this->peg_c29);
                        }
                      }
                      if ($s9 !== $this->peg_FAILED) {
                        $s5 = array($s5, $s6, $s7, $s8, $s9);
                        $s4 = $s5;
                      } else {
                        $this->peg_currPos = $s4;
                        $s4 = $this->peg_FAILED;
                      }
                    } else {
                      $this->peg_currPos = $s4;
                      $s4 = $this->peg_FAILED;
                    }
                  } else {
                    $this->peg_currPos = $s4;
                    $s4 = $this->peg_FAILED;
                  }
                } else {
                  $this->peg_currPos = $s4;
                  $s4 = $this->peg_FAILED;
                }
              } else {
                $this->peg_currPos = $s4;
                $s4 = $this->peg_FAILED;
              }
            }
          } else {
            $s3 = $this->peg_FAILED;
          }
          if ($s3 !== $this->peg_FAILED) {
            $this->peg_reportedPos = $s0;
            $s1 = $this->peg_f3($s1, $s2, $s3);
            $s0 = $s1;
          } else {
            $this->peg_currPos = $s0;
            $s0 = $this->peg_FAILED;
          }
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsearguments() {

      $key = $this->peg_currPos * 13 + 9;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      $s1 = $this->peg_parseWS();
      if ($s1 === $this->peg_FAILED) {
        $s1 = null;
      }
      if ($s1 !== $this->peg_FAILED) {
        $s2 = $this->peg_parsestart();
        if ($s2 !== $this->peg_FAILED) {
          $s3 = $this->peg_parseWS();
          if ($s3 === $this->peg_FAILED) {
            $s3 = null;
          }
          if ($s3 !== $this->peg_FAILED) {
            $s4 = array();
            $s5 = $this->peg_currPos;
            if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c30) {
              $s6 = $this->peg_c30;
              $this->peg_currPos++;
            } else {
              $s6 = $this->peg_FAILED;
              if ($this->peg_silentFails === 0) {
                  $this->peg_fail($this->peg_c31);
              }
            }
            if ($s6 !== $this->peg_FAILED) {
              $s7 = $this->peg_parseWS();
              if ($s7 === $this->peg_FAILED) {
                $s7 = null;
              }
              if ($s7 !== $this->peg_FAILED) {
                $s8 = $this->peg_parsestart();
                if ($s8 !== $this->peg_FAILED) {
                  $s9 = $this->peg_parseWS();
                  if ($s9 === $this->peg_FAILED) {
                    $s9 = null;
                  }
                  if ($s9 !== $this->peg_FAILED) {
                    $s6 = array($s6, $s7, $s8, $s9);
                    $s5 = $s6;
                  } else {
                    $this->peg_currPos = $s5;
                    $s5 = $this->peg_FAILED;
                  }
                } else {
                  $this->peg_currPos = $s5;
                  $s5 = $this->peg_FAILED;
                }
              } else {
                $this->peg_currPos = $s5;
                $s5 = $this->peg_FAILED;
              }
            } else {
              $this->peg_currPos = $s5;
              $s5 = $this->peg_FAILED;
            }
            while ($s5 !== $this->peg_FAILED) {
              $s4[] = $s5;
              $s5 = $this->peg_currPos;
              if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c30) {
                $s6 = $this->peg_c30;
                $this->peg_currPos++;
              } else {
                $s6 = $this->peg_FAILED;
                if ($this->peg_silentFails === 0) {
                    $this->peg_fail($this->peg_c31);
                }
              }
              if ($s6 !== $this->peg_FAILED) {
                $s7 = $this->peg_parseWS();
                if ($s7 === $this->peg_FAILED) {
                  $s7 = null;
                }
                if ($s7 !== $this->peg_FAILED) {
                  $s8 = $this->peg_parsestart();
                  if ($s8 !== $this->peg_FAILED) {
                    $s9 = $this->peg_parseWS();
                    if ($s9 === $this->peg_FAILED) {
                      $s9 = null;
                    }
                    if ($s9 !== $this->peg_FAILED) {
                      $s6 = array($s6, $s7, $s8, $s9);
                      $s5 = $s6;
                    } else {
                      $this->peg_currPos = $s5;
                      $s5 = $this->peg_FAILED;
                    }
                  } else {
                    $this->peg_currPos = $s5;
                    $s5 = $this->peg_FAILED;
                  }
                } else {
                  $this->peg_currPos = $s5;
                  $s5 = $this->peg_FAILED;
                }
              } else {
                $this->peg_currPos = $s5;
                $s5 = $this->peg_FAILED;
              }
            }
            if ($s4 !== $this->peg_FAILED) {
              $this->peg_reportedPos = $s0;
              $s1 = $this->peg_f4($s2, $s4);
              $s0 = $s1;
            } else {
              $this->peg_currPos = $s0;
              $s0 = $this->peg_FAILED;
            }
          } else {
            $this->peg_currPos = $s0;
            $s0 = $this->peg_FAILED;
          }
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parsehint() {

      $key = $this->peg_currPos * 13 + 10;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c32) {
        $s1 = $this->peg_c32;
        $this->peg_currPos++;
      } else {
        $s1 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c33);
        }
      }
      if ($s1 === $this->peg_FAILED) {
        if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c8) {
          $s1 = $this->peg_c8;
          $this->peg_currPos++;
        } else {
          $s1 = $this->peg_FAILED;
          if ($this->peg_silentFails === 0) {
              $this->peg_fail($this->peg_c9);
          }
        }
      }
      if ($s1 !== $this->peg_FAILED) {
        if ($this->input_substr($this->peg_currPos, 6) === $this->peg_c34) {
          $s2 = $this->peg_c34;
          $this->peg_currPos += 6;
        } else {
          $s2 = $this->peg_FAILED;
          if ($this->peg_silentFails === 0) {
              $this->peg_fail($this->peg_c35);
          }
        }
        if ($s2 === $this->peg_FAILED) {
          if ($this->input_substr($this->peg_currPos, 5) === $this->peg_c36) {
            $s2 = $this->peg_c36;
            $this->peg_currPos += 5;
          } else {
            $s2 = $this->peg_FAILED;
            if ($this->peg_silentFails === 0) {
                $this->peg_fail($this->peg_c37);
            }
          }
          if ($s2 === $this->peg_FAILED) {
            if ($this->input_substr($this->peg_currPos, 7) === $this->peg_c38) {
              $s2 = $this->peg_c38;
              $this->peg_currPos += 7;
            } else {
              $s2 = $this->peg_FAILED;
              if ($this->peg_silentFails === 0) {
                  $this->peg_fail($this->peg_c39);
              }
            }
            if ($s2 === $this->peg_FAILED) {
              if ($this->input_substr($this->peg_currPos, 8) === $this->peg_c40) {
                $s2 = $this->peg_c40;
                $this->peg_currPos += 8;
              } else {
                $s2 = $this->peg_FAILED;
                if ($this->peg_silentFails === 0) {
                    $this->peg_fail($this->peg_c41);
                }
              }
              if ($s2 === $this->peg_FAILED) {
                if ($this->input_substr($this->peg_currPos, 6) === $this->peg_c42) {
                  $s2 = $this->peg_c42;
                  $this->peg_currPos += 6;
                } else {
                  $s2 = $this->peg_FAILED;
                  if ($this->peg_silentFails === 0) {
                      $this->peg_fail($this->peg_c43);
                  }
                }
                if ($s2 === $this->peg_FAILED) {
                  if ($this->input_substr($this->peg_currPos, 13) === $this->peg_c44) {
                    $s2 = $this->peg_c44;
                    $this->peg_currPos += 13;
                  } else {
                    $s2 = $this->peg_FAILED;
                    if ($this->peg_silentFails === 0) {
                        $this->peg_fail($this->peg_c45);
                    }
                  }
                  if ($s2 === $this->peg_FAILED) {
                    if ($this->input_substr($this->peg_currPos, 15) === $this->peg_c46) {
                      $s2 = $this->peg_c46;
                      $this->peg_currPos += 15;
                    } else {
                      $s2 = $this->peg_FAILED;
                      if ($this->peg_silentFails === 0) {
                          $this->peg_fail($this->peg_c47);
                      }
                    }
                  }
                }
              }
            }
          }
        }
        if ($s2 !== $this->peg_FAILED) {
          $this->peg_reportedPos = $s0;
          $s1 = $this->peg_f5($s2);
          $s0 = $s1;
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parseNCName() {

      $key = $this->peg_currPos * 13 + 11;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      $s1 = $this->peg_currPos;
      $this->peg_silentFails++;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c8) {
        $s2 = $this->peg_c8;
        $this->peg_currPos++;
      } else {
        $s2 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c9);
        }
      }
      $this->peg_silentFails--;
      if ($s2 === $this->peg_FAILED) {
        $s1 = null;
      } else {
        $this->peg_currPos = $s1;
        $s1 = $this->peg_FAILED;
      }
      if ($s1 !== $this->peg_FAILED) {
        if (peg_regex_test($this->peg_c48, $this->input_substr($this->peg_currPos, 1))) {
          $s2 = $this->input_substr($this->peg_currPos, 1);
          $this->peg_currPos++;
        } else {
          $s2 = $this->peg_FAILED;
          if ($this->peg_silentFails === 0) {
              $this->peg_fail($this->peg_c49);
          }
        }
        if ($s2 !== $this->peg_FAILED) {
          $s3 = array();
          if (peg_regex_test($this->peg_c50, $this->input_substr($this->peg_currPos, 1))) {
            $s4 = $this->input_substr($this->peg_currPos, 1);
            $this->peg_currPos++;
          } else {
            $s4 = $this->peg_FAILED;
            if ($this->peg_silentFails === 0) {
                $this->peg_fail($this->peg_c51);
            }
          }
          while ($s4 !== $this->peg_FAILED) {
            $s3[] = $s4;
            if (peg_regex_test($this->peg_c50, $this->input_substr($this->peg_currPos, 1))) {
              $s4 = $this->input_substr($this->peg_currPos, 1);
              $this->peg_currPos++;
            } else {
              $s4 = $this->peg_FAILED;
              if ($this->peg_silentFails === 0) {
                  $this->peg_fail($this->peg_c51);
              }
            }
          }
          if ($s3 !== $this->peg_FAILED) {
            $this->peg_reportedPos = $s0;
            $s1 = $this->peg_f6();
            $s0 = $s1;
          } else {
            $this->peg_currPos = $s0;
            $s0 = $this->peg_FAILED;
          }
        } else {
          $this->peg_currPos = $s0;
          $s0 = $this->peg_FAILED;
        }
      } else {
        $this->peg_currPos = $s0;
        $s0 = $this->peg_FAILED;
      }

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

    private function peg_parseWS() {

      $key = $this->peg_currPos * 13 + 12;
      $cached = isset($this->peg_cache[$key]) ? $this->peg_cache[$key] : null;

      if ($cached) {
        $this->peg_currPos = $cached["nextPos"];
        return $cached["result"];
      }

      $s0 = $this->peg_currPos;
      if ($this->input_substr($this->peg_currPos, 1) === $this->peg_c52) {
        $s1 = $this->peg_c52;
        $this->peg_currPos++;
      } else {
        $s1 = $this->peg_FAILED;
        if ($this->peg_silentFails === 0) {
            $this->peg_fail($this->peg_c53);
        }
      }
      if ($s1 !== $this->peg_FAILED) {
        $this->peg_reportedPos = $s0;
        $s1 = $this->peg_f7();
      }
      $s0 = $s1;

      $this->peg_cache[$key] = array ("nextPos" => $this->peg_currPos, "result" => $s0 );

      return $s0;
    }

  public function parse($input) {
    $arguments = func_get_args();
    $options = count($arguments) > 1 ? $arguments[1] : array();
    $this->cleanup_state();

    if (is_array($input)) {
        $this->input = $input;
    } else {
        preg_match_all("/./us", $input, $match);
        $this->input = $match[0];
    }
    $this->input_length = count($this->input);

    $old_regex_encoding = mb_regex_encoding();
    mb_regex_encoding("UTF-8");

    $this->peg_FAILED = new \stdClass;
    $this->peg_c0 = "-";
    $this->peg_c1 = array( "type" => "literal", "value" => "-", "description" => "\"-\"" );
    $this->peg_c2 = ".";
    $this->peg_c3 = array( "type" => "literal", "value" => ".", "description" => "\".\"" );
    $this->peg_c4 = "/^[0-9]/";
    $this->peg_c5 = array( "type" => "class", "value" => "[0-9]", "description" => "[0-9]" );
    $this->peg_c6 = "$";
    $this->peg_c7 = array( "type" => "literal", "value" => "$", "description" => "\"$\"" );
    $this->peg_c8 = ":";
    $this->peg_c9 = array( "type" => "literal", "value" => ":", "description" => "\":\"" );
    $this->peg_c10 = "common";
    $this->peg_c11 = array( "type" => "literal", "value" => "common", "description" => "\"common\"" );
    $this->peg_c12 = "structure";
    $this->peg_c13 = array( "type" => "literal", "value" => "structure", "description" => "\"structure\"" );
    $this->peg_c14 = "chemistry";
    $this->peg_c15 = array( "type" => "literal", "value" => "chemistry", "description" => "\"chemistry\"" );
    $this->peg_c16 = "matrix";
    $this->peg_c17 = array( "type" => "literal", "value" => "matrix", "description" => "\"matrix\"" );
    $this->peg_c18 = "equations";
    $this->peg_c19 = array( "type" => "literal", "value" => "equations", "description" => "\"equations\"" );
    $this->peg_c20 = "chemical-element";
    $this->peg_c21 = array( "type" => "literal", "value" => "chemical-element", "description" => "\"chemical-element\"" );
    $this->peg_c22 = "chemical-formula";
    $this->peg_c23 = array( "type" => "literal", "value" => "chemical-formula", "description" => "\"chemical-formula\"" );
    $this->peg_c24 = "chemical-equation";
    $this->peg_c25 = array( "type" => "literal", "value" => "chemical-equation", "description" => "\"chemical-equation\"" );
    $this->peg_c26 = "(";
    $this->peg_c27 = array( "type" => "literal", "value" => "(", "description" => "\"(\"" );
    $this->peg_c28 = ")";
    $this->peg_c29 = array( "type" => "literal", "value" => ")", "description" => "\")\"" );
    $this->peg_c30 = ",";
    $this->peg_c31 = array( "type" => "literal", "value" => ",", "description" => "\",\"" );
    $this->peg_c32 = "@";
    $this->peg_c33 = array( "type" => "literal", "value" => "@", "description" => "\"@\"" );
    $this->peg_c34 = "prefix";
    $this->peg_c35 = array( "type" => "literal", "value" => "prefix", "description" => "\"prefix\"" );
    $this->peg_c36 = "infix";
    $this->peg_c37 = array( "type" => "literal", "value" => "infix", "description" => "\"infix\"" );
    $this->peg_c38 = "postfix";
    $this->peg_c39 = array( "type" => "literal", "value" => "postfix", "description" => "\"postfix\"" );
    $this->peg_c40 = "function";
    $this->peg_c41 = array( "type" => "literal", "value" => "function", "description" => "\"function\"" );
    $this->peg_c42 = "silent";
    $this->peg_c43 = array( "type" => "literal", "value" => "silent", "description" => "\"silent\"" );
    $this->peg_c44 = "decimal-comma";
    $this->peg_c45 = array( "type" => "literal", "value" => "decimal-comma", "description" => "\"decimal-comma\"" );
    $this->peg_c46 = "thousands-comma";
    $this->peg_c47 = array( "type" => "literal", "value" => "thousands-comma", "description" => "\"thousands-comma\"" );
    $this->peg_c48 = "/^[a-zA-Z_]/";
    $this->peg_c49 = array( "type" => "class", "value" => "[a-zA-Z_]", "description" => "[a-zA-Z_]" );
    $this->peg_c50 = "/^[a-zA-Z0-9._-]/";
    $this->peg_c51 = array( "type" => "class", "value" => "[a-zA-Z0-9._-]", "description" => "[a-zA-Z0-9._-]" );
    $this->peg_c52 = " ";
    $this->peg_c53 = array( "type" => "literal", "value" => " ", "description" => "\" \"" );

    $peg_startRuleFunctions = array( 'start' => array($this, "peg_parsestart") );
    $peg_startRuleFunction  = array($this, "peg_parsestart");
    if (isset($options["startRule"])) {
      if (!(isset($peg_startRuleFunctions[$options["startRule"]]))) {
        throw new \Exception("Can't start parsing from rule \"" . $options["startRule"] . "\".");
      }

      $peg_startRuleFunction = $peg_startRuleFunctions[$options["startRule"]];
    }
    $peg_result = call_user_func($peg_startRuleFunction);

    $this->peg_cache = array();

    mb_regex_encoding($old_regex_encoding);

    if ($peg_result !== $this->peg_FAILED && $this->peg_currPos === $this->input_length) {
      $this->cleanup_state(); // Free up memory
      return $peg_result;
    } else {
      if ($peg_result !== $this->peg_FAILED && $this->peg_currPos < $this->input_length) {
        $this->peg_fail(array("type" => "end", "description" => "end of input" ));
      }

      $exception = $this->peg_buildException(null, $this->peg_maxFailExpected, $this->peg_maxFailPos);
      $this->cleanup_state(); // Free up memory
      throw $exception;
    }
  }

};