;; -*- 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 . ;;
;; ;;
;; Written by and Copyright (C) Francois Fleuret ;;
;; Contact 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))
)
)