--- /dev/null
+;; -*- mode: emacs-lisp -*-
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; This program is free software; you can redistribute it and/or ;;
+;; modify it under the terms of the GNU General Public License as ;;
+;; published by the Free Software Foundation; either version 3, or (at ;;
+;; your option) any later version. ;;
+;; ;;
+;; This program is distributed in the hope that it will be useful, but ;;
+;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;
+;; General Public License for more details. ;;
+;; ;;
+;; You should have received a copy of the GNU General Public License ;;
+;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;;
+;; ;;
+;; Written by and Copyright (C) Francois Fleuret ;;
+;; Contact <francois@fleuret.org> for comments & bug reports ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; This short scripts performs arithmetic computations in a latex
+;; file. It collects variables defined by \fflet{VARNAME}{VALUE}
+;; expressions in the latex file, and updates the VALUE in the
+;; expressions of the form \ffeval{EXPRESSION}{VALUE}
+
+;; Note that you have to add
+;;
+;; \newcommand{\fflet}[2]{}
+;; \newcommand{\ffeval}[2]{#2}
+;;
+;; Somewhere in your latex file.
+;;
+;; EXAMPLE
+;; \fflet{X}{12} \fflet{Y}{19 + X * (X + 3)} \ffeval{18 * Y}{}
+
+
+(defun arithmlatex/eval (expression var-table)
+ (let ((nb-loops 0))
+ (while (string-match "\\([A-Za-z][A-Za-z0-9_]*\\)" expression)
+ (setq nb-loops (1+ nb-loops)
+ expression
+ (replace-match
+ (concat "(" (gethash (sxhash (match-string 1 expression)) var-table) ")")
+ t t expression))
+ (when (> nb-loops 100) (error "Too many evaluation levels"))
+ ))
+ (calc-eval expression))
+
+(defun arithmlatex (&optional universal) (interactive "P")
+ (let ((var-table (make-hash-table))
+ (nb-changes 0))
+
+ (save-excursion
+
+ ;; First we collect the variable definitions
+ (goto-char (point-min))
+ (while (re-search-forward
+ "\\fflet{\\([^}]*\\)}{\\([^}]*\\)}"
+ nil t)
+ (let ((a (match-string-no-properties 1))
+ (b (match-string-no-properties 2)))
+ (if (gethash (sxhash a) var-table)
+ (error "%s is multiply defined" a))
+ (puthash (sxhash a) b var-table)
+ ))
+
+ ;; Then we evaluate the expressions
+ (goto-char (point-min))
+ (while (re-search-forward
+ "\\ffeval{\\([^}]*\\)}{\\([^}]*\\)}"
+ nil t)
+ (let* ((a (match-string-no-properties 1))
+ (b (match-string-no-properties 2))
+ (start (match-beginning 2))
+ (end (match-end 2))
+ (v (condition-case nil (arithmlatex/eval a var-table) (error "???"))))
+ (if (not (stringp v)) (setq v "???"))
+
+ ;; Do the change only if necessary
+ (unless (string= v b)
+ (setq nb-changes (1+ nb-changes))
+ (unless universal
+ (kill-region start end)
+ (backward-char)
+ (insert v)))
+ ))
+ )
+
+ (if universal
+ (message "There would be %s substitutions" nb-changes)
+ (message "There have been %s substitutions" nb-changes))
+ )
+ )