This page is the formal source of truth. When you are unsure whether a construct is allowed, look here. For everyday language use the more compact Syntax overview, which is organised by use case.
File shape
<file> ::= <directive>* <query>
<directive> ::= '@' <name> <directive-value>
<directive-value> ::= <string-literal> | <identifier>
<query> ::= <query-syntax> | <fluent-syntax>
A .ccr file contains zero or more directives followed by exactly one
query. More than one top-level query is a parse error.
Directives
Directives set metadata. All of them are optional. Unknown directive names are ignored so rules stay forward-compatible.
| Directive | Required | Value | Description |
|---|---|---|---|
@name |
no | string | Human-readable rule title |
@severity |
no | enum | info, warn (or warning), error. Default warn |
@category |
no | string | Free-form grouping (e.g. Async, Design) |
@description |
no | string | One-line description of what the rule checks |
@recommendation |
no | string | One-line description of how to fix a finding |
Strings use C-style escapes for \", \\, \n, \r, \t, \0.
Unknown escape sequences are preserved verbatim so "Handler\d*$"
reads natural for regex authors.
Query syntax (LINQ form)
<query-syntax> ::= 'from' <id> 'in' <id>
( <let-clause> | <where-clause> )*
<orderby-clause>?
'select' <expression>
<let-clause> ::= 'let' <id> '=' <expression>
<where-clause> ::= 'where' <expression>
<orderby-clause> ::= 'orderby' <expression> ('asc' | 'desc')?
Example:
from t in Types
where t.Kind == "Class"
let methodCount = t.Methods.Count
where methodCount > 20
orderby methodCount desc
select t
Fluent syntax (method form)
<fluent-syntax> ::= <id> <method-call>+
<method-call> ::= '.' <id> ( '(' <argument>* ')' )?
<argument> ::= <expression> | <lambda>
<lambda> ::= <id> '=>' <expression>
Example:
Methods.Where(m => m.IsAsync && m.Parameters.Count > 5)
Both forms produce identical findings. With multiple where clauses
the LINQ form usually reads better. For short filter chains, fluent is
often more compact.
Expression grammar
Operator precedence from tightest to loosest binding:
- Primary: literals, identifiers,
( expr ) - Member access and method call:
a.b,a.b(args) - Unary:
!expr,-expr - Multiplicative:
*,/,% - Additive:
+,- - Comparison:
==,!=,>,>=,<,<= - Logical AND:
&& - Logical OR:
||
Literals recognised by the lexer: integers, floats, strings ("..."),
true, false.
Whitespace and comments
- Whitespace between tokens is insignificant.
- Line comments start with
//and run to the end of line. - There are no block comments in
.ccrfiles.
What is intentionally not allowed
The DSL is deliberately small and side-effect-free:
- No file IO from a query body.
- No mutable state, every property is a read.
- No async, evaluation is synchronous over the cached code model.
If a predicate you need is missing, please file an issue with the use case rather than reaching for reflection. The DSL grows on purpose, not by accident.
Where to go next
- Predicate catalog: full list of properties available on each entity.
- Syntax overview: compact, use-case-oriented variant of this page.
- Rule examples: practical applications of the grammar.