From db6bb2ccac901f9a315b36de5676ae6991ccc8b9 Mon Sep 17 00:00:00 2001 From: evazion Date: Fri, 22 Apr 2022 02:07:36 -0500 Subject: [PATCH] Fix #5136: Regular tags are now case-sensitive. * Fix `AST.tag` to downcase the tag name. * Change PostQuery::Parser to use build nodes using `AST.tag`, `AST.metatag`, `AST.wildcard`, etc methods instead of building nodes directly. This way all the normalization happens in the node constructor methods instead of in the parser. --- app/logical/post_query/ast.rb | 28 +++++++++++++++++++- app/logical/post_query/parser.rb | 39 +++++++++------------------- test/unit/post_query_builder_test.rb | 1 + 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/app/logical/post_query/ast.rb b/app/logical/post_query/ast.rb index 0d5d1b0dc..ef9f1a365 100644 --- a/app/logical/post_query/ast.rb +++ b/app/logical/post_query/ast.rb @@ -68,11 +68,37 @@ class PostQuery AST.new(:all, []) end + def none + AST.new(:none, []) + end + + def not(ast) + AST.new(:not, [ast]) + end + + def opt(ast) + AST.new(:opt, [ast]) + end + def tag(name) - AST.new(:tag, [name]) + AST.new(:tag, [name.downcase]) + end + + def wildcard(name) + AST.new(:wildcard, [name.downcase]) end def metatag(name, value, quoted = false) + name = name.downcase + name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) + + if name == "order" + attribute, direction, _tail = value.to_s.downcase.partition(/_(asc|desc)\z/i) + if attribute.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) + value = attribute.singularize + "_count" + direction + end + end + AST.new(:metatag, [name, value, quoted]) end end diff --git a/app/logical/post_query/parser.rb b/app/logical/post_query/parser.rb index 736e9e40c..73dd91de6 100644 --- a/app/logical/post_query/parser.rb +++ b/app/logical/post_query/parser.rb @@ -65,7 +65,7 @@ class PostQuery def parse parse! rescue Error - node(:none) + AST.none end # Parse the search and return the AST, or raise an error if the parse failed. @@ -86,11 +86,11 @@ class PostQuery space if a.empty? - node(:all) + AST.all elsif a.size == 1 a.first else - node(:and, *a) + AST.new(:and, a) end end @@ -101,7 +101,7 @@ class PostQuery space if accept(/or +/i) b = or_clause - node(:or, a, b) + AST.new(:or, [a, b]) else a end @@ -114,7 +114,7 @@ class PostQuery space if accept(/and +/i) b = and_clause - node(:and, a, b) + AST.new(:and, [a, b]) else a end @@ -123,7 +123,7 @@ class PostQuery # factor_list = factor [factor_list] def factor_list a = one_or_more { factor } - node(:and, *a) + AST.new(:and, a) end # factor = "-" expr | "~" expr | expr @@ -131,9 +131,9 @@ class PostQuery space if accept("-") - node(:not, expr) + AST.not(expr) elsif accept("~") - node(:opt, expr) + AST.opt(expr) else expr end @@ -166,20 +166,10 @@ class PostQuery # metatag = metatag_name ":" quoted_string # metatag_name = "user" | "fav" | "pool" | "order" | ... def metatag - name = expect(METATAG_NAME_REGEX) + name = expect(METATAG_NAME_REGEX).delete_suffix(":") quoted, value = quoted_string - name = name.delete_suffix(":").downcase - name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) - - if name == "order" - attribute, direction, _tail = value.to_s.downcase.partition(/_(asc|desc)\z/i) - if attribute.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) - value = attribute.singularize + "_count" + direction - end - end - - node(:metatag, name, value, quoted) + AST.metatag(name, value, quoted) end def quoted_string @@ -201,7 +191,7 @@ class PostQuery t = string(/(?=[^ ]*\*)[^ \)~-][^ ]*/, skip_balanced_parens: true) raise Error if t.match?(/\A#{METATAG_NAME_REGEX}/) space - node(:wildcard, t.downcase) + AST.wildcard(t) end # A tag is a string that begins with a nonspace, non-')', non-'~', or non-'-' character, followed by nonspace characters. @@ -209,7 +199,7 @@ class PostQuery t = string(/[^ \)~-][^ ]*/, skip_balanced_parens: true) raise Error if t.downcase.in?(%w[and or]) || t.include?("*") || t.match?(/\A#{METATAG_NAME_REGEX}/) space - node(:tag, t.downcase) + AST.tag(t) end def string(pattern, skip_balanced_parens: false) @@ -292,11 +282,6 @@ class PostQuery raise Error, "expected one of: #{parsers}" end - - # Build an AST node of the given type. - def node(type, *args) - AST.new(type, args) - end end memoize :parse, :parse! diff --git a/test/unit/post_query_builder_test.rb b/test/unit/post_query_builder_test.rb index 1fa78f579..e5327a92b 100644 --- a/test/unit/post_query_builder_test.rb +++ b/test/unit/post_query_builder_test.rb @@ -85,6 +85,7 @@ class PostQueryBuilderTest < ActiveSupport::TestCase post3 = create(:post, tag_string: "bbb ccc") assert_tag_match([post2, post1], "aaa") + assert_tag_match([post2, post1], "AAA") assert_tag_match([post2, post1], " aaa ") end