GEMA(1)

๐Ÿ“ฆ version 2.0, in nixos 26.05, nixpkgs unstable

๐ŸŒธ GEMA(1) โ€” ok bestie, let me tell you about gema ๐ŸŒธ

so like. gema. section 1 of the manual. she's a general purpose macro processor and she is genuinely that girl ๐Ÿ’…โœจ


๐Ÿ’– NAME ๐Ÿ’–

gema โ€” a general purpose macro processor. that's it. that's the name. iconic behavior honestly ๐ŸŽ€


โœจ SYNOPSIS โœจ

okay so here's how you call her, in all her forms:

gema options... [ input_file [ output_file ]]

gema options... -out output_file input_file...

gema options... -odir output_directory [-otyp output_suffix]
input_file...

and where options is all of these:

-f pattern_file | [-p] 'patterns' | -b | -i | -k | -line | -match | -t |
-w | -idchars charset | -filechars charset | -inenc encoding | -outenc
encoding | -nobom | -literal charset | -ml | -trace | -arglen num

like yes that's a lot of flags but she contains multitudes ๐Ÿ’€


๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

okay so gema is a general purpose text processing utility built around the whole concept of pattern matching. in general โ€” like, the normal flow โ€” it reads an input file and copies it to an output file, and while it's doing that it performs certain transformations to the data as specified by a set of patterns that you define. you define them. you're the main character here ๐ŸŒŸ

she can do the sorts of things that get done by Unix utilities like cpp, grep, sed, awk, or strings. yes, all of them. versatile queen ๐Ÿ‘‘

she can be used as a macro processor, but she's much more general than cpp or m4 because she does not impose any particular syntax for what a macro call looks like. no dress code. no rules. unlike utilities like sed or awk, gema can deal with patterns that span multiple lines and with nested constructs. and she's also distinguished by being able to use multiple sets of rules in different contexts โ€” which means you can implement parsers for arbitrarily complex context-free grammars. she's not just a vibe, she's a whole compiler toolkit fr fr ๐Ÿ˜ญ

beginning with version 2.0, gema supports the full Unicode character set, and supports files in 8-bit, UTF-8, or UTF-16 format. and just so we're clear: in this document, the word character should be understood to mean the same thing as what the Unicode standard calls a code point. ok? ok. noted. ๐Ÿ’…

this man page gives you a tutorial introduction plus a brief reference summary. see the user manual for a more detailed specification โ€” that's where the deep lore lives ๐Ÿ“š

now, the file handling deets, because these matter:

  • if no file names are given on the command line, the program reads from standard input and writes to standard output. comfy. default. slay.
  • with two file names provided, it reads from the first and writes to the second.
  • if the output file previously existed, the old file is saved with a ".bak" suffix. she's polite, she backs things up ๐Ÿซถ
  • the input file and output file may be the same, in which case the program actually reads from the backup file. sneaky but correct.
  • with the -out option, it will write to the designated file, concatenating the results of reading from any number of input files โ€” which may be specified by wild cards ๐ŸŒˆ
  • with the -odir option, it reads any number of input files, creating a corresponding output file for each in the designated output directory, with the output file suffix optionally specified by the -otyp option.

๐ŸŽ€ TUTORIAL EXAMPLES ๐ŸŽ€

okay bestie, let's get into the actual examples because this is where it gets good ๐Ÿ’–

for a simple example, consider first the following command line:

gema 'Abram=Abraham;Sarai=Sarah' old.text new.text

this copies file "old.text" to "new.text", replacing each occurrence of the string "Abram" with the string "Abraham" and replacing "Sarai" with "Sarah". so cute. so simple. baby's first rule set ๐ŸŽ€

the first command line argument consists of two transformation rules separated by ;. each rule consists of a template followed by = and an action. any text that matches a template gets replaced by the result of the corresponding action. (and yes โ€” here the action is just a literal replacement string, but it's called an action instead of a replacement because it's way more general than that. she's flexible like that.)

the presence of the equal sign is what distinguishes a pattern argument from a file name. iykyk ๐Ÿ˜Œ and note that pattern arguments will usually need to be enclosed in single quotes on Unix (as shown in the example), or double quotes on MS-DOS or Windows. don't forget or the shell will eat your lunch ๐Ÿ˜ญ


๐ŸŒธ pattern files & the special characters ๐ŸŒธ

patterns can also be defined in one or more files loaded with the -f option. in a pattern file:

  • new lines separate rules
  • blank lines are ignored
  • and a ! causes the rest of the line to be ignored as a comment

the following characters have special meaning in patterns:

: * ? # / < > \ ^ = $ @ { } ; ! Space NewLine

memorize them? no. just know they're special. ๐Ÿ’€


๐Ÿงš being careful with replacement ๐Ÿงš

usually when doing string replacement we need to be a little more careful. for example, suppose we want to replace the variable name "x" with the name "horizontal". we do not want to replace instances of "x" that appear as parts of words โ€” only the ones that are a word by themselves. (imagine turning max into maxhorizontal. embarrassing. we don't do that here ๐Ÿ™ˆ)

this can easily be done like this:

gema -t 'x=horizontal' ...

where the -t option (token mode) requires all identifiers in templates to match a complete identifier in the input data.

and if you don't want this behavior everywhere, it can instead be specified locally in the template like this:

gema '\Ix\I=horizontal' ...

here the notation \I denotes an identifier delimiter โ€” it matches an empty string, but only if one of the adjacent characters can not be part of an identifier. there are a number of other such operators listed in the reference section below โœจ

another one that's particularly useful is \W, which says that any number of whitespace characters will be accepted and skipped over if they appear at that point. so a template like:

x\W+\Wy

will match regardless of whether the input text contains optional spaces. the -w option may be used to ignore spaces everywhere (except within identifiers or numbers). slay ๐Ÿ’…


๐Ÿ’œ spaces are not what you think they are ๐Ÿ’œ

a template such as:

first down

will not only match these two words separated by a space โ€” it will also match if they're separated by multiple spaces or even by a newline. why? because the space character actually has special meaning, which is to match one or more whitespace characters. the way that a single space is lowkey a whole operator ๐Ÿ˜ญ

in the less likely event that you really want to match exactly one space character, you can use \ or \s. noted. bookmarked. ๐ŸŽ€


โญ arguments โ€” templates that match variable text โญ

templates can also have arguments โ€” i.e. portions which match variable text. and there are in fact several different kinds of arguments supported. buckle up ๐Ÿ’€

the first kind of argument is the wild card characters:

  • * โ€” matches any number of characters (up to a limit that can be adjusted by the -arglen option)
  • ? โ€” matches any one character

for example, consider the C macro definition:

#define frob(p,m) do(Frob,p[m])

a first approximation to doing this with gema is to use a rule like this:

"\Ifrob\W(*,*)=do(Frob,*[*])"

here the asterisks in the template match any text up to the delimiting comma or parenthesis, and the same text is substituted where the asterisks appear in the action โœจ

note though that in gema, the comma and parentheses do not have any special meaning โ€” a template may delimit the arguments with whatever characters you want. there may be as many as 20 arguments, and the * or ? in the action will be substituted in the same order as in the template.

in cases where the arguments need to be used in some different order, or an argument needs to be used more than once, a different notation may be used that designates the arguments by number. for example:

Rule:  ADD * TO *.=$2 \:\= $2 + $1\;
Input: ADD ITEM TO SUM.
Output: SUM := SUM + ITEM;

note that some of the characters are escaped so that they'll be treated as literals. a backslash preceding a special character always means that the character is to be taken literally โ€” so it won't hurt to escape all special character literals if you aren't sure which ones have special meaning. when in doubt, backslash it out ๐Ÿซถ

and if there are more than nine arguments, the number needs to be enclosed in braces, like: ${10} โ€” don't forget the braces, bestie, ${10} is not $10 ๐Ÿ’€

๐Ÿ”ฅ the * gobbling problem (real) ๐Ÿ”ฅ

one problem to watch out for with * arguments is that since gema allows patterns to span line boundaries, the argument may read many lines ahead, gobbling up much more text than intended. she's hungry. she's eating your whole file. ๐Ÿ˜ญ

in cases where you want to match only on a single line, the operator \L can be used in a template to specify that the following arguments will not be allowed to match on a newline character. this mode may also be selected globally with the command line option -line. problem solved, we move ๐Ÿงš


๐Ÿ’€ the \N situation ๐Ÿ’€

when a template is to match an entire line, the first inclination might be to write a template such as:

\nCommand *\n

where \n designates the new line character. but there are two problems with this:

  1. it won't match on the first line of the file (because there isn't a preceding new line)
  2. it won't match on lines where the preceding new line has already been read as part of the match for the previous line

both of these are solved by using \N โ€” which matches if it is at the beginning or end of a line, but doesn't actually read the new line character. and in an action, \N outputs a new line character if the output stream is not already at the beginning of a line. she's thoughtful like that ๐Ÿ’…


๐ŸŒˆ recognizer domains ๐ŸŒˆ

another way to further constrain argument matching is to use the next kind of argument, which is called a recognizer domain. (we'll see more general user-defined domains later. foreshadowing ๐Ÿ‘€)

for example, in a template:

  • <D> designates an argument that will match one or more digits
  • <L> matches one or more letters
  • <I> matches an identifier (letters, digits, and underscore)
  • <N> matches a number (digits with optional sign and decimal point)

other kinds are listed in the reference section below โœจ

and here's the cute part โ€” with a lower case letter, the argument becomes optional. thus, <d> matches zero or more digits. and preceding the letter with a minus sign inverts the test, so that, for example, <-D> matches one or more characters that are not digits. the letter may be followed by a number to limit how many characters are matched. for example:

  • <D3> matches exactly three digits
  • <d3> will match from zero up to a maximum of three digits

a universal argument such as <U10> matches exactly 10 characters of any kind; this may be useful for splitting an input record into fixed-length fields. and note that <U1> is equivalent to ?. same energy, different font ๐ŸŽ€

(for these arguments, the action must access the value using $ instead of repeating the argument designator. that short cut is only supported for arguments designated by a single character. okay? okay. noted. ๐Ÿ’…)

๐Ÿซถ regular expression arguments ๐Ÿซถ

an even more general way of specifying the set of characters to be accepted is to use a regular expression. a regular expression argument is designated by slashes before and after.

for example, the template:

c/[ad]+/r

will match input text "cadar" with $1 having the value "ada". cute!! โœจ

and important: between the slashes, the special characters have the meaning defined by the usual regular expression syntax โ€” not their meaning elsewhere in templates. two different universes, two different rulebooks, don't mix them up ๐Ÿ’€

Okay so the last kind of argument is the recursive one, and bestie, this is where it gets so good. To show you why we even need it, look at this cutie trying to convert Lisp s-expressions into function call notation:

Rule: (* * *)=*(*,*)
Input: (fn xyz 34)
Output: fn(xyz,34)

Slay, works perfectly. But thenโ€ฆ look what happens here ๐Ÿ˜ญ

Input: (fn (g a b) z)
Output: fn((g,a b)

Yeah. A mess. What we actually need is a way to properly associate matching nested parentheses and to translate nested constructs โ€” and you get both by literally just tweaking the rule like this:

Rule: (# # #)=#(#,#)
Input: (fn (g a b) z)
Output: fn(g(a,b),z)

The # is the recursive argument, which means the argument value is the result of translating input text until reaching the terminator character(s) following it. And here's the iconic part: the space following g does not terminate the outer-level argument, because it's read as part of the inner-level template match. Same energy with the inner ( โ€” it's read by the inner match, which also reads the matching inner ). Nested. Effortless. Periodt. ๐Ÿ’…


Actions can also do a whole wide variety of activities by using the big set of built-in functions that are available. A function call is designated by @ followed by the name of the function, followed by any arguments enclosed in curly braces and separated by semicolons. The "functions" section below lists every single one that's available. For example, you can define a default rule like this:

"\N.*\N=@err{@file line @line\: Unrecognized\: $1\n}"

The err function writes its argument to the error output stream. The file and line functions (which have no arguments, they're lowkey independent like that) return the name and line number of the input file. ๐ŸŽ€


Now, gema supports defining multiple sets of rules, and each set is called a domain. By default, rules are defined in the default domain, which is also the one used to translate the input file. Alternate domains are created by prefixing a rule with a domain name followed by :. The domain name applies until the end of the line. The name of the default domain is the empty string, so a rule line beginning with a colon is the same as one without any colon at all. โœจ

Alternate domains are used for several purposes, including:

  • defining new kinds of arguments for use in templates,
  • defining new functions for use in actions,
  • and for translations that require different rules for various contexts in the input data.

To illustrate using an alternate domain as a new argument type, suppose we want a template argument that will match on either yes or no, so that we can write a rule like:

done\? <yesno>=Finished \= $1

All that's needed is to define the following:

yesno:yes=yes@end;no=no@end;=@fail

This says that if you see yes or no, output it to the argument value and then return, and if anything is seen that doesn't match either of those, then the argument match fails. Note that the last rule has an empty template โ€” this matches as a last resort if no other template in the domain can be matched. And since it doesn't advance the input stream, this makes sense only if the action is to exit. ๐Ÿซถ Also note: domain names should have at least two letters in order to not conflict with recognizers. Important!! Don't skip that!!

Domain names can also be used as functions of one argument, which means that the function returns the result of translating the argument value with the patterns of the domain. This is typically used in a two-step translation process where the first pattern match is used to split the input into fields, and then other domains are used to process individual fields. Remember that the default domain has an empty name, so @{arg} means to translate the argument with the default domain. ๐ŸŒธ

For an example of the use of alternate domains for different contexts, suppose that we're doing name substitution in C source code and we don't want to make any changes inside of character strings. We could add a rule ""*"="*"" to match on string constants and pass them through. However โ€” and this is the plot twist ๐Ÿ™ˆ โ€” string constants can contain "\"" and we don't want the argument to be terminated by that quote. To handle this, we can use a separate domain for processing the contents of a string. Then the rule becomes ""<sbody>"="$1"" and we add a rule: "sbody:\\"=\\"

โœจ OPTIONS โœจ

okay bestie, the OPTIONS section is the full menu and it's giving main-character energy ๐Ÿ’… โ€” every flag matters, so here's the whole lineup, no skips.

  • -f pattern_file โ€” reads pattern definitions from a file. ๐ŸŽ€
  • -p patterns โ€” patterns defined directly as a command line argument. The -p can usually be omitted since an argument containing = or beginning with @ is automatically recognized as a pattern argument. Any number of -f and/or -p options may be given. โœจ
  • -in file โ€” explicitly specifies the input file pathname. If the file name is - then standard input is used. Usually the -in is not necessary since the first file name on the command line is understood to be the input file. ๐Ÿฆ‹
  • -out file โ€” specifies the pathname of the output file. If the name is - then standard output is used. After an explicit -out option has been used, the remainder of the command line can have any number of input file names (without -in) which will be read in sequence, with the concatenated result going to the single output file. For example, a command such as the following can be used to do a grep-like search of a group of files:
gema -match -p 'Copyright *\n=@file\: $0' -out - *.c

(The special notation $0 copies all of the matched text into the output.) ๐Ÿ”ฅ

  • -odir directory โ€” specifies the output directory. For each input file that follows, a corresponding output file will be written in the designated directory. ๐ŸŒธ
  • -otyp suffix โ€” when used with the -odir option, this specifies that each output file will have the designated suffix replacing the suffix of the input file. For example, given the command line:
gema -f patterns -odir /stuff -otyp .list *.text

then if the current directory contains a file named foo.text, it will be translated to an output file named /stuff/foo.list. ๐ŸŽ€

  • -backup suffix โ€” the argument will be used as the backup file suffix in place of the default .bak. ๐Ÿ’พ
  • -nobackup โ€” output files will be overwritten instead of saving the old file as a backup file. (This can be dangerous, so is best used only by adding it to a scripted command which has already been tested.)
โš ๏ธ bestie, this one is lowkey spicy: overwriting is permanent energy. Test first, fr fr.
  • -outenc encoding โ€” output encoding. Specifies what encoding to use for output files opened subsequently. The valid values are (case-insensitive): auto, ASCII, 8bit, UTF-8, UTF-16LE, and UTF-16BE. The default is automatic, meaning the same as the input file. If ASCII is specified, any characters outside the ASCII range will be represented using \x or \u escape sequences. For example, the following shell command can be used to convert text files to UTF-8 format:
gema -outenc UTF-8 -odir NewDirectory *.txt
  • -inenc encoding โ€” input encoding. Specifies what encoding to use for input files or pattern files opened subsequently. (It may be used more than once.) The values are the same as for -outenc. This option should seldom be necessary as the default is to determine the encoding automatically. You might need the option -inenc 8bit if eight-bit characters are being inappropriately interpreted as UTF-8 encoding. ๐Ÿงš
  • -nobom โ€” suppresses writing of a Byte Order Mark at the beginning of the output file. (This is only relevant if the output encoding is UTF-8 or UTF-16.) ๐ŸŽ€
  • -line โ€” places the program in line mode, which means that all pattern matching is limited to single lines. Arguments and template operators will never cross a line boundary except where the template explicitly specifies so with \n. ๐Ÿ“
  • -b โ€” binary. With this option, all input and output files are opened in binary mode instead of text mode. This makes no difference on most Unix systems, but on Windows it changes the meaning of the new line character and doesn't treat Control-Z as the end of the file. You may want to accompany this with -inenc 8bit if you just want to see raw bytes without trying to apply UTF-8 decoding. (This option only affects files, not stdin or stdout.) ๐Ÿ’€
  • -k โ€” keep going. With this option, the program will try to continue execution after certain errors that would normally cause it to abort. This may be useful when you want to see everything that is wrong before starting to fix the errors. Errors will still cause a non-zero exit status despite this option. ๐Ÿซถ
  • -match โ€” matches only mode. Input text that doesn't match any template will be discarded instead of being copied to the output. This would be used when you want to extract selected information (like with grep) instead of doing a translation. This option applies only to the default domain. Another way to discard otherwise unmatched text is with the default rule ?= while the rule ?=? can be used to explicitly copy. ๐Ÿ”
  • -i โ€” case insensitive mode. All letters in templates will be matched without regard to distinctions of upper case or lower case. This also makes the names of domains and built-in functions case insensitive. ๐ŸŒˆ
  • -w โ€” whitespace insensitive mode. Space and tab characters in rules will be ignored except where they separate identifiers. Template matching will ignore whitespace characters in the input data as though templates had an implicit \W everywhere except within identifiers. Templates can use \J to indicate where space is not allowed. ๐Ÿ“
  • -t โ€” token mode. All identifiers appearing in templates will match only against a complete identifier, as though each identifier was implicitly surrounded by \I except where counter-acted by \J. ๐Ÿ”ค
  • -idchars charset โ€” identifier characters. The argument value specifies the set of characters that will be considered to be identifier constituents, in addition to letters and digits. The default value is _. This affects the behavior of \I, <I>, and <Y>, and the -w and -t options. For example, if you were processing COBOL source code, you would need -idchars '-'. For Lisp code, you would probably want something like: -idchars '-+=*/_<>' In version 2.0, this option is deprecated in favor of @defset{I;charset}. ๐Ÿง 
  • -filechars charset โ€” file name characters. The argument value specifies the set of characters which are accepted by <F> as being file name constituents, in addition to letters and digits. The default value is -./_~#@%+= for Unix. On Windows, colon and backslash are also included in the default set. In version 2.0, this option is deprecated in favor of @defset{F;charset}. ๐Ÿ“
  • -literal charset โ€” this option specifies that each of the characters in the argument value will be treated as an ordinary literal character in patterns, instead of whatever special meaning it might normally have. For example, rather than saying something like:
gema '\/usr\/foo\/<F>=\/usr\/bar\/$1' ...

you could instead say:

gema -literal / '/usr/foo/<F>=/usr/bar/$1' ...

iconic shortcut behavior, honestly. ๐ŸŽ€

  • -ml โ€” for convenience in processing Markup Languages (HTML, XML, etc.), this option (which is new in version 1.4) changes the syntax so that the characters <, > and / are taken as literals, using [, ] and | respectively in their places. For example:
gema -ml -p '<i>[T]</i>=<em>$1</em>' ...

has the same effect as:

gema -p '\<i\><T>\<\/i\>=\<em\>$1\<\/em\>' ...

The -ml option is an abbreviation for @set-syntax{</>LLL;[|]</>}. ๐Ÿฆ‹

  • -arglen number โ€” specify the maximum length of a * argument. The default is 4096. ๐Ÿ“
  • -prim pattern_file โ€” primitive mode (for advanced users only). Like the -f option, this loads patterns from a file. It also suppresses loading of the built-in patterns for command line processing. This option is meaningful only when it appears as the first argument, and then it becomes the only argument that has any predefined meaning. The designated pattern file must define ARGV domain rules sufficient to specify what to do with the remainder of the command line. ๐Ÿšจ
๐Ÿšจ advanced users only, bestie โ€” this is deep-lore territory.
  • -help โ€” display brief usage message on the standard error output. The syntax description reflects any preceding options -ml, -literal, or @set-syntax. ๐Ÿซถ
  • -version โ€” display program version identification on the standard error output. โญ
  • -trace โ€” if the program was compiled with -DTRACE, then this option can be used to enable a report of template matches and failures to be written to the error output. This may be helpful for diagnosing obscure cases of unexpected results, but this is a crude experimental feature, so don't expect too much. Each line describes one event, possibly showing the line and column number at the beginning of the current template, the line and column of the current position, and a description of the event, indented for recursive arguments. ๐Ÿ”ฌ

โœจ PATTERNS REFERENCE SUMMARY โœจ

okay bestie, gather round, this is the cheat sheet for all the characters that are Doing The Most in Gema ๐Ÿ’… these ones have special meaning, so don't come for me when you use one literally and it does something unhinged:

  • * โ€” matches any number of characters (greedy little icon)
  • ? โ€” matches any one character
  • # โ€” argument recursively translated in the current domain
  • = โ€” end of template, beginning of action (the plot twist โœจ)
  • $0 โ€” copies the template into the action to show all matched text
  • $digit โ€” inserts argument value
  • $letter โ€” inserts value of a variable with single-letter name
  • ${name} โ€” value of named variable (only in action)
  • ${name;default} โ€” variable with default value if not defined (action only)
  • \ โ€” escape character; see the section on "escape sequences" below.
  • ^x โ€” combine control key with the following character
  • Space โ€” matches one or more whitespace characters (same as \S).
  • NewLine โ€” end of action
  • ; โ€” end of action, or separator between function arguments
  • @name{args} โ€” invoke built-in function or user-defined translation domain (action only)
  • @special-character โ€” has the default meaning of the special character by itself, as documented here; this can be used to access the original functionality of a character that has been changed by the -literal option or @set-syntax function.
  • : โ€” separates domain name from rule
  • <name> โ€” recursive argument translated according to the named domain, or a recognizer argument. (template only)
  • /regexp/ โ€” regular expression argument (template only)
  • ! โ€” the rest of the line is a comment

Also, as a special case (iykyk ๐Ÿ‘€), the first line of a pattern file is ignored if the first two characters are #!. This allows a pattern file to be made directly executable on Unix by putting something like #!/usr/local/bin/gema -f as the first line. she's self-executing, she's main character energy ๐ŸŒˆ


๐Ÿ”ฎ ESCAPE SEQUENCES ๐Ÿ”ฎ

The backslash character denotes special handling for the character that follows it, fr fr. So like:

  • when it's followed by a lower-case letter or a digit, it represents a particular control character
  • when it's followed by an upper case letter, it is a pattern match operator ๐Ÿ‘‘
  • a backslash at the end of a line designates continuation by causing the newline to be ignored along with any leading white space on the following line
  • before any other character, the backslash quotes the character so that it simply represents itself. In particular, a literal backslash is represented by two backslashes (\\).

Following are the defined escape sequences, no gatekeeping:

  • \a โ€” Alert (a.k.a. bell) character
  • \b โ€” Backspace character
  • \cx โ€” Control key combined with the following character
  • \d โ€” Delete character
  • \e โ€” Escape character (i.e. ESC, not backslash โ€” yes bestie this trips everyone up ๐Ÿ˜ญ)
  • \f โ€” Form feed character
  • \n โ€” New line character
  • \r โ€” carriage Return character
  • \s โ€” Space character
  • \t โ€” horizontal Tab character
  • \uxxxx or \u{xxxx} โ€” Unicode character specified by its hexadecimal code
  • \v โ€” Vertical tab character
  • \xxx โ€” character specified by its two-digit heXadecimal code
  • \digits โ€” character specified by its octal code
  • \A โ€” matches beginning of input data
  • \B โ€” matches Beginning of file
  • \C โ€” Case-insensitive comparison for the rest of the template
  • \E โ€” matches End of file
  • \G โ€” Goal โ€” complete preceding argument before considering rest of template
  • \I โ€” Identifier separator
  • \J โ€” Join โ€” locally counteracts the -w and/or -t option by saying that spaces in the input will not be ignored at this position, and an identifier delimiter is not required here. If neither of these options is being used, then it has no effect.
  • \L โ€” Line mode โ€” following arguments can't cross line boundary
  • \N โ€” New line; matches beginning or end of line
  • \P โ€” Position โ€” leave input stream here after the template matches
  • \S โ€” Space โ€” matches one or more whitespace characters
  • \W โ€” Whitespace โ€” skips over any optional whitespace characters
  • \X โ€” word separator
  • \Z โ€” matches end of input data

๐Ÿ•ต๏ธ RECOGNIZERS ๐Ÿ•ต๏ธ

okay so these are SO useful. The following argument designators, consisting of a single English letter between angle brackets, can be used in templates to match on various kinds of characters. Here's the tea โ˜•:

  • Preceding the letter with - inverts the test (sneaky little "not" vibe)
  • The argument requires at least one matching character if the letter is uppercase, or is optional if the letter is lowercase
  • The letter may be followed by a number to match on that many characters, or up to that maximum for an optional argument
  • If the number is 0, the argument matches if the next character is of the indicated kind, but the input stream is not advanced past it โ€” in other words, this acts as a one-character look-ahead ๐Ÿ‘€
  • By default, recognition of letters is limited to the ASCII letters, but that may be extended by using @set-locale or @defset.

And the letters themselves, bestie:

  • <A> โ€” Alphanumeric (letters and digits)
  • <B> <Q> <R> <Z> โ€” available for the user to define by using @defset (new in version 2.0) โœจ
  • <C> โ€” Control characters
  • <D> โ€” Digits
  • <E> โ€” Emojis, emoticons, pictographs, arrows, and other geometric shapes (new in version 2.0) ๐Ÿฆ‹ literally her
  • <F> โ€” File pathname
  • <G> โ€” Graphic characters, i.e. any non-space printable character
  • <H> โ€” Han characters, also known as Chinese, Kanji, or CJK (new in version 2.0)
  • <I> โ€” Identifier
  • <J> โ€” lower case letters
  • <K> โ€” upper case letters
  • <L> โ€” Letters (either upper or lower case)
  • <M> โ€” Mathematical operator symbols (new in version 2.0)
  • <N> โ€” Number, i.e. digits with optional sign and decimal point
  • <O> โ€” Octal digits
  • <P> โ€” Printing characters, including space
  • <S> โ€” white Space characters (space, tab, newline, FF, VT)
  • <T> โ€” Text characters, including all printing characters and white space
  • <U> โ€” Universal (matches anything except end-of-file)
  • <V> โ€” Valid characters. Any Unicode characters except for control characters (other than whitespace), local use codes, unpaired surrogates, out-of-range values, unallocated blocks, and explicit not-a-character codes.
  • <W> โ€” Word (letters, apostrophe, and hyphen)
  • <X> โ€” hexadecimal digits
  • <Y> โ€” punctuation characters

๐Ÿ› ๏ธ FUNCTIONS ๐Ÿ› ๏ธ

The following built-in functions may be used in actions, either in the action portion of a rule, or appearing by itself as an immediate action. When a line in a pattern file begins with @, the actions are executed before reading the next line. periodt ๐Ÿ’…

Function calls have the form @name{args}, with arguments separated by ;. For functions without arguments, the argument delimiters {} may be omitted if not needed to separate the name from the following character. All arguments are evaluated, so all of the special characters available in actions apply within the arguments also. (In a few cases, arguments that are not used are skipped instead of evaluated, but arguments are never used literally.) Arguments shown as number or length must have a value which is a valid decimal representation of an integer, with optional leading whitespace and optional sign. All numbers are considered to be 32 bit signed integers. The descriptions given here for the functions is just a terse reference summary; refer to the user manual for more detailed information โ€” don't @ me, the full lore is elsewhere ๐Ÿ“–

  • @abort{} โ€” Immediately terminate execution. (dramatic exit, we love it ๐Ÿ’€)
  • @add{number;number} โ€” Return the sum of the two numbers.
  • @and{number;number} โ€” Return the bit-wise and of the two numbers.
  • @append{var;string} โ€” Append the string to the end of the named variable's value. No return value.
  • @as-ascii{string} โ€” Returns a copy of the argument with any non-ASCII characters converted to \x or \u escape sequences. (new in version 2.0)
  • @bind{var;string} โ€” Bind named variable to a value. No return value.
  • @center{length;string} โ€” Center the string within a field of the designated length.
  • @char-int{character} โ€” Returns decimal number representation of internal character code.
  • @close{pathname} โ€” Closes a file previously opened by @write{pathname}
  • @cmpi{string;string;less-value;equal-value;greater-value} โ€” Compare, case-insensitive. Return either the third, fourth, or fifth argument value depending on whether the first argument is less than, equal to, or greater than the second. The two unused arguments are not evaluated.
  • @cmpn{number;number;less-value;equal-value;greater-value} โ€” Compare numbers.
  • @cmps{string;string;less-value;equal-value;greater-value} โ€” Compare, case-sensitive.
  • @column{} โ€” Returns the current column number in the input stream.
  • @date{} โ€” Returns the current date, in the form mm/dd/yyyy
  • @datime{} โ€” Returns the current date and time, formatted by the C function ctime(3).
  • @decr{var} โ€” Decrement value of variable. No return value.
  • @define{patterns} โ€” Run-time definition of additional rules. No return value.
  • @defset{letter;characters} โ€” Define or extend the set of characters accepted by the recognizer named by the letter. (new in version 2.0) No return value.
  • @div{number;number} โ€” Return result of dividing the first argument by the second.
  • @downcase{string} โ€” Convert any letters from upper case to lower case.
  • @encoding{} โ€” Return the string naming the encoding of the current input file. (new in version 2.0)
  • @end{} โ€” End translation. No return value.
  • @err{string} โ€” Write the argument value to the error output stream. No return value.
  • @exit-status{number} โ€” Specify exit code to return when the program terminates. No return value.
  • @expand-wild{path} โ€” Expand wild card pathname on MS-DOS or Windows.
  • @fail{} โ€” Signal translation failure; causes failed match of recursive argument.
  • @file{} โ€” Returns the name of the input file.
  • @file-time{} โ€” Returns the modification time and date of the input file, formatted by the C function ctime(3).
  • @fill-center{background;value} โ€” Center the value on top of the background string.
  • @fill-left{background;value} โ€” Left-justify the value on top of the background string.
  • @fill-right{background;value} โ€” Right-justify the value on top of the background string.
  • @getenv{name;default} โ€” Return the value of an environment variable. Returns the optional second argument if the environment variable is not defined.
  • @getset{letter} โ€” Return the set of characters named by the letter. (new in version 2.0)
  • @get-switch{name} โ€” Return value of switch (see @set-switch)
  • @incr{var} โ€” bumps a variable's value up by one, that's it, nothing comes back ๐Ÿ’…
  • @inpath{} โ€” hands you the pathname of the input file, no cap
  • @int-char{number} โ€” gives you the character whose internal code is whatever number you passed in
  • @line{} โ€” the current line number in the input file, so you always know where you're at โœจ
  • @left{length;string} โ€” left-justifies the string, padding it out with spaces until it hits the length you asked for
  • @length{string} โ€” returns how long the argument is, periodt
  • @makepath{directory;name;suffix} โ€” returns the file pathname you get by merging the second argument with the default directory in the first argument, and replacing the suffix from the third argument if it's not empty ๐ŸŽ€
  • @mergepath{pathname;name;suffix} โ€” same vibe, except the default directory gets extracted from the first argument, then merged with the second, and the suffix from the third gets replaced if it's not empty
  • @mul{number;number} โ€” multiplies the two numbers
  • @mod{number;number} โ€” first argument modulo the second, iykyk
  • @not{number} โ€” bit-wise inverse of the argument
  • @or{number;number} โ€” bit-wise or of the two numbers
  • @out{string} โ€” writes the argument value directly to the current output file. no return value, it just goes
  • @out-column{} โ€” the current column number in the output file
  • @outpath{} โ€” pathname of the output file, slay
  • @push{var;value} โ€” sets a variable's value while remembering the previous one ๐Ÿซถ same as @bind
  • @pop{var} โ€” restores the variable to whatever it was before the most recent @push. same as @unbind
  • @probe{pathname} โ€” vibe check on the path: "F" if it names a file, "D" if a directory, "V" if a device, or "U" if undefined
  • @quote{string} โ€” returns a copy of the argument with backslashes inserted wherever necessary so that @define treats all the characters as literals ๐Ÿฆ‹
  • @radix{from;to;value} โ€” radix conversion, bestie. the first two arguments must be decimal integers. the third argument is read as a number whose base is specified by the first argument, and the result is that number represented in the base given by the second argument. math ate ๐Ÿ’…
  • @read{pathname} โ€” returns an input stream that reads the contents of the named file. heads up: this just specifies where the input comes from โ€” it's usually passed as an argument to another function that specifies what to actually do with the data ๐Ÿ™ˆ
  • @relative-path{pathname;pathname} โ€” if the two pathnames have the same directory portion, returns the second argument with that common directory removed; otherwise returns the whole second argument
  • @repeat{number;action} โ€” runs the second argument the number of times given by the first. if that number is less than or equal to zero, the second argument isn't evaluated at all, like not even once โญ
  • @reset-syntax{} โ€” re-initializes the syntax tables, undoing whatever @set-syntax or the -literal option did
  • @reverse{string} โ€” returns the characters of the argument in reverse order ๐Ÿ”„
  • @right{length;string} โ€” right-justifies the string, padding with spaces to the designated length
  • @set{var;value} โ€” sets the named variable to the designated value. no return value
  • @set-locale{name} โ€” sets the internationalization locale using setlocale(3). this may affect which characters count as letters, plus the format of times and dates ๐ŸŒˆ no result value
  • @set-parm{name;value} โ€” sets a string-valued option, and it's gotta be one of "idchars", "filechars", "backup", "inenc", or "outenc". peep the description of the corresponding command line options. no result value
  • @set-switch{name;value} โ€” flips one of these to 1 for true or 0 for false: "line" for line mode, "b" for binary mode, "k" to keep going after errors, "match" for match-only mode, "i" for case-insensitive mode, "w" for whitespace insensitive mode, "t" for token mode, "bom" to write a Byte Order Mark, or "trace" ๐Ÿ‘‘
  • @set-syntax{type;charset} โ€” the characters in the second argument get the same meaning as the corresponding special character(s) in the first argument, or you can use one of the alphabetic type codes: "L" for literal, "I" for ignore, etc. no result value
  • @set-wrap{number;string} โ€” for @wrap: first argument is the number of columns, second argument is the indentation string. no result value
  • @shell{string} โ€” the argument gets executed as a shell command ๐Ÿš no return value
  • @show-help{} โ€” displays the usage message on the standard error stream
  • @sub{number;number} โ€” subtracts, iconically
  • @subst{patterns;operand} โ€” substitution time โœจ returns the result of translating the operand according to the patterns temporarily defined by the first argument
  • @substring{skip;length;string} โ€” returns a substring of the third argument by skipping the number of characters indicated by the first argument and then taking the number of characters indicated by the second
  • @tab{number} โ€” outputs spaces until the output stream reaches the designated column ๐ŸŽ€
  • @terminate{} โ€” ends translation of a recursive argument: success if any characters have been accepted, or failure if the argument value is empty
  • @time{} โ€” returns the current time, in the form hh:mm:ss โฐ
  • @unbind{var} โ€” restores the variable to the value, if any, that it had before the most recent @bind
  • @undefine{patterns} โ€” deletes pattern definitions, gone girl ๐Ÿ’€
  • @upcase{string} โ€” converts any letters from lower case to upper case
  • @var{var;default} โ€” returns the value of the named variable. if the variable isn't defined, it returns the optional second argument if you supplied one โ€” otherwise it reports an error ๐Ÿ˜ญ @var has the same effect as $ when the name isn't a number
  • @version{} โ€” returns the program version identification string
  • @wrap{string} โ€” outputs the string, starting a new line first if necessary according to the parameters set by @set-wrap. the default is 80 columns and no indentation ๐ŸŒธ
  • @write{pathname;string} โ€” the second argument gets evaluated with its result value written to the file named by the first argument. subsequent calls to @write with the same pathname will append to the file, until a @close ๐Ÿซถ

๐Ÿ’€ BUGS ๐Ÿ’€

ngl the implementation of the -t and -w options is a little sloppy ๐Ÿ˜ฌ and may produce unexpected results that require you to use explicit \J, \I, or \W to work around in certain contexts. so like, fair warning bestie.


๐Ÿ”— SEE ALSO ๐Ÿ”—

The gema user manual and the example pattern files provided. fr fr go read those.

The source files and documentation can be downloaded from "http://sourceforge.net/projects/gema/" and the documentation can be read at "http://gema.sourceforge.net/" โœจ


๐Ÿ‘‘ AUTHORS ๐Ÿ‘‘

gema was written by David N. Gray <DGray@acm.org>. legend behavior, honestly ๐Ÿซถ


okay that's the whole page bestie, you're officially a gema girlie now ๐Ÿ’…๐Ÿ’– this was version 2.0, dated 30 Jan 2024 โ€” go forth and translate some patterns, you absolute icon ๐Ÿฆ‹โœจ

๐Ÿ“ฆ where this page comes from