Skip to content

feat(dsl): movement modifiers on the existing runtime actions (p8-s3) - #111

Merged
JArmandoAnaya merged 1 commit into
mainfrom
feat/p8-s3-modifiers
Aug 2, 2026
Merged

feat(dsl): movement modifiers on the existing runtime actions (p8-s3)#111
JArmandoAnaya merged 1 commit into
mainfrom
feat/p8-s3-modifiers

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

drive() plus modifiers is the idiomatic DSL scenario — the standard's own
style guide says so (§9.1.8) — and until now the generic actions lowered to
nothing because there was nothing to shape them with. The modifiers are what
give them content.

Every modifier lands on an action the runtime already has

No modifier machinery is added to the runtime; each one becomes an IR action
P2/P5 already implements, or is reported. Same rule ADR-0031 applied to
composition, and it is what keeps one runtime under two frontends.

§8.9 modifier IR action
speed (absolute) SpeedAction
speed (faster_than/slower_than/same_as, factor) SpeedAction with a RelativeTargetSpeed
change_speed SpeedAction relative to the actor's own speed
position at the start TeleportAction to a RelativeObjectPosition
position over the phase LongitudinalDistanceAction
lateral LateralDistanceAction with side_of, else LaneOffsetAction
lane LaneChangeAction, absolute or relative
change_lane LaneChangeAction relative to the actor
keep_lane continuous LaneOffsetAction at zero

The at anchor decides how, and needs the phase's length

§8.9.1.1.1's anchor is optional. Absent, all and start set the value when
the phase begins — a Step. end reaches it over the phase, so it becomes a
transition spread across the phase's length.

That is what makes p8-s2's durations load-bearing: without a concrete duration
there is no interval to spread the change over. Lowering reports and sets the
value at once rather than fabricating one, for the same reason ADR-0030 refuses
to invent performance limits.

position is the modifier where the anchor changes the kind of action, not
just its shape — a placement at the start, a gap to reach and hold over the
phase. Both readings are in §8.9.2; the anchor chooses.

"Keep doing what you are doing" lowers to nothing

keep_speed (§8.9.6) and keep_position (§8.9.3) constrain the actor not to
change, and the runtime already holds speed and relative position between
actions. So the faithful lowering is no action: one that set the current value
would be a no-op that still appears in the trace, occupies the longitudinal
action domain (§7.5) and could supersede something running.

keep_lane is different and does produce an action, because holding a lane is
active work — a continuous LaneOffsetAction at zero is the runtime's way of
saying it.

What is reported, with reasons (matrix rows moved to Post)

  • acceleration shapes an acceleration the phase is already performing, and
    there is no acceleration-target action in the IR — the same reason §8.8's are
    deferred.
  • along / along_trajectory need a concrete route or trajectory value.
    The DSL has no struct constructor (§7.2.2.6.7), so one can only come from
    §8.12.2's map.create_route(...) — and the standard itself says map methods
    may be external implementations (§7.3.7.4), which is post-v0.0.1.
  • distance bounds a phase by distance travelled, and ADR-0031 sequences
    phases by time.

A checker gap found, filed rather than absorbed — #110

§7.3.3 says an overloaded enum literal "will depend on the type requirements of
the place it is used in". Scena reports the ambiguity before consulting them,
and at (§8.9.19) and route_overlap_kind (§8.12) both declare start and
end — so the specification's own at: start is rejected while at: at!start
is accepted.

It blocks nothing (the qualified spelling works, and is what the tests use) and
it predates this sprint — every overloaded literal the library has declared
since 43c has the same issue. Filed as #110 with the section, the failing
spelling and where the fix belongs, and recorded as a Gap row in the coverage
matrix.

Tests and docs

  • dsl_lowering_test.cpp 45 → 59: each modifier's mapping, both position
    readings, at: end with and without a duration, the relative speed forms and
    their sign, change_lane without a side, the two that lower to nothing, and
    each reported one saying why.
  • ADR-0032; coverage-matrix rows for §8.9 including the DSL: an overloaded enum literal should resolve by the expected type (§7.3.3) #110 gap; the DSL
    frontend README.

1392 gtest (+14), 194 pytest.

Closes #46

`drive()` plus modifiers is the idiomatic DSL scenario — the standard's own
style guide says so (§9.1.8) — and until now the generic actions lowered to
nothing because there was nothing to shape them with. Now the modifiers are what
give them content.

Every §8.9 modifier lands on an IR action P2/P5 already implements, or is
reported: speed targets absolute and relative, placements and gaps, lane targets
and offsets. Nothing modifier-shaped is added to the runtime, which is the same
rule ADR-0031 applied to composition.

§8.9.1.1.1's `at` anchor decides *how*. Absent, `all` and `start` set the value
when the phase begins — a Step. `end` reaches it over the phase, which is what
makes p8-s2's durations load-bearing: without a concrete duration there is no
interval to spread the change over, and lowering reports rather than inventing
one. `position` is the modifier where the anchor changes the kind of action, not
just its shape: a TeleportAction placement at the start, a
LongitudinalDistanceAction gap over the phase.

`keep_speed()` and `keep_position()` lower to no action at all. The runtime
already holds speed and relative position between actions, so one that set the
current value would be a no-op that still occupies the action domain (§7.5) and
could supersede a running action. `keep_lane()` does produce one, because
holding a lane is active work.

`acceleration`, `along`, `along_trajectory` and `distance` are reported with
their reasons and moved to Post in the coverage matrix: there is no
acceleration-target action in the IR, a concrete route or trajectory can only
come from §8.12.2's map methods (which the standard says may be external,
§7.3.7.4), and a distance-bounded phase is not something time-based sequencing
expresses.

Files #110 rather than absorbing it: §7.3.3 says an overloaded enum literal
resolves by "the type requirements of the place it is used in", but Scena
reports the ambiguity first — so the specification's own `at: start` is rejected
while `at: at!start` works. It blocks nothing and predates this sprint.

1392 gtest (+14), 194 pytest, ADR-0032.
@JArmandoAnaya
JArmandoAnaya merged commit d404e7d into main Aug 2, 2026
12 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the feat/p8-s3-modifiers branch August 2, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[p8-s3] Movement modifiers

1 participant