Cutting PARSE's "DO" Rule

R3-Alpha PARSE had a partially-implemented feature that Red did not carry forward...and I don't think that anyone uses...which is the do keyword.

What it does is perform a "DO/NEXT" step on the input as a code block, and matches the following rule against the evaluative result.

r3-alpha>> parse [reverse copy "aab"] [do "baa"]
== true

I gather that things like the following were likely intended to work, but don't seem to:

r3-alpha>> parse [reverse copy "aab"] [do ["b" some "a"]]
== false

It seems this feature was added to make it easier to do dialect processing where the dialect had code inline in the block you were processing...but not in a block or group. This means only the evaluator knows how much to advance to do one step of processing.

It can be a little hard to get one's head around, because it says do [...] and yet the ... is a parse rule and not the code to be executed. But PARSE has this odd aspect in other operations like COPY, so it's something you're supposed to be used to.

Raises Complex Questions

The way this feature was implemented in R3-Alpha isolates it from participating in iteration or as the target of an outer rule, e.g.

r3-alpha>> parse [1 + 2] [set var do [quote 3]]
== true

r3-alpha>> var
== 1  ; not 3, which you might expect

Other problems of the naive implementation expose just how non-integrated it is, like not working with THRU:

r3-alpha>> parse [1 + 2] [thru do integer!]
== false

This is partially a problem with the code for DO, and partially a problem of PARSE being a somewhat organically evolved and ad-hoc codebase...where adding new operations that work coherently is just not that feasible. There was no "right way" to plug in such a primitive, so it was just hacked in however.

Interesting Feature To Keep In Mind. But Cutting It.

It took me a while when I first looked at PARSE's code to even figure out what it was supposed to do.

The broken implementation has hung around as a "thing to think about". But I think this forum post describing it serves that purpose better.

I've kept in mind the idea of being able to extend PARSE with new operations...and this is a good example of what could be a fairly simple test of that extensibility model.

2 Likes

I had a new concept for what DO should do in the parse dialect:

Run code, and yield the result as a value to any value-consuming rules, but do not match against the input.

And because DO of a QUOTED! gives you the thing unquoted, this gives us a syntax for doing things like KEEP'ing literal material.

>> uparse [1 2 3] [data: collect [some [keep do 'demo keep integer!]]]
== [1 2 3]

>> data
== [demo 1 demo 2 demo 3]

This is better than a more belabored syntax like keep :[...] to try and convolute into meaning that the block is interpreted as raw material.

So, Why Again Do We Not Let GROUP! Do This?

The appearance of COMMA! has softened my stance on the readability issue of passing GROUP!s as value-bearing, to combinators that want values.

uparse [1 2 3] [data: collect [some [keep ('demo), keep integer!]]]

So I thought "well, what's the harm in letting the GROUP! combinator be used in argument slots?"

On the one hand, this brings us to the question of what exactly would be stopping [some (print [1 + 2])]. That's not a deal breaker, but it would be possible to write...and presumably an infinite loop.

More troubling is what to do when a GROUP! looks "just like any other value-bearing entity". What happens if you say:

parse [1] [x: [(print "testing for integer") integer! | text!]]

What would be the property that differentiates GROUP!s from INTEGER! and TEXT! as value-bearing? Why shouldn't the X get the result of the group, instead of the integer? If GROUP!s simply say they never generate results, you can solve the problem.

I'm really a bit puzzled about the SET feature and BLOCK! just in general. Blocks are a potentially very amorphous set of alternates, and SET has tried to cover the case of setting a single item among alternates. Right now I've set up blocks to error if there's more than one value production per alternate, and so we'd be excluding GROUP!s from that.

In any case, the bigger point here is that if we allow the likes of x: (1 + 2) to work, we'd have to explain why x: [(1 + 2) | (3 + 4)] should or shouldn't work, which is a slippery slope.

Like I say, one possibility is to let DO take over the value-bearing role. Or fill in your answer about how SET interacts with BLOCK! here.