protected function getRules()

in src/lexer/PhutilLexer.php [104:227]


  protected function getRules() {
    $class = get_class($this);

    $raw_rules = $this->getRawRules();

    if (!is_array($raw_rules)) {
      $type = gettype($raw_rules);
      throw new UnexpectedValueException(
        pht(
          'Expected %s to return array, got %s.',
          $class.'->getRawRules()',
          $type));
    }

    if (empty($raw_rules['start'])) {
      throw new UnexpectedValueException(
        pht(
          "Expected %s rules to define rules for state '%s'.",
          $class,
          'start'));
    }

    $processed_rules = array();
    foreach ($raw_rules as $state => $rules) {

      if (!is_array($rules)) {
        $type = gettype($rules);
        throw new UnexpectedValueException(
          pht(
            "Expected list of rules for state '%s' in %s, got %s.",
            $state,
            $class,
            $type));
      }

      foreach ($rules as $key => $rule) {
        $n = count($rule);
        if ($n < 2 || $n > 4) {
          throw new UnexpectedValueException(
            pht(
              "Expected rule '%s' in state '%s' in %s to have 2-4 elements ".
              "(regex, token, [next state], [options]), got %d.",
              $key,
              $state,
              $class,
              $n));
        }
        $rule = array_values($rule);
        if (count($rule) == 2) {
          $rule[] = null;
        }
        if (count($rule) == 3) {
          $rule[] = array();
        }

        foreach ($rule[3] as $option => $value) {
          switch ($option) {
            case 'context':
              if ($value !== 'push' &&
                  $value !== 'pop' &&
                  $value !== 'discard' &&
                  $value !== null) {
                throw new UnexpectedValueException(
                  pht(
                    "Rule '%s' in state '%s' in %s has unknown ".
                    "context rule '%s', expected '%s', '%s' or '%s'.",
                    $key,
                    $state,
                    $class,
                    $value,
                    'push',
                    'pop',
                    'discard'));
              }
              break;
            default:
              throw new UnexpectedValueException(
                pht(
                  "Rule '%s' in state '%s' in %s has unknown option '%s'.",
                  $key,
                  $state,
                  $class,
                  $option));
          }
        }

        $flags = 'sS';

        // NOTE: The "\G" assertion is an offset-aware version of "^".
        $rule[0] = '(\\G'.$rule[0].')'.$flags;

        if (@preg_match($rule[0], '') === false) {
          $error = error_get_last();
          throw new UnexpectedValueException(
            pht(
              "Rule '%s' in state '%s' in %s defines an ".
              "invalid regular expression ('%s'): %s",
              $key,
              $state,
              $class,
              $rule[0],
              idx($error, 'message')));
        }

        $next_state = $rule[2];
        if ($next_state !== null && $next_state !== '!pop') {
          if (empty($raw_rules[$next_state])) {
            throw new UnexpectedValueException(
              pht(
                "Rule '%s' in state '%s' in %s transitions to ".
                "state '%s', but there are no rules for that state.",
                $key,
                $state,
                $class,
                $next_state));
          }
        }

        $processed_rules[$state][] = $rule;
      }
    }

    return $processed_rules;
  }