Skip to content

Commit c6ec9ef

Browse files
reidspencerclaude
andcommitted
Update RIDDL language reference for January 2026
Reconciled EBNF grammar and language guide with current parser code. Key changes: - Removed statements: if, foreach, call, stop, focus, reply, return, read, write - Added statements: when, match, let, prompt - Added BAST import syntax - Updated all examples to use new statement syntax - Added documentation for new keywords: default, let, match, prompt Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 53f7160 commit c6ec9ef

2 files changed

Lines changed: 99 additions & 68 deletions

File tree

docs/riddl/references/ebnf-grammar.md

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Formal EBNF Grammar
22

3-
Below is the formal Extended Backus-Naur Form (EBNF) grammar for RIDDL.
4-
This grammar provides a precise definition of RIDDL syntax and can be used as a
5-
reference when constructing valid RIDDL expressions. This grammar was
6-
automaticaly extracted from the reference grammar written in Scala/fastparse
7-
form at March 1, 2025. The maintainers will keep it up to date.
3+
Below is the formal Extended Backus-Naur Form (EBNF) grammar for RIDDL.
4+
This grammar provides a precise definition of RIDDL syntax and can be used as a
5+
reference when constructing valid RIDDL expressions. This grammar was
6+
automatically extracted from the reference grammar written in Scala/fastparse
7+
form and last updated January 17, 2026. The maintainers will keep it up to date.
88

99
```ebnf
1010
(* RIDDL Grammar in EBNF *)
@@ -29,9 +29,10 @@ end_of_line_comment = "//" {any_char_except_newline} newline ;
2929
3030
(* Main Structure *)
3131
root = {root_content}+ ;
32-
root_content = module_content | module | root_include ;
32+
root_content = bast_import | module_content | module | root_include ;
3333
module_content = domain | author | comment ;
3434
root_include = "include" literal_string ;
35+
bast_import = "import" literal_string ; (* path must end with .bast *)
3536
3637
(* Module *)
3738
module = "module" identifier "is" "{" {module_content | module_include}+ "}" [with_metadata] ;
@@ -41,7 +42,7 @@ module_include = "include" literal_string ;
4142
domain = "domain" identifier "is" "{" domain_body "}" [with_metadata] ;
4243
domain_body = domain_definitions | "???" ;
4344
domain_definitions = {domain_content}+ ;
44-
domain_content = vital_definition_contents | author | context | domain | user | epic | saga | import_def | domain_include | comment ;
45+
domain_content = vital_definition_contents | author | context | domain | user | epic | saga | import_def | bast_import | domain_include | comment ;
4546
import_def = "import" "domain" identifier "from" literal_string ;
4647
domain_include = "include" literal_string ;
4748
@@ -73,13 +74,13 @@ aggregate_use_case = "type" | "command" | "query" | "event" | "result" | "record
7374
(* Type Expressions *)
7475
type_expression = cardinality(
7576
predefined_types | pattern_type | unique_id_type | enumeration | sequence_type | mapping_from_to | a_set_type |
76-
graph_type | table_type | replica_type | range_type | decimal_type | alternation | entity_reference_type |
77+
graph_type | table_type | replica_type | range_type | decimal_type | alternation | entity_reference_type |
7778
aggregation | aggregate_use_case_type_expression | aliased_type_expression
7879
) ;
7980
8081
cardinality = ["many"] ["optional"] type_expression_base ["?" | "*" | "+"] ;
8182
type_expression_base = predefined_types | pattern_type | unique_id_type | enumeration | sequence_type | mapping_from_to | a_set_type |
82-
graph_type | table_type | replica_type | range_type | decimal_type | alternation | entity_reference_type |
83+
graph_type | table_type | replica_type | range_type | decimal_type | alternation | entity_reference_type |
8384
aggregation | aggregate_use_case_type_expression | aliased_type_expression ;
8485
8586
(* Predefined Types *)
@@ -181,28 +182,36 @@ on_message_clause = "on" message_ref ["from" [identifier ":"] message_origins] "
181182
message_origins = inlet_ref | processor_ref | user_ref | epic_ref ;
182183
183184
(* Statements *)
184-
statement = send_statement | arbitrary_statement | error_statement | the_set_statement | tell_statement | call_statement |
185-
stop_statement | if_then_else_statement | for_each_statement | code_statement | comment | reply_statement |
186-
focus_statement | morph_statement | become_statement | return_statement | read_statement | write_statement ;
185+
(* Core statements available in all contexts *)
186+
statement = when_statement | match_statement | send_statement | tell_statement |
187+
the_set_statement | let_statement | prompt_statement | code_statement |
188+
error_statement | comment ;
187189
190+
(* Entity-specific statements *)
191+
entity_statement = statement | morph_statement | become_statement ;
192+
193+
(* Control flow *)
194+
when_statement = "when" literal_string "then" pseudo_code_block "end" ;
195+
match_statement = "match" literal_string "{" {match_case}+ ["default" "{" {statement} "}"] "}" ;
196+
match_case = "case" literal_string "{" {statement} "}" ;
197+
198+
(* Message operations *)
188199
send_statement = "send" message_ref "to" (outlet_ref | inlet_ref) ;
189-
arbitrary_statement = literal_string ;
190-
error_statement = "error" literal_string ;
191-
the_set_statement = "set" field_ref "to" literal_string ;
192200
tell_statement = "tell" message_ref "to" processor_ref ;
193-
call_statement = "call" function_ref ;
194-
stop_statement = "stop" ;
195-
if_then_else_statement = "if" literal_string "then" pseudo_code_block ["else" pseudo_code_block "end"] ;
196-
for_each_statement = "foreach" (field_ref | inlet_ref | outlet_ref) "do" pseudo_code_block "end" ;
201+
202+
(* Variable operations *)
203+
the_set_statement = "set" field_ref "to" literal_string ;
204+
let_statement = "let" identifier "=" literal_string ;
205+
206+
(* General statements *)
207+
prompt_statement = "prompt" literal_string ;
197208
code_statement = "```" ("scala" | "java" | "python" | "mojo") code_contents "```" ;
198209
code_contents = {any_char_except_triple_backtick} ;
199-
reply_statement = "reply" ["with"] message_ref ;
200-
focus_statement = "focus" "on" group_ref ;
210+
error_statement = "error" literal_string ;
211+
212+
(* Entity state transitions *)
201213
morph_statement = "morph" entity_ref "to" state_ref "with" message_ref ;
202214
become_statement = "become" entity_ref "to" handler_ref ;
203-
return_statement = "return" literal_string ;
204-
read_statement = ("read" | "get" | "query" | "find" | "select") literal_string "from" type_ref "where" literal_string ;
205-
write_statement = ("write" | "put" | "create" | "update" | "delete" | "remove" | "append" | "insert" | "modify") literal_string "to" type_ref ;
206215
207216
(* Pseudo Code Block *)
208217
pseudo_code_block = "???" | {statement} | "{" {statement} "}" ;
@@ -290,7 +299,7 @@ interaction = parallel_interactions | optional_interactions | sequential_interac
290299
parallel_interactions = "parallel" "{" interactions "}" ;
291300
optional_interactions = "optional" "{" interactions "}" ;
292301
sequential_interactions = "sequence" "{" interactions "}" ;
293-
step_interactions = "step" (focus_on_group_step | direct_user_to_url | select_input_step | take_input_step |
302+
step_interactions = "step" (focus_on_group_step | direct_user_to_url | select_input_step | take_input_step |
294303
show_output_step | self_processing_step | send_message_step | arbitrary_step | vague_step) ;
295304
focus_on_group_step = "focus" user_ref "on" group_ref [with_metadata] ;
296305
direct_user_to_url = "direct" user_ref "to" http_url [with_metadata] ;
@@ -306,7 +315,7 @@ vague_step = "is" literal_string literal_string literal_string [with_metadata] ;
306315
user = "user" identifier "is" literal_string [with_metadata] ;
307316
308317
(* Author-related *)
309-
author = "author" identifier "is" "{" [("???" | ("name" "is" literal_string "email" "is" literal_string
318+
author = "author" identifier "is" "{" [("???" | ("name" "is" literal_string "email" "is" literal_string
310319
["organization" "is" literal_string] ["title" "is" literal_string] ["url" "is" http_url]))] "}" [with_metadata] ;
311320
312321
(* URLs *)
@@ -334,4 +343,4 @@ mime_type = ("application" | "audio" | "example" | "font" | "image" | "model" |
334343
mime_type_chars = lower_letter | "." | "-" | "*" ;
335344
```
336345

337-
This EBNF grammar provides a formal representation of the RIDDL language syntax. It's particularly useful for understanding the exact structure of each language element and how they relate to each other.
346+
This EBNF grammar provides a formal representation of the RIDDL language syntax. It's particularly useful for understanding the exact structure of each language element and how they relate to each other.

docs/riddl/references/language-reference.md

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,9 @@ Commands should always result in one or more events being emitted. This follows
242242
```
243243
handler ProductCommandHandler is {
244244
on command UpdatePrice is {
245-
if "newPrice > 0" then {
245+
when "newPrice > 0" then {
246246
morph entity Product to state ProductData with command UpdatePrice
247247
tell event PriceUpdated to entity Product // Emitting an event is essential
248-
} else {
249-
error "Price must be greater than zero"
250248
} end
251249
}
252250
} with {
@@ -265,10 +263,11 @@ Handlers process commands and emit events:
265263
```
266264
handler ProductCommandHandler is {
267265
on command UpdatePrice is {
268-
if "newPrice > 0" then {
266+
when "newPrice > 0" then {
269267
morph entity Product to state ProductData with command UpdatePrice
270268
tell event PriceUpdated to entity Product
271-
} else {
269+
} end
270+
when "newPrice <= 0" then {
272271
error "Price must be greater than zero"
273272
} end
274273
}
@@ -297,45 +296,56 @@ tell command ProcessPayment to entity PaymentService
297296
```
298297

299298
### Set Statement
300-
Assigns values:
299+
Assigns values to fields:
301300
```
302301
set field status to "Active"
303302
```
304303

305-
### If Statement
306-
Conditional logic:
304+
### Let Statement
305+
Creates a local variable binding:
307306
```
308-
if "condition" then {
309-
// actions
310-
} else if "another condition" then {
311-
// actions
312-
} else {
307+
let totalPrice = "subtotal + tax + shipping"
308+
```
309+
310+
### When Statement
311+
Conditional logic (replaces the former `if` statement):
312+
```
313+
when "condition" then {
313314
// actions
314315
} end
315316
```
316317

317-
The `end` keyword is required to terminate if statements.
318+
The `end` keyword is required to terminate when statements. Note: there is no
319+
`else` clause - use multiple `when` statements for different conditions.
318320

319-
### Foreach Statement
320-
Iteration:
321+
### Match Statement
322+
Pattern matching for multiple conditions:
321323
```
322-
foreach field Cart.items do {
323-
// actions for each item
324-
} end
324+
match "orderStatus" {
325+
case "pending" {
326+
tell event OrderPending to entity Order
327+
}
328+
case "shipped" {
329+
tell event OrderShipped to entity Order
330+
}
331+
case "delivered" {
332+
tell event OrderDelivered to entity Order
333+
}
334+
default {
335+
error "Unknown order status"
336+
}
337+
}
325338
```
326339

327-
The `end` keyword is required to terminate foreach loops.
328-
329-
### Arbitrary Statement
330-
Allows for implementation code inside functions and handler actions:
340+
### Prompt Statement
341+
Describes an action in natural language for implementation:
331342
```
332-
"var subtotal = 0;
333-
for (var i = 0; i < items.length; i++) {
334-
subtotal += items[i].totalPrice;
335-
}
336-
return subtotal;"
343+
prompt "Calculate the total price including all applicable taxes and discounts"
337344
```
338345

346+
This is useful for describing complex business logic that will be implemented
347+
in target code.
348+
339349
## Functions
340350

341351
Functions define reusable operations:
@@ -351,9 +361,9 @@ function calculateTotal is {
351361
returns {
352362
total is Price
353363
}
354-
355-
// Implementation using arbitrary statement
356-
"return subtotal + taxes + shipping - discount;"
364+
365+
// Implementation using prompt statement
366+
prompt "Calculate total by adding subtotal, taxes, and shipping, then subtracting discount"
357367
} with {
358368
briefly as "Calculates the final cart total"
359369
described by {
@@ -414,12 +424,12 @@ repository CartRepository is {
414424
schema CartData is relational of
415425
cart as Cart
416426
link cartItems as field Cart.items.id to field Product.id
417-
427+
418428
handler CartRepositoryHandler is {
419429
on event CartCreated is {
420-
write "Create new cart record" to Cart
430+
prompt "Persist the new cart record to the database"
421431
}
422-
432+
423433
// Other event handlers
424434
} with {
425435
briefly as "Handles persistence of cart events"
@@ -562,9 +572,9 @@ entity Product is {
562572
1. **Include Metadata**: Add descriptions to all definitions with `with` clauses after their closing braces
563573
2. **Be Explicit**: Always specify reference types (entity, command, event, etc.)
564574
3. **Place Functions Close to Usage**: Define functions within the entities that use them
565-
4. **End Control Structures**: Always terminate control structures with `end` keyword
566-
5. **Use Field References**: In foreach loops, use `foreach field X.items`
567-
6. **Provide Default Actions**: Use `"nothing"` for empty else branches
575+
4. **End When Statements**: Always terminate `when` statements with the `end` keyword
576+
5. **Use Match for Multiple Conditions**: Prefer `match` over multiple `when` statements for readability
577+
6. **Use Prompt for Complex Logic**: Use `prompt` to describe business logic for implementation
568578
7. **Model Complete Flows**: Include UI components and user interactions
569579
8. **Maintain Semantic Consistency**: Use the same field names for the same concepts
570580
9. **Emit Events from Commands**: Ensure commands emit events to follow reactive principles
@@ -574,8 +584,8 @@ entity Product is {
574584

575585
## Common Syntax Issues
576586

577-
1. Don't use assignment operators (`=`); use `set field x to "value"`
578-
2. Don't forget `end` after if statements and foreach loops
587+
1. Don't use assignment operators (`=`) for fields; use `set field x to "value"` (but `let x = "value"` is valid for local bindings)
588+
2. Don't forget `end` after `when` statements
579589
3. Always include reference types before identifiers
580590
4. Use proper syntax for function parameters and return values
581591
5. Make sure all morph/tell statements have correct type references
@@ -584,6 +594,8 @@ entity Product is {
584594
8. Never place entity, type, or repository definitions directly within a domain - they must be in a context
585595
9. Never place definitions inside an author - authors only contain metadata
586596
10. Make sure each context contains related definitions that form a bounded context
597+
11. Use `when` for conditionals (not `if` which is no longer supported)
598+
12. Use `prompt` for describing implementation logic (not bare quoted strings)
587599

588600
## Incomplete Definitions
589601

@@ -650,8 +662,8 @@ From the formal grammar analysis, several important syntax points deserve specia
650662
3. **Nested Element Validation**: Elements can only be nested within specific parent elements according to strict containment rules. For example, types must be defined within contexts, not directly within domains.
651663

652664
4. **Termination of Statements**: Control flow statements must be properly terminated:
653-
- If statements must end with the `end` keyword
654-
- Foreach loops must end with the `end` keyword
665+
- `when` statements must end with the `end` keyword
666+
- `match` statements are terminated by the closing brace
655667
- Other statements are implicitly terminated
656668

657669
5. **Handler Clauses**: Handlers must use specific clause types:
@@ -663,3 +675,13 @@ From the formal grammar analysis, several important syntax points deserve specia
663675

664676
6. **Readability Words**: While readability words like `is`, `as`, `by`, etc. are often optional, their proper placement significantly improves model clarity. Use them consistently.
665677

678+
7. **New Statement Types** (as of January 2026):
679+
- `when` replaces the former `if-then-else` statement
680+
- `match` provides pattern matching for multiple conditions
681+
- `let` creates local variable bindings
682+
- `prompt` replaces bare quoted strings for describing implementation logic
683+
684+
8. **Removed Statements**: The following statements have been removed from the language:
685+
- `if`, `foreach`, `call`, `stop`, `focus`, `reply`, `return`, `read`, `write`
686+
- Use `when` for conditionals, `match` for pattern matching, and `prompt` for implementation descriptions
687+

0 commit comments

Comments
 (0)