Emacs tutorial notes - Part 2 17 November 2003 Mike Daniels Conventions: C-x means hold down the control key and press x S-x means hold down the shift key and press x M-X means hold down the meta key and press x OR press Escape, and then press x Note that C-S-x and C-X are the same thing. ================== Scripting in Emacs ================== Emacs's scripting language is called elisp, for "emacs lisp". It is based on lisp, one of the earliest programming languages still in (reasonably) common use today. (Initial implementation of lisp began in 1958; to compare, prolog dates from 1972, as does C. Fortran is older still, with its first manual published in 1956.) Lisp and prolog are together considered the AI languages; lisp being slightly favored in the American tradition, prolog in the European. _LIS_t _P_rocessing language (or Lots of Irritating Superfluous Parentheses -- take your pick) Fun fact: lisp was the first language to have conditional (i.e. if-then-else statements). (Fortran had only a computed goto; the concept was subsequently incorporated in algol, and from there to most other languages.) Lisp originally had a syntax -- M-expressions -- which were translated into S-expressions that the computer could work with. Over time, people working with lisp decided that the S-expressions were all they needed. In the ontology of programming languages, lisp is a weakly-typed functional programming language. ========== Basic Math ========== People generally do not write emacs scripts to compute factorials. But it remains a decent way to explain the basics of how functions/programs are structured: (defun factorial (x) (cond ((eq x 1) 1) (t (* x (factorial (- x 1)))))) As we all know, factorial is a function from numbers to numbers such that, e.g. factorial(4) = 4! = 4 * 3 * 2 * 1 = 24. You see a number of forms in the above: (* {val1} {val2}) evaluates to the product of val1 and val2 (- {val1} {val2}) evaluates to the difference of val1 and val2 (cond ({test1} {val1}) ({test2} {val2}) ... (t {val3})) evaluates test1, which either succeeds or fails if it succeeds, the expression evaluates to {val1} otherwise, evaluate test2, etc. Good practice has the final test be "t" which is always true; thus the corresponding value acts as a failsafe or default value. (= x 1) evaluates to t if x = 1, and to nil otherwise. Best used with numbers. Should generally NOT be used with strings. (defun {name} {params} {body}) binds name to the function taking params and evaluating to body So we can trace the execution of a simple program through a substitution model: (factorial 2) (cond ((eq 2 1) 1) (t (* 2 (factorial (- 2 1))))) --(eq 2 1) evaluates to false-- (* 2 (factorial (- 2 1))) (* 2 (factorial 1)) (* 2 (cond ((eq 1 1) 1) (t (* 1 (factorial (- 1 1)))))) --(eq 1 1) evaluates to true-- (* 2 1) 2 =============== Main Data Types =============== integer float cons a pair of values string "this is a string" "this is \"quote-containing\" string" etc. ====================== Common primitive forms ====================== ----- Tests ----- (= A B) (/= A B) -- not equal (< A B) (<= A B) -- (different from prolog!) (> A B) (>= A B) ------- Numeric ------- (max A B C ...) (min A B C ...) (abs A) (1+ A) (1- A) (+ A B C ...) (- A B) (- A B C ...) == (- A (+ B C ...)) (* A B C ...) (/ A B) (/ A B C ...) == (/ A (* B C ...)) (/ 25 3 2) => 4 (% A B) -- remainder ------- Strings ------- (substring string start [end]) (substring "abcdefg" 0 3) => "abc" (substring "abcdefg" -3 -1) => "ef" (concat A B C ...) (split-string string separators) (split-string "This is a test." " ") => ("This" "is" "a" "test.") (string= "abs" "abs") => t (string< "abc" "abd") => t (format "format string %d" 1) => "format string 1" (downcase "this IS a Test") => "this is a test" (upcase "this IS a Test") => "THIS IS A TEST" (capitalize "this IS a Test") => "This Is A Test" (upcase-initials "this IS a Test") => "This IS A Test" ----------------- List Manipulation ----------------- (car X) [contents of the address register] the first value of the pair X = the head of the list X (cdr X) [contents of the decrement register] the second value of the pair X = the tail of the list X (cons A B) [construct] creates a pair with A as the head and B as the tail. (null A) is A the null list? (pop A) returns (car A) and mutates A, removing its head (nth n A) returns the nth element of A (nthcdr n A) returns A after the nth element (nthcdr 1 '(1 2 3 4)) => (2 3 4) (last A) returns last element of A (length A) returns length of A (butlast A) (butlast A n) returns A without the last (last n) elements (push newelem A) (append A B C ...) (append '(1 2) '(3 4)) => (1 2 3 4) ------------ Control flow ------------ (progn A B C ...) do A, then do B, then do C, etc. (if condition then else) (when condition A B C...) do A, B, and C if condition (unless condition A B C ...) do A, B, and C if not condition (not condition) (and A B C ...) (or A B C ...) (while condition A B C...) ----------- Interactive ----------- (interactive) - takes no args (interactive "p") - takes one numeric argument from prefix (interactive "nCount:") - takes one numeric argument from minibuffer (interactive "MLine:") - takes arbitrary text from the minibuffer ======= Example ======= (defun skt-2prepare-line (line) (interactive "MLine: ") (progn (let* ((linelist (split-string line "|")) (line1 (car linelist)) (line2 (cadr linelist))) (insert "\\begin{hanging}\n") (insert (concat "{\\skt " line1 "}\\\\\n{\\skt " line2 "}\\\\\n{\\skti " line1 "}\\\\\n{\\skti " line2 "}\\\\\n{\\skti " line1 "}\\\\\n{\\skti " line2 "}\\\\\n")) (let ((wordlist (append (split-string line1 " ") (split-string line2 " ")))) (while (not (null wordlist)) (insert (concat "{\\skti " (car wordlist) "} - \\\\\n")) (setq wordlist (cdr wordlist)))) (insert "`'\\\\\n\\end{hanging}\n\\vskip2pt\\hrule\\vskip2pt\n"))))