DEV Community

loading...

emacs regex with emacs lisp

Jean-Christophe Helary
In Japan since '97. Translating and localizing on Mac since 2000. FOSS advocate and user. Super proud that I have some code that's been accepted in Emacs :^)
Originally published at mac4translators.blogspot.com on ・4 min read

(Including a really super short introduction to Lisp. Yes. No kidding :-)

Not strictly related to translation but here is what's happening...

I've resumed studies last year, trying to finish an MA in Japan Studies I started 25 years ago.

For the first year, I only have to write a 30ish pages dissertation on my subject (representation of women in kendo magazines in Japan) and I decided to go the emacs + org-mode way, with the easy export to ODF function that's packaged with the thing.

So I decided to write each chapter in a different org file, and send them one by one to my director. But then, for the final delivery I needed to put all that in one big file and was faced with the fact that all my footnotes would need to be re-indexed manually because each file had notes starting at 1...

I usually use BBedit for any serious regex work. Mostly because the interface is clearer than emacs, and the regexp feels more modern (\d vs [:digit:])

But one thing you can't do in BBEdit is to send commands to the replace string. For ex, in my case, add 21 to the matching number, which seems pretty trivial, when you think of it, but doing that will involve other technologies, like using perl or some other command line thing.

In emacs, however, everything can be interpreted as an expression, hence you can insert code wherever you want and get the result from that code right in the document.

The org-mode footnotes all look like [fn:12], where "12" is the note number that I need to replace with an incremented number. Since there are no instances of fn:\d+ without the brackets that are not footnotes, I figured I could just be searching for that string:

fn:\([:digit:]+\)

Notice that in emacs, "(" and ")" need to be escaped, also I could have used the [0-9] class.

In BBedit I'd just need:

fn:(\d+)

And now I need to replace that with the expression that will add 21 to the number.

In BBEdit, I'd be stuck here. I just can't add anything to a match. In emacs, I can replace the match with that:

fn:\,(+ 21 (string-to-number \1))

The emacs lisp expression is "(+ 21 (string-to-number \1))", which means "convert the \1 match that is a string into its numerical value and add 21 to it".

But, wasn't \1 supposed to match [0-9]+, which is a number? Well, yes, but really it's just digits, hence strings, that have no numerical value whatsoever, so first, we need the expression to convert them to a numerical value before adding 21 to them.

Now, the trick is to have the expression be handled as an operation and not as an arbitrary string, and that's where the "\," prefix comes into play.

"\," tells the replace engine that the string that follows must be interpreted as an emacs lisp expression and not as a mere string. With it, the regexp replaces properly adds 21 to my note numbers, and I get two dozen footnotes updated in one fell swoop...

I love BBEdit and its people, but emacs is really a gift that keeps on giving.


Here are some handy references:

the emacs regexp-replace function
Regexp Replacement
the emacs regexp syntax
Syntax of Regular Expressions
the emacs-lisp string-to-number function
Conversion of Characters and Strings

And here is a really super short introduction to lisp syntax

There is no real need to have a very deep understanding of emacs lisp to use this regexp-replace function. Just remember that a lisp expression generally looks like this:

(operator operands)

Where the operator is a generally a function, like + or string-to-number above, and the operands can be any expression that is accepted by the operator. So, here:

(+ 21 (string-to-number \1))

means:

add 21 to the result of the expression (string-to-number \1)

with (string-to-number \1) meaning:

convert the string matched by \1 to its numeric value

Obviously, if \1 is not a string, the conversion will fail and the addition won't work. And without that conversion, if we had just added \1 as a string, the addition that expects numbers as operands would have failed.

I just realized that this is my first emacs lisp related post ever ! I'd like to thank that person I met in Tokyo about 15 years ago who showed me the way. It's an egg that definitely took some time to hatch...

Discussion (0)