rhombus-prototype icon indicating copy to clipboard operation
rhombus-prototype copied to clipboard

Lone `$()` is allowed in a `~group` syntax class, and seems to never match anything

Open distractedlambda opened this issue 1 month ago • 5 comments

> syntax_class Bad:
    kind: ~group
  | '$()'

> '' matches '$(_ :: Bad)'
#false

> '()' matches '$(_ :: Bad)'
#false

> '$('$')()' matches '$(_ :: Bad)'
#false

distractedlambda avatar Dec 04 '25 19:12 distractedlambda

I think it's working as intended, although the intent is subtle.

The key thing to remember is that a group is never empty. (If groups are allowed to be empty, then the term/group/sequence design falls apart.) The syntax object '' is a multi-group sequence with zero groups, so it will never match a one-group pattern. Simialrly, () is a term that contains zero groups.

A $() matches an empty tail of a group, and the implication is that there must be a group to match. At the same time, it cannot match a whole group, because a group cannot be empty.

Edit: A change to make ~group match only non-empty sequences means that this example will no longer work. A ~group pattern can be used at the end of a group, so it more generally matches the tail of a group. Here's an example of how a ~group pattern that is just $() might be used in a successful match:

syntax_class TheEnd:
  kind: ~group
| '$()'

'1' matches '1 $(_ :: TheEnd)' // => #true

I suppose that ~group_tail would be a more precisely correct keyword instead of ~group. I am not inclined to change to the more verbose name, but the documentation of ~group could be clearer about how such patterns matches group tails, not necessarily whole groups.

mflatt avatar Dec 05 '25 16:12 mflatt

Edit: Revisiting the documentation of Group, I see that even "group tail" is not quite right, either. It's really a sequence pattern that is constrained to certain positions within a group. A $() pattern really means the end of the group, not just the end of some matching sequence. Edit: Not anymore, and not quite true in the first place.

mflatt avatar Dec 05 '25 17:12 mflatt

Yet more: Like $(), the syntax class

syntax_class AGroup:
  kind: ~group
| '$a ...'

currently can match 0 terms, which is not like a Group match.

So far, I'm thinking that ~group should be constrained to non-empty matches, which is true of Group. (To match potentially empty term sequences, there's ~sequence.) At the same time, I don't think that '$a ...' should be disallowed as a ~group pattern on the grounds that it might match 0 terms. Allowing $() in a group pattern seems consistent with allowing $a ....

mflatt avatar Dec 05 '25 17:12 mflatt

Disallowing a nonempty match to ~group seems like the right thing and fixes several inconsistencies. It is not completely backward-compatible, and I had to fix a few places in existing code on my machine, partly by changing ~group to ~sequence. I'm also tweaking syntax_class to infer ~group mode when some pattern ends with $().

After the changes, the claim in the issue title is true: a '$()' pattern in a syntax class will never match. But allowing it still seems like the right thing to me, and maybe it helps that the documentation will clearer on the point that a ~group syntax class cannot match an empty sequence.

mflatt avatar Dec 06 '25 14:12 mflatt

Related commit: 7d5be7efd0a1e5f6dbe6e19149c6e6893204949f

mflatt avatar Dec 06 '25 14:12 mflatt

These changes are sounding right to me.

distractedlambda avatar Dec 14 '25 18:12 distractedlambda