ANTLR4-based parser for RegelSpraak v2.1.0 - a Dutch domain-specific language for business rules.
Business rules as they should be: readable, verifiable, executable.
Traditional code for Dutch flight tax calculation:
def calculate_tax(passenger, flight):
tax = 0
if flight.distance <= 2500:
if passenger.age < 18 or (25 <= passenger.age <= 64):
tax = 45.50 - (0.011 * flight.distance)
# ... special cases for Easter, high season, sustainability
elif flight.distance <= 3500:
if passenger.age >= 65:
tax = 22.00
else:
tax = 45.50 - (0.008 * flight.distance)
# ... 40+ more lines of nested conditions
return round(tax, 2)Same logic in RegelSpraak:
Regel belasting op basis van afstand
De belasting van een passagier = het lage basistarief eerste schijf -
het lage tarief vermindering eerste schijf × afstand
indien afstand ≤ bovengrens eerste schijf en
(minderjarig of een passagier van 25 tot en met 64 jaar).
The entire Dutch TOKA flight tax law: 291 lines of RegelSpraak. Readable by lawmakers, executable by computers.
| Traditional Programming | RegelSpraak |
|---|---|
| 100+ lines of if-else chains | 10 lines of business rules |
| Unit errors cause Mars Climate Orbiter crashes | Units enforced: km × EUR/km = EUR |
| "What does this calculate?" requires code analysis | Rules read like requirements |
| Change requires developer + testing | Domain experts validate directly |
| Temporal logic requires complex state management | Built-in timeline support: voor elke dag |
Real example: The entire TOKA implementation including tax calculation, treinmiles distribution, and decision tables is 291 lines of RegelSpraak vs ~3000 lines of Java.
- Python 3.7+
- Java (for ANTLR):
brew install openjdk(macOS) orapt install default-jre(Linux)
# 1. Download ANTLR JAR (required)
mkdir -p lib
curl -o lib/antlr-4.13.1-complete.jar https://www.antlr.org/download/antlr-4.13.1-complete.jar
# 2. Setup Python environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
pip install -e .
# 4. Generate parser files (required for first-time setup)
make parser-python
# 5. Verify installation
make testCLI Validation:
python -m regelspraak validate rules.rsRun Rules with Data:
python -m regelspraak run rules.rs --data initial_data.jsonInteractive REPL:
python -m regelspraak
RegelSpraak REPL v0.1.0
Type ':help' for commands, ':quit' to exit
RegelSpraak>Key REPL features:
:load <file.rs>- Load RegelSpraak definitions:show ctx- Display current state (parameters, objects, rules):trace on/off- Enable/disable execution tracing:reset- Clear session statepy: <code>- Execute Python (accesscontext,RuntimeObject,Value)Evaluate <instance> is <kenmerk>- Quick expression evaluation
from regelspraak import parse_text, RuntimeContext, RuntimeObject, Evaluator
# TOKA flight tax rules
model = parse_text("""
Objecttype Passagier
de leeftijd Numeriek met eenheid jr;
is minderjarig kenmerk;
de belasting Bedrag;
Objecttype Vlucht
de afstand Numeriek met eenheid km;
Regel minderjarig
Een passagier is minderjarig indien zijn leeftijd < 18 jr.
Regel belasting berekening
De belasting van een passagier =
0 EUR indien minderjarig,
anders afstand van zijn vlucht × 0.10 EUR/km.
""")
# Execute for specific passenger
context = RuntimeContext(domain_model=model)
evaluator = Evaluator(context)
passagier = RuntimeObject("Passagier", "p1")
context.add_object(passagier)
context.set_attribute(passagier, "leeftijd", 15)
context.set_attribute(passagier, "vlucht", flight_ref) # Link to flight
evaluator.execute_model(model)
print(passagier.attributen["belasting"].value) # 0 EUR (minor pays no tax)RegelSpraak> :load tests/resources/steelthread_example.rs
Loaded and merged model from tests/resources/steelthread_example.rs
RegelSpraak> py: context.set_parameter('volwassenleeftijd', 18, unit='jr')
RegelSpraak> py: p1 = RuntimeObject(object_type_naam='Natuurlijk persoon', instance_id='p1')
RegelSpraak> py: context.add_object(p1)
RegelSpraak> py: context.set_attribute(p1, 'leeftijd', 15)
RegelSpraak> py: evaluator.execute_model(domain_model)
TRACE: (Line 7) RULE_FIRED - rule_name='Kenmerktoekenning persoon minderjarig'
RegelSpraak> Evaluate p1 is minderjarig
waar
RegelSpraak handles complex allocations declaratively - a feature that would require hundreds of lines of imperative code:
Regel verdeling treinmiles op basis van leeftijd
Het totaal aantal treinmiles wordt verdeeld over alle passagiers:
- op volgorde van toenemende leeftijd
- bij gelijke leeftijd naar rato van woonregio factor
- met maximum 1000 per persoon
- afgerond naar beneden.
No loops. No sorting. No rounding errors. Just business logic.
The complete Dutch flight tax system including:
- Tax calculation: Distance/age-based progressive rates with unit safety
- Decision tables: Travel duration brackets mapped to tax percentages
- Temporal rules:
voor elke dag- taxes that vary by date - Filtered aggregation:
som van belasting van alle passagiers die minderjarig zijn - Distribution logic: Fair allocation of treinmiles based on complex criteria
All in 291 lines of auditable RegelSpraak vs thousands of lines of Java/Python.
regelspraak-parser/
├── grammar/ # ANTLR grammar files
├── src/regelspraak/ # Core implementation
│ ├── parsing.py # Parser facade
│ ├── builder.py # AST builder
│ ├── ast.py # AST nodes
│ ├── engine.py # Execution engine
│ ├── runtime.py # Runtime data structures
│ └── cli.py # Command-line interface
├── tests/ # 568+ unit tests
└── specification/ # Language specification
- System Architecture: See ARCHITECTURE.md
- Development Roadmap: See ROADMAP.md
- Grammar Implementation: See GRAMMAR_IMPLEMENTATION_NOTES.md
- Language Specification: See
specification/directory
make test
# or
python -m unittest discover -s tests- ✅ All rule types (Gelijkstelling, Kenmerktoekenning, Consistentie, etc.)
- ✅ Decision tables (Beslistabel) and distribution rules (Verdeling)
- ✅ Object relationships (Feittype) and creation rules
- ✅ Timeline expressions and dimensions
- ✅ Filtered collections (Subselectie) and recursion
- ✅ Advanced predicates (elfproef, dagsoort, uniqueness)
- ✅ Compound predicates with quantifiers
- ✅ All aggregation functions
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run tests (
make test) - Commit changes (
git commit -m 'Add feature') - Push and open a Pull Request
Apache License 2.0 - see LICENSE file.