Update.
[elisp.git] / arithmlatex.el
1 ;; -*- mode: emacs-lisp -*-
2
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; This program is free software; you can redistribute it and/or         ;;
5 ;; modify it under the terms of the GNU General Public License as        ;;
6 ;; published by the Free Software Foundation; either version 3, or (at   ;;
7 ;; your option) any later version.                                       ;;
8 ;;                                                                       ;;
9 ;; This program is distributed in the hope that it will be useful, but   ;;
10 ;; WITHOUT ANY WARRANTY; without even the implied warranty of            ;;
11 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      ;;
12 ;; General Public License for more details.                              ;;
13 ;;                                                                       ;;
14 ;; You should have received a copy of the GNU General Public License     ;;
15 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.  ;;
16 ;;                                                                       ;;
17 ;; Written by and Copyright (C) Francois Fleuret                         ;;
18 ;; Contact <francois@fleuret.org> for comments & bug reports             ;;
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20
21 ;; This short scripts performs arithmetic computations in a latex
22 ;; file. It collects variables defined by \fflet{VARNAME}{VALUE}
23 ;; expressions in the latex file, and updates the VALUE in the
24 ;; expressions of the form \ffeval{EXPRESSION}{VALUE}
25
26 ;; Note that you have to add
27 ;;
28 ;; \newcommand{\fflet}[2]{}
29 ;; \newcommand{\ffeval}[2]{#2}
30 ;;
31 ;; Somewhere in your latex file.
32 ;;
33 ;; EXAMPLE
34 ;; \fflet{X}{12} \fflet{Y}{19 + X * (X + 3)} \ffeval{18 * Y}{}
35
36
37 (defun arithmlatex/eval (expression var-table)
38   (let ((nb-loops 0))
39     (while (string-match "\\([A-Za-z][A-Za-z0-9_]*\\)" expression)
40       (setq nb-loops (1+ nb-loops)
41             expression
42             (replace-match
43              (concat "(" (gethash (sxhash (match-string 1 expression)) var-table) ")")
44              t t expression))
45       (when (> nb-loops 100) (error "Too many evaluation levels"))
46       ))
47     (calc-eval expression))
48
49 (defun arithmlatex (&optional universal) (interactive "P")
50   (let ((var-table (make-hash-table))
51         (nb-changes 0))
52
53     (save-excursion
54
55       ;; First we collect the variable definitions
56       (goto-char (point-min))
57       (while (re-search-forward
58               "\\fflet{\\([^}]*\\)}{\\([^}]*\\)}"
59               nil t)
60         (let ((a (match-string-no-properties 1))
61               (b (match-string-no-properties 2)))
62           (if (gethash (sxhash a) var-table)
63               (error "%s is multiply defined" a))
64           (puthash (sxhash a) b var-table)
65           ))
66
67       ;; Then we evaluate the expressions
68       (goto-char (point-min))
69       (while (re-search-forward
70               "\\ffeval{\\([^}]*\\)}{\\([^}]*\\)}"
71               nil t)
72         (let* ((a (match-string-no-properties 1))
73                (b (match-string-no-properties 2))
74                (start (match-beginning 2))
75                (end (match-end 2))
76                (v (condition-case nil (arithmlatex/eval a var-table) (error "???"))))
77           (if (not (stringp v)) (setq v "???"))
78
79           ;; Do the change only if necessary
80           (unless (string= v b)
81             (setq nb-changes (1+ nb-changes))
82             (unless universal
83               (kill-region start end)
84               (backward-char)
85               (insert v)))
86           ))
87       )
88
89     (if universal
90         (message "There would be %s substitutions" nb-changes)
91       (message "There have been %s substitutions" nb-changes))
92     )
93   )