Note: this article originally appeared on http://brhefele.brainaxle.com. Links may still point there, formatting may be off, this may all get fixed.
I use dc as my primary calculator for day-to-day needs. I use other calculators as well, but I try to largely stick to dc for two reasons – I was raised on postfix (HP 41CX, to be exact) and I’m pretty much guaranteed to find dc on any *nix machine I happen to come across. Recently, however, I’ve been expanding my horizons, experimenting with dc as a programming environment, something safe and comfortable to use as a mental exercise. All of that is another post for another day, however – right now I want to discuss writing a dc syntax definition for vim.
The first amazing thing, to me, was that there was not already a syntax definition for dc! I have thoughts on the matter – dc is a fairly obscure, archaic tool that I’m sure is rarely used; those who do use it likely use it in interactive mode rather than writing code for it; languages like Python (etc.) provide great platforms for math coding as well as interactive use. Yet there are syntax definitions for far more obscure languages (LOLCODE), and dc is perfectly suited for quick math scripts. But I digress, I don’t really care that nobody has come up with a syntax definition for dc, it just surprises me. So I figured I’d roll my own. Not a difficult task, but a few things made it easier for me…
Vim has wonderful documentation, but it’s almost too straightforward, lacking in examples, etc. I find it easier to start with a little tutorial – like this one – to wrap my head around what I’m getting into. So that was a good start, but I quickly realized a problem – syntax keywords are expected to be just that – words. dc largely ignores whitespace, and treating every command as a word (adding a space between each command) would nearly double the amount of typing necessary. At this point I realized I wouldn’t be able to use any syntax keywords, and would instead need to do everything through regex.
Sometimes it helps to compare your strategy to that of an existing, similar product. To that end, I decided to check out Mathias Panzenböck’s syntax definition for brainfuck, another language in which whitespace between commands is unimportant. The brainfuck definition, unsurprisingly, was all done through regex. So, new problem, Vim’s regex support is weird at best – determining what does/doesn’t need escaping is a complete crapshoot. Fortunately, at some point, I read about Vim’s very magic mode. So that makes things pretty easy. Then I came to the point where I had to figure out how to handle strings.
Because macros in dc are stored in strings, and are in fact completely indistinguishable from other strings, a problem arises. I want my macros to be what Vim calls ‘transparent’ – that is, the commands inside them should be properly highlighted. Strings, on the other hand, should be their own highlight, all one color (and with spellchecking enabled, for my uses). First I had to make some rules for myself – any string followed immediately by an execute command is obviously a macro, but that’s not the only case. So, I looked at my own code and realized that whenever I’m writing a macro, I give it its own line – just the macro and its save command. So, while this is meaningless to dc, it’s meaningful to me, and I needed something meaningful to apply.
The final trick was in getting this to work. If I have a string defined as a string, and I have this arbitrary scenario defined as a ‘macro,’ and I want my macro to be transparent, well, all that’s going to show through is the string! Then we’re back to square one… The trick I used, and I’m not sure if there’s an easier way (and I’m not sure if there needs to be – this is probably a rather rare situation) was to set the contains parameter of my macro to NONE, telling it that it contains no other highlights, and then manually adding every other highlight except for string. I was also able to leave out comment and shell command, since they eat up that follows until newline.