Skip to content

Commit dd55a00

Browse files
authored
Merge pull request #365 from hjotha/submit/microflow-inheritance-split-cast
feat: support inheritance split and cast statements
2 parents 3ec2961 + a93aa29 commit dd55a00

29 files changed

Lines changed: 1414 additions & 3 deletions

.claude/skills/mendix/write-microflows.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,23 @@ end case;
340340

341341
`(empty)` represents an unset enumeration value. Multiple values can share one `when` branch by separating them with commas. Case values are bare identifiers — do **not** quote them.
342342

343+
### Type Split And Cast Statements
344+
345+
Use `split type` when a microflow branches on an object's runtime specialization.
346+
Use `cast` inside a type branch to create the specialized variable used by the branch body.
347+
348+
```mdl
349+
split type $Input
350+
case Sample.SpecializedInput
351+
cast $SpecificInput;
352+
return true;
353+
else
354+
return false;
355+
end split;
356+
```
357+
358+
`case` values are qualified entity names. The optional `else` branch handles objects that do not match any listed specialization.
359+
343360
### LOOP Statements
344361

345362
```mdl

docs/01-project/MDL_QUICK_REFERENCE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ authentication basic, session
243243
| Free annotation | `@annotation 'text'` before `@position(...)` | Free-floating visual note preserved by order |
244244
| IF | `if condition then ... [else ...] end if;` | |
245245
| Enum split | `case $Var when Value then ... end case;` | Enumeration decision branches |
246+
| Type split | `split type $Var case Module.Entity ... end split;` | Runtime specialization branches |
247+
| Cast | `cast $SpecificVar;` | Downcast inside a type split branch |
246248
| LOOP | `loop $item in $list begin ... end loop;` | FOR EACH over list |
247249
| WHILE | `while condition begin ... end while;` | Condition-based loop |
248250
| Return | `return $value;` | Required at end of every flow path |
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Proposal: Microflow Inheritance Split And Cast Statements
2+
3+
Status: Draft
4+
5+
## Summary
6+
7+
Add round-trip MDL support for type-based microflow decisions and cast actions:
8+
9+
```mdl
10+
split type $Input
11+
case Sample.SpecializedInput
12+
cast $SpecificInput;
13+
else
14+
return false;
15+
end split;
16+
```
17+
18+
## Motivation
19+
20+
Studio Pro represents specialization/type decisions as `InheritanceSplit` objects and stores downcasts as `CastAction` activities. Without first-class MDL statements, `describe` can only emit unsupported comments or incomplete split output, and `exec` cannot rebuild the same graph.
21+
22+
## Semantics
23+
24+
`split type $Var` evaluates the runtime specialization of an object variable. Each `case Module.Entity` branch corresponds to an outgoing sequence flow with an `InheritanceCase`. The optional `else` branch maps to the outgoing flow without an inheritance case.
25+
26+
`cast $Output` emits a `CastAction` that produces the downcast variable. `$Output = cast $Input` is accepted for source-preserving authoring, but current Mendix BSON stores the generated cast variable as the primary persisted field.
27+
28+
## Tests And Examples
29+
30+
`mdl-examples/doctype-tests/inheritance_split_statement.test.mdl` demonstrates the syntax. Go regression tests cover parser construction, builder output, describer output, validation recursion, and BSON writer support for inheritance case values and cast actions.
31+
32+
## Open Questions
33+
34+
- Should `exec` validate `case Module.Entity` against the project's specialization hierarchy when connected?
35+
- Should the source-preserving `$Output = cast $Input` form round-trip both variable names once the underlying BSON fields are confirmed for all supported Mendix versions?

docs/11-proposals/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ BSON schema Registry ◄──── multi-version Support
5454
| [Microflow ENUM SPLIT Statement](PROPOSAL_microflow_enum_split_statement.md) | Implemented | Enumeration decision splits via `case $Var when Value then … end case;` ||
5555
| [Microflow CHANGE Refresh Modifier](PROPOSAL_microflow_change_refresh_modifier.md) | Draft | Preserve `RefreshInClient` on change-object actions ||
5656
| [Microflow ADD Expression To List](PROPOSAL_microflow_add_expression_to_list.md) | Draft | Preserve expression-valued list-add actions in microflow round-trips ||
57+
| [Microflow Inheritance Split And Cast Statements](PROPOSAL_microflow_inheritance_split_statement.md) | Draft | Preserve type-based microflow decisions and cast actions in round-trips ||
5758
| [LLM MDL Assistance](PROPOSAL_llm_mdl_assistance.md) | Proposed | Enhanced error messages with examples, reorganized skills by use case ||
5859

5960
### Testing & Evaluation
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
create module InheritanceSplitExample;
2+
3+
create persistent entity InheritanceSplitExample.BaseInput (
4+
Name: String(200)
5+
);
6+
/
7+
8+
create persistent entity InheritanceSplitExample.SpecializedInput extends InheritanceSplitExample.BaseInput (
9+
Code: String(50)
10+
);
11+
/
12+
13+
create microflow InheritanceSplitExample.RouteInput (
14+
$Input: InheritanceSplitExample.BaseInput
15+
)
16+
returns boolean
17+
begin
18+
split type $Input
19+
case InheritanceSplitExample.SpecializedInput
20+
cast $SpecializedInput;
21+
return true;
22+
else
23+
return false;
24+
end split;
25+
end;
26+
/

mdl/ast/ast_microflow.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,31 @@ type EnumSplitStmt struct {
116116
Annotations *ActivityAnnotations // Optional @position, @caption, @color, @annotation
117117
}
118118

119+
// InheritanceSplitCase represents one typed branch in an inheritance split.
120+
type InheritanceSplitCase struct {
121+
Entity QualifiedName
122+
Body []MicroflowStatement
123+
}
124+
125+
// InheritanceSplitStmt represents: SPLIT TYPE $Var ... END SPLIT
126+
type InheritanceSplitStmt struct {
127+
Variable string // Variable name without $ prefix
128+
Cases []InheritanceSplitCase
129+
ElseBody []MicroflowStatement
130+
Annotations *ActivityAnnotations // Optional @position, @caption, @color, @annotation
131+
}
132+
119133
func (s *EnumSplitStmt) isMicroflowStatement() {}
134+
func (s *InheritanceSplitStmt) isMicroflowStatement() {}
135+
136+
// CastObjectStmt represents: $Output = CAST $Object
137+
type CastObjectStmt struct {
138+
OutputVariable string // Output variable name without $ prefix
139+
ObjectVariable string // Source object variable name without $ prefix
140+
Annotations *ActivityAnnotations // Optional @position, @caption, @color, @annotation
141+
}
142+
143+
func (s *CastObjectStmt) isMicroflowStatement() {}
120144

121145
// MfSetStmt represents: SET $Var = expr or SET $Var/Attr = expr
122146
// (Named MfSetStmt to avoid conflict with existing SetStmt for SET key = value)

mdl/executor/cmd_diff_mdl.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,29 @@ func microflowStatementToMDL(ctx *ExecContext, stmt ast.MicroflowStatement, inde
447447
}
448448
lines = append(lines, indentStr+"end case;")
449449

450+
case *ast.InheritanceSplitStmt:
451+
lines = append(lines, fmt.Sprintf("%ssplit type $%s", indentStr, s.Variable))
452+
for _, c := range s.Cases {
453+
lines = append(lines, fmt.Sprintf("%scase %s", indentStr, c.Entity.String()))
454+
for _, caseStmt := range c.Body {
455+
lines = append(lines, microflowStatementToMDL(ctx, caseStmt, indent+1)...)
456+
}
457+
}
458+
if len(s.ElseBody) > 0 {
459+
lines = append(lines, indentStr+"else")
460+
for _, elseStmt := range s.ElseBody {
461+
lines = append(lines, microflowStatementToMDL(ctx, elseStmt, indent+1)...)
462+
}
463+
}
464+
lines = append(lines, indentStr+"end split;")
465+
466+
case *ast.CastObjectStmt:
467+
if s.ObjectVariable == "" {
468+
lines = append(lines, fmt.Sprintf("%scast $%s;", indentStr, s.OutputVariable))
469+
} else {
470+
lines = append(lines, fmt.Sprintf("%s$%s = cast $%s;", indentStr, s.OutputVariable, s.ObjectVariable))
471+
}
472+
450473
case *ast.LoopStmt:
451474
lines = append(lines, fmt.Sprintf("%sloop $%s in $%s", indentStr, s.LoopVariable, s.ListVariable))
452475
for _, bodyStmt := range s.Body {

0 commit comments

Comments
 (0)