The code for generating the checklist is written in Factor. Factor is rather outside the mainstream, but it works beautifully for my purposes.
I store the checklist data in a text file that looks like the following:
CHECKLIST:
FAMILY: Wrens
SPECIES: Carolina Wren
STATUS: Fairly common year-round. Can be found almost anywhere. There's too many around to not be breeding, but I have yet to find a nest.
PB
END
SPECIES: House Wren
STATUS: Regular in spring and summer. Possible almost anywhere. Young at Paine Estate imply breeding.
CB
END
SPECIES: Winter Wren
STATUS: Probably a rare visitor, possible at all seasons?
RECORD: M. Emmons, 5/14/1997, ? , 2
RECORD: R. Haaseth/D. Finch, 2/6/2004, near Prospect Hill, 1
RECORD: R. Haaseth/D. Finch, 10/2005, ?, 1
PERSONAL: 11/25/2006, Met State, 1
END
SPECIES: Marsh Wren
STATUS: Likely a regular migrant and possibly a breeder at Waverly Oaks Marsh.
RECORD: M. Rines, 4/20/2006, Waverly Oaks Marsh, 1
END
END
HYPOTHETICAL: White-winged Crossbill | irruptive species
HYPOTHETICAL: Hoary Redpoll | irruptive species
HISTORICAL: Black Vulture | coming soon
HISTORICAL: Boreal Chickadee | coming soon
HISTORICAL: Louisiana Waterthrush | coming soon
This is a nice clean structure that is easy to parse. In fact, the code to parse it is "USE: checklistn" swap dup file-length swap <file-reader> stream-read append parse call ;
Yep, the checklist is actually code that parses itself.
To do this, I defined a few tuples:
TUPLE: checklist confirmed hypotheticals historicals
TUPLE: family name species
TUPLE: species name status breeding records
TUPLE: record observer date place quantity
TUPLE: historical name details
TUPLE: hypothetical name reason
Each one simply contains the data and stores anything below it in a vector. I then defined a bunch of words that handle the parsing.
CHECKLIST:
creates a new checklist
FAMILY:
creates a new family and gives it the name of whatever is on the line
SPECIES:
does the same for species
STATUS:
stores the status
PB
and CB
store the breeding
RECORD:
and PERSONAL:
create and store records (PERSONAL:
creates a record with observer “me”)
END
is a generic word that adds the species or family to the family or checklist respectively (I’d like to define it for the checklist as well, but the stack effects don’t match so it doesn’t work)
HISTORICAL:
and HYPOTHETICAL:
do the obvious
For the most part, defining those was straightforward. The trickiest part was dealing with status, as I had to be able to call a word for the next object on the stack after reading the status in. I ended up with the ugly
: STATUS: rest-of-line swap ?push swap swap ?push set-species-status V{ } clone [ push ] keep >quotation swap ?push keep swap ?push ; parsing
mess. I’m sure there’s a better way but that took a couple days to get and I haven’t fiddled with it since.
Getting from the checklist tuple to the output was pretty easy, just doing lots of formatting with make
and working down.