Skip to content

Commit 52ff279

Browse files
Implement conjunction and negation over glob terms (#3926)
This change allows us to write the following filter: ``` kore|booster>!(kore-term|detail). ``` i.e. log everything from kore and booster except for the innermost `kore-term` or `detail` contexts, which contain the pretty printed terms and extra details about e.g. rule locations which may be too noisy. This was previously not possible because we couldn't apply `!` over a bracketed expression. I've added conjunction `&` for good measure, so we could also write the above filter as `kore|booster>!kore-term&!detail.`
1 parent 892df63 commit 52ff279

File tree

2 files changed

+81
-21
lines changed

2 files changed

+81
-21
lines changed

booster/library/Booster/Log/Context.hs

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,96 @@ import Data.Generics (Data, extQ)
1111
import Data.List.Extra (replace)
1212
import Language.Haskell.TH (ExpQ, Lit (StringL), appE, litE, varE)
1313
import Language.Haskell.TH.Quote (QuasiQuoter (..), dataToExpQ)
14+
import Options.Applicative (Alternative)
1415

1516
data ContextFilterSingle
1617
= Exact BS.ByteString
1718
| Prefix BS.ByteString
1819
| Suffix BS.ByteString
1920
| Infix BS.ByteString
20-
| Negate ContextFilterSingle
21+
deriving (Show, Data)
22+
23+
data ContextFilterBoolean
24+
= And ContextFilterBoolean ContextFilterBoolean
25+
| Or ContextFilterBoolean ContextFilterBoolean
26+
| Negate ContextFilterBoolean
27+
| Single ContextFilterSingle
2128
deriving (Show, Data)
2229

2330
data ContextFilter
24-
= First [ContextFilterSingle]
25-
| ThenDirectChild [ContextFilterSingle] ContextFilter
26-
| ThenChild [ContextFilterSingle] ContextFilter
27-
| Last [ContextFilterSingle]
31+
= First ContextFilterBoolean
32+
| ThenDirectChild ContextFilterBoolean ContextFilter
33+
| ThenChild ContextFilterBoolean ContextFilter
34+
| Last ContextFilterBoolean
2835
deriving (Show, Data)
2936

3037
reserved :: String
31-
reserved = "|*!>,."
38+
reserved = "()&|*!>,."
3239

3340
stringP :: A.Parser BS.ByteString
3441
stringP = A.takeWhile1 (not . (`elem` reserved))
3542

3643
singleP :: A.Parser ContextFilterSingle
3744
singleP =
38-
A.char '!' *> A.skipSpace *> (Negate <$> singleP)
39-
<|> A.char '*' *> (Infix <$> stringP) <* A.char '*'
45+
A.char '*' *> (Infix <$> stringP) <* A.char '*'
4046
-- we want to allow * being parsed as `Suffix ""` so we allow the empty string via `takeWhile`
4147
<|> A.char '*' *> (Suffix . BS.dropWhileEnd isSpace <$> A.takeWhile (not . (`elem` reserved)))
4248
<|> Prefix . BS.dropWhile isSpace <$> stringP <* A.char '*'
4349
<|> Exact . BS.strip <$> stringP
4450

45-
orP :: A.Parser [ContextFilterSingle]
46-
orP = singleP `A.sepBy` (A.char '|')
51+
parens :: A.Parser a -> A.Parser a
52+
parens p = A.char '(' *> A.skipSpace *> p <* A.skipSpace <* A.char ')' <* A.skipSpace
53+
54+
chainl1 :: (Alternative m, Monad m) => m b -> m (b -> b -> b) -> m b
55+
chainl1 p op = do x <- p; rest x
56+
where
57+
rest x =
58+
do
59+
f <- op
60+
y <- p
61+
rest (f x y)
62+
<|> return x
63+
64+
-- adapted from https://gist.github.com/pedrominicz/6867c298608a96e6db4dedd798f49e60
65+
expression :: A.Parser ContextFilterBoolean
66+
expression =
67+
orExpr
68+
<|> andExpr
69+
<|> negExpr
70+
<|> Single <$> singleP
71+
<|> parens expression
72+
73+
orExpr :: A.Parser ContextFilterBoolean
74+
orExpr = A.try $ expression' `chainl1` (Or <$ A.char '|' <* A.skipSpace)
75+
where
76+
expression' =
77+
andExpr
78+
<|> negExpr
79+
<|> Single <$> singleP
80+
<|> parens expression
81+
82+
andExpr :: A.Parser ContextFilterBoolean
83+
andExpr = A.try $ expression' `chainl1` (And <$ A.char '&' <* A.skipSpace)
84+
where
85+
expression' =
86+
negExpr
87+
<|> Single <$> singleP
88+
<|> parens expression
89+
90+
negExpr :: A.Parser ContextFilterBoolean
91+
negExpr = A.try $ A.char '!' *> A.skipSpace *> (Negate <$> expression')
92+
where
93+
expression' =
94+
Single <$> singleP
95+
<|> parens expression
4796

4897
contextFilterP :: A.Parser ContextFilter
4998
contextFilterP =
5099
A.skipSpace
51-
*> ( ThenChild <$> (orP <* A.skipSpace <* A.char '>') <*> contextFilterP
52-
<|> ThenDirectChild <$> (orP <* A.skipSpace <* A.char ',') <*> contextFilterP
53-
<|> Last <$> (orP <* A.skipSpace <* A.char '.')
54-
<|> First <$> orP
100+
*> ( ThenChild <$> (expression <* A.skipSpace <* A.char '>') <*> contextFilterP
101+
<|> ThenDirectChild <$> (expression <* A.skipSpace <* A.char ',') <*> contextFilterP
102+
<|> Last <$> (expression <* A.skipSpace <* A.char '.')
103+
<|> First <$> expression
55104
)
56105

57106
readContextFilter :: String -> Either String ContextFilter
@@ -63,19 +112,24 @@ matchSingle (Exact c) s = c == s
63112
matchSingle (Prefix c) s = BS.isPrefixOf c s
64113
matchSingle (Suffix c) s = BS.isSuffixOf c s
65114
matchSingle (Infix c) s = BS.isInfixOf c s
66-
matchSingle (Negate c) s = not $ matchSingle c s
115+
116+
matchBoolean :: ContextFilterBoolean -> BS.ByteString -> Bool
117+
matchBoolean (Single e) s = matchSingle e s
118+
matchBoolean (And e1 e2) s = matchBoolean e1 s && matchBoolean e2 s
119+
matchBoolean (Or e1 e2) s = matchBoolean e1 s || matchBoolean e2 s
120+
matchBoolean (Negate e) s = not $ matchBoolean e s
67121

68122
mustMatch :: ContextFilter -> [BS.ByteString] -> Bool
69-
mustMatch (First cs) [] = any (flip matchSingle "") cs
70-
mustMatch (First cs) (x : _) = any (flip matchSingle x) cs
71-
mustMatch (Last cs) [x] = any (flip matchSingle x) cs
123+
mustMatch (First cs) [] = matchBoolean cs ""
124+
mustMatch (First cs) (x : _) = matchBoolean cs x
125+
mustMatch (Last cs) [x] = matchBoolean cs x
72126
mustMatch Last{} _ = False
73127
mustMatch (_ `ThenDirectChild` _) [] = False
74128
mustMatch (cs `ThenDirectChild` css) (x : xs) =
75-
any (flip matchSingle x) cs && mustMatch css xs
129+
matchBoolean cs x && mustMatch css xs
76130
mustMatch (_ `ThenChild` _) [] = False
77131
mustMatch (cs `ThenChild` css) (x : xs) =
78-
any (flip matchSingle x) cs && mayMatch css xs
132+
matchBoolean cs x && mayMatch css xs
79133

80134
mayMatch :: ContextFilter -> [BS.ByteString] -> Bool
81135
mayMatch c [] = mustMatch c []

docs/logging.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,21 @@ To turn on context logging, use the `--log-context` flag to select which of the
142142
* `*foo*` - match context with the infix `foo`
143143
* `!foo*` - don't match `foo`
144144
* `foo|bar` - match `foo` or `bar` context, e.g. `kore|booster`
145+
* `foo&bar` - match `foo` and `bar` context, most likely used with negation e.g. `!kore&!proxy`
145146
* `foo,bar` - match a context `foo` with an immediately nested context `bar`
146147
* `foo>bar` - match a context `foo` with a child context `bar`
147148
* `foo>bar.` - match a context `foo` with a child context `bar` which is the last context, e.g. the context `[foo][baz][bar]` would match but `[foo][bar][baz]` does not.
148149

150+
Other allowed expressions:
151+
152+
* `(foo|bar)&!baz`
153+
* `!(foo|bar)`
154+
149155

150156
Here are some common patterns we may want to use:
151157

152158
* `kore|booster` - log everything from kore and booster
153-
* `kore|booster>!kore-term.` - log everything from kore and booster except for the innermost `kore-term` contexts, which contain the pretty printed terms
159+
* `kore|booster>!(kore-term|detail).` - log everything from kore and booster except for the innermost `kore-term` or `detail` contexts, which contain the pretty printed terms and extra details about e.g. rule locations which may be too noisy.
154160
* `kore|booster>simplification*` - log every simplification rule attempt from kore and booster
155161
* `kore|booster>simplification*|equation*` - log every simplification and function application attempt from kore and booster
156162
* `ceil>partial-symbols.` - log a minimal ceil analysis showing all the rules which contain partial symbols on the RHS and aren't marked as preserving definedness

0 commit comments

Comments
 (0)