Building the Weir Language

Most large or­ga­ni­za­tions have a style guide. A doc­u­ment that de­cides which ver­sions of a lin­guis­tic rule to use. That could be whether to use the Oxford comma, or if con­trac­tions are al­lowed. It could de­clare that a cer­tain word should be cap­i­tal­ized in a spe­cific con­text.

Harper can cover most of the rules in most style guides, but there will al­ways be out­liers that we can’t sup­port (or sim­ply don’t know about). That is why it is crit­i­cal that Harper al­low in­di­vid­u­als and or­ga­ni­za­tions to de­fine rules and con­ven­tions for Harper to en­force.

For the last few days, I’ve been pro­to­typ­ing a sim­ple (not Turing-complete) pro­gram­ming lan­guage for de­scrib­ing these con­ven­tions. I’ve called it Weir. Weir is sim­ple and thus easy to learn, but has es­cape hatches for in­stances where more com­plex logic is nec­es­sary.

Before I be­gin, a quick dis­claimer: This is early work. The Weir lan­guage will likely change and evolve be­fore it makes it into core. What you see here is rep­re­sen­ta­tive of the over­all de­sign, but noth­ing is fi­nal yet.

History

There are in­evitably go­ing to be a good num­ber of edge cases that need to be ad­dressed when build­ing a gram­mar checker. For these, Harper has a sim­ple map from prob­lem­atic phrases to the cor­rected ver­sions. Edge cases that are not sim­ple enough to be de­scribed with a map are im­ple­mented as Rust code. Here’s what the map looks like:

add_exact_mappings!(group, {
    // The name of the rule
    "ACoupleMore" => (
        // The phrase(s) to look for.
        ["a couple of more"],
        // The corrections to provide.
        ["a couple more"],
        // The message to be shown with the error.
        "The correct wording is `a couple more`, without the `of`.",
        // A description of the rule.
        "Corrects `a couple of more` to `a couple more`.",
        LintKind::Redundancy
    ),
    "CondenseAllThe" => (
        ["all of the"],
        ["all the"],
        "Consider simplifying to `all the`.",
        "Suggests removing `of` in `all of the` for a more concise phrase.",
        LintKind::Redundancy
    ),
    "CoursingThroughVeins" => (
        ["cursing through veins"],
        ["coursing through veins"],
        "In this idiom, blood “courses” (flows) through veins, not “curses”.",
        "In English idioms, “to course” means to flow rapidly—so avoid the eggcorn `cursing through veins.`",
        LintKind::Eggcorn
    ),
});

As for the Rust code, it is of­ten heinous and a chore to re­view. I won’t do my­self the em­bar­rass­ment of in­clud­ing it here.

Introducing Weir

The heart of Weir is an ex­pres­sion lan­guage that mim­ics the pseudocode Harper con­trib­u­tors tend to use when de­scrib­ing the Rust code they in­tend to write.

Imagine you work at Google. You’ve just re­branded the G Suite” col­lec­tion of apps and ser­vices to the new name Google Workspace”. Before that, they were col­lec­tively named Google Apps for Work”. Moving for­ward, you don’t want you or your cowork­ers to ac­ci­den­tally write G Suite” on pub­lic doc­u­men­ta­tion, be­cause do­ing so might con­fuse users. To solve this, you use the fol­low­ing Weir rule:

set main [(G [Suite, Suit]), (Google Apps for Work)]

declare message "Use the updated brand."
declare description "`G Suite` or `Google Apps for Work` is now called `Google Workspace`"
declare kind "Miscellaneous"
declare becomes "Google Workspace"

The first line de­scribes the pat­tern of the prob­lem­atic text. There are two cases here:

  1. The let­ter G” fol­lowed by Suite” or its mis­spelling Suit”
  2. The lit­eral phrase Google Apps for Work”

Here is a se­man­ti­cally equiv­a­lent ex­am­ple that I find a bit eas­ier to read:

set main [(G Suite), (G Suit), (Google Apps for Work)]

The re­main­ing lines de­scribe:

  1. The mes­sage to be shown to the user when the er­ror in en­coun­tered.
  2. A de­scrip­tion of the rule it­self, ex­plain­ing why it ex­ists.
  3. What kind of rule it is. I sus­pect most end users will mark it as miscellaneous”. I’m con­sid­er­ing mak­ing this field op­tional.
  4. What cor­rec­tions to pro­vide to the user.

One of my goals here was to make the sys­tem sim­ple enough for an end-user to copy and paste an ex­ist­ing rule into a new file, make some tweaks, and they would be done. For the more com­plex cases, I think we could set up a cus­tom GPT to write these pretty eas­ily.

Here’s an­other ex­am­ple, one that will ac­tu­ally be a part of the Harper source code. It uses a fil­ter (the <> syn­tax) to first se­lect the broader phrase, then an­other to se­lect the white­space in-be­tween (that’s the ( ) part).

set main <([right, middle, left] $click), ( )>
declare message "Hyphenate this mouse command"
declare description "Hyphenates right-click style mouse commands."
declare kind "Punctuation"
declare becomes "-"

This is a great ex­am­ple of the $ tag. In­stead of in­struct­ing Harper to look for the lit­eral word af­ter­ward, it in­stead in­structs it to look for all de­riv­a­tive words. Those in­clude click”, clicking”, clicked”, etc.

We also have spe­cific syn­tax that can be used to test the ex­pres­sion in­line. These as­sert that the left string, af­ter be­ing passed through the rule, be­comes the right string.

test "Right click the icon." "Right-click the icon."
test "Please right click on the link." "Please right-click on the link."
test "They right clicked the submit button." "They right-clicked the submit button."
test "Right clicking the item highlights it." "Right-clicking the item highlights it."
test "Right clicks are tracked in the log." "Right-clicks are tracked in the log."
test "He RIGHT CLICKED the file." "He RIGHT-CLICKED the file."
test "Left click the checkbox." "Left-click the checkbox."
test "Middle click to open in a new tab." "Middle-click to open in a new tab."

I sus­pect Weir will make it ex­tremely easy for AI agents to it­er­ate quickly on these rules. There’s no com­pi­la­tion step, and we al­ready have ex­cel­lent pipelines for gen­er­at­ing lists of test cases.

Why Call It Weir”?

I am nam­ing Weir af­ter the au­thor of Project Hail Mary, Andy Weir. I guess I just like nam­ing things af­ter au­thors. Af­ter all, Harper it­self was named af­ter Harper Lee.

Moving Forward

Once the syn­tax is fi­nal­ized and a cor­pus of tests have been writ­ten, I sus­pect Weir rules will be­come the de­fault for new gram­mat­i­cal rules in­side the Harper repos­i­tory and for in­di­vid­ual users. I’d love to cre­ate a mar­ket­place for rule sets cre­ated by and for the com­mu­nity.

If you (the reader) have any thoughts on syn­tax or any­thing else, don’t hes­i­tate to speak your mind.

Published December 11, 2025 at 7:00 AM

Proofread by Harper.

Comments