# Emacs Lisp as a scripting language

I don’t use Emacs as an editor—I subscribe to the belief that it’s a great operating system, lacking only a decent text editor—but I sometimes wish I had easy access to its libraries of functions. It turns out that you can run Elisp (the Lisp interpreter Emacs is built on) programs from outside Emacs by starting the program with this shebang line:

#!/usr/bin/emacs --script


This will make Emacs work like Perl or Python or Ruby or Bash—an interpreter that reads the rest of the program and executes the code. You will, of course, have to give the correct path to the Emacs binary; /usr/bin/emacs is correct for OS X and for every Linux distribution I’ve ever used, so it’s probably right for you.

Lispers may be perplexed by that line, because, unlike Perl, Python, etc., Lisp doesn’t use the sharp symbol for comments. But since version 22, Emacs Lisp has been extended to handle the shebang (sharp-bang, #!) line the way all those other languages do. If you’re using an older version of Emacs, you’ll have use a more arcane top line, as described here

[The #! is magical for reasons that go way back in Unix history. Shebang lines even have their own Wikipedia page.]

One area where Emacs really excels is in the manipulation and conversion of dates. The Emacs calendar functions were written by Edward Reingold and Nachum Dershowitz, two computer scientists who literally wrote the book, as well as several papers, on Calendrical Calculations. I have an earlier edition of the book, which is now available at Google Books. You can also get a PostScript version of their original paper from Reingold’s site.

Reingold and Dershowitz are very good writers and if you read any of their calendrical stuff, you’ll learn not only a lot about calendars, but also how to build complex programs out of very simple pieces.

Here’s the source code of an Elisp program that takes a date in the Gregorian calendar and converts it into several other calendars. The script is very short because of the power of Emacs’ calendar functions.

 1:  #!/usr/bin/emacs --script
2:
3:  (require 'calendar)
4:
5:  ; Use current date if no date is given on the command line
6:  (if (= 3 (length command-line-args-left))
7:   (setq my-date (mapcar 'string-to-int command-line-args-left))
8:   (setq my-date (calendar-current-date)))
9:
10:  ; Make the conversions and print the results
11:  (princ
12:    (concat
13:      "Gregorian:  " (calendar-date-string          my-date)  "\n"
14:      "      ISO:  " (calendar-iso-date-string      my-date)  "\n"
15:      "   Julian:  " (calendar-julian-date-string   my-date)  "\n"
16:      "   Hebrew:  " (calendar-hebrew-date-string   my-date)  "\n"
17:      "  Islamic:  " (calendar-islamic-date-string  my-date)  "\n"
18:      "  Chinese:  " (calendar-chinese-date-string  my-date)  "\n"
19:      "    Mayan:  " (calendar-mayan-date-string    my-date)  "\n" ))


Lisp has a reputation for being hard to read, but it’s not so tough once you understand a few things:

1. Function calls put parentheses in different places from Algol-based languages. You say

(function argument)


function(argument)

2. Arguments are separated by whitespace, not commas. So a function with three arguments is called like this:

(function arg1 arg2 arg3)

3. Everything is a function, even things that you usually think of as operators. So you say

(+ 2 2)


2 + 2

4. Functions return a value, which may then be operated on by an enclosing function. It’s common to see things like this

(func3 (func2 (func1 argument)))

where the functions are applied in 1-2-3 order.

Line 3 of my script imports the Reingold and Dershowitz calendar library. Lines 6-8 handle the command line arguments passed to the program (see below). The built-in Elisp variable command-line-args-left is the list of arguments passed to the script as strings. Lines 14-19 actually do the conversions to the six listed calendars. Line 12 concatenates the date strings, and Line 11 prints them out.

I’ve called this program “date-convert,” and put it in my ~/bin directory, which is in my \$PATH. If it’s called without any arguments, like this

date-convert


it will print out the current date in seven different calendars:

Gregorian:  Tuesday, April 8, 2008
ISO:  Day 2 of week 15 of 2008
Julian:  March 26, 2008
Hebrew:  Nisan 3, 5768
Islamic:  Rabi II 1, 1429
Chinese:  Cycle 78, year 25 (Wu-Zi), month 3 (Bing-Chen), day 3 (Wu-Yin)
Mayan:  Long count = 12.19.15.4.2; tzolkin = 2 Ik; haab = 5 Pop


If it’s called with three numeric parameters—month, day, and year—it will print out that date in those same calendars. So

date-convert 6 6 1945


Gregorian:  Wednesday, June 6, 1945
ISO:  Day 3 of week 23 of 1945
Julian:  May 24, 1945
Hebrew:  Sivan 25, 5705