genmark

Genmark is a plain-text genealogy domain-specific language (DSL) that compiles to GEDCOM 5.5.1. The compilation is important because GEDCOM is so common in genealogical work, but Genmark on its own forms an extremely useful plain text file format (.gmd) for recording and organizing genealogical data. There’s good a chance this is the project I’m most proud of. It feels like more of an achievement to invent a new file format, and I’m actually using it for all of my own genealogical research. As far as I know, it could be the only plain text format specifically for writing/reading genealogical data directly.

GEDCOM 5.5.1 is the standard interchange format for genealogy software, but it’s a machine format, not something anyone would want to write by hand. Here’s an example from the Genmark README:

0 @I_mary_smith@ INDI
1 NAME Mary Ellen /Smith/
1 SEX F
1 BIRT
2 DATE MAR 1895
2 PLAC Portland, Oregon
1 DEAT
2 DATE 28 FEB 1978
2 PLAC Portland, Oregon
1 FAMS @F1@

That same data represented in Genmark is:

Mary Ellen Smith [mary_smith]
  sex: F
  b: 1895-03 @ Portland, Oregon
  d: 1978-02-28 @ Portland, Oregon

So Genmark exists basically to put a readable layer on top of that: you write a person as one block of plain text, and the compiler produces valid GEDCOM. And really, if you don’t need to use GEDCOM, Genmark is a great format by itself. The idea is that the .gmd file is the actual research archive - not an import/export intermediate - so it can be read, diffed, grepped, and edited directly. The repo includes configuration files for Emacs, Vim, and VS Code to make editing there even more convenient.

The Genmark program is written in Go and ships as a single static binary via go install or prebuilt cross-platform releases (Linux, macOS Intel/ARM, Windows). The compilation process follows the following pipeline: a lexer tokenizes the source, a parser builds an AST, and a compiler walks it to emit GEDCOM, with a first pass to register every record’s ID before a second pass resolves references - so forward references (citing a person before their record appears) will work fine.

I didn’t include absolutely everything available in GEDCOM, but I tried to cover most of what people would need for real genealogical research. Life events - birth, death, christening, burial, immigration, naturalization, residence, census, occupation, military service, education, arbitrary custom events - all take a common description/date/place shape:

occ: Factory Foreman (1921..1945) @ Portland, Oregon

There’s a wide range of options for how you might want to organize your genealogical data, largely based on how much data you’re storing on how many people. You could keep it all in one file if you want to, but Genmark compiles across multiple files and even whole directory trees, resolving every [id] reference globally. You can organize the data however makes sense: one file per person, one per branch of the tree, one per surname or region, etc. A combine subcommand concatenates multiple .gmd files back into one, preserving comments and formatting, for when you want a single reviewable document without producing GEDCOM.

Sharing a tree usually means redacting any living people, so compile --privatize strips anyone not demonstrably (or assumed, by configuration) dead. Media files can be attached to a person via a media: field and can live in per-person or per-branch directories alongside the .gmd files; --copy-media gathers everything actually referenced in the compiled output into a single bundle beside the GEDCOM (skipping media orphaned by privatization) so the tree is portable without manually collecting/reorganizing files.

Beyond compiling, there’s a small set of analysis subcommands aimed at research itself rather than output: check validates a set of files for undefined references and conflicting facts without producing a GEDCOM, and shows every maybe: as a running list of things to verify; places tallies where events happened, with a --by-region mode that rolls up to state or country and an --at YEAR mode that infers each living person’s approximate location at a given point in time from their most recent dated event; and names tallies given, middle, and surnames across the dataset, useful for spotting naming patterns or picking a name that runs in the family.

You can see the code and download the binary here on GitHub. Again, I’m using this for my own work without really needing the GEDCOM compilation at all (so far), so the most important details are in the documentation/specs, not the code (although the program’s other subcommands are also useful). For a very thorough description of the syntax see SYNTAX.md, and for more examples you can look at EXAMPLES.gmd.

← Back to Projects