summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2023-09-01 11:21:47 +0200
committerClaire <claire.github-309c@sitedethib.com>2023-09-08 16:29:15 +0200
commite5caa462c1e95f3a499acc4f21b09dca3cbc76c9 (patch)
tree8dc6cfc349b9c233d2a51f839eacf545d6a1fc70
parent32eb0e77447fd0af23da8360e6c5831e2d43a97c (diff)
Add an optimized version of `str(…) | str(…) | …`
-rw-r--r--app/lib/search_query_parser.rb38
1 files changed, 37 insertions, 1 deletions
diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb
index fd8e2202fb1..e32e299b4da 100644
--- a/app/lib/search_query_parser.rb
+++ b/app/lib/search_query_parser.rb
@@ -1,11 +1,47 @@
# frozen_string_literal: true
class SearchQueryParser < Parslet::Parser
+ # Efficiently matches disjoint strings
+ class StrList < Parslet::Atoms::Base
+ attr_reader :strings
+
+ def initialize(strings)
+ super()
+
+ @strings = strings
+ @pattern = Regexp.union(strings)
+ @min_length = strings.map(&:length).min
+ end
+
+ def error_msgs
+ @error_msgs ||= {
+ premature: 'Premature end of input',
+ failed: "Expected any of #{strings.inspect}, but got ",
+ }
+ end
+
+ def try(source, context, _consume_all)
+ match = source.match(@pattern)
+ return succ(source.consume(match)) unless match.nil?
+
+ # Input ending early:
+ return context.err(self, source, error_msgs[:premature]) if source.chars_left < @min_length
+
+ # Expected something, but got something else instead:
+ error_pos = source.pos
+ context.err_at(self, source, [error_msgs[:failed], source.consume(@len)], error_pos)
+ end
+
+ def to_s_inner(_prec)
+ "[#{strings.map { |str| "'#{str}'" }.join(',')}]"
+ end
+ end
+
rule(:term) { match('[^\s]').repeat(1).as(:term) }
rule(:colon) { str(':') }
rule(:space) { match('\s').repeat(1) }
rule(:operator) { (str('+') | str('-')).as(:operator) }
- rule(:prefix_operator) { str('has') | str('is') | str('language') | str('from') | str('before') | str('after') | str('during') | str('in') }
+ rule(:prefix_operator) { StrList.new(%w(has is language from before after during in)) }
rule(:prefix) { prefix_operator.as(:prefix_operator) >> colon }
rule(:phrase) do
(str('"') >> match('[^"]').repeat.as(:phrase) >> str('"')) |