Maybe overdoing.
So... it always happens that if you make something that takes no parameters, someone will want to add a parameter...
...and once they can add 1 parameter, they will want to add N parameters, for any N.
...and once they can add N parameters they'll want to be able to variably add (0...N) parameters...
So of course this is the case with combinators. This ACTION! combinator (which runs when a PATH! ends in a slash, and looks up to an ACTION!) is effectively variadic.
Here it is with NEGATE, which takes one argument:
>> parse [1] [/negate integer!]
== -1
Here it is with ADD, which takes two arguments:
>> parse [1 2] [/add integer! integer!]
== 3
Which goes to show you that the combinator for ACTION! doesn't take a fixed number of parsers. Each instance of the combinator takes as many parsers as the function takes arguments.
The Nightmare That Is Variadic Combinators
This really grates against the way the combinators work. Because the body of the combinator only runs once it has been turned into a parser... e.g. all the combinator's arguments have been "combinated out". So all that remains is the input parameter.
To clarify what I mean by "combinated out": If you say some between "(" ")", the fact that BETWEEN takes two parsers is of no matter to SOME. BETWEEN gets "combinated" to where it looks like a parser function that's no different from any other. By the time SOME sees it, the left and right parser parameters have all been wired up--those parameters have vanished. Hence SOME's code is stylized in a way that works just as well for some "a" or any other "fully combinated" parser.
But ACTION! throws a wrench into this. The combinator can't say in advance how many parsers it needs, it depends on the action. And by the time you're running the body of the combinator it is too late...the expectation is that all the "combinating" is done by then.
Making matters more frustrating is that even if you do manage to AUGMENT the interface of something like the ACTION! combinator, the rules are that the base combinator doesn't see those fields. That's a good rule: there is a reason for it. But it means that if parameters get added then there has to be a way to tunnel the augmented frame with the view of the higher level variables down so that when the fulfilled combinator is run as a parser it can see those variables.
I managed to hack around this to implement the first version--and I knew what I was doing was a bit dicey. But the cracks show when trying to design a unified rollback service. The service has to know about all parsers that are parameters...to automatically put in the piping that does rollback.
I've Hacked It Up Again, By Not Using Auto-Rollback
So the ACTION! combinator just goes without the auto rollback mechanism, and manages it manually like the BLOCK! combinator does. But there's no conceptual reason why it would need custom code for its rollback. It's just because the generalized analysis doesn't get a chance to see the parsers and do the automatic thing to them...because the parser parameters are not visible at that phase.
This is going to be a thorn so I thought I'd talk about it. This way everyone knows that although the ACTION! combinators are working, they are on the ropes a bit as being maybe "overdoing it".
But I do like this feature, and am definitely going to try to figure out a better way and keep it alive if I can. This kind of UPARSE feature is powerful and I hate to rule things like it out.