Skip to content

Instantly share code, notes, and snippets.

@dorianmariecom
Created March 12, 2022 10:16
Show Gist options
  • Save dorianmariecom/5c505a0690d768a97c48ea5cb8b15e30 to your computer and use it in GitHub Desktop.
Save dorianmariecom/5c505a0690d768a97c48ea5cb8b15e30 to your computer and use it in GitHub Desktop.
require 'parslet'
class SearchParser < Parslet::Parser
# space, e.g. " " or " "
rule(:space) { match('\s').repeat(1) }
rule(:space?) { space.maybe }
# string, e.g "hello" or ""hello world""
rule(:quote) { str('"') }
rule(:char) { match('.') }
rule(:escape) { str('\\') }
rule(:escaped_char) { escape >> char }
rule(:unquoted_char) do
escaped_char |
(
quote.absent? >> space.absent? >> left_parenthesis.absent? >>
right_parenthesis.absent? >> char
)
end
rule(:quoted_char) { escaped_char | (quote.absent? >> char) }
rule(:unquoted_string) { unquoted_char.repeat(1) }
rule(:quoted_string) { quote >> quoted_char.repeat(1) >> quote }
rule(:string) { unquoted_string | quoted_string }
# key operators, e.g. ">", "<", ":"
rule(:colon) { str(':') }
rule(:greater) { str('>') }
rule(:greater_or_equal) { str('>=') }
rule(:inferior) { str('<') }
rule(:inferior_or_equal) { str('<=') }
rule(:equal) { str('=') }
rule(:matches) { str('~') }
rule(:equal_equal) { str('==') }
rule(:matches_equal) { str('~=') }
rule(:equal_matches) { str('=~') }
# key, e.g extras.user
rule(:key_separator) { str('.') }
rule(:key_string) do
(key_operator.absent? >> unquoted_char).repeat(1) | quoted_string
end
rule(:key_operator) do
(colon >> key_operator) | colon | greater | greater_or_equal | inferior |
inferior_or_equal | equal_equal | matches_equal | equal_matches | equal |
matches
end
rule(:key) { key_string >> (key_separator >> key_string).repeat(0) }
# rule, e.g. extras.user:dorianmariefr
rule(:value) { string }
rule(:rule) { key.as(:key) >> key_operator.as(:operator) >> value.as(:value) }
# expression, e.g. ruby, Rails, content~=rails
rule(:expression) { rule.as(:rule) | value.as(:value) | space }
# groups, e.g. (ruby or Ruby) and (rails or Rails)
rule(:left_parenthesis) { str('(') }
rule(:right_parenthesis) { str(')') }
rule(:group_expression) do
(
left_parenthesis >> space? >> and_expression >> space? >>
right_parenthesis
) | expression
end
# not, e.g. NOT node, !mongodb
rule(:not_operator) { str('not') >> space | str('NOT') >> space | str('!') }
rule(:not_expression) do
(not_operator >> group_expression).as(:not) | group_expression
end
# or, e.g. ruby OR rails
rule(:or_operator) { str('or') | str('OR') }
rule(:or_expression) do
(
not_expression.as(:left) >> space >> or_operator >> space >>
or_expression.as(:right)
).as(:or) | not_expression
end
# and, e.g. ruby AND rails
rule(:and_operator) { str('and') | str('AND') }
rule(:and_expression) do
(
or_expression.as(:left) >> space >> and_operator >> space >>
and_expression.as(:right)
).as(:and) |
(or_expression.as(:left) >> space >> and_expression.as(:right)).as(:and) |
or_expression |
str("").as(:value)
end
root(:and_expression)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment