;; -*- 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)) ) )