Simplify filter grammar

This commit is contained in:
Oleksii Holub 2022-08-25 21:07:02 +03:00
parent 99d721c63b
commit f48af53d72

View file

@ -18,60 +18,61 @@ internal static class FilterGrammar
private static readonly TextParser<string> UnquotedString = private static readonly TextParser<string> UnquotedString =
Parse.OneOf( Parse.OneOf(
EscapedCharacter, EscapedCharacter,
Character.Matching( // Avoid whitespace as it's treated as an implicit 'and' operator.
c => // Also avoid all special tokens used by other parsers.
!char.IsWhiteSpace(c) && Character.ExceptIn(' ', '(', ')', '"', '\'', '-', '~', '|', '&')
// Avoid all special tokens used by the grammar
c is not ('(' or ')' or '"' or '\'' or '-' or '~' or '|' or '&'),
"any character except whitespace or `(`, `)`, `\"`, `'`, `-`, `|`, `&`"
)
).AtLeastOnce().Text(); ).AtLeastOnce().Text();
private static readonly TextParser<string> String = private static readonly TextParser<string> String =
Parse.OneOf(QuotedString, UnquotedString).Named("text string"); Parse.OneOf(QuotedString, UnquotedString).Named("text string");
private static readonly TextParser<MessageFilter> ContainsFilter = private static readonly TextParser<MessageFilter> ContainsFilter =
String.Select(v => (MessageFilter) new ContainsMessageFilter(v)); String.Select(v => (MessageFilter)new ContainsMessageFilter(v));
private static readonly TextParser<MessageFilter> FromFilter = Span private static readonly TextParser<MessageFilter> FromFilter =
.EqualToIgnoreCase("from:") Span
.IgnoreThen(String) .EqualToIgnoreCase("from:")
.Select(v => (MessageFilter) new FromMessageFilter(v)) .IgnoreThen(String)
.Named("from:<value>"); .Select(v => (MessageFilter)new FromMessageFilter(v))
.Named("from:<value>");
private static readonly TextParser<MessageFilter> MentionsFilter = Span private static readonly TextParser<MessageFilter> MentionsFilter =
.EqualToIgnoreCase("mentions:") Span
.IgnoreThen(String) .EqualToIgnoreCase("mentions:")
.Select(v => (MessageFilter) new MentionsMessageFilter(v)) .IgnoreThen(String)
.Named("mentions:<value>"); .Select(v => (MessageFilter)new MentionsMessageFilter(v))
.Named("mentions:<value>");
private static readonly TextParser<MessageFilter> ReactionFilter = Span private static readonly TextParser<MessageFilter> ReactionFilter =
.EqualToIgnoreCase("reaction:") Span
.IgnoreThen(String) .EqualToIgnoreCase("reaction:")
.Select(v => (MessageFilter) new ReactionMessageFilter(v)) .IgnoreThen(String)
.Named("reaction:<value>"); .Select(v => (MessageFilter)new ReactionMessageFilter(v))
.Named("reaction:<value>");
private static readonly TextParser<MessageFilter> HasFilter = Span private static readonly TextParser<MessageFilter> HasFilter =
.EqualToIgnoreCase("has:") Span
.IgnoreThen(Parse.OneOf( .EqualToIgnoreCase("has:")
Span.EqualToIgnoreCase("link").IgnoreThen(Parse.Return(MessageContentMatchKind.Link)), .IgnoreThen(Parse.OneOf(
Span.EqualToIgnoreCase("embed").IgnoreThen(Parse.Return(MessageContentMatchKind.Embed)), Span.EqualToIgnoreCase("link").IgnoreThen(Parse.Return(MessageContentMatchKind.Link)),
Span.EqualToIgnoreCase("file").IgnoreThen(Parse.Return(MessageContentMatchKind.File)), Span.EqualToIgnoreCase("embed").IgnoreThen(Parse.Return(MessageContentMatchKind.Embed)),
Span.EqualToIgnoreCase("video").IgnoreThen(Parse.Return(MessageContentMatchKind.Video)), Span.EqualToIgnoreCase("file").IgnoreThen(Parse.Return(MessageContentMatchKind.File)),
Span.EqualToIgnoreCase("image").IgnoreThen(Parse.Return(MessageContentMatchKind.Image)), Span.EqualToIgnoreCase("video").IgnoreThen(Parse.Return(MessageContentMatchKind.Video)),
Span.EqualToIgnoreCase("sound").IgnoreThen(Parse.Return(MessageContentMatchKind.Sound)), Span.EqualToIgnoreCase("image").IgnoreThen(Parse.Return(MessageContentMatchKind.Image)),
Span.EqualToIgnoreCase("pin").IgnoreThen(Parse.Return(MessageContentMatchKind.Pin)) Span.EqualToIgnoreCase("sound").IgnoreThen(Parse.Return(MessageContentMatchKind.Sound)),
)) Span.EqualToIgnoreCase("pin").IgnoreThen(Parse.Return(MessageContentMatchKind.Pin))
.Select(k => (MessageFilter) new HasMessageFilter(k)) ))
.Named("has:<value>"); .Select(k => (MessageFilter)new HasMessageFilter(k))
.Named("has:<value>");
private static readonly TextParser<MessageFilter> PrimitiveFilter = Parse.OneOf( private static readonly TextParser<MessageFilter> PrimitiveFilter =
FromFilter, Parse.OneOf(
MentionsFilter, FromFilter,
ReactionFilter, MentionsFilter,
HasFilter, ReactionFilter,
ContainsFilter HasFilter,
); ContainsFilter
);
private static readonly TextParser<MessageFilter> GroupedFilter = private static readonly TextParser<MessageFilter> GroupedFilter =
from open in Character.EqualTo('(') from open in Character.EqualTo('(')
@ -79,33 +80,35 @@ internal static class FilterGrammar
from close in Character.EqualTo(')') from close in Character.EqualTo(')')
select content; select content;
private static readonly TextParser<MessageFilter> NegatedFilter = Character private static readonly TextParser<MessageFilter> NegatedFilter =
// Dash is annoying to use from CLI due to conflicts with options, so we provide tilde as an alias Character
.In('-', '~') // Dash is annoying to use from CLI due to conflicts with options, so we provide tilde as an alias
.IgnoreThen(Parse.OneOf(GroupedFilter, PrimitiveFilter)) .In('-', '~')
.Select(f => (MessageFilter) new NegatedMessageFilter(f)); .IgnoreThen(Parse.OneOf(GroupedFilter, PrimitiveFilter))
.Select(f => (MessageFilter)new NegatedMessageFilter(f));
private static readonly TextParser<MessageFilter> ChainedFilter = Parse.Chain( private static readonly TextParser<MessageFilter> ChainedFilter =
// Operator Parse.Chain(
Parse.OneOf( // Operator
// Explicit operator Parse.OneOf(
Character.In('|', '&').Token().Try(), // Explicit operator
// Implicit operator (resolves to 'and') Character.In('|', '&').Token().Try(),
Character.WhiteSpace.AtLeastOnce().IgnoreThen(Parse.Return(' ')) // Implicit operator (resolves to 'and')
), Character.EqualTo(' ').AtLeastOnce().IgnoreThen(Parse.Return(' '))
// Operand ),
Parse.OneOf( // Operand
NegatedFilter, Parse.OneOf(
GroupedFilter, NegatedFilter,
PrimitiveFilter GroupedFilter,
), PrimitiveFilter
// Reducer ),
(op, left, right) => op switch // Reducer
{ (op, left, right) => op switch
'|' => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.Or), {
_ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And) '|' => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.Or),
} _ => new BinaryExpressionMessageFilter(left, right, BinaryExpressionKind.And)
); }
);
public static readonly TextParser<MessageFilter> Filter = public static readonly TextParser<MessageFilter> Filter =
ChainedFilter.Token().AtEnd(); ChainedFilter.Token().AtEnd();