A run() function to deal with the data is an interpreter (the data has stuff like calls to the wait function and subroutines in it) or at least a command-replayer, if the control flow all runs before any gameplay happens. The behavior might also be dynamic such that the command-replayer approach doesn't work.
One reason BulletML is useful compared to a general-purpose language is that you could arbitrarily recombine BulletML ASTs to produce valid bullet patterns, which Kenta Cho did for rRootage[0]. I think another reason that people implement VMs for these languages is that the host language lacks coroutines, so it cannot express the wait() function, and they don't want to turn their bullet pattern code inside-out. Even if the host language has coroutines, they may not be serializable, so saving your game in the middle of a level may not be possible if you build on top of coroutines, whereas it is definitely possible if you control the interpreter state of your DSL.
[0]: https://www.asahi-net.or.jp/~cs8k-cyu/windows/rr_e.html