# Tips for golfing in Lexurgy

What general tips do you have for golfing in Lexurgy? I'm looking for ideas which can be applied to code-golf problems and which are also at least somewhat specific to Lexurgy (e.g. "remove comments" is not an answer).

Lexurgy is a tool created by Graham Hill/def-gthill meant for conlangers (people who create constructed languages) to apply sound changes to their lexicon. As a result, Lexurgy is highly geared towards rule-based string operations. Its feature set includes capturing, character classes, and much more as detailed in the documentation below.

The documentation for Lexurgy is here, and a cheatsheet is here.

You can access Lexurgy here.

### Reducing Whitespace

Lexurgy has specific rules about where whitespace can be removed. Consider the following rule (taken from my submission to Covfefify a string):

part-1:
@consonant * => @consonant ; / $@consonant* @vowel* _ // {$ _, $@consonant _}  Lexurgy parses the rules as such, with <> separating individual elements: <part-1>: <@consonant *> => <@consonant ;> / <$ @consonant* @vowel* _> // <{<$_>, <$ @consonant _>}>


You cannot remove any whitespace within the <> or else it will yield a parsing error. As a result, with all extraneous whitespace removed, the rule turns into:

part-1:
@consonant *=>@consonant ;/$@consonant* @vowel* _//{$ _,$@consonant _}  If both sides have only 1 possible string on each side, then you can combine them: # single string a: s t r=>o t h b: # -1 byte per space str=>oth # combinable strings c: s t r *=>* o t h d: # -1 byte per space str *=>* oth # two or more strings e: str ing=>oth er f: # -1 byte per string string=>other  ### Caveats about Lexurgy • Lexurgy treats whitespace in the input as word separators, so whitespace must be replaced by some other character. Individual inputs are separated by newlines, and those cannot be escaped in the input box. • Escaping certain characters is done with \, i.e. \! => !. • Lexurgy is designed for working on characters from a set of characters; use [] for matching any single character. ### Use class variables within class declarations Lexurgy allows you to define arbitrary "classes" of characters which can be accessed by @classname. Classes must have at least 1 character within them. Consider the following classes: # 75 bytes Class v {a,e,i,o,u} Class c {p,t,k,b,d,g} Class l {a,e,i,o,u,p,t,k,b,d,g} # 59 bytes (-16 bytes) Class v {a,e,i,o,u} Class c {p,t,k,b,d,g} Class l {@v,@c}  A class is minimum 11 bytes long (Class n {c} to account for the single character needed for a class), and each additional character is +2 bytes. Consider these two minimum classes, and wanting to make a class c that is the union of a and b: Class a {a} Class b {b} Class c {a,b} # +13 bytes Class c {@a,@b} # +15 bytes  For minimal classes it's better to use the explicit declaration instead of using class variables. The break-even point is at three characters for 2 classes: Class a {a,e} Class b {b} Class c {a,b,e} # +15 bytes Class c {@a,@b} # +15 bytes  Let there be $$\c \ge 2\$$ classes. The total number of characters $$\n\$$ in classes will cause the byte count to break even (i.e. using class variables is just as good as explicitly declaring the union) at $$\2n=3c \Rightarrow n=\frac{3}{2}c\$$. Therefore, it is better to use variables when $$\n \ge \frac{3}{2}c\$$, and explicit declarations when $$\n \le \frac{3}{2}c\$$. ### Programs with no input Programs with no input are possible in Lexurgy, with a rule like: # +7 bytes a: *=>c  It takes an empty string and outputs a character for you to further mutate as needed. You can put an entire string on the right-hand side as well. This must be the first rule for anything useful to happen. However, if the program is given input, it will apply the rule to each input and every possible match in the input strings. ### Use classes as variables holding strings You can place entire, arbitrary strings into class definitions, provided they are properly escaped: Class s {Hello\,\ world\!}  Then, use it like any other class: # 38 bytes a: []=>Hello\,\world\!Hello\,\world\! # 41 bytes Class s {Hello\,\ world\!} a: []=>@s @s  The above example gains a 3 bytes, but if you use the string multiple times you get even more savings: # 53 bytes a: []=>Hello\,\world\!Hello\,\world\!Hello\,\world\! # 44 bytes Class s {Hello\,\ world\!} a: []=>@s @s @s  ### The "Find/Marker/Delete" Pattern A common pattern that I've found, which I've named the "Find/Marker/Delete" pattern, shows up often whenever the output is unrelated to the input except for some value that cannot be done using a simple replacement. For example, consider this code that will output true or false if the word ends with e: a: *=>|true/e _$
else:
*=>|false/_ $b propagate: []=>*/_ | c: |=>*  This code has three parts: # part a: find and place the marker. (here it's a |) a: *=>|true/e _$
else:
*=>|false/e _ $# part b: delete everything before/after the marker. (here it's everything before) b propagate: []=>*/_ | # part c: delete the marker c: |=>*  This pattern is the only real way you can have an output that is not some simple replacement of the input. The minimal version for 2 outputs is as such (68 bytes): a: *=>|a/e _$
else:
*=>|b/_ \$
b propagate:
[]=>*/_ |
c:
|=>*