Skip to main content

Regards,

tests

An esoteric programming language whose source code is a passive-aggressive corporate email thread.

Subject: Q3 headcount — quick sync?

Hi Priya,

Just to level-set, we have 5 open reqs.

Per my last email, while we still have open reqs:
> Circling back on open reqs.
> Quick flag: one req is now closed.

+Leadership for visibility.

Best,
Ihor

Output:

5
4
3
2
1

Run it with python3 regards.py examples/countdown.rgrd. Or install it with pip install regards, which gives you a regards command for any .rgrd program or saved .eml email. Either way it needs Python 3.9 or newer and nothing else.

This is Regards, 1.0. Programs written for 1.0 will keep running on every 1.x version, and anything that would break them waits for 2.0. The changelog lists what's in each version.


1. Why email

Corporate email turns out to be a programming language that nobody bothered to write down. Everything a language needs is already in there:

  1. things people keep bringing up (open reqs) → variables
  2. If we still have budget: → branches
  3. Per my last email and As discussed, Dave. → loops and calls
  4. Circling back on … → output

Regards, follows a proud esolang tradition of finding a language somewhere nobody was looking. Chef found stacks in mixing bowls, and Shakespeare found registers in two characters sharing a stage. This one found one in your inbox.

Email even brings its own indentation, which is generous of the most-despised artifact in office life:

Quote depth is block depth. > and >> already mean "this belongs to that" in every email client, so there is no new delimiter to learn.

It brings its own ending, too. A message is over when it is signed off, so the language is named after its halt instruction.


2. Anatomy of a program

A .rgrd file is one email message, optionally quoting a thread beneath it.

Subject: <program name>              ← program name, ignored by the interpreter
Cc: <Name>, <Name>                   ← who `Replying all.` reaches (optional)

Hi <name>,                           ← entry point (Hello, Hey and Dear work too)

<body>                               ← statements; `>` depth is block depth

Best,                                ← HALT
Ihor                                 ← ignored
P.S. …                               ← also ignored (optional)

Sent from my iPhone                  ← turns on autocorrect (optional)

On Tue, 8 Sep 2026, Priya Raman <priya@…> wrote:
> <the quoted thread below the signature is the standard library>
> <each quoted message defines a person you can call>

Three regions, in order:

Region Role
Header lines Subject: is the program name, and decorative. Cc: lists who Replying all. reaches.
Body, from Hi …, to the sign-off The program.
Quoted thread below the signature The past. Previously-defined messages, available to call.

That last one is the part I like most: the history you keep dragging along below your signature is the library you are importing, and nobody ever reads it.

Below the sign-off, only four things are allowed: the signature name, P.S. lines, the Sent from my iPhone pragma, and On …, <Name> wrote: blocks. Anything else is unreachable code and an error. Nobody reads a P.S., so the interpreter doesn't either.

Quote depth inside the thread

A quoted message's own text sits at one level of >. Its blocks are counted from there, so a loop body inside Dave's message is at > >. Messages quoted inside Dave's message are one level deeper again, and everyone in the thread, however deep, is callable.

>> and > > are the same depth. Email clients rewrite one into the other, so both are accepted.


3. Statement reference

The idiom table is the language. Everything below is matched case-insensitively with normalized whitespace.

Email clients rewrite text as you type, so the lexer undoes it: curly quotes become straight quotes, and , and -- are all the same dash. A program pasted out of Outlook still runs.

Variable names

People never refer to the same thing the same way twice, so a variable is named by its last word, lowercased, with a trailing s dropped. open reqs, req and Reqs are one variable. This is what lets you declare 5 open reqs and then close one req.

The corollary is that open reqs and closed reqs are also one variable. Pick distinct nouns.

Words of three letters or fewer, and words ending in ss, keep their s, so gas and business stay as they are.

it and that stand for whichever variable was mentioned last before the current statement, so Doubling down on it. right after Just to level-set, budget is 5. doubles the budget.

Numbers are integers with no upper limit. Halving and splitting round down, toward negative infinity.

Wherever a table below says <n>, <a> or <expr>, you can write a number, a variable, what's left after splitting …, or someone's take (section 4). The one exception is Just to level-set, we have <n> <var>., where <n> has to be a plain number.

The backlog has its own statements in section 8, and you can add phrases of your own with jargon (section 9).

Declaration and assignment

Idiom Semantics
Just to level-set, we have <n> <var>. declare var, assign n
Just to level-set, <var> is <n>. declare var, assign n
Looping in <Name>. / +<Name> no effect; everyone in the quoted thread is already callable
Per the attached, <var> is now <expr>. reassign
<var> is off the table. var = 0
Happy to take this offline. variables declared from here to the end of the block are private
We have a hiring freeze on <var>. writes to var silently do nothing from here on
The freeze on <var> is lifted. writes to var work again

Arithmetic

Idiom Semantics
Good news — we've added another <var>. var += 1
Quick flag: one <var> is now closed. var -= 1
Doubling down on <var>. var *= 2
Let's cut <var> in half. var //= 2
Rolling <a> into <b>. b += a
Backing <a> out of <b>. b -= a
Scaling <b> by <a>. b *= a
Splitting <b> across <a>. b //= a
what's left after splitting <b> across <a> b % a (expression)
somewhere between <a> and <b> a random integer from a to b, both included (expression)

what's left after splitting X across Y being exactly modulo, in unmodified corporate English, is the single best argument for this language existing.

somewhere between 3 and 8 is also how estimates work: you get a whole number in that range, and nobody can tell you in advance which one.

Conditions

Idiom Semantics
we still have <var> var > 0
<var> is at zero var == 0
we're under <n> on <var> var < n
we're over <n> on <var> var > n
we're at <n> on <var> var == n
there's nothing left after splitting <var> across <n> var % n == 0

Control flow

Idiom Semantics
If <cond>: if; the body is the next quote level
That said, if <cond>: else if
That said: else
Per my last email, while <cond>: while
Bumping this. re-send the current message: restart the program, or the person being called, from the top
As discussed, <Name>. call person <Name>
As discussed, <Name>, re: <a>. call <Name> with <a> as the ask
<Name>'s take call <Name> and use their net-net (expression)
<Name>'s take on <a> the same, with <a> as the ask (expression)
Net-net, <a>. end the current person's message and report <a> back
I am currently OOO, returning <date>: error handler for the rest of the block
Resending with the attachment: <file>. import the people quoted in another program
Replying all. / Replying all, re: <a>. call everyone on the Cc: line at once
Going forward, "<phrase>" means "<statement>". define jargon (section 9)

Bumping this. does not remember anything about the first attempt. Variables keep their values, but every statement runs again, including the ones that set them up. Put your setup in the caller and your bump in the person.

I/O

Idiom Semantics
Circling back on <var>. print var (or any expression), then a newline
Circling back on "<literal>". print literal, filling in [placeholders]
+Leadership for visibility. flush stdout
Thoughts? / Please advise. read a value into the last-mentioned variable
Let me spell <a> out. / Let me spell out <a>. print the character with code a, no newline
Reading between the lines. read one character's code into the last-mentioned variable, or -1 at the end of input

Literals work like a mail merge. Each [placeholder] is filled from the variable of that name, and one that doesn't match any variable goes out exactly as typed:

Circling back on "Hi [First Name], we have [reqs] open reqs".

With reqs at 3 and no variable called name, that prints Hi [First Name], we have 3 open reqs.

Let me spell it out. is the passive-aggressive way to print one character. Together with Reading between the lines. it is enough for a cat program and a Brainfuck interpreter, both in section 12.

Meta and pragmas

Idiom Semantics
Best, / Thanks, / Cheers, / Warm regards, / Kind regards, / Best regards, / Many thanks, / Sincerely, HALT, exit 0
Regards, HALT, exit 1
Sent from my iPhone disable all optimizations (there aren't any) and turn on autocorrect
Sorry for the delay! sleep 1s
Thanks in advance. warn if the previous Thoughts? didn't get a number back
Hope you're well., Thanks! and other pleasantries nothing, unless HR is cc'd (section 10)
Blocking <n> minutes for this. (or hours) start a timebox
FYI … a comment; the whole line is ignored

A bare Regards, is not a happy sign-off, and anyone who has received one knows it. It halts with exit code 1. Warm regards, is fine. Only the original email's sign-off sets the exit code. When Dave signs off with a bare Regards,, the call returns normally and nobody mentions it.

Thanks in advance. is coercive but not binding, so it is a warning, not an assert, and there is no way to turn the warning off.

With Sent from my iPhone in the message, a line that doesn't parse gets a second chance. Misspelled words are matched against the language's own vocabulary, one word at a time before several at once, so a name you made up only changes if nothing else works. Every fix prints a warning, such as autocorrected `Circlign` to `circling` .

Blocking 30 minutes for this. starts a timebox. Every statement after it costs one second of the meeting, and so does every loop check, which means 30 minutes buys 1,800 statements. Once time is up the program stops with we're over time. An out-of-office can't save it, because the auto-reply is over time too. Everyone on a reply-all shares the same clock.


4. People are functions

A person becomes callable by being defined in the quoted thread below the signature. Their name is the first name in the On …, <Name> wrote: line. Calling is As discussed, Dave.

There is one shared set of variables. Dave reads and changes the same variables the caller does, unless someone takes the conversation offline. Values can also travel on purpose: the ask goes in and a net-net comes back out (both below).

As discussed, Dave.

Best,
Ihor

On Mon, 7 Sep 2026, Dave Okonkwo <dave@…> wrote:
> Hi Ihor,
>
> Circling back on "the deck is attached".
>
> Best,
> Dave

When Dave signs off, control returns to the caller. A person who never signs off is a parse error, because the thread is still open.

A person can call themselves. After 1000 nested calls the interpreter gives up and suggests a meeting.

Net-net

Net-net, <a>. ends the person's message on the spot and reports <a> back. To use that value, ask for their take:

Just to level-set, budget is Priya's take.
Circling back on Dave's take on budget.

A take calls the person and evaluates to their net-net. Signing off without one is an error when someone asked for a take. As discussed, Dave. still works on a person who gives a net-net; the net-net ends Dave's message early and the value goes nowhere. A net-net in the original email is an error, because there is nobody to report back to.

The ask

As discussed, Dave, re: <a>. and Dave's take on <a> both pass Dave one value, which Dave reads as the ask. It is a copy, so Dave can double it without touching anything of yours.

Passing an argument takes the call offline. Dave gets a private scope that holds the ask, and anything Dave declares with Just to level-set stays in it. A plain As discussed, Dave. shares everything, as before.

Private variables are what make recursion possible. This is examples/forecast.rgrd, which prints 3628800:

Subject: Re: 10-year forecast — can Dave run the numbers?

Hi Dave,

Circling back on Dave's take on 10.

Best,
Ihor

On Mon, 7 Sep 2026, Dave Okonkwo <dave@example.com> wrote:
> Hi Ihor,
>
> If the ask is at zero:
> > Net-net, 1.
>
> Just to level-set, prior year is the ask.
> Quick flag: one year is now closed.
> Just to level-set, forecast is Dave's take on prior year.
> Scaling forecast by the ask.
> Net-net, forecast.
>
> Best,
> Dave

Taking it offline

Happy to take this offline. makes the rest of its block private. Anything declared after it with Just to level-set disappears when the block ends, and so does anything created by assigning to a name nobody had declared. Variables that already existed are still shared: Good news — we've added another req. inside the block changes the same req as outside.

People never see the caller's offline variables, only the shared ones.


5. Out of office

Just to level-set, we have 12 open reqs.
Just to level-set, we have 0 teams.

I am currently OOO, returning Monday:
> Circling back on "no teams yet, so no split. Back Monday".

Splitting reqs across teams.
Circling back on reqs.

I am currently OOO, returning <date>: (or I'm currently OOO) covers the rest of the block it sits in. When a statement after it fails at runtime, even deep inside a person it called, the reply quoted underneath runs in its place and the program continues after that block. The date is for whoever reads the program. The interpreter ignores it.

Nothing before the auto-reply is covered, and an error inside the auto-reply itself goes straight through. Parse errors in the program itself are never caught, since the program hasn't started yet when they happen. A broken attachment is different (see below). Net-net and Bumping this. pass through untouched, because they are not errors.


6. Attachments

Resending with the attachment: finance.rgrd. reads another program and makes everyone quoted in its thread callable from then on. The attachment's own body never runs. File names are relative to the program you ran, and anyone in the attachment with the same name as someone already on the thread replaces them.

Subject: Fwd: budget owner

Hi Dave,

Resending with the attachment: finance.rgrd.

Just to level-set, budget is Priya's take.
Circling back on budget.

Best,
Ihor

This prints 250, which is Priya's net-net in examples/finance.rgrd. If the file is missing or doesn't parse, that is an error at the line that asked for it, so an out-of-office can catch it.


7. Reply-all

A Cc: line above the greeting lists people by name. Replying all. calls all of them at once, each on its own Python thread (the other kind), and waits for every one to sign off before moving on. Replying all, re: <a>. gives each of them the same the ask.

Subject: Q4 planning — please add your numbers
Cc: Dave Okonkwo <dave@example.com>, Priya Raman <priya@example.com>

Replies share variables and nothing locks them. If two replies change the same variable, updates get lost, which is both the easiest implementation and the accurate one. In one run where Dave and Priya each added 1 to a shared counter 20,000 times, the counter finished at 23,187. Lines printed by different people can also arrive in any order.

When a reply fails, the program stops with that error after the other replies finish, unless an out-of-office around Replying all. catches it. A quoted message can carry its own Cc: line, which is the list for any Replying all. in that message. HR is never called, because HR is only cc'd for visibility (section 10).


8. The backlog

Idiom Semantics
Adding <a> to the backlog. add a to the backlog
Picking up the next backlog item as <var>. take the oldest item
Picking up the most urgent backlog item as <var>. take the newest item
we still have a backlog the backlog isn't empty (condition)
the backlog is empty the backlog is empty (condition)
the size of the backlog how many items it holds (expression)

The next item is the one that has waited longest. The most urgent one is whatever came in last, which is how urgency usually works. Picking up from an empty backlog is an error.

A word in front names a separate backlog: Adding 5 to the design backlog. and we still have a design backlog. Backlogs belong to the whole thread. Offline scopes don't hide them, and every reply to a reply-all sees the same ones.


9. Jargon

Going forward, "let's action [ticket]" means "Adding [ticket] to the backlog".

Let's action 3.
Let's action 5.

Going forward, "<phrase>" means "<statement>". teaches the thread a new phrase. Before anything runs, every later line that matches the phrase is replaced with the statement, including lines in the quoted thread below. Lines above the definition keep their old meaning, since it only applies going forward.

A word in [brackets] matches anything and carries it across. When the statement doesn't end in punctuation, it takes the phrase's, so Let's action 3. becomes Adding 3 to the backlog. Jargon can be defined in terms of other jargon, and it can take over a built-in phrase, sign-offs included. Jargon that keeps expanding into itself is an error.


10. When HR is cc'd

With HR on the Cc: line, the email has to stay professional. Between a fifth and a third of its statements, counting the ones inside blocks, must be pleasantries:

  • Hope you're well. or Hope you are doing well.
  • Hope this helps. or Hope you had a great weekend.
  • Thanks!, Thank you., Appreciate it. or Much appreciated.
  • Thanks in advance. or Sorry for the delay!

Too few is this email is too curt, and too many is this email is sycophantic. Both are errors before anything runs. FYI lines and jargon definitions don't count either way. The rule comes from INTERCAL, which rejects a program that doesn't say PLEASE often enough, and also one that says it too often.

Without HR, pleasantries do nothing and nobody is counting. Replying all. never calls HR, because HR never replies.


11. Saved emails

python3 regards.py examples/budget.eml

A program can be a real email saved as an .eml file, which is what Gmail's "Download message" gives you. Email encodings are decoded first, and the Subject: and Cc: headers count, so reply-all and HR politeness work as usual.

The plain-text version of the email is used when there is one. An HTML-only email is flattened to text, and its quoted replies (<blockquote>, which is how Gmail quotes) become > quoting, so the thread below the signature still defines people.

Resending with the attachment: finance.rgrd. looks inside the email before it looks on disk. If a file called finance.rgrd is attached, that is the one it reads, so a thread with its attachments is self-contained. examples/budget.eml is one of those: it attaches finance.rgrd, cc's HR, and prints 250.

Outlook doesn't quote replies with >. It stacks earlier messages under From:, Sent:, To: and Subject: lines instead, and that works too, in any program: each header block counts as On …, <Name> wrote:, and the message under it counts as quoted. Forwarded messages work the same way. The sender can be written Dave Okonkwo <dave@example.com>, Okonkwo, Dave or just dave.okonkwo@example.com, and in every case the person is Dave. A Cc: line in the block is the cc list for that message.


12. Examples

Hello World

Subject: quick one

Hi team,

Circling back on "Hello, World".

Best,
Ihor

FizzBuzz

Subject: Sprint cadence — recurring invite

Hi all,

Just to level-set, we have 1 sprint.

Per my last email, while we're under 101 on sprint:
> If there's nothing left after splitting sprint across 15:
> > Circling back on "retro + all-hands".
> That said, if there's nothing left after splitting sprint across 3:
> > Circling back on "retro".
> That said, if there's nothing left after splitting sprint across 5:
> > Circling back on "all-hands".
> That said:
> > Circling back on sprint.
> Good news — we've added another sprint.

+Leadership for visibility.

Best,
Ihor

Sent from my iPhone

Factorial of 5

Subject: Re: Re: Fwd: modelling assumptions (was: quick q)

Hi Priya,

Just to level-set, runway is 1.
Just to level-set, we have 5 quarters.

Per my last email, while we still have quarters:
> Scaling runway by quarters.
> Quick flag: one quarter is now closed.

Circling back on runway.
+Leadership for visibility.

Best,
Ihor

Async standup

A backlog, jargon, a hiring freeze, a pronoun and a mail merge, with HR watching. It prints 4 engineers, 16 points this sprint.

Subject: Async standup — no call today
Cc: HR <hr@example.com>

Hi team,

Hope you're well.
FYI, this replaces the 9am call.
Going forward, "let's action [ticket]" means "Adding [ticket] to the backlog".

Just to level-set, we have 4 engineers.
We have a hiring freeze on engineers.
Good news — we've added another engineer.

Let's action 3.
Let's action 5.
Let's action 8.

Just to level-set, points is 0.
Per my last email, while we still have a backlog:
> Picking up the next backlog item as ticket.
> Rolling it into points.

Circling back on "[engineers] engineers, [points] points this sprint".
Thanks!
Hope this helps.

Best,
Ihor

Truth-machine

The esolangs truth-machine: read a number, print 0 once, or print 1 forever. Here "forever" is Dave bumping the thread. Put Blocking 1 minute for this. under the greeting and forever lasts 28 ones, then we're over time.

Subject: Quick yes/no — need an answer

Hi Dave,

Just to level-set, answer is 0.
Please advise.
Thanks in advance.

If answer is at zero:
> Circling back on answer.
That said:
> As discussed, Dave.

Best,
Ihor

On Tue, 8 Sep 2026, Dave Okonkwo <dave@example.com> wrote:
> Hi Ihor,
>
> Circling back on answer.
> Bumping this.
>
> Best,
> Dave

99 unread emails

99 bottles of beer, adapted for the office. It starts with 99 unread emails in the inbox, 99 unread emails. and ends at inbox zero, which never lasts.

Subject: Re: Re: Re: inbox zero initiative

Hi all,

Just to level-set, we have 99 unread emails.

Per my last email, while we still have unread emails:
> If we're over 1 on emails:
> > Circling back on "[emails] unread emails in the inbox, [emails] unread emails.".
> That said:
> > Circling back on "1 unread email in the inbox, 1 unread email.".
> Quick flag: one email is now closed.
> If we're over 1 on it:
> > Circling back on "Archive one, mark it as read, [emails] unread emails in the inbox.".
> That said, if we're over 0 on it:
> > Circling back on "Archive one, mark it as read, 1 unread email in the inbox.".
> That said:
> > Circling back on "Archive one, mark it as read, inbox zero.".
> Circling back on "".

Circling back on "No unread emails in the inbox, no unread emails.".
Circling back on "Check again, and there are 99 unread emails in the inbox.".

Best,
Ihor

Planning poker

Each story in the backlog gets a random estimate. The round is offline, so story and estimate vanish after each vote, while total keeps adding up. One run printed:

Story 101: 8 points
Story 102: 12 points
Story 103: 6 points
Total: 26 points, give or take
Subject: Planning poker — async, please vote by EOD

Hi team,

Adding 101 to the backlog.
Adding 102 to the backlog.
Adding 103 to the backlog.

Just to level-set, total is 0.

Per my last email, while we still have a backlog:
> Happy to take this offline.
> Picking up the next backlog item as story.
> Just to level-set, estimate is somewhere between 1 and 13.
> Circling back on "Story [story]: [estimate] points".
> Rolling estimate into total.

Circling back on "Total: [total] points, give or take".

Best,
Ihor

Sent from my iPhone

Five typos, and it still prints 4 and then all reqs filled, with a warning for every fix.

Subject: Re: quick update on hiring

Hi Priya,

Just to levl-set, we have 3 open reqs.
Good news — we've addded another req.
Circlign back on reqs.

Per my last emaill, while we still have reqs:
> Quick flag: one req is now closedd.

Circling back on "all reqs filled".

Best,
Ihor

Sent from my iPhone

Cat program

Everything that comes in goes back out, exactly as received.

Subject: Fwd: Fwd: Fwd: FW: read this

Hi all,

FYI, forwarding this exactly as I received it.

Just to level-set, letter is 0.
Reading between the lines.

Per my last email, while we're over -1 on letter:
> Let me spell it out.
> Reading between the lines.

Best,
Ihor

Brainfuck interpreter

Send it a Brainfuck program, then a !, then whatever the program should read:

printf '%s!' '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.' \
  | python3 regards.py examples/brainfuck.rgrd

The program sits in two backlogs used as stacks, one for what's ahead and one for what's behind, and the tape is two more. Cells wrap at 256, and reading past the end of the input gives 0.

Subject: Re: Fwd: Brainfuck — can someone from IT take a look?

Hi IT,

FYI, send the Brainfuck program first, then a !, then whatever it should read.
FYI, the program sits in two backlogs, what's ahead and what's behind. So does the tape.

Just to level-set, letter is 0.
Reading between the lines.
Per my last email, while we're over -1 on letter:
> If we're at 33 on letter:
> > Per the attached, letter is now -1.
> That said:
> > Adding letter to the intake backlog.
> > Reading between the lines.

Per my last email, while we still have an intake backlog:
> Picking up the most urgent intake backlog item as instruction.
> Adding instruction to the ahead backlog.

Just to level-set, cell is 0.

Per my last email, while we still have an ahead backlog:
> Picking up the most urgent ahead backlog item as instruction.
> Adding instruction to the behind backlog.
> If we're at 43 on instruction:
> > Good news — we've added another cell.
> > If we're at 256 on cell:
> > > cell is off the table.
> That said, if we're at 45 on instruction:
> > Quick flag: one cell is now closed.
> > If we're at -1 on cell:
> > > Per the attached, cell is now 255.
> That said, if we're at 62 on instruction:
> > Adding cell to the left backlog.
> > cell is off the table.
> > If we still have a right backlog:
> > > Picking up the most urgent right backlog item as cell.
> That said, if we're at 60 on instruction:
> > Adding cell to the right backlog.
> > cell is off the table.
> > If we still have a left backlog:
> > > Picking up the most urgent left backlog item as cell.
> That said, if we're at 46 on instruction:
> > Let me spell cell out.
> That said, if we're at 44 on instruction:
> > cell is off the table.
> > Reading between the lines.
> > If we're at -1 on cell:
> > > cell is off the table.
> That said, if we're at 91 on instruction:
> > If cell is at zero:
> > > Just to level-set, we have 1 layers.
> > > Per my last email, while we still have layers:
> > > > Picking up the most urgent ahead backlog item as instruction.
> > > > Adding instruction to the behind backlog.
> > > > If we're at 91 on instruction:
> > > > > Good news — we've added another layer.
> > > > That said, if we're at 93 on instruction:
> > > > > Quick flag: one layer is now closed.
> That said, if we're at 93 on instruction:
> > If we still have cell:
> > > Picking up the most urgent behind backlog item as instruction.
> > > Adding instruction to the ahead backlog.
> > > Just to level-set, we have 1 layers.
> > > Per my last email, while we still have layers:
> > > > Picking up the most urgent behind backlog item as instruction.
> > > > Adding instruction to the ahead backlog.
> > > > If we're at 93 on instruction:
> > > > > Good news — we've added another layer.
> > > > That said, if we're at 91 on instruction:
> > > > > Quick flag: one layer is now closed.

Best,
IT

13. Errors

Diagnostics are written in register. An error that no out-of-office catches stops the program with exit code 2.

warning: `Sent from my iPhone` present; optimizations disabled, autocorrect on.

warning: autocorrected `Circlign` to `circling` (line 6)

error: Dave isn't on this thread. Happy to resend (line 3)

error: nobody told me about `budget`. Can you send it over? (line 3)

error: `Let's synergize.` isn't something I can action (line 3)

error: unreachable code after `Best,`. (line 8)
       Nothing below a sign-off is read. This is true of email generally.

error: splitting across zero. That's not a realistic plan (line 5)

error: this thread has too many replies. Let's set up a meeting (line 4)

error: Dave signed off without a net-net. What's the takeaway? (line 4)

error: `Net-net` in the original email. There's nobody to report back to (line 6)

error: the attachment `finance.rgrd` didn't come through. Can you resend? (line 5)

error: nobody is cc'd. Reply-all to whom? (line 7)

error: HR is cc'd and this email is too curt. Maybe open with `Hope you're well.` (line 2)

error: the backlog is empty. Nothing to pick up, so enjoy the quiet sprint (line 9)

error: we're over time. Let's continue next week (line 12)

error: somewhere between 8 and 3 isn't a range. Did you mean between 3 and 8? (line 5)

error: not sure what `it` refers to. Can you be more specific? (line 5)

error: this jargon goes in circles. Can someone say it in plain English? (line 7)

error: -1 isn't a letter, so there's nothing to spell out (line 5)

error: reading between the lines of nothing in particular (line 5)

error: this email has no text in it. Was it all screenshots?

error: no sign-off. The thread is still open.

The last one has no line number, because the problem is everything after the last line.


14. Computational class

Regards, is Turing complete, because it can simulate a two-counter Minsky machine, and those are known to be Turing complete.

Integers have no upper limit, so two variables can hold the counters. Good news — we've added another increments one and Quick flag: one … is now closed decrements it. A third variable holds the machine's current state. The program is a single Per my last email, while loop with an If / That said, if branch for each state, and a state that jumps on zero tests its counter with is at zero before setting the next state.

The Brainfuck interpreter in section 12 is a more direct demonstration: Brainfuck with an unbounded tape is Turing complete, and the backlogs holding the tape have no size limit.


15. Design decisions

Settled

  • Per my last email is a while loop. The faithful unconditional backward jump already exists as Bumping this., and control flow gets one idiom per job. Sign-offs are another matter, because email has a lot of ways to say goodbye.
  • Bumping this. re-sends the current message. It restarts the program, or the person being called, from the top. It does not target labels, because email has none.
  • Thanks in advance. is a warning that cannot be suppressed.
  • Sign-offs carry exit codes. Bare Regards, exits 1. Everything warmer exits 0.
  • Passing an argument takes the call offline. Recursion needs private variables, and a plain call keeps sharing everything, so programs written before re: existed still work.
  • Reply-all has no locking. Replies share variables and updates can be lost. Nobody coordinates a real reply-all either.
  • Sent from my iPhone turns on autocorrect. A tree-walking interpreter has no optimizations to disable, and excusing typos is what that signature is for anyway.
  • Politeness is only checked when HR is cc'd. Otherwise every two-line email would need a pleasantry, and nobody writes those unless someone is watching.
  • we're at joined we're under and we're over. Writing the Brainfuck interpreter showed that the language had no equality test, and the phrase for one was already there.

Still open

Nothing right now. Suggestions are welcome, ideally without a meeting.


16. Implementation

The reference interpreter is a tree-walking interpreter written in Python. It needs Python 3.9 or newer and nothing else.

python3 regards.py examples/fizzbuzz.rgrd      # run a program
python3 regards.py examples/budget.eml         # run a saved email
python3 regards.py --version                   # prints the version
pip install regards                            # installs the same thing as a `regards` command
python3 -m unittest discover -s tests          # run the tests

Exit codes: 0 for a warm sign-off, 1 for a bare Regards,, 2 for an error in the program, 3 for a bug in the interpreter itself.

regards/
  README.md              this file
  CHANGELOG.md           what's in each version
  CONTRIBUTING.md        how to change the language without breaking anyone's email
  LICENSE                CC0 1.0
  regards.py             lexer, parser, interpreter
  examples/
    hello.rgrd
    countdown.rgrd
    fizzbuzz.rgrd
    factorial.rgrd
    dave.rgrd            calling a person from the quoted thread
    forecast.rgrd        recursion with a net-net and the ask
    ooo.rgrd             an auto-reply catching a division by zero
    attachment.rgrd      calling Priya from finance.rgrd
    finance.rgrd
    reply_all.rgrd       Dave and Priya replying at once
    standup.rgrd         backlog, jargon, a freeze and a mail merge, with HR on cc
    truth_machine.rgrd   read a number, then bump the thread forever
    unread.rgrd          99 bottles of beer, as 99 unread emails
    planning_poker.rgrd  random estimates for a backlog of stories
    typos.rgrd           five typos, fixed by Sent from my iPhone
    cat.rgrd             a cat program: input goes straight back out
    brainfuck.rgrd       a Brainfuck interpreter
    budget.eml           a saved email with an attachment, cc'ing HR
  tests/
    test_regards.py

A saved .eml file is turned into plain text first, with Python's own email package. The lexer then works line by line. It counts the leading > markers to get the quote depth and matches the rest of the line against the idiom table, after dropping FYI lines and expanding jargon. Quote depth then becomes a tree of blocks, much as Python turns indentation into INDENT and DEDENT tokens, and the interpreter walks that tree. Variables live in a stack of scopes, with the shared ones at the bottom and any offline ones above. Each reply to a reply-all runs on its own Python thread.

The parser is the interesting part, and it is small. The idiom table is where the work is, and the idiom table is also the joke, so the work and the joke are the same work.


17. License

Everything in this repository, including this specification, is dedicated to the public domain under CC0 1.0. Feel free to reply-all.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

regards-1.0.1.tar.gz (58.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

regards-1.0.1-py3-none-any.whl (28.4 kB view details)

Uploaded Python 3

File details

Details for the file regards-1.0.1.tar.gz.

File metadata

  • Download URL: regards-1.0.1.tar.gz
  • Upload date:
  • Size: 58.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for regards-1.0.1.tar.gz
Algorithm Hash digest
SHA256 93b5e5a3bf79b9121747e51c3bc044e9cfb257fbcae139fb9b62e0f4454b27ba
MD5 7713114da62ad90e0b7cc7d9062229d7
BLAKE2b-256 1aaf20b71530bc6684d78cb114eea4c56cf4d6998c703cb55fb0570d75b2772a

See more details on using hashes here.

Provenance

The following attestation bundles were made for regards-1.0.1.tar.gz:

Publisher: publish.yml on KvashaIhor/regards

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file regards-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: regards-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 28.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for regards-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7281817429733b86669b2af253a450e7bfd90789291f5184f05140263ccdb63c
MD5 effbcd6982c692b075f81bb2f3d10aa3
BLAKE2b-256 24c58ae8a7ffe89799340897eb4c463ecf1ea8bd6a0115f209db3c7a14ea53d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for regards-1.0.1-py3-none-any.whl:

Publisher: publish.yml on KvashaIhor/regards

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page