3

How can I convert a lisp expression to LaTeX math expression.

From simple things like (- 3 2 1) which should be converted to 3 - 2 - 1, to relatively complex things like: (* 1 2 (/ 2 3))1 \times 2 \times \frac{2}{3}, or (somefunc 1 2)somefunc(1,2), and so on.

Even being able to use some variables instead of numbers and convert them would be nice.

I type Lisp expressions in Emacs a lot, because I can just evaluate that there and get the output. That means I don't have to actually run a calculator or a terminal elsewhere. But I'd like to put valid LaTeX math expressions into the reports, and instead of typing again.

Is there any package that can help with that, or do I have to write the Elisp with a parser myself?

I found https://alejandrogallo.github.io/listex/ which seems to be for writing LaTeX in Lisp. I do not want to write LaTeX in Lisp. I want to convert already written valid Lisp expressions to LaTeX math expressions.


EDIT: Based on the accepted answer I ended up making a whole minor mode centering around this functionality.

Here's the link: https://github.com/Atreyagaurav/litex-mode

Atreyagaurav
  • 133
  • 5
  • https://emacs.stackexchange.com/tags/elisp/info – Drew Feb 02 '22 at 19:55
  • Is your question about LaTeX expressions in general or just LaTeX math expressions. Please clarify the question for this. – Drew Feb 02 '22 at 19:59
  • It's about latex math expressions. Also about the elisp tag info, the solution to this depends on parsing elisp expressions, so I considered it specific to elisp, I hope that tag is fine. – Atreyagaurav Feb 02 '22 at 20:16
  • No, tag `elisp` is not about using Elisp to do something. It's about Elisp as a particular Lisp dialect. – Drew Feb 02 '22 at 21:34

1 Answers1

4

Here is a toy function:

(defvar lisp2latex-need-parens nil)
(defun lisp2latex (form)
  (pcase form
    (`(+ . ,args)
     (let ((ret (let ((lisp2latex-need-parens nil))
                  (mapconcat #'lisp2latex args " + "))))
       (if lisp2latex-need-parens
           (concat "(" ret ")")
         ret)))
    (`(- ,a1 . ,args)
     (let ((ret
            (if args
                (format "%s - %s" (lisp2latex a1)
                        (let ((lisp2latex-need-parens t))
                          (mapconcat #'lisp2latex args " - ")))
              (format "- %s" (lisp2latex a1)))))
       (if lisp2latex-need-parens
           (concat "(" ret ")")
         ret)))
    (`(* . ,args)
     (let ((lisp2latex-need-parens t))
       (mapconcat #'lisp2latex args " \\times ")))
    (`(/ ,a1 . ,args)
     (if args
         (format "\\frac{%s}{%s}" (lisp2latex a1)
                 (lisp2latex (cons '* args)))
       (format "\\frac1{%s}" (lisp2latex a1))))
    (`(,func . ,args)
     (format "%s(%s)" func (mapconcat #'lisp2latex args ",")))
    (_ (prin1-to-string form))))

(lisp2latex '(* (/ (log 4 64)) (/ 3 (+ 4 6) 8)))
"\\frac1{log(4,64)} \\times \\frac{3}{(4 + 6) \\times 8}"

It probably adds too many parens...

sds
  • 5,928
  • 20
  • 39
  • That's like super fast, I can't believe how fast you did it, also it seems to work if I add other functions in the pcase, like `(\`(setq . ,args) (mapconcat #'lisp2latex args " = "))`. – Atreyagaurav Feb 02 '22 at 20:14
  • I think your `setq` clause is _wrong_ for more than 1 assignment. – sds Feb 02 '22 at 20:16
  • I was also thinking that, but for now I just wanted to see if I could add any functions there. If it works then, can you modify the default expression to print `function(arg1,arg2, ...)` format instead of `(function arg1 arg2 ...)` format? – Atreyagaurav Feb 02 '22 at 20:18
  • I tried `(_ (format "%s%s" (car form) (cdr form)))` for the last one, and it does convert `(sin 1)` to `sin(1)` form, but again it doesn't work for multiple arguments. I tried `(mapconcat #'lisp2latex (list (cdr form)) ", ")` for second `%s` but it didn't work at all. – Atreyagaurav Feb 03 '22 at 02:48
  • what's wrong with my current version? – sds Feb 03 '22 at 02:49
  • Oh sorry, I didn't realize you made an edit, I was just trying to make that in my local copy and added that comment in the tab that wasn't refreshed since I added the comment above that one. Your version does indeed work. I think I can now make add more functions, it's clear now. Thank you. – Atreyagaurav Feb 03 '22 at 03:06
  • By adding `let ((lisp2latex-need-parens t))` in the evaluation for `-` operator were you perhaps trying to do `(lisp2latex (cons '+ args))` for everything after the first argument? Because currently changing t to nil doesn't change anything. – Atreyagaurav Feb 03 '22 at 03:35