Screenplay's stated role is to provide libraries that make the language compilable to anything, with visitors. The current surface is closer to a tree handoff.
What exists
Syntax/Visitors.cs declares five visitor interfaces:
IApplicationSyntaxVisitor<TApplication>
IModuleSyntaxVisitor<TModule>
IFeatureSyntaxVisitor<TFeature>
ISliceSyntaxVisitor<TSlice>
IConstraintSyntaxVisitor<TConstraint>
ScreenplayCompiler drives exactly one of them — IApplicationSyntaxVisitor, at :33. (The projection, specification and capture visitors are also driven, but those are separate entry points for compiling a sub-language fragment on its own, not part of the application walk.)
So IModuleSyntaxVisitor, IFeatureSyntaxVisitor, ISliceSyntaxVisitor and IConstraintSyntaxVisitor are contracts with no traversal engine behind them: a consumer implementing one gets no dispatch and writes the whole walk by hand.
There is also no visitor at all for command, event, query, screen, reactor, concept, type, policy, persona, seed or authentication.
Why it matters
Every consumer hand-walks public record types, so any AST change is a potential source break for all of them, and there are no compatibility tests. That is manageable at today's number of consumers and will not scale to "compile to anything".
Concretely, the two consumers that exist both walk the tree themselves rather than through the visitors, which is the signal that the abstraction is not carrying its weight.
Suggested direction
A real traversal/dispatch layer: a base visitor that walks the whole tree and calls a method per node kind, so a consumer overrides only what it cares about and is not broken by a node type it never asked about. Worth pairing with a statement of what the AST's compatibility guarantees actually are.
Screenplay's stated role is to provide libraries that make the language compilable to anything, with visitors. The current surface is closer to a tree handoff.
What exists
Syntax/Visitors.csdeclares five visitor interfaces:IApplicationSyntaxVisitor<TApplication>IModuleSyntaxVisitor<TModule>IFeatureSyntaxVisitor<TFeature>ISliceSyntaxVisitor<TSlice>IConstraintSyntaxVisitor<TConstraint>ScreenplayCompilerdrives exactly one of them —IApplicationSyntaxVisitor, at:33. (The projection, specification and capture visitors are also driven, but those are separate entry points for compiling a sub-language fragment on its own, not part of the application walk.)So
IModuleSyntaxVisitor,IFeatureSyntaxVisitor,ISliceSyntaxVisitorandIConstraintSyntaxVisitorare contracts with no traversal engine behind them: a consumer implementing one gets no dispatch and writes the whole walk by hand.There is also no visitor at all for
command,event,query,screen,reactor,concept,type,policy,persona,seedorauthentication.Why it matters
Every consumer hand-walks public
recordtypes, so any AST change is a potential source break for all of them, and there are no compatibility tests. That is manageable at today's number of consumers and will not scale to "compile to anything".Concretely, the two consumers that exist both walk the tree themselves rather than through the visitors, which is the signal that the abstraction is not carrying its weight.
Suggested direction
A real traversal/dispatch layer: a base visitor that walks the whole tree and calls a method per node kind, so a consumer overrides only what it cares about and is not broken by a node type it never asked about. Worth pairing with a statement of what the AST's compatibility guarantees actually are.