§

Voor de regelanalist

Regels schrijven en toetsen in RegelSpraak

Je schrijft regels en hun verwachte uitkomsten in dezelfde taal en krijgt per scenario PASS of FAIL. Op verzoek legt de toolchain in gewoon Nederlands uit waarom een kenmerk wel of niet is toegekend, welke variant van een defaults-groep besliste en hoe een waarde berekend is. Java schrijf je niet; de hulpmiddelen draaien wel op Java 21 of hoger, dus dat moet op je machine staan.

For the rule analyst

Writing and testing rules in RegelSpraak

You write rules and their expected outcomes in the same language and get PASS or FAIL for each scenario. On request the toolchain explains in plain Dutch why a characteristic was or was not assigned, which variant of a defaults group decided and how a value was computed. You write no Java; the tools do run on Java 21 or later, so your machine needs it.

§ 1de rolthe role

Wat de rol inhoudt

Als regelanalist schrijf je drie dingen in .rgs-bestanden: het gegevensmodel in GegevensSpraak, het declaratiedeel van de taal; de regels in RegelSpraak; en scenariotoetsen, elk een situatie met de verwachte uitkomst. Bestanden worden in de opgegeven volgorde samengevoegd. De toolchain parseert de tekst, resolvet en typeert hem tegen het model en schrijft hem als IR-document; de Drools-backend van uchar bouwt dat document en voert het uit. Scenario's worden casebestanden die uchar naspeelt.

De grammatica, de resolver, de IR-emitter, de scenariolaag, de semantic tokens en de regelgraaf komen uit uchar (frontends/regelspraak/, backends/drools/). De distributie regelspraak-toolchain, opvolger van regelspraak-java, bouwt ze uit de broncode van uchar, die zij als git-submodule op een vastgepinde commit meedraagt, en voegt de commandoregel bin/regelspraak, de uitleg, de beslisservice en de playground toe. Wat uchar zelf is, staat op de overzichtspagina.

What the role involves

As a rule analyst you write three things in .rgs files: the data model in GegevensSpraak, the declaration part of the language; the rules in RegelSpraak; and scenario tests, each a situation with its expected outcome. Files are concatenated in the order given. The toolchain parses the text, resolves and type-checks it against the model and writes it as an IR document; uchar's Drools backend builds that document and runs it. Scenarios become case files that uchar replays. RegelSpraak is Dutch by definition, so rule text on this page stays in Dutch.

The grammar, the resolver, the IR emitter, the scenario layer, the semantic tokens and the rule graph come from uchar (frontends/regelspraak/, backends/drools/). The distribution regelspraak-toolchain, successor of regelspraak-java, builds them from uchar's source, which it carries as a git submodule at a pinned commit, and adds the command line bin/regelspraak, the explanations, the decision service and the playground. What uchar itself is, is on the overview page.

§ 2casuscase

De casus: de Jutterswet

De voorbeelden komen uit de Jutterswet, de Wet op de jutterspenning van het verzonnen eiland Scholde. De wet regelt wie erkend jutter is en wie recht heeft op de jutterspenning; de eilandraad stelt daarvoor jaarlijks een vondstdrempel en een penningbedrag vast. De wet kent vijf versies; v4 geldt sinds 1 januari 2025 en telt dertien artikelen. Negen dossiers leggen per versie de bedoelde uitkomst vast, ook bij open begrippen en waar de wet zwijgt. De wet is verzonnen omdat alleen een rechtenvrije casus in een openbare specificatie kan, en hij volgt artikel voor artikel het British Nationality Act-experiment van 1986 (zie herkomst).

Het kernartikel, dat in § 3 terugkomt:

Art. 3 (penningrecht) Jutterswet · corpus/jutterswet/wet-v4.md

Recht op de jutterspenning heeft de erkende jutter van onbesproken juttersgedrag die in het penningjaar ten minste zoveel vondsten als de vondstdrempel bij de strandvoogd heeft aangemeld.

The case: the Jutterswet

The examples come from the Jutterswet, the Beachcombers' Token Act of the fictional island of Scholde. The act determines who is a recognized beachcomber and who is entitled to the beachcombers' token; for this, the island council sets a find threshold and a token amount every year. The act has five versions; v4 has applied since 1 January 2025 and has thirteen articles. Nine case files record the intended outcome per version, including for open concepts and where the act is silent. The act is fictional because only a case free of rights can go into a public specification, and it follows the 1986 British Nationality Act experiment article by article (see provenance).

The core article, which returns in § 3:

Art. 3 (entitlement to the token) Jutterswet (translation)

The recognized beachcomber of irreproachable beachcombing conduct who, in the token year, has reported at least as many finds as the find threshold to the beach warden is entitled to the beachcombers' token.

§ 3van artikel naar regelfrom article to rule

Van artikel naar regel

Het corpus legt drie artikelen naast een RegelSpraak-bron en een case.json met invoer en verwachte uitkomst. Afwijkingen van de wetstekst zijn vastgelegd.

Artikel 3 en jutterswet-penningdrempel

Art. 3 (penningrecht) Jutterswet · corpus/jutterswet/wet-v4.md

Recht op de jutterspenning heeft de erkende jutter van onbesproken juttersgedrag die in het penningjaar ten minste zoveel vondsten als de vondstdrempel bij de strandvoogd heeft aangemeld.

From article to rule

The corpus places three articles next to a RegelSpraak source and a case.json with input and expected outcome. Departures from the statute text are recorded.

Article 3 and jutterswet-penningdrempel

Art. 3 (entitlement to the token) Jutterswet (translation)

The recognized beachcomber of irreproachable beachcombing conduct who, in the token year, has reported at least as many finds as the find threshold to the beach warden is entitled to the beachcombers' token.

RegelSpraakcorpus/golden/jutterswet-penningdrempel/source.rgs
Objecttype de Jutter (mv: Jutters) (bezield)
    is penninggerechtigd kenmerk (bijvoeglijk);
    het aantal aangemelde vondsten Numeriek (niet-negatief geheel getal);

Parameter de vondstdrempel : Numeriek (geheel getal)

Regel Kenmerktoekenning jutter penninggerechtigd
    geldig altijd
»        Een Jutter is penninggerechtigd
»        indien zijn aantal aangemelde vondsten groter of gelijk is aan de vondstdrempel.
invoer
vondstdrempel 12; J1 met 14 aangemelde vondsten, J2 met 3
verwacht
J1 penninggerechtigd, J2 niet; consistent
afgespeeld
oracle: PASS (3 expectations) in de referentie-interpreter (7-11-2026)

De bron bevat een objecttype met een bijvoeglijk kenmerk, een parameter en een kenmerktoekenning met voorwaarde; een kenmerk dat geen regel toekent, staat uit, dus ook "niet penninggerechtigd" is toetsbaar. Afwijkingen: het aantal vondsten is invoer, geen telling, en "erkende jutter" en "van onbesproken juttersgedrag" ontbreken; dat laatste is bewust een open begrip, dat het corpus als Unresolved-grens behandelt (§ 9); deze bron laat het weg. Drempel 12 is de waarde voor penningjaar 2025.

input
find threshold 12; J1 with 14 reported finds, J2 with 3
expected
J1 penninggerechtigd (entitled to the token), J2 not; consistent
replayed
oracle: PASS (3 expectations) in the reference interpreter (2026-11-07)

The source contains an object type with an adjectival characteristic, a parameter and a characteristic assignment with a condition; a characteristic that no rule assigns is off, so "not entitled" can be tested too. Departures: the number of finds is input, not a count, and "recognized beachcomber" and "of irreproachable beachcombing conduct" are missing; the latter is deliberately an open concept, which the corpus treats as an Unresolved boundary (§ 9); this source leaves it out. Threshold 12 is the value for token year 2025.

Artikel 3b en jutterswet-penningjaar

Art. 3b (telling) Jutterswet · corpus/jutterswet/wet-v4.md

Voor de toepassing van artikel 3 telt iedere in het penningjaar aangemelde vondst eenmaal; een vondst waarvan de dag een stormvloeddag is, telt tweemaal. Het penningjaar is het kalenderjaar.

Article 3b and jutterswet-penningjaar

Art. 3b (counting) Jutterswet (translation)

For the purposes of article 3, every find reported in the token year counts once; a find whose day is a storm-surge day counts twice. The token year is the calendar year.

RegelSpraakcorpus/golden/jutterswet-penningjaar/source.rgs
»Tijdlijn het penningjaar heeft tijdvakken van een jaar startend op dag 1 van maand 1;

Eenheidsysteem Munt
    de euro (mv: euros) EUR €

Objecttype de Jutter (mv: Jutters) (bezield)
    is penninggerechtigd kenmerk (bijvoeglijk);
    is aanmeldplichtig kenmerk (bijvoeglijk);
»    het aantal aangemelde vondsten Numeriek (niet-negatief geheel getal) voor elke penningjaar;
    het totaal aangemelde vondsten Numeriek (geheel getal);

Parameter de vondstdrempel : Numeriek (geheel getal)
»Parameter het penningbedrag : Numeriek (geheel getal) met eenheid € voor elk jaar

Regel totaal aangemelde vondsten
    geldig altijd
»        Het totaal aangemelde vondsten van een Jutter moet berekend worden als het totaal van zijn aantal aangemelde vondsten.

Regel Kenmerktoekenning penninggerechtigd
    geldig altijd
        Een Jutter is penninggerechtigd
        indien zijn totaal aangemelde vondsten groter of gelijk is aan de vondstdrempel.

Regel Kenmerktoekenning aanmeldplichtig
    geldig altijd
        Een Jutter is aanmeldplichtig
»        indien hij niet penninggerechtigd is.
invoer
evaluatiedatum 15 maart 2026, vondstdrempel 12; J1 met 14 aangemelde vondsten, J2 met 3, J3 zonder het tijdsafhankelijke attribuut
verwacht
J1 totaal 14 en penninggerechtigd; J2 en J3 aanmeldplichtig, J3 met totaal 0

Ten opzichte van de vorige bron komen hier een benoemde tijdlijn, een attribuut per penningjaar, een parameter per jaar, de aggregatie het totaal van en negatie bij: aanmeldplichtig is de eerste negatieve afhankelijkheid in het corpus. Voor J3 ontbreekt de tijdlijn; het totaal daarvan is 0, niet leeg, en 0 is kleiner dan 12, dus is J3 niet penninggerechtigd en wel aanmeldplichtig. Afwijkingen: de dubbele telling op stormvloeddagen en de afbakening tot het penningjaar ontbreken; drempel 12 is niet gekoppeld aan de jaarwaarde voor 2026 (14).

input
evaluation date 15 March 2026, find threshold 12; J1 with 14 reported finds, J2 with 3, J3 without the time-dependent attribute
expected
J1 total 14 and entitled; J2 and J3 aanmeldplichtig (required to report), J3 with a total of 0

Compared with the previous source, this one adds a named timeline, an attribute per token year, a parameter per year, the aggregation het totaal van (the total of) and negation: being required to report is the first negative dependency in the corpus. For J3 the timeline is missing; its total is 0, not empty, and 0 is less than 12, so J3 is not entitled and is required to report. Departures: the double count on storm-surge days and the restriction to the token year are missing; threshold 12 is not tied to the annual value for 2026 (14).

Artikel 9 en jutterswet-berging

Art. 9 (gezamenlijke berging) Jutterswet · corpus/jutterswet/wet-v4.md

Bergen twee of meer jutters gezamenlijk één zaak, dan wordt de opbrengst van de berging in euro's over de bergers verdeeld naar rato van ieders aandeel in het geborgen gewicht, met voor iedere berger ten hoogste het bergingsmaximum dat de strandvoogd bij de aanvang van de berging voor hem heeft vastgesteld, en ieder aandeel afgerond op hele euro's naar beneden. Wat door het maximum of de afronding onverdeeld blijft, valt toe aan het eilandfonds. Is geen gewicht per berger vastgesteld, dan wordt in gelijke delen verdeeld.

Article 9 and jutterswet-berging

Art. 9 (joint salvage) Jutterswet (translation)

If two or more beachcombers jointly salvage one object, the proceeds of the salvage in euros are divided among the salvors in proportion to each one's share of the salvaged weight, with for each salvor at most the salvage maximum that the beach warden set for him at the start of the salvage, and each share rounded down to whole euros. Whatever remains undivided because of the maximum or the rounding goes to the island fund. If no weight has been established per salvor, the proceeds are divided in equal shares.

RegelSpraakcorpus/golden/jutterswet-berging/source.rgs
Eenheidsysteem Munt
    de euro (mv: euros) EUR €

Eenheidsysteem Gewicht
    het kilogram (mv: kilogrammen) kg
    de ton (mv: tonnen) t = 1000 kg

Objecttype de Berging (mv: Bergingen)
    de opbrengst Numeriek (geheel getal) met eenheid €;
    de onverdeelde rest Numeriek (geheel getal) met eenheid €;

Objecttype de Berger (mv: Bergers) (bezield)
    het bergingsaandeel (mv: bergingsaandelen) Numeriek (geheel getal) met eenheid €;
    het geborgen gewicht Numeriek (getal met 2 decimalen) met eenheid kg;
    het bergingsmaximum Numeriek (geheel getal) met eenheid €;

Feittype bergingsdeelname
    de berging Berging
    de berger (mv: bergers) Berger
Eén berging heeft meerdere bergers

Regel verdeel de opbrengst van een berging
    geldig altijd
»        De opbrengst van een berging wordt verdeeld over de bergingsaandelen van alle bergers van de berging, waarbij wordt verdeeld:
»        - naar rato van het geborgen gewicht,
»        - met een maximum van het bergingsmaximum,
»        - afgerond op 0 decimalen naar beneden.
»        Als onverdeelde rest blijft de onverdeelde rest van de berging over.
invoer
opbrengst 1200; bergers W, H en D met 3,00, 1,00 en 2,00 kg, elk met bergingsmaximum 300
verwacht
bergingsaandeel W 300, H 200, D 300; onverdeelde rest 400
afgespeeld
oracle: PASS (5 expectations) in de referentie-interpreter (7-11-2026)

Naar rato zouden W en D 600 en 400 krijgen; het deel boven het maximum wordt niet herverdeeld maar valt in de rest. De bron gebruikt eenheidsystemen, een feittype met rollen en kardinaliteit, en een verdeelregel naar rato, met maximum, naar beneden afgerond en met een attribuut voor de rest. Afwijkingen: de rest blijft op de Berging in plaats van naar het eilandfonds te gaan, een bewuste en vastgelegde benadering; verdeling in gelijke delen staat in een aparte golden, jutterswet-bergingsdeling, zonder de voorwaarde uit artikel 9.

Wat je verder kunt schrijven, staat in § 4; een overzicht van de constructen van RegelSpraak staat op de pagina De talen.

input
proceeds 1200; salvors W, H and D with 3.00, 1.00 and 2.00 kg, each with salvage maximum 300
expected
share (bergingsaandeel) W 300, H 200, D 300; undivided remainder 400
replayed
oracle: PASS (5 expectations) in the reference interpreter (2026-11-07)

Pro rata, W and D would receive 600 and 400; the part above the maximum is not redistributed but falls into the remainder. The source uses unit systems, a fact type with roles and cardinality, and a distribution rule that is pro rata, capped, rounded down and has an attribute for the remainder. Departures: the remainder stays on the Berging object instead of going to the island fund, a deliberate and recorded approximation; distribution in equal shares is shown in a separate golden, jutterswet-bergingsdeling, without art. 9's condition.

What else you can write is in § 4; an overview of the constructs of RegelSpraak is on the page The languages.

§ 4constructiesconstructs

Constructies van het dialect

De RegelSpraak-frontend van uchar leest RegelSpraak v2.3.0 met uitbreidingen en vernauwingen, elk beschreven in een sectie van frontends/regelspraak/grammar/DIVERGENCES.md. De tabbladen hieronder tonen acht vormen die verder gaan dan de bronnen van § 3, elk met een fragment uit een golden van het corpus. Elke golden heeft casebestanden met de verwachte uitkomsten en speelt in de referentie-interpreter, Clingo en de Drools-backend. Een regel op een tijdsafhankelijk attribuut rekent per periode en schrijft alleen in de perioden waarin zijn voorwaarde geldt (jutterswet-loon-per-periode); zie de constructencatalogus van De talen.

Constructs of the dialect

uchar's RegelSpraak frontend reads RegelSpraak v2.3.0 with extensions and narrowings, each described in a section of frontends/regelspraak/grammar/DIVERGENCES.md. The tabs below show eight forms that go beyond the sources of § 3, each with an excerpt from a golden of the corpus. Every golden has case files with the expected outcomes and plays in the reference interpreter, Clingo and the Drools backend. A rule on a time-dependent attribute computes per period and writes only in the periods in which its condition holds (jutterswet-loon-per-periode); see the catalogue of constructs on The languages.

Een kwantor mag lopen over leden die twee of meer relaties van het regelonderwerp af liggen, zoals de vondsten van de melder van een Melding. Dat kan met elke kwantor (alle, geen, ten minste, ten hoogste, precies n), met als toets een kenmerk, een vergelijking of een leeg/gevuld-toets, in de voorwaarde van een regel en in een Uitzondering op. Een lid dat langs twee routes bereikt wordt, telt één keer. Een telling over een pad, "het aantal vondsten van de melder van de Melding", is een fout.

A quantifier may range over members two or more relations away from the rule subject, such as the finds of the reporter of a Melding. This works with every quantifier (alle, geen, ten minste, ten hoogste, precies n), with a characteristic, a comparison or an empty/filled test as the test, in the condition of a rule and in an Uitzondering op. A member reached along two routes counts once. A count over a path, "het aantal vondsten van de melder van de Melding", is an error.

RegelSpraakcorpus/golden/jutterswet-dringende-melding/source.rgs
// Bron: art. 3l lid 2
Regel dringende melding
    geldig altijd
        Een Melding is dringend
»        indien ten minste één van de vondsten van de melder van de Melding zwaar is.

// Bron: art. 3l lid 3
Regel voorrang
    geldig altijd
        Een Melding heeft voorrang.

// Bron: art. 3l lid 3
Uitzondering op voorrang: wrakstuk
    geldig altijd
        Een Melding heeft geen voorrang
»        indien ten minste één van de vondsten van de melder van de Melding een wrakstuk is.
§ 5scenariotoetsenscenario tests

Scenariotoetsen

Een scenariotoets zegt in RegelSpraak wat een case in JSON zegt. RegelSpraak v2.3.0 heeft geen testhoofdstuk; scenariotoetsen zijn een gedocumenteerde uitbreiding van de RegelSpraak-frontend van uchar, met een eigen startregel (testDocument). Het idee komt uit ALEF, de ontwikkelomgeving van de Belastingdienst: daar legt de regelanalist in de testtaal TestSpraak naast de regels testgevallen vast met een situatie, een verwacht resultaat en een rekendatum, waaruit ALEF JUnit-tests en een testrapport genereert. uchar geeft dat een eigen tekstvorm, in hetzelfde .rgs-bestand of een volgend, met een andere syntaxis dan ALEF. Het bindende contract is de paragraaf Scenariotoetsen in frontends/regelspraak/grammar/DIVERGENCES.md. Scenario's horen niet in de IR: elk scenario wordt een casebestand naast het IR-document, en de uitvoerders spelen het na zoals elke andere case.

Een scenario begint met Scenario <scenarionaam>. Daarna volgen, elk optioneel en in deze volgorde: één scope (toetst de regel <regelnaam>, toetst de regels <a>, <b> en <c> of toetst de regelgroep <groepnaam>), met peildatum <dd-mm-jjjj>, met parameterset <naam> en een of meer keren gebaseerd op de situatie <naam>. Met een scope draaien alleen de genoemde regels, zonder scope het hele document; alle scenario's van één document hebben dezelfde scope. Let op: leest een geteste regel een waarde die een regel buiten de scope afleidt, dan wordt die waarde in het scenario niet afgeleid en kan het scenario falen; het rapport noemt de regel buiten de scope niet, de uitleg (§ 6) wel de voorwaarde die gold. De situatie staat inline na Gegeven is de volgende situatie: of in een herbruikbaar blok Situatie <naam>:, parameterwaarden desgewenst in een blok Parameterset <naam>:, dat het scenario kiest met met parameterset <naam>. Met als verwacht resultaat: is verplicht. De vormen:

Scenario tests

A scenario test says in RegelSpraak what a case says in JSON. RegelSpraak v2.3.0 has no test chapter; scenario tests are a documented extension of uchar's RegelSpraak frontend, with a start rule of their own (testDocument). The idea comes from ALEF, the Dutch Tax Administration's development environment: there the rule analyst records test cases next to the rules in the test language TestSpraak, with a situation, an expected result and a calculation date, from which ALEF generates JUnit tests and a test report. uchar gives this its own textual form, in the same .rgs file or a following one, with a syntax that differs from ALEF's. The binding contract is the section Scenariotoetsen in frontends/regelspraak/grammar/DIVERGENCES.md. Scenarios are not part of the IR: each scenario becomes a case file next to the IR document, and the executors replay it like any other case.

A scenario starts with Scenario <scenarionaam>. Then follow, each optional and in this order: one scope (toetst de regel <regelnaam>, toetst de regels <a>, <b> en <c> or toetst de regelgroep <groepnaam>: tests the rule, the rules, the rule group), met peildatum <dd-mm-jjjj> (reference date), met parameterset <naam> (with parameter set) and one or more gebaseerd op de situatie <naam> (based on the situation). With a scope only the named rules run, without one the whole document; all scenarios of one document share the same scope. Note: if a tested rule reads a value that a rule outside the scope derives, that value is not derived in the scenario and the scenario may fail; the report does not name the rule outside the scope, the explanation (§ 6) does name the condition that held. The situation is written inline after Gegeven is de volgende situatie: or in a reusable Situatie <naam>: block, parameter values optionally in a Parameterset <naam>: block, which the scenario chooses with met parameterset <naam>. Met als verwacht resultaat: (expected result) is mandatory. The forms:

RegelSpraaksamengevat uitsummarised from uchar · frontends/regelspraak/grammar/DIVERGENCES.md (Scenariotoetsen)
een <objecttypenaam> <LABEL>
- de|het <attribuutnaam> is <waarde> [van <datum> tot <datum>]
- de|het <rolnaam> is <LABEL>
- de|het <rolnaam-meervoud> zijn <LABEL>, <LABEL> en <LABEL>
- is [een|de|het] <kenmerknaam> [van <datum> tot <datum>]     (bijvoeglijk kenmerk)
- heeft [een|de|het] <kenmerknaam> [van <datum> tot <datum>]  (bezittelijk kenmerk)

de parameter <parameternaam> is <waarde> [van <datum> tot <datum>]

Parameterset <naam>:
de parameter <parameternaam> is <waarde> [van <datum> tot <datum>]

- de|het <attribuutnaam> van <LABEL> is <waarde>
- de|het <attribuutnaam> van <LABEL> is leeg
- de|het <attribuutnaam> van <LABEL> is <waarde> van <datum> tot <datum>   (één segment van een tijdlijn)
- de|het <attribuutnaam> van <LABEL> is <waarde> tot <datum>               (segment met open begin)
- de|het <attribuutnaam> van <LABEL> is <waarde> vanaf <datum>             (segment met open eind)
- de|het <attribuutnaam> van <LABEL> is altijd <waarde>
- de|het <attribuutnaam> van <LABEL> is altijd leeg
- <LABEL> is|heeft [een|de|het] <kenmerknaam>     (kenmerk aanwezig)
- <LABEL> is|heeft niet|geen <kenmerknaam>        (kenmerk afwezig)
- de gegevens zijn consistent
- de gegevens zijn inconsistent [volgens "<regel>" [voor <LABEL>]]

Waarden krijgen de eenheid die de declaratie eist en hebben niet meer decimalen dan zij toelaat; een verenigbare andere eenheid wordt exact omgerekend. Een attribuut dat (of onbekend) of (of niet van toepassing) declareert, neemt die afwezigheidsmarker ook als waarde, in de situatie en in een verwachting. Een afwezigheidsmarker die de declaratie niet noemt, is een fout (RGS-377). Elk scenario draait in een verse sessie. Elke parameter die een geteste regel leest, heeft een waarde nodig in de situatie of in de gekozen parameterset, en een regel met een geldigheidsvenster heeft met peildatum nodig. Verwachtingen worden exact vergeleken; een typefout of syntaxfout, ook in de scenariolaag, is een melding met een regelnummer, geteld over de bestanden in de opgegeven volgorde.

Een deling van een gevulde waarde door een numerieke nul geeft in alle uitvoerders een lege waarde en een inconsistentie op de regel en het object (EF-005); de berekening van de andere objecten gaat door. In een scenario toets je dat met de gegevens zijn inconsistent volgens "<regel>" voor <LABEL>. De regel in het voorbeeld rondt af, want een deling door een attribuut garandeert geen eindig aantal decimalen; zonder afronding of een doel van de vorm Numeriek (getal) weigert de frontend haar (RGS-370). Het volgende voorbeeld gaf op 7 november 2026 PASS:

Values carry the unit the declaration requires and no more decimals than it allows; a compatible different unit is converted exactly. An attribute that declares (of onbekend) (or unknown) or (of niet van toepassing) (or not applicable) also takes that absence marker as a value, in the situation and in an expectation. An absence marker the declaration does not name is an error (RGS-377). Every scenario runs in a fresh session. Every parameter that a tested rule reads needs a value in the situation or in the chosen parameter set, and a rule with a validity window needs met peildatum. Expectations are compared exactly; a type error or a syntax error, also in the scenario layer, is a message with a line number, counted over the files in the order given.

Dividing a filled value by a numeric zero gives, in every executor, an empty value and an inconsistency on the rule and the object (EF-005); the computation of the other objects goes on. In a scenario you test it with de gegevens zijn inconsistent volgens "<regel>" voor <LABEL>. The rule in the example rounds, because a division by an attribute guarantees no finite number of decimals; without a rounding or a target of the form Numeriek (getal) the frontend refuses it (RGS-370). The following example gave PASS on 7 November 2026:

RegelSpraakzelf opgesteld, gedraaid op 7-11-2026; geen onderdeel van een testsuiteown example, run on 2026-11-07; not part of a test suite
Objecttype de Jutter (mv: Jutters) (bezield)
    het aantal aangemelde vondsten Numeriek (geheel getal);
    het aantal strandvoogddagen Numeriek (geheel getal);
    het vondstgemiddelde Numeriek (getal met 2 decimalen);

Regel vondstgemiddelde
    geldig altijd
»        Het vondstgemiddelde van een Jutter moet berekend worden als zijn aantal aangemelde vondsten gedeeld door zijn aantal strandvoogddagen rekenkundig afgerond op 2 decimalen.

Scenario deling door nul
Gegeven is de volgende situatie:
een Jutter J1
- het aantal aangemelde vondsten is 14
- het aantal strandvoogddagen is 0
een Jutter J2
- het aantal aangemelde vondsten is 14
- het aantal strandvoogddagen is 4

Met als verwacht resultaat:
»- het vondstgemiddelde van J1 is leeg
- het vondstgemiddelde van J2 is 3,50
»- de gegevens zijn inconsistent volgens "vondstgemiddelde" voor J1

Hieronder twee scenario's bij de bronnen uit § 3. Beide zijn op 7 november 2026 tegen de toolchain gedraaid en gaven PASS; omgezet naar casebestanden zijn ze gelijk aan de golden-cases uit § 3.

Below are two scenarios for the sources in § 3. Run against the toolchain on 7 November 2026, both gave PASS; turned into case files, they equal the golden cases of § 3.

RegelSpraakvoorbeeld, gedraaid op 7-11-2026; geen onderdeel van een testsuiteexample, run on 2026-11-07; not part of a test suite
Scenario drempeltoets penningjaar 2025
toetst de regel Kenmerktoekenning jutter penninggerechtigd

Gegeven is de volgende situatie:
een Jutter J1
- het aantal aangemelde vondsten is 14
een Jutter J2
- het aantal aangemelde vondsten is 3
de parameter de vondstdrempel is 12

Met als verwacht resultaat:
- J1 is penninggerechtigd
- J2 is geen penninggerechtigd
- de gegevens zijn consistent
RegelSpraakvoorbeeld, gedraaid op 7-11-2026; geen onderdeel van een testsuiteexample, run on 2026-11-07; not part of a test suite
Situatie gezamenlijke berging:
een Berging B1
- de opbrengst is 1200 €
- de bergers zijn W en H en D
een Berger W
- het geborgen gewicht is 3,00 kg
- het bergingsmaximum is 300 €
een Berger H
- het geborgen gewicht is 1,00 kg
- het bergingsmaximum is 300 €
een Berger D
- het geborgen gewicht is 2,00 kg
- het bergingsmaximum is 300 €

Scenario opbrengst naar rato met maximum
toetst de regel verdeel de opbrengst van een berging
gebaseerd op de situatie gezamenlijke berging

Met als verwacht resultaat:
- het bergingsaandeel van W is 300 €
- het bergingsaandeel van H is 200 €
- het bergingsaandeel van D is 300 €
- de onverdeelde rest van B1 is 400 €
- de gegevens zijn consistent

Staat in het eerste scenario - J2 is penninggerechtigd in plaats van - J2 is geen penninggerechtigd, dan faalt het, en geeft de runner met --trace dit rapport:

If the first scenario says - J2 is penninggerechtigd instead of - J2 is geen penninggerechtigd, it fails, and with --trace the runner gives this report:

loguitvoer van bin/regelspraak scenario --trace, 7-11-2026output of bin/regelspraak scenario --trace, 2026-11-07
FAIL  drempeltoets penningjaar 2025
    verwachting 2 FOUT: characteristicPresent J2 penninggerechtigd  (verwacht=aanwezig, werkelijk=afwezig)
    trace (1 vuring(en)):
      Kenmerktoekenning jutter penninggerechtigd @ [J1] x1
    dekking:
      Kenmerktoekenning jutter penninggerechtigd : FIRED

De status is PASS, FAIL of MARKER. Bij FAIL volgt per afwijkende verwachting een regel met FOUT, de soort verwachting, het onderwerp en de verwachte en werkelijke waarde; dan de trace, per regel de labels waarvoor ze vuurde en hoe vaak, en het blok dekking:, dat per vorm FIRED of NOT_FIRED meldt. Met --trace staat de trace ook bij PASS; met --explanation volgt onder elk gespeeld scenario het blok uitleg: (§ 6). De exitcode is 1 bij een falend scenario of een resolverfout, anders 3 bij een MARKER, anders 0. MARKER geldt per document: kan uchar het IR-document waartegen de scenario's draaien niet uitvoeren, dan is elk scenario van dat document MARKER, met de reden, en dat telt nooit als PASS.

Je draait scenario's met bin/regelspraak scenario, met de bestanden in volgorde en desgewenst --trace, --explanation of --vocabulary <naam>; make scenario FILE="…" doet hetzelfde. In een checkout bouwt bin/regelspraak zijn jar zelf met Maven als een bron nieuwer is dan de jar; de eerste build op een machine heeft daarvoor eenmaal netwerk nodig. make scenario-test is iets anders: het draait in één JVM de vaste referentieset van scenariobestanden van de toolchain, tegen een manifest dat per bestand de verwachte exitcode vastlegt, en is een van de gates van de toolchain.

The status is PASS, FAIL or MARKER. On FAIL, one FOUT (error) line per failing expectation follows, with the kind of expectation, its subject and the expected and actual value; then the trace, per rule the labels it fired for and how often, and the dekking: (coverage) block, which reports FIRED or NOT_FIRED per form. With --trace the trace is printed on PASS too; with --explanation an uitleg: (explanation) block follows under each scenario that played (§ 6). The exit code is 1 for a failing scenario or a resolver error, otherwise 3 for a MARKER, otherwise 0. MARKER applies per document: if uchar cannot run the IR document the scenarios run against, every scenario of that document is MARKER, with the reason, and that never counts as PASS.

You run scenarios with bin/regelspraak scenario, giving the files in order and optionally --trace, --explanation or --vocabulary <naam>; make scenario FILE="…" does the same. In a checkout, bin/regelspraak builds its own jar with Maven when a source is newer than the jar; the first build on a machine needs the network once for that. make scenario-test is something else: it runs the toolchain's fixed reference set of scenario files in one JVM, against a manifest that pins the expected exit code per file, and is one of the toolchain's gates.

shellregelspraak-toolchain · README.md, Makefile
bin/regelspraak scenario model.rgs regels.rgs scenario.rgs
make scenario FILE="model.rgs regels.rgs scenario.rgs"
§ 6uitlegexplanation

Uitleg bij de uitkomst

Met --explanation geeft de runner onder elk gespeeld scenario een blok uitleg: met zinnen per object. De uitleg verantwoordt drie dingen. Elk kenmerk dat een kenmerktoekenning toekent of niet toekent, met de voorwaarde die gold of het lid waar de voorwaarde stopte. Elke defaults-groep (Uitzondering op, Afwijking van), met de variant die besliste, de varianten vóór haar die niet golden en waarom, en de varianten erna als verdrongen, met wat zij vonden. En elk attribuut dat een gelijkstelling, berekening of initialisatie schrijft, met de regel die gold, de waarde, de waarden die de berekening las, de leden van een vouw en de waarde vóór een afronding of begrenzing. Staan er // Bron:-regels boven de regel, dan eindigt de zin op Grondslag: <tekst>; bij een defaults-groep is dat de bron van de variant die besliste. Consistentieregels, creaties en verdelingen krijgen geen zin: bij het bergingsscenario van § 5 staat onder uitleg: alleen (geen kenmerktoekenning om te verantwoorden).

De waarden in de zinnen komen van de Drools-motor. De replay van uchar speelt het scenario op een build met lezingen, per voorwaarde een verslag van wat de motor op het fixpunt las, en geeft de sessie die hij beoordeelde door aan de uitleg; die rekent zelf niets na. Een lege waarde staat er als leeg, een afwezigheidsmarker (onbekend, niet van toepassing) bij haar naam, getallen en datums in Nederlandse notatie. Bij een kwantor noemt de zin de leden die de uitkomst bepalen, met hun aantal. Een kenmerk staat er met het werkwoord van zijn soort: bijvoeglijk "is (niet) zwaar", bezittelijk "heeft (geen) voorrang". Voor het eerste scenario uit § 5:

Explaining the outcome

With --explanation the runner prints, under each scenario that played, an uitleg: block with sentences per object. The explanation accounts for three things. Every characteristic that a characteristic assignment assigns or does not assign, with the condition that held or the member where the condition stopped. Every defaults group (Uitzondering op, Afwijking van), with the variant that decided, the variants before it that did not hold and why, and the variants after it as displaced, with what they found. And every attribute that an equation, a calculation or an initialisation writes, with the rule that held, the value, the values the computation read, the members of a fold and the value before a rounding or a bound. If // Bron: lines stand above the rule, the sentence ends with Grondslag: <tekst> (legal basis); for a defaults group that is the source of the variant that decided. Consistency rules, creations and distributions get no sentence: for the salvage scenario of § 5, uitleg: holds only (geen kenmerktoekenning om te verantwoorden).

The values in the sentences come from the Drools engine. uchar's replay plays the scenario on a build with readings, per condition a record of what the engine read at the fixpoint, and hands the session it judged to the explanation, which computes nothing itself. An empty value appears as leeg (empty), an absence marker (onbekend, niet van toepassing) by its name, numbers and dates in Dutch notation. For a quantifier the sentence names the members that determine the outcome, with their count. A characteristic appears with the verb of its kind: adjectival "is (niet) zwaar", possessive "heeft (geen) voorrang". For the first scenario in § 5:

uitleguitvoer van bin/regelspraak scenario --explanation, 7-11-2026output of bin/regelspraak scenario --explanation, 2026-11-07
PASS  drempeltoets penningjaar 2025
    uitleg:
      J1 is penninggerechtigd, omdat zijn aantal aangemelde vondsten (14) is groter dan of gelijk aan de vondstdrempel (12)
      J2 is niet penninggerechtigd, want niet geldt: zijn aantal aangemelde vondsten (3) is groter dan of gelijk aan de vondstdrempel (12)

De golden jutterswet-dringende-melding (§ 4, tabblad pad) heeft // Bron:-regels, kwantoren over een pad en de defaults-groep voorrang. Een keuze uit de zinnen bij het eerste scenario van haar scenario.rgs; … staat voor weggelaten zinnen:

The golden jutterswet-dringende-melding (§ 4, tab path) has // Bron: lines, quantifiers over a path and the defaults group voorrang. A selection of the sentences for the first scenario of its scenario.rgs; … stands for omitted sentences:

uitlegcorpus/golden/jutterswet-dringende-melding/scenario.rgs · bin/regelspraak scenario --explanation, 7-11-2026bin/regelspraak scenario --explanation, 2026-11-07
PASS  dringend en voorrang
    uitleg:
      …
      V1 is zwaar, omdat zijn gewicht (12) is groter dan 10. Grondslag: art. 3l lid 1
      V2 is niet zwaar, want niet geldt: zijn gewicht (5) is groter dan 10. Grondslag: art. 3l lid 1
      …
      M1 is dringend, omdat aan de voorwaarde "ten minste één van de vondsten van de melder van de Melding zwaar is" is voldaan: voor 1 van de 2 (V1). Grondslag: art. 3l lid 2
      …
      M1 heeft voorrang volgens de regel 'voorrang' (onvoorwaardelijk); 'wrakstuk' gold niet, want aan de voorwaarde "ten minste één van de vondsten van de melder van de Melding een wrakstuk is" is niet voldaan: voor geen van de 2 (V1 en V2). Grondslag: art. 3l lid 3
      …
      M2 heeft geen voorrang volgens 'wrakstuk', omdat aan de voorwaarde "ten minste één van de vondsten van de melder van de Melding een wrakstuk is" is voldaan: voor 1 van de 2 (V4); verdrongen: de regel 'voorrang' (onvoorwaardelijk, toekennen). Grondslag: art. 3l lid 3
      M3 is niet dringend, want aan de voorwaarde "ten minste één van de vondsten van de melder van de Melding zwaar is" is niet voldaan: niet voor V5, het enige lid. Grondslag: art. 3l lid 2
      …

Een berekende waarde, uit de golden jutterswet-havengeld: het havengeld met de gelezen waarden en de waarde vóór de afronding, en een toeslag waarvoor geen regel gold.

A computed value, from the golden jutterswet-havengeld: the harbour fee with the values read and the value before the rounding, and a surcharge for which no rule held.

uitlegcorpus/golden/jutterswet-havengeld/scenario.rgs · bin/regelspraak scenario --explanation, 7-11-2026bin/regelspraak scenario --explanation, 2026-11-07
PASS  onder de drempel met afronding
    uitleg:
      V heeft als havengeld 9,41 volgens de regel 'havengeld' (onvoorwaardelijk): (het percentage havengeld van het taxatiebedrag van de taxatie van de Vondst) naar beneden afgerond op 2 decimalen, met percentage havengeld (12,5 %) en het taxatiebedrag van de taxatie van de Vondst (75,30); vóór de afronding 9,4125. Grondslag: art. 3i lid 1
      …
      V heeft geen toeslag, want geen regel gold; de regel 'toeslag' gold niet, want niet geldt: het taxatiebedrag van de taxatie van de Vondst verminderd met de drempel (-24,70) is groter dan 0

Andere zinsvormen zijn er voor een conflict in een defaults-groep ("want er is een conflict"), voor een groep waarin geen variant besliste ("want geen variant besliste"), voor een regel die op de rekendatum buiten haar geldigheid valt en voor een attribuut dat zijn invoerwaarde houdt ("houdt als … de invoerwaarde …, want geen regel gold"). Van een regel die geen waarde gaf naast een waarde die bleef staan, zegt de zin "gaf geen waarde en besliste niets". Bij een tijdsafhankelijk attribuut noemt de zin per periode welke regel of variant besliste, en een schrijver die op geen dag gold ("gold op geen dag"); bij een gemaakt object heet de invoerwaarde "de bij de objectcreatie toegekende waarde". Ze staan in Verbaliser.java en in docs/ENGINE.md van de toolchain, onder "The parts of an explanation".

Bij de beslisservice geeft ?explanation=true een lijst explanation in het antwoord, naast instances en inconsistencies; de dienst vuurt zo'n verzoek op een tweede build van hetzelfde document, met lezingen. ?explanationParts=true geeft daarnaast elke zin als boom van stukken met een soort (onderwerp, naam, waarde, regel, grondslag en andere), voor een programma dat de zin zelf opmaakt. Met --vocabulary <naam> bij de runner en ?vocabulary=<naam> bij de dienst staan de regelnamen in de trace en de gedeclareerde namen in de uitleg in dat vocabulaire; de regeltekst blijft zoals ze geschreven is. Een regel buiten de scope van een entrypoint krijgt bij de dienst één zin: de regel '…' staat buiten deze berekening. Hoe de dienst draait, staat op de pagina Voor de DevOps-engineer.

There are other sentence forms for a conflict in a defaults group ("want er is een conflict"), for a group in which no variant decided ("want geen variant besliste"), for a rule that falls outside its validity on the calculation date and for an attribute that keeps its input value ("houdt als … de invoerwaarde …, want geen regel gold"). Of a rule that gave no value beside a value that stood, the sentence says "gaf geen waarde en besliste niets". On a time-dependent attribute the sentence names per period which rule or variant decided, and a writer that held on no day ("gold op geen dag"); on a made object the input value is "de bij de objectcreatie toegekende waarde". They are in the toolchain's Verbaliser.java and docs/ENGINE.md, under "The parts of an explanation".

On the decision service, ?explanation=true adds a list explanation to the response, next to instances and inconsistencies; the service fires such a request on a second build of the same document, with readings. ?explanationParts=true adds each sentence as a tree of parts with a kind (subject, name, value, rule, legal basis and others), for a program that lays out the sentence itself. With --vocabulary <naam> on the runner and ?vocabulary=<naam> on the service, the rule names in the trace and the declared names in the explanation are in that vocabulary; the rule text itself stays as written. On the service, a rule outside the scope of an entrypoint gets one sentence: de regel '…' staat buiten deze berekening (the rule is outside this computation). How the service runs is on the page For the DevOps engineer.

shellregelspraak-toolchain · README.md, docs/ENGINE.md
bin/regelspraak scenario --explanation model.rgs scenario.rgs
curl -X POST 'http://…/<entrypoint>?explanation=true' -d @verzoek.json
§ 7regelgraafrule graph

De regelgraaf

De regelgraaf tekent het opgeloste model als graaf van producenten en consumenten, met de vormen van de regellaag (regels, creaties, verdelingen, consistentie- en uniciteitscontroles, defaults-groepen), attributen en kenmerken als knopen en de gegevensstroom als pijlen. Invoer (door niets geproduceerd) en terminale uitvoer (door niets geconsumeerd) hebben een eigen kleur: de invoer is precies wat een aanvraag moet leveren, de terminale uitvoer wat de dienst oplevert.

The rule graph

The rule graph draws the resolved model as a graph of producers and consumers, with the forms of the rule layer (rules, creations, allocations, consistency and uniqueness checks, defaults groups), attributes and characteristics as nodes and the data flow as edges. Inputs (produced by nothing) and terminal outputs (consumed by nothing) have their own colour: the inputs are exactly what a request must supply, the terminal outputs what the service delivers.

shellregelspraak-toolchain · README.md, Makefile
make graph FILE="a.rgs …" [OUT=… MMD=1]
bin/regelspraak graph [--mmd] out.html a.rgs …

De graaf komt uit uchar (backends/drools) en wordt daar door gate 15 bewaakt; de toolchain tekent hem. De analyse deelt haar kern (ProducerConsumer) met de scopecontrole van de dienst, zodat tekening en handhaving dezelfde afhankelijkheden gebruiken. Ze werkt op modelniveau, zonder Drools. make graph schrijft standaard out/rule-graph.html. De HTML laadt Mermaid via een CDN; --mmd (of MMD=1) geeft kale Mermaid-tekst voor offline gebruik.

The graph comes from uchar (backends/drools), where gate 15 guards it; the toolchain draws it. The analysis shares its core (ProducerConsumer) with the service's scope check, so the picture and the enforcement use the same dependencies. It works at model level, without Drools. make graph writes out/rule-graph.html by default. The HTML loads Mermaid from a CDN; --mmd (or MMD=1) produces bare Mermaid text for offline use.

§ 8playgroundplayground

De playground

De playground is een ontwikkelwerkbank in de beslisservice, alleen in de variant op de JDK-HttpServer. GET /playground levert één pagina met links een tekstvak en een veld voor een vocabulaire, rechts acht tabbladen: Parse, Resolver, IR, Dekking, DRL, Graaf, Scenario en Vocabulaires. De analyse loopt via POST /playground/analyse. Bij een parse- of resolverfout krijg je alleen de diagnoses, een parsefout met regel, kolom en de verwachte tokens. Bij een foutloos model volgen het IR-document, het oordeel van uchar of het model uitvoerbaar is (met de dekking per vorm), de DRL-tekst, de regelgraaf en de vocabulaires van de bron: per declaratie de benaming in elk vocabulaire, met de bronregel en het commentaar erboven.

Een Scenario-blok in de tekst vuurt echt. Het tabblad Scenario toont per scenario de verwachtingen met hun bronregel, de situatie (objecten, attributen, kenmerken, rollen met hun feittype, parameters en peildatum), het fixpunt (afgeleide en gemaakte waarden met hun herkomst, koppelingen, evaluatiefouten en inconsistenties, met een filter op label of naam) en uitklapbaar de dekking, de vuringen en de uitleg. Met een vocabulaire in het veld (?vocabulary=<naam>) staan graphJson, trace, dekking, uitleg, situatie en fixpunt in dat vocabulaire; de Mermaid-graaf, het IR en het oordeel blijven in de canonieke namen. Meldingen van uchar komen in het Engels bij een Accept-Language die en noemt.

Het tabblad DRL toont de regels als DRL-tekst. Die tekst is de tweede weergave van dezelfde analyse waarmee de Drools-backend van uchar vuurt, met truth maintenance. Een regel van een vorm heet als de RegelSpraak-regel; hulpregels van de verlaging, zoals de bijdragen aan een vouw, hebben een eigen label. Elke toets of berekening staat als Java-functie vóór de regel, met de RegelSpraak-zin als commentaarregel erboven en een naam die de soort en de regel noemt (toets_…, waarde_…). Zo lees je per regel welke voorwaarde de motor toetst en wat hij invoegt. Het JSON-antwoord van POST /playground/analyse geeft bovendien in drlRules per DRL-regel welke vorm of welke default van een defaults-groep hij verlaagt en waar hij met zijn functies in de tekst staat; de pagina zelf toont dat veld niet, het is er voor andere programma's.

Elk verzoek wordt opnieuw gecompileerd, met een tijdslimiet van 15 seconden (daarna volgt 503); er wordt niets bewaard. De playground staat standaard uit: de routes bestaan alleen als de dienst start met PLAYGROUND=true, en hij is niet bedoeld voor productie. Hoe je de dienst start, staat op de pagina Voor de DevOps-engineer.

The playground

The playground is a development workbench in the decision service, only in the variant on the JDK HttpServer. GET /playground serves one page with a text area and a field for a vocabulary on the left and eight tabs on the right: Parse, Resolver, IR, Dekking (coverage), DRL, Graaf (graph), Scenario and Vocabulaires (vocabularies). The analysis runs through POST /playground/analyse. With a parse or resolver error you get only the diagnostics, a parse error with line, column and the expected tokens. For an error-free model you also get the IR document, uchar's verdict on whether the model can run (with the coverage per form), the DRL text, the rule graph and the vocabularies of the source: per declaration its designation in each vocabulary, with the source line and the comment above it.

A Scenario block in the text fires for real. The Scenario tab shows per scenario the expectations with their source line, the situation (objects, attributes, characteristics, roles with their fact type, parameters and reference date), the fixpoint (derived and created values with their origin, links, evaluation faults and inconsistencies, with a filter on label or name) and, collapsible, the coverage, the firings and the explanation. With a vocabulary in the field (?vocabulary=<naam>), graphJson, trace, coverage, explanation, situation and fixpoint are in that vocabulary; the Mermaid graph, the IR and the verdict stay in the canonical names. uchar's messages come in English for an Accept-Language that names en.

The DRL tab shows the rules as DRL text. That text is the second rendering of the same analysis with which uchar's Drools backend fires, with truth maintenance. The rule of a form is named after the RegelSpraak rule; helper rules of the lowering, such as the contributions to a fold, have a label of their own. Every test or computation stands as a Java function before the rule, with the RegelSpraak sentence as a comment line above it and a name that gives the kind and the rule (toets_…, waarde_…). So you can read per rule which condition the engine tests and what it inserts. The JSON answer of POST /playground/analyse also gives, in drlRules, per DRL rule which form or which default of a defaults group it lowers and where it stands in the text with its functions; the page itself does not show that field, which is there for other programs.

Every request is compiled afresh, with a limit of 15 seconds (after which the service returns 503); nothing is stored. The playground is off by default: its routes exist only when the service starts with PLAYGROUND=true, and it is not meant for production. How to start the service is on the page For the DevOps engineer.

§ 9werkwijzeworkflow

Werkwijze en beperkingen

  1. Model schrijven In GegevensSpraak; bin/regelspraak check model.rgs controleert het zonder iets te schrijven (exitcode 1 bij een fout, 3 als uchar het model niet kan uitvoeren).
  2. Regels schrijven In hetzelfde of een volgend bestand.
  3. Scenario's schrijven Met gedeelde Situatie-blokken waar dat kan.
  4. Toetsen bin/regelspraak scenario … (§ 5); lees status en exitcode.
  5. Uitleg lezen Met --explanation: waarom is een kenmerk wel of niet toegekend, welke variant besliste, hoe is een waarde berekend?
  6. Graaf bekijken make graph FILE=…: klopt de invoer met wat een aanvraag levert?

De opdrachten van bin/regelspraak, zoals bin/regelspraak help ze toont. Vóór de opdracht kiest --language en Engelse meldingen (standaard nl). Behalve tokens en jar bestaat elke opdracht ook als make-doel met dezelfde naam en de bestanden in FILE="…":

check
het model controleren: samenvatting en diagnoses, in een keten van bestanden met bestand:regel
ir
het IR-document (uchar-ir-v1): bin/regelspraak ir <out.json> <source.rgs…>
cases
de scenario's als casebestanden van uchar, naast <dir>/ir.json: bin/regelspraak cases <dir> <source.rgs…>
tokens
de semantic tokens van de bronnen: bin/regelspraak tokens <out.json> <source.rgs…> (hieronder)
scenario
de scenariorunner (§ 5), met --trace, --explanation (§ 6) en --vocabulary <naam>
drl
de regels als DRL-tekst (§ 8); exitcode 3 met de reden als uchar het model niet draagt, exitcode 1 met de zin van uchar als de tekst niet compileert
graph
de regelgraaf (§ 7)
facts
de fact-beans die de generator van uchar voor het model schrijft: bin/regelspraak facts <outdir> <source.rgs…>
serve
de beslisservice; zie de pagina Voor de DevOps-engineer
jar
het pad van de jar waaruit de opdrachten draaien; bouwt hem eerst als een bron nieuwer is

Meldingen met een code

Elke melding van de frontend heeft een code (RGS-###), benoemde parameters en een tekst uit een catalogus in het Nederlands en het Engels; het register is diagnostic-codes.tsv in uchar. De toolchain meldt standaard in het Nederlands; bin/regelspraak --language en check … geeft dezelfde meldingen in het Engels. De eigen zinnen van de toolchain blijven Nederlands, ook de samenvatting van check en de uitleg. Een regel van check, drl of scenario die een waarde of zin van uchar draagt, staat geheel in de taal van uchar, onder --language en bijvoorbeeld expectation 1 FAIL: … (expected=present, actual=absent). check drukt per melding de ernst, de regel en de tekst af, zonder de code; tokens schrijft de code met de parameters. Een lijst waarin elke bullet - op één kolom staat, ook onder een groepskop:

Workflow and limitations

  1. Write the model In GegevensSpraak; bin/regelspraak check model.rgs checks it without writing anything (exit code 1 on an error, 3 when uchar cannot run the model).
  2. Write the rules In the same file or a following one.
  3. Write the scenarios With shared Situatie blocks where possible.
  4. Test bin/regelspraak scenario … (§ 5); read the status and the exit code.
  5. Read the explanation With --explanation: why was a characteristic assigned or not, which variant decided, how was a value computed?
  6. Inspect the graph make graph FILE=…: do the inputs match what a request supplies?

The commands of bin/regelspraak, as bin/regelspraak help shows them. Before the command, --language en selects English messages (nl by default). Apart from tokens and jar, every command also exists as a make target of the same name, with the files in FILE="…":

check
check the model: a summary and the diagnostics, in a chain of files with file:line
ir
the IR document (uchar-ir-v1): bin/regelspraak ir <out.json> <source.rgs…>
cases
the scenarios as uchar case files, next to <dir>/ir.json: bin/regelspraak cases <dir> <source.rgs…>
tokens
the semantic tokens of the sources: bin/regelspraak tokens <out.json> <source.rgs…> (below)
scenario
the scenario runner (§ 5), with --trace, --explanation (§ 6) and --vocabulary <naam>
drl
the rules as DRL text (§ 8); exit code 3 with the reason if uchar does not carry the model, exit code 1 with uchar's sentence if the text does not compile
graph
the rule graph (§ 7)
facts
the fact beans that uchar's generator writes for the model: bin/regelspraak facts <outdir> <source.rgs…>
serve
the decision service; see the page For the DevOps engineer
jar
the path of the jar the commands run from; builds it first when a source is newer

Messages with a code

Every message of the frontend has a code (RGS-###), named parameters and a text from a catalogue in Dutch and English; the register is diagnostic-codes.tsv in uchar. The toolchain reports in Dutch by default; bin/regelspraak --language en check … gives the same messages in English. The toolchain's own sentences stay Dutch, the summary of check and the explanation included. A line of check, drl or scenario that carries a value or sentence of uchar is entirely in uchar's language, under --language en for instance expectation 1 FAIL: … (expected=present, actual=absent). check prints the severity, the line and the text of each message, without the code; tokens writes the code with the parameters. A list in which every bullet - stands on one column, also under a group header:

RegelSpraakzelf opgesteld: vrijstelling.rgsown example: vrijstelling.rgs
Domein Vondstsoort is van het type Enumeratie
    'Touw'
    'Glas'
    'Blik'

Objecttype de Jutter (mv: Jutters) (bezield)
    de leeftijd Numeriek (geheel getal);
    de soort Vondstsoort;
    de vrijstelling kenmerk (bezittelijk);

Regel vrijstelling
    geldig altijd
        Een Jutter heeft de vrijstelling
        indien hij aan alle volgende voorwaarden voldoet:
        - zijn leeftijd is groter dan 65
»        - hij voldoet aan ten minste één van de volgende voorwaarden:
»        - zijn soort is gelijk aan 'Touw'
        - zijn soort is gelijk aan 'Glas'.
jsondiagnose uit de uitvoer van bin/regelspraak tokens, 7-11-2026; opgemaakt, de melding ingekortdiagnostic from the output of bin/regelspraak tokens, 2026-11-07; formatted, the message shortened
{
  "severity": "ERROR",
  "code": "RGS-353",
  "params": {
    "header": "- hij voldoet aan ten minste één van de volgende voorwaarden:",
    "next": "de bullet erna, '- zijn soort is gelijk aan 'Touw'', staat niet dieper"
  },
  "message": "de bullet '- hij voldoet aan ten minste één van de volgende voorwaarden:' opent een groep, maar de bullet erna, '- zijn soort is gelijk aan 'Touw'', staat niet dieper: een bullet onder een groepskop staat dieper, …",
  "file": "vrijstelling.rgs",
  "line": 16
}

tokens schrijft per bronbestand de stukken tekst met hun soort (keyword, objectType, attribute, characteristic, parameter, namedForm, unresolved en andere, in een bron met scenario's ook label, situation, parameterSet en scenario), hun plaats (regel vanaf 1, kolom vanaf 0 in UTF-16) en bij een verwijzing de plaats van de declaratie, ook in een ander bestand, en daarnaast de diagnoses. Het bestand wordt ook geschreven als het model een fout of een syntaxfout heeft, met exitcode 0. Een editor of een ander programma kan daarmee regels kleuren naar wat een naam noemt en van een verwijzing naar de declaratie springen. De vorm en de soorten staan in DIVERGENCES.md, onder "Semantic tokens".

Zonder Java, niet zonder JVM

De hulpmiddelen draaien op Java 21 of hoger; in een checkout vraagt het bouwen ervan ook Maven. Zonder JVM werken alleen de conversie van een CSV-bestand naar een Beslistabel-blok, in Python zonder afhankelijkheden (make csv-beslistabel FILE=<csv> [OUT=<pad>]), en de referentie-interpreter van uchar, in OCaml, die een ir.json met een case*.json afspeelt (runner [--noop | --trace] <ir.json> <case.json>). Die ir.json en de casebestanden uit je scenario's (bin/regelspraak cases) komen wel uit de Java-frontend, of zijn met de hand geschreven; de referentie-interpreter speelt ze daarna zonder JVM na.

Wat niet kan

Een lezing die de resolver niet kan binden, zoals een kenmerk dat twee subtypen naast elkaar declareren (§ 4), wordt in de IR een Unresolved-knoop, met een waarschuwing: de markering voor wat de taal niet dekt. Een welgevormde IR mag zulke knopen bevatten, maar nooit verbergen. De rode case corpus/red/wf044-unresolved-reached legt vast dat de referentie-interpreter en Clingo erop stoppen met een fout (EF-019). De toolchain weigert zo'n model: check en drl eindigen met exitcode 1 (onopgeloste naam …) en de beslisservice start niet, ook niet met --allow-markers. Een scenario dat de knoop bereikt, faalt.

Een vorm die een uitvoerder niet ondersteunt, wordt met een reden geweigerd, nooit stil. In de toolchain is een scenario op zo'n regel MARKER (§ 5), en een entrypoint van de beslisservice dat de regel nodig heeft, houdt de start van de dienst tegen, tenzij die start met --allow-markers (ALLOW_MARKERS=true in de container); elke aanvraag aan dat entrypoint krijgt dan 503. In het dialect weigert de frontend met een fout onder meer een telling over een pad, een tweede deelselectie op één keten (RGS-351), een lezing buiten de afbakening van een kring over instanties (RGS-357) en, in een geneste deelselectie, het element van de omsluitende deelselectie bij zijn typenaam (RGS-358).

Objectcreatie, machtsverheffing, alle afrondingsrichtingen, verdeling op volgorde van en beslistabellen zijn gewoon RegelSpraak en werken in de toolchain; een beslistabel wordt ontsuikerd tot één regel per rij. Wat je niet kunt uitdrukken, zijn terugwerkende kracht en kennistijd: een besluit dat later met terugwerking wordt genomen. Dat valt buiten v1 (zie de reikwijdte van v1).

tokens writes, per source file, the pieces of text with their kind (keyword, objectType, attribute, characteristic, parameter, namedForm, unresolved and others, in a source with scenarios also label, situation, parameterSet and scenario), their position (line from 1, column from 0 in UTF-16) and for a reference the position of the declaration, also in another file, and next to that the diagnostics. The file is written also when the model has an error or a syntax error, with exit code 0. An editor or another program can use it to colour rules by what a name refers to and to jump from a reference to its declaration. The format and the kinds are in DIVERGENCES.md, under "Semantic tokens".

No Java, but a JVM

The tools run on Java 21 or later; building them from a checkout also needs Maven. Only two things work without a JVM: converting a CSV file into a Beslistabel (decision table) block, in Python without dependencies (make csv-beslistabel FILE=<csv> [OUT=<pad>]), and uchar's reference interpreter, in OCaml, which replays an ir.json with a case*.json (runner [--noop | --trace] <ir.json> <case.json>). That ir.json and the case files made from your scenarios (bin/regelspraak cases) do come from the Java frontend, or are written by hand; the reference interpreter then replays them without a JVM.

What does not work

A reading the resolver cannot bind, such as a characteristic that two sibling subtypes declare (§ 4), becomes an Unresolved node in the IR, with a warning: the marking of what the language does not cover. A well-formed IR may contain such nodes but never hide them. The red case corpus/red/wf044-unresolved-reached records that the reference interpreter and Clingo stop on it with an error (EF-019). The toolchain refuses such a model: check and drl end with exit code 1 (onopgeloste naam …: unresolved name) and the decision service does not start, not even with --allow-markers. A scenario that reaches the node fails.

A form that an executor does not carry is refused with a reason, never silently. In the toolchain a scenario on such a rule is MARKER (§ 5), and a decision-service entrypoint that needs the rule stops the service from starting, unless it starts with --allow-markers (ALLOW_MARKERS=true in the container); every request to that entrypoint then gets 503. In the dialect, the frontend refuses with an error, among other things, a count over a path, a second subselection on one chain (RGS-351), a read outside the bounds of a cycle across instances (RGS-357) and, in a nested subselection, the element of the enclosing subselection by its type name (RGS-358).

Object creation, exponentiation, all rounding directions, distribution in order of rank and decision tables are ordinary RegelSpraak and work in the toolchain; a decision table is desugared into one rule per row. What you cannot express are retroactive effect and knowledge time: a decision taken later with retroactive force. That is outside v1 (see the scope of v1).