From a4d43ae72aacdacd98b830a81938a4dfbf21cf3d Mon Sep 17 00:00:00 2001 From: evazion Date: Wed, 6 Apr 2022 17:15:02 -0500 Subject: [PATCH] post queries: track whether metatag values are quoted. This is necessary for the `commentary:` metatag, which has different behavior depending on whether the metatag value is quoted. For example, `commentary:translated` finds translated commentaries, while `commentary:"translated"` finds commentaries containing the literal word "translated". --- app/logical/post_query/ast.rb | 15 ++++++++++----- app/logical/post_query/parser.rb | 10 +++++----- test/unit/post_query_parser_test.rb | 4 ++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/logical/post_query/ast.rb b/app/logical/post_query/ast.rb index 284b23997..4e5eefda5 100644 --- a/app/logical/post_query/ast.rb +++ b/app/logical/post_query/ast.rb @@ -60,8 +60,8 @@ class PostQuery AST.new(:tag, [name]) end - def metatag(name, value) - AST.new(:metatag, [name, value]) + def metatag(name, value, quoted = false) + AST.new(:metatag, [name, value, quoted]) end end @@ -211,7 +211,7 @@ class PostQuery "none" in [:tag, name] name - in [:metatag, name, value] + in [:metatag, name, value, quoted] "#{name}:#{quoted_value}" in [:wildcard, name] "(wildcard #{name})" @@ -231,7 +231,7 @@ class PostQuery name in [:tag, name] name - in [:metatag, name, value] + in [:metatag, name, value, quoted] "#{name}:#{quoted_value}" in :not, child child.term? ? "-#{child.to_infix}" : "-(#{child.to_infix})" @@ -354,11 +354,16 @@ class PostQuery args.second if metatag? end + # @return [String, nil] True if the metatag's value was enclosed in quotes. + def quoted? + args.third if metatag? + end + # @return [String, nil] The value of the metatag as a quoted string, if a metatag node. def quoted_value return nil unless metatag? - if value.include?(" ") || value.starts_with?('"') || value.starts_with?("'") || value.empty? + if quoted? %Q{"#{value.gsub(/"/, '\\"')}"} else value diff --git a/app/logical/post_query/parser.rb b/app/logical/post_query/parser.rb index 160362bec..5f8f631ca 100644 --- a/app/logical/post_query/parser.rb +++ b/app/logical/post_query/parser.rb @@ -164,8 +164,8 @@ class PostQuery if accept(METATAG_NAME_REGEX) name = @scanner.matched.delete_suffix(":").downcase name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS) - value = quoted_string - node(:metatag, name, value) + quoted, value = quoted_string + node(:metatag, name, value, quoted) end end @@ -173,13 +173,13 @@ class PostQuery if accept('"') a = accept(/([^"\\]|\\")*/).gsub(/\\"/, '"') # handle backslash escaped quotes expect('"') - a + [true, a] elsif accept("'") a = accept(/([^'\\]|\\')*/).gsub(/\\'/, "'") # handle backslash escaped quotes expect("'") - a + [true, a] else - string(/[^ ]+/) + [false, string(/[^ ]+/)] end end diff --git a/test/unit/post_query_parser_test.rb b/test/unit/post_query_parser_test.rb index 7de999e2e..328df2d4c 100644 --- a/test/unit/post_query_parser_test.rb +++ b/test/unit/post_query_parser_test.rb @@ -69,14 +69,14 @@ class PostQueryParserTest < ActiveSupport::TestCase assert_parse_equals('source:""', 'source:""') assert_parse_equals('source:"\""', 'source:"\""') assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:"don't say \"lazy\" okay"}) - assert_parse_equals(%q{(and source:foo)bar a)}, %q{(a (source:"foo)bar"))}) + assert_parse_equals(%q{(and source:"foo)bar" a)}, %q{(a (source:"foo)bar"))}) assert_parse_equals('source:"foo bar"', "source:'foo bar'") assert_parse_equals("source:foobar'(", "source:foobar'(") assert_parse_equals('source:""', "source:''") assert_parse_equals('source:"\'"', "source:'\\''") assert_parse_equals(%q{source:"don't say \"lazy\" okay"}, %q{source:'don\'t say "lazy" okay'}) - assert_parse_equals(%q{(and source:foo)bar a)}, %q{(a (source:'foo)bar'))}) + assert_parse_equals(%q{(and source:"foo)bar" a)}, %q{(a (source:'foo)bar'))}) end should "parse metatag synonyms correctly" do