ThibautVerron / ob-magma

Support for magma in org-babel
1 stars 1 forks source link

Option to use free online version of magma #2

Open Eloitor opened 1 week ago

Eloitor commented 1 week ago

Magma code can be run by calling the API of the free version of magma available online. For example: http://magma.maths.usyd.edu.au/xml/calculator.xml?input=Factorization(15)

I might work on this in the future.

ThibautVerron commented 1 week ago

Hi!

Thanks for the suggestion. Is this API documented somewhere, as well as how they permit its usage?

At any rate, I wouldn't call the online calculator a "free online version of Magma". With strict time (120s) and memory (~350MB) limits, and no persistent memories between runs, it is really more of a calculator than a CAS. It is to Magma what Wolfram Alpha is to Mathematica, in a sense.

I guess that in order to use it with ob-magma, one would have to first write some kind of interactive wrapper over those API calls in emacs (a buffer with a magma-like prompt where one could enter a magma expression, press enter, and get the result from the calculator). Once this exists, it should be possible to pass it directly to ob-magma.

Eloitor commented 1 week ago

This api is the one used by sagemath: https://github.com/sagemath/sage/blob/develop/src/sage/interfaces/magma_free.py

in theory is the same as http://magma.maths.usyd.edu.au/calc/

Eloitor commented 1 week ago
(require 'url)
(require 'xml)

(defun magma-send-request (input)
  "Send INPUT to the Magma calculator API and return the response."
  (let ((url-request-method "GET")
        (url (format "http://magma.maths.usyd.edu.au/xml/calculator.xml?input=%s" (url-hexify-string input))))
    (with-current-buffer (url-retrieve-synchronously url)
      (goto-char (point-min))
      (re-search-forward "\n\n")
      (let ((response (buffer-substring (point) (point-max))))
        (kill-buffer (current-buffer))
        response))))

(defun magma-parse-response (response)
  "Parse the XML RESPONSE from Magma and return the result as a string."
  (let ((xml (with-temp-buffer
               (insert response)
               (xml-parse-region (point-min) (point-max)))))
    (mapconcat (lambda (line)
                 (car (xml-node-children line)))
               (xml-get-children (car (xml-get-children (car xml) 'results)) 'line)
               "\n")))

(defun magma-buffer ()
  "Open a buffer for interacting with the Magma API."
  (interactive)
  (switch-to-buffer "*Magma Buffer*"))

(defun magma-send-buffer ()
  "Send the entire buffer content to the Magma calculator and display the result in a temporary buffer."
  (interactive)
  (let* ((input (with-current-buffer "*Magma Buffer*"
                  (buffer-substring-no-properties (point-min) (point-max))))
         (response (magma-send-request input))
         (result (magma-parse-response response)))
    (with-output-to-temp-buffer "*Magma Result*"
      (princ result))))

This is an implementation of magma-buffer which opens a buffer where you can write a magma script. You send it using magma-send-buffer and it shows the response in a temporary buffer.

ThibautVerron commented 1 week ago

Thanks for the links. I had no idea that sage had such a feature. I guess if it works in sage it can work in org just as well, one can use org to manage the variables between calls as a substitute for a running session memory.

Nice functions too, I think that with that the hardest part is done. I now think that going the "make a magma-run and plug it in" way might not be the best option for you. Not the easiest, because you would still need to get comint to work in your interactive buffer. And also maybe not the most performant, because if I remember correctly there were a number of tradeoffs made because of difficulties parsing the output in a magma session, and your neat send input / read output functions don't have this problem.

You can have a look at this function https://github.com/ThibautVerron/ob-magma/blob/f4ea6853f021bbf2a7df382ec51f5059f7e7527b/ob-magma.el#L144

drop the stuff you don't need (mostly the session-related stuff) and replace the calls to comint-with-output with calls to your methods. The wrappers in ob-magma to help detect the type and format the output can probably work just as well. But there again, the lack of persistent memory will bite you.

Or, maybe even easier, start from a clean org-babel-generic setup, get something working with your functions, and then work in the helpers and formatters.