function _searchQueryWord($word, $report_error)
 {
     global $Conf;
     // check for paper number or "#TAG"
     if (preg_match('/\\A#?(\\d+)(?:-#?(\\d+))?\\z/', $word, $m)) {
         $m[2] = isset($m[2]) && $m[2] ? $m[2] : $m[1];
         return new SearchTerm("pn", 0, array(range($m[1], $m[2]), array()));
     } else {
         if (substr($word, 0, 1) === "#") {
             $qe = $this->_searchQueryWord("tag:" . $word, false);
             if (!$qe->is_false()) {
                 return $qe;
             }
         }
     }
     // Allow searches like "ovemer>2"; parse as "ovemer:>2".
     if (preg_match('/\\A([-_A-Za-z0-9]+)((?:[=!<>]=?|≠|≤|≥)[^:]+)\\z/', $word, $m)) {
         $qe = $this->_searchQueryWord($m[1] . ":" . $m[2], false);
         if (!$qe->is_false()) {
             return $qe;
         }
     }
     $keyword = null;
     if (($colon = strpos($word, ":")) > 0) {
         $x = substr($word, 0, $colon);
         if (strpos($x, '"') === false) {
             $keyword = get(self::$_keywords, $x) ?: $x;
             $word = substr($word, $colon + 1);
             if ($word === false) {
                 $word = "";
             }
         }
     }
     // Treat unquoted "*", "ANY", and "ALL" as special; return true.
     if ($word === "*" || $word === "ANY" || $word === "ALL" || $word === "") {
         return new SearchTerm("t");
     } else {
         if ($word === "NONE") {
             return new SearchTerm("f");
         }
     }
     $qword = $word;
     $quoted = $word[0] === '"';
     $negated = false;
     if ($quoted) {
         $word = str_replace('*', '\\*', preg_replace('/(?:\\A"|"\\z)/', '', $word));
     }
     if ($keyword === "notag") {
         $keyword = "tag";
         $negated = true;
     }
     $qt = array();
     if ($keyword ? $keyword === "ti" : isset($this->fields["ti"])) {
         $this->_searchField($word, "ti", $qt);
     }
     if ($keyword ? $keyword === "ab" : isset($this->fields["ab"])) {
         $this->_searchField($word, "ab", $qt);
     }
     if ($keyword ? $keyword === "au" : isset($this->fields["au"])) {
         $this->_searchAuthors($word, $qt, $keyword, $quoted);
     }
     if ($keyword ? $keyword === "co" : isset($this->fields["co"])) {
         $this->_searchField($word, "co", $qt);
     }
     if ($keyword ? $keyword === "re" : isset($this->fields["re"])) {
         $this->_search_reviewer($qword, "re", $qt);
     } else {
         if ($keyword && get(self::$_canonical_review_keywords, $keyword)) {
             $this->_search_reviewer($qword, $keyword, $qt);
         }
     }
     if (preg_match('/\\A(?:(?:draft-?)?\\w*resp(?:onse)|\\w*resp(?:onse)?(-?draft)?|cmt|aucmt|anycmt)\\z/', $keyword)) {
         $this->_search_comment($word, $keyword, $qt, $quoted);
     }
     if ($keyword === "pref" && $this->amPC) {
         $this->_search_revpref($word, $qt, $quoted, false);
     }
     if ($keyword === "prefexp" && $this->amPC) {
         $this->_search_revpref($word, $qt, $quoted, true);
     }
     foreach (array("lead", "shepherd", "manager") as $ctype) {
         if ($keyword === $ctype) {
             $x = $this->_one_pc_matcher($word, $quoted);
             $qt[] = new SearchTerm("pf", self::F_XVIEW, array("{$ctype}ContactId", $x));
             if ($ctype === "manager" && $word === "me" && !$quoted && $this->privChair) {
                 $qt[] = new SearchTerm("pf", self::F_XVIEW, array("{$ctype}ContactId", "=0"));
             }
         }
     }
     if (($keyword ? $keyword === "tag" : isset($this->fields["tag"])) || $keyword === "order" || $keyword === "rorder") {
         $this->_search_tags($word, $keyword, $qt);
     }
     if ($keyword === "color") {
         $this->_search_color($word, $qt);
     }
     if ($keyword === "topic") {
         $type = "topic";
         $value = null;
         if ($word === "none" || $word === "any") {
             $value = $word;
         } else {
             $x = strtolower(simplify_whitespace($word));
             $tids = array();
             foreach ($Conf->topic_map() as $tid => $tname) {
                 if (strstr(strtolower($tname), $x) !== false) {
                     $tids[] = $tid;
                 }
             }
             if (count($tids) == 0 && $word !== "none" && $word !== "any") {
                 $this->warn("“" . htmlspecialchars($x) . "” does not match any defined paper topic.");
                 $type = "f";
             } else {
                 $value = $tids;
             }
         }
         $qt[] = new SearchTerm($type, self::F_XVIEW, $value);
     }
     if ($keyword === "option") {
         $this->_search_options($word, $qt, true);
     }
     if ($keyword === "status" || $keyword === "is") {
         $this->_search_status($word, $qt, $quoted, true);
     }
     if ($keyword === "decision") {
         $this->_search_status($word, $qt, $quoted, false);
     }
     if ($keyword === "conflict" && $this->amPC) {
         $this->_search_conflict($word, $qt, $quoted, false);
     }
     if ($keyword === "pcconflict" && $this->amPC) {
         $this->_search_conflict($word, $qt, $quoted, true);
     }
     if ($keyword === "reconflict" && $this->privChair) {
         $this->_searchReviewerConflict($word, $qt, $quoted);
     }
     if ($keyword === "round" && $this->amPC) {
         $this->reviewAdjust = true;
         if ($word === "none") {
             $qt[] = new SearchTerm("revadj", 0, array("round" => array(0)));
         } else {
             if ($word === "any") {
                 $qt[] = new SearchTerm("revadj", 0, array("round" => range(1, count($Conf->round_list()) - 1)));
             } else {
                 $x = simplify_whitespace($word);
                 $rounds = Text::simple_search($x, $Conf->round_list());
                 if (count($rounds) == 0) {
                     $this->warn("“" . htmlspecialchars($x) . "” doesn’t match a review round.");
                     $qt[] = new SearchTerm("f");
                 } else {
                     $qt[] = new SearchTerm("revadj", 0, array("round" => array_keys($rounds)));
                 }
             }
         }
     }
     if ($keyword === "rate") {
         $this->_searchReviewRatings($word, $qt);
     }
     if ($keyword === "has") {
         $this->_search_has($word, $qt, $quoted);
     }
     if ($keyword === "formula") {
         $this->_search_formula($word, $qt, $quoted);
     }
     if ($keyword === "ss" && $this->amPC) {
         if ($nextq = self::_expand_saved_search($word, $this->_ssRecursion)) {
             $this->_ssRecursion[$word] = true;
             $qe = $this->_searchQueryType($nextq);
             unset($this->_ssRecursion[$word]);
         } else {
             $qe = null;
         }
         if (!$qe && $nextq === false) {
             $this->warn("Saved search “" . htmlspecialchars($word) . "” is incorrectly defined in terms of itself.");
         } else {
             if (!$qe && !$Conf->setting_data("ss:{$word}")) {
                 $this->warn("There is no “" . htmlspecialchars($word) . "” saved search.");
             } else {
                 if (!$qe) {
                     $this->warn("The “" . htmlspecialchars($word) . "” saved search is defined incorrectly.");
                 }
             }
         }
         $qt[] = $qe ?: new SearchTerm("f");
     }
     if ($keyword === "HEADING") {
         $heading = simplify_whitespace($word);
         $qt[] = SearchTerm::make_float(["heading" => $heading]);
     }
     if ($keyword === "show" || $keyword === "hide" || $keyword === "edit" || $keyword === "sort" || $keyword === "showsort" || $keyword === "editsort") {
         $editing = strpos($keyword, "edit") !== false;
         $sorting = strpos($keyword, "sort") !== false;
         $views = array();
         $a = $keyword === "hide" ? false : ($editing ? "edit" : true);
         $word = simplify_whitespace($word);
         $ch1 = substr($word, 0, 1);
         if ($ch1 === "-" && !$sorting) {
             list($a, $word) = array(false, substr($word, 1));
         }
         $wtype = $word;
         if ($sorting) {
             $sort = self::parse_sorter($wtype);
             $wtype = $sort->type;
         }
         if ($wtype !== "" && $keyword !== "sort") {
             $views[$wtype] = $a;
         }
         $f = array("view" => $views);
         if ($sorting) {
             $f["sort"] = array($word);
         }
         $qt[] = SearchTerm::make_float($f);
     }
     // Finally, look for a review field.
     if ($keyword && !isset(self::$_keywords[$keyword]) && count($qt) == 0) {
         if ($field = ReviewForm::field_search($keyword)) {
             $this->_search_review_field($word, $field, $qt, $quoted);
         } else {
             if (!$this->_search_options("{$keyword}:{$word}", $qt, false) && $report_error) {
                 $this->warn("Unrecognized keyword “" . htmlspecialchars($keyword) . "”.");
             }
         }
     }
     $qe = SearchTerm::make_op("or", $qt);
     return $negated ? SearchTerm::make_not($qe) : $qe;
 }
 private static function _make_column($name)
 {
     if (($f = ReviewForm::field_search($name)) && $f->has_options && $f->display_order !== false) {
         $c = parent::lookup_local($f->id);
         $c = $c ?: PaperColumn::register(new ScorePaperColumn($f->id));
         return $c;
     } else {
         return null;
     }
 }
예제 #3
0
 private function _parse_expr(&$t, $level, $in_qc)
 {
     global $Conf;
     if (($t = ltrim($t)) === "") {
         return null;
     }
     $lpos = -strlen($t);
     $e = null;
     if ($t[0] === "(") {
         $t = substr($t, 1);
         $e = $this->_parse_ternary($t, false);
         $t = ltrim($t);
         if (!$e || $t === "" || $t[0] !== ")") {
             return null;
         }
         $t = substr($t, 1);
     } else {
         if ($t[0] === "-" || $t[0] === "+" || $t[0] === "!") {
             $op = $t[0];
             $t = substr($t, 1);
             if (!($e = $this->_parse_expr($t, self::$opprec["u{$op}"], $in_qc))) {
                 return null;
             }
             $e = $op == "!" ? new NegateFexpr($e) : new Fexpr($op, $e);
         } else {
             if (preg_match('/\\Aopt(?:ion)?:\\s*(.*)\\z/s', $t, $m)) {
                 $rest = self::_pop_argument($m[1]);
                 $os = PaperSearch::analyze_option_search($rest[1]);
                 foreach ($os->warn as $w) {
                     $this->_error_html[] = $w;
                 }
                 if (!count($os->os) && !count($os->warn)) {
                     $this->_error_html[] = "“" . htmlspecialchars($rest[1]) . "” doesn’t match a submission option.";
                 }
                 if (!count($os->os)) {
                     return null;
                 }
                 foreach ($os->os as $o) {
                     $ex = new OptionFexpr($o->option);
                     if ($o->kind) {
                         $this->_error_html[] = "“" . htmlspecialchars($rest[1]) . "” can’t be used in formulas.";
                     } else {
                         if ($o->value_word === "") {
                             /* stick with raw option fexpr */
                         } else {
                             if (is_array($o->value) && $o->compar === "!=") {
                                 $ex = new NegateFexpr(new InFexpr($ex, $o->value));
                             } else {
                                 if (is_array($o->value)) {
                                     $ex = new InFexpr($ex, $o->value);
                                 } else {
                                     $ex = new Fexpr(get(self::$_oprewrite, $o->compar, $o->compar), $ex, new ConstantFexpr($o->value, $o->option));
                                 }
                             }
                         }
                     }
                     $e = $e ? new Fexpr("||", $e, $ex) : $ex;
                 }
                 if ($os->negate) {
                     $e = new NegateFexpr($e);
                 }
                 $t = $rest[2];
             } else {
                 if (preg_match('/\\Anot([\\s(].*|)\\z/i', $t, $m)) {
                     $t = $m[1];
                     if (!($e = $this->_parse_expr($t, self::$opprec["u!"], $in_qc))) {
                         return null;
                     }
                     $e = new NegateFexpr($e);
                 } else {
                     if (preg_match('/\\A(\\d+\\.?\\d*|\\.\\d+)(.*)\\z/s', $t, $m)) {
                         $e = new ConstantFexpr($m[1] + 0.0);
                         $t = $m[2];
                     } else {
                         if (preg_match('/\\A(false|true)\\b(.*)\\z/si', $t, $m)) {
                             $e = new ConstantFexpr($m[1], Fexpr::FBOOL);
                             $t = $m[2];
                         } else {
                             if (preg_match('/\\A(?:pid|paperid)\\b(.*)\\z/si', $t, $m)) {
                                 $e = new PidFexpr();
                                 $t = $m[1];
                             } else {
                                 if (preg_match('/\\A(?:dec|decision):\\s*' . self::ARGUMENT_REGEX . '(.*)\\z/si', $t, $m)) {
                                     $e = $this->field_search_fexpr(["outcome", PaperSearch::matching_decisions($m[1])]);
                                     $t = $m[2];
                                 } else {
                                     if (preg_match('/\\A(?:dec|decision)\\b(.*)\\z/si', $t, $m)) {
                                         $e = new DecisionFexpr();
                                         $t = $m[1];
                                     } else {
                                         if (preg_match('/\\A(?:is|status):\\s*' . self::ARGUMENT_REGEX . '(.*)\\z/si', $t, $m)) {
                                             $e = $this->field_search_fexpr(PaperSearch::status_field_matcher($m[1]));
                                             $t = $m[2];
                                         } else {
                                             if (preg_match('/\\A(?:tag(?:\\s*:\\s*|\\s+)|#)(' . TAG_REGEX . ')(.*)\\z/is', $t, $m) || preg_match('/\\Atag\\s*\\(\\s*(' . TAG_REGEX . ')\\s*\\)(.*)\\z/is', $t, $m)) {
                                                 $e = new TagFexpr($m[1], false);
                                                 $t = $m[2];
                                             } else {
                                                 if (preg_match('/\\Atag(?:v|-?val|-?value)(?:\\s*:\\s*|\\s+)(' . TAG_REGEX . ')(.*)\\z/is', $t, $m) || preg_match('/\\Atag(?:v|-?val|-?value)\\s*\\(\\s*(' . TAG_REGEX . ')\\s*\\)(.*)\\z/is', $t, $m)) {
                                                     $e = new TagFexpr($m[1], true);
                                                     $t = $m[2];
                                                 } else {
                                                     if (preg_match('/\\A(r|re|rev|review|r(?:|e|ev|eview)type|(?:|r|re|rev|review)round|reviewer|r(?:|e|ev|eview)(?:|au)words)(?::|(?=#))\\s*' . self::ARGUMENT_REGEX . '(.*)\\z/is', $t, $m)) {
                                                         $e = $this->_reviewer_decoration($this->_reviewer_base($m[1]), $m[2]);
                                                         $t = $m[3];
                                                     } else {
                                                         if (preg_match('/\\A((?:r|re|rev|review)(?:type|round|(?:|au)words)|(?:round|reviewer))\\b(.*)\\z/is', $t, $m)) {
                                                             $e = $this->_reviewer_base($m[1]);
                                                             $t = $m[2];
                                                         } else {
                                                             if (preg_match('/\\A(my|all|any|avg|average|mean|median|quantile|count|min|max|atminof|atmaxof|argmin|argmax|std(?:d?ev(?:_pop|_samp|[_.][ps])?)?|sum|var(?:iance)?(?:_pop|_samp|[_.][ps])?|wavg)\\b(.*)\\z/is', $t, $m)) {
                                                                 $t = $m[2];
                                                                 if (!($e = $this->_parse_function($m[1], $t, true))) {
                                                                     return null;
                                                                 }
                                                             } else {
                                                                 if (preg_match('/\\A(greatest|least|round|floor|trunc|ceil|log|sqrt|pow|exp)\\b(.*)\\z/is', $t, $m)) {
                                                                     $t = $m[2];
                                                                     if (!($e = $this->_parse_function($m[1], $t, false))) {
                                                                         return null;
                                                                     }
                                                                 } else {
                                                                     if (preg_match('/\\Anull\\b(.*)\\z/s', $t, $m)) {
                                                                         $e = ConstantFexpr::cnull();
                                                                         $t = $m[1];
                                                                     } else {
                                                                         if (preg_match('/\\A(?:is:?)?(rev?|pc(?:rev?)?|pri(?:mary)?|sec(?:ondary)?|ext(?:ernal)?)\\b(.*)\\z/is', $t, $m)) {
                                                                             $rt = ReviewSearchMatcher::parse_review_type($m[1]);
                                                                             $op = $rt == 0 || $rt == REVIEW_PC ? ">=" : "==";
                                                                             $e = new Fexpr($op, new RevtypeFexpr(), new ConstantFexpr($rt, Fexpr::FREVTYPE));
                                                                             $t = $m[2];
                                                                         } else {
                                                                             if (preg_match('/\\Atopicscore\\b(.*)\\z/is', $t, $m)) {
                                                                                 $e = new TopicScoreFexpr();
                                                                                 $t = $m[1];
                                                                             } else {
                                                                                 if (preg_match('/\\Aconf(?:lict)?\\b(.*)\\z/is', $t, $m)) {
                                                                                     $e = new ConflictFexpr(false);
                                                                                     $t = $m[1];
                                                                                 } else {
                                                                                     if (preg_match('/\\Apcconf(?:lict)?\\b(.*)\\z/is', $t, $m)) {
                                                                                         $e = new ConflictFexpr(true);
                                                                                         $t = $m[1];
                                                                                     } else {
                                                                                         if (preg_match('/\\A(?:rev)?pref\\b(.*)\\z/is', $t, $m)) {
                                                                                             $e = new PrefFexpr(false);
                                                                                             $t = $m[1];
                                                                                         } else {
                                                                                             if (preg_match('/\\A(?:rev)?prefexp(?:ertise)?\\b(.*)\\z/is', $t, $m)) {
                                                                                                 $e = new PrefFexpr(true);
                                                                                                 $t = $m[1];
                                                                                             } else {
                                                                                                 if (preg_match('/\\A([A-Za-z0-9_]+|\\".*?\\")(.*)\\z/s', $t, $m) && $m[1] !== "\"\"" && !preg_match('/\\A\\s*\\(/', $m[2])) {
                                                                                                     $field = $m[1];
                                                                                                     $t = $m[2];
                                                                                                     if ($quoted = $field[0] === "\"") {
                                                                                                         $field = substr($field, 1, strlen($field) - 2);
                                                                                                     }
                                                                                                     if (($f = ReviewForm::field_search($field)) && $f->has_options) {
                                                                                                         $e = new ScoreFexpr($f);
                                                                                                     } else {
                                                                                                         if (!$quoted) {
                                                                                                             $e = new ConstantFexpr($field, false);
                                                                                                         } else {
                                                                                                             return null;
                                                                                                         }
                                                                                                     }
                                                                                                 }
                                                                                             }
                                                                                         }
                                                                                     }
                                                                                 }
                                                                             }
                                                                         }
                                                                     }
                                                                 }
                                                             }
                                                         }
                                                     }
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     if (!$e) {
         return null;
     }
     $e->set_landmark($lpos, -strlen($t));
     while (1) {
         if (($t = ltrim($t)) === "") {
             return $e;
         } else {
             if (preg_match(self::BINARY_OPERATOR_REGEX, $t, $m)) {
                 $op = $m[0];
                 $tn = substr($t, strlen($m[0]));
             } else {
                 if (preg_match('/\\A(and|or)([\\s(].*|)\\z/i', $t, $m)) {
                     $op = strlen($m[1]) == 3 ? "&&" : "||";
                     $tn = $m[2];
                 } else {
                     if (!$in_qc && substr($t, 0, 1) === ":") {
                         $op = ":";
                         $tn = substr($t, 1);
                     } else {
                         return $e;
                     }
                 }
             }
         }
         $opprec = self::$opprec[$op];
         if ($opprec < $level) {
             return $e;
         }
         $t = $tn;
         $op = get(self::$_oprewrite, $op) ?: $op;
         if (!($e2 = $this->_parse_expr($t, get(self::$_oprassoc, $op) ? $opprec : $opprec + 1, $in_qc))) {
             return null;
         }
         $e = new Fexpr($op, $e, $e2);
         $e->set_landmark($lpos, -strlen($t));
     }
 }