c="[^aeiou]"
v="[aeiou]"
cP$rXw(yS hdS)$fo["([mpbkgfvcsx][lr]|[cs][mnptkf]|[jz][mbdgv]|t[csr]|d[jzr])",v,c,v,"|",c,v,"([cjsz]{2}|x[kc]|[kc]x|mz|/p)!([ptkfcsxlmnr]{2}|[bdgvjzlmnr]{2})",v]
Attempt This Online!
Without regex, 189 bytes
y=jzW(p2**).*wR
v=xys W5
c=xys d
d="ptkfcsxlmnr bdgvjzlmnr"
cP$fo[asy$y"mpbkgfvcsx cs jz t d""lr mnptkf mbdgv csr jzr",v,c,v]++fo[c,v,asy$cX(y d d)$zW p2 d d<>y"cjsz x kc m""cjsz kc x z",v]
Attempt This Online!
Explanation
First we define some helpers. y=jzW(p2**).*wR
is the most complex. It's a weird synthetic operation that happens to be useful in this usecase, so it doesn't really have a simple explanation. But you can break it down part by part:
(p2**)
takes two lists of characters and creates all strings formed by taking one character from the first and one from the second, so
>>> (p2**) "abc" "ae"
["aa","ae","ba","be","ca","ce"]
jzW
applies it pairwise to two lists. This allows us to get a bunch of pairs made from combinations of specific sets of characters.
(.*wR)
makes it so it takes the lists as space separated strings. This is just a denser format than lists of strings.
v
is relatively simple. It's a parser that parses any vowel, aeiou
. Similarly c
parses any Lojban consonant (or a space). d
is our list of all consonants, it contains a space and some repeats because this format is useful for other things. We can do this since xys
basically treats its input as a set and we know the input will never have a space.
Specifically the string is <voiceless consonants><sonorants> <voiced consonants><sonorants>
.
Now we get into the body, this has the form:
cP$fo[??,v,c,v]++fo[c,v,??,v]
with the two ??
s each being replaced with the clustering rules.
Reflection
The non-regex answer is so close to beating the regex answer. I have some improvements:
- While
jzW(p2**).*wR
is overly synthetic jzW(p2**)
and zW p2
are both probably useful.
"[aeiou]"
is shorter than '[':W5<>"]"
. If there were a function, ekQ
, to enclose a string in square brackets ekQ W5
would be shorter than both.
- There should probably be constants for consonants of the ISO alphabet as well as the ones for vowels.
- Back-referencing would be useful in the regex answer. It's already a planned feature, there are just technical hurdles on the way to implementing it.
- There could be more versatile ways to handle user input parsers.
v
and c
could potentially have been user input parsers if there existed the ability to supply more than one.
- Along the lines of that, I could make a way for the user to assign escape sequences in some sort of header string, like
rwh"v[aeiou];c[^aeiou]""/c/v/c/c/v"
.