Verhältnis zu anderen Seiten: diese Seite ist der formale Grammatik-Anhang. Für den alltäglichen DSL-Einsatz (Datei-Aufbau, Query-Formen, Collections-Übersicht) gibt es die Syntax-Übersicht. Alle abfragbaren Properties aller Modell-Typen stehen im Prädikat-Katalog.
Diese Seite ist die formale Wahrheit. Wenn Sie unsicher sind, ob ein Konstrukt erlaubt ist, schauen Sie hier nach. Für den alltäglichen Sprachgebrauch gibt es die kompakte Syntax-Übersicht, die nach Anwendungsfall sortiert ist.
Datei-Struktur
<file> ::= <directive>* <query>
<directive> ::= '@' <name> <directive-value>
<directive-value> ::= <string-literal> | <identifier>
<query> ::= <query-syntax> | <fluent-syntax>
Eine .ccr-Datei enthält null oder mehr Direktiven, gefolgt von
genau einer Query. Der Parser lehnt nachfolgenden Inhalt nicht ab:
alles nach der ersten Query wird stillschweigend ignoriert. Schreiben
Sie deshalb nur eine Query pro Datei.
Direktiven
Direktiven setzen Metadaten. Alle sind optional. Unbekannte Direktiv-Namen werden ignoriert, damit Regeln zukunftskompatibel bleiben.
| Direktive | Pflicht | Wert | Beschreibung |
|---|---|---|---|
@name |
nein | string | Menschenlesbarer Titel der Regel. Default: Dateiname ohne Endung |
@severity |
nein | enum | info, warn (auch warning), error. Default warn; unbekannte Werte fallen ebenfalls auf warn zurück |
@category |
nein | string | Frei wählbare Gruppe (z.B. Async, Design). Default General |
@description |
nein | string | Eine Zeile, was die Regel prüft |
@recommendation |
nein | string | Eine Zeile, wie man es behebt |
Strings nutzen C-Escapes für \", \\, \n, \r, \t, \0.
Unbekannte Escapes bleiben unverändert stehen, damit "Handler\d*$"
für Regex-Patterns natürlich bleibt.
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')?
Zwei semantische Hinweise:
orderbywird vom Parser akzeptiert, hat in dieser Form aber derzeit keine Wirkung: die Ergebnisse behalten die Reihenfolge der Collection. Zum Sortieren nutzen Sie stattdessen die Fluent-MethodenOrderBy/OrderByDescending.- Alle
let-Klauseln werden vor allenwhere-Klauseln ausgewertet, auch wenn sie im Quelltext gemischt stehen.
Beispiel:
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>)* )? ')' )?
<argument> ::= <expression> | <lambda>
<lambda> ::= <id> '=>' <expression>
Argumente werden durch Kommas getrennt. Aufrufe ohne Argumente wie
.Count(), .Any() oder .ToLower() funktionieren sowohl als
Schritte der äußeren Fluent-Kette als auch innerhalb von Ausdrücken
oder Lambda-Bodys, etwa in einer where-Bedingung: m.Name.ToLower()
wird als Methodenaufruf geparst, nicht als Zugriff auf ein Property.
Beispiel:
Methods.Where(m => m.IsAsync && m.Parameters.Count > 5)
Bei reinen Filtern treffen beide Formen dieselben Entitäten, sie sind
aber nicht vollständig austauschbar: Sortieren kann nur die
Fluent-Form (siehe den Hinweis zu orderby oben), und bei einem
Auswertungsfehler überspringt die LINQ-Form nur das betroffene
Element, während die Fluent-Form die ganze Regel ohne Findings
abbricht. Bei mehreren where-Bedingungen liest sich die LINQ-Form
meist besser. Bei kurzen Filter-Ketten ist Fluent oft kompakter.
Ausdrucksgrammatik
Operatoren von festester zu lockerster Bindung:
- Primär: Literale, Identifier,
( expr ) - Member-Access und Method-Call:
a.b,a.b(args) - Unär:
!expr,-expr - Multiplikativ:
*,/,% - Additiv:
+,- - Vergleich:
==,!=,>,>=,<,<= - Logisches UND:
&& - Logisches ODER:
||
Literale die der Lexer kennt: Integer, Float, String ("..."),
true, false.
Ein Identifier (<id>) beginnt mit einem Buchstaben oder Unterstrich,
gefolgt von beliebig vielen Buchstaben, Ziffern oder Unterstrichen.
null ist kein Lexer-Literal, sondern wird bei der Auswertung als
spezieller Identifier aufgelöst. Vergleiche wie t.BaseType == null
sind daher erlaubt.
Whitespace und Kommentare
- Whitespace zwischen Tokens ist bedeutungslos.
- Zeilenkommentare beginnen mit
//und gehen bis Zeilenende. - Es gibt keine Block-Kommentare in
.ccr-Dateien.
Was nicht geht
Die DSL ist absichtlich klein und seiteneffektfrei:
- Kein File-IO aus einem Query-Body.
- Kein mutabler State, jedes Property ist ein Read.
- Kein async, Auswertung ist synchron über dem gecachten Code-Modell.
Wenn Ihnen ein Prädikat fehlt, melden Sie uns den Anwendungsfall bitte als kurze Anfrage, statt auf Reflection auszuweichen. Die DSL wächst absichtlich, nicht zufällig.
Wo geht's weiter
- Prädikat-Katalog: vollständige Liste der Properties auf jeder Entität
- Syntax-Übersicht: kompakte, anwendungsfallorientierte Variante dieser Seite
- Regel-Beispiele: praktische Anwendungen der Grammatik