dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

ob-lua.el (14009B)


      1 ;;; ob-lua.el --- Org Babel functions for Lua evaluation -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2014, 2016-2023 Free Software Foundation, Inc.
      4 
      5 ;; Authors: Dieter Schoen
      6 ;; Keywords: literate programming, reproducible research
      7 ;; URL: https://orgmode.org
      8 
      9 ;; This file is part of GNU Emacs.
     10 
     11 ;; GNU Emacs is free software: you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; GNU Emacs is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 
     26 ;; Org-Babel support for evaluating lua source code.
     27 
     28 ;; Requirements:
     29 ;; for session support, lua-mode is needed.
     30 ;; lua-mode is not part of GNU Emacs/orgmode, but can be obtained
     31 ;; from marmalade or melpa.
     32 ;; The source repository is here:
     33 ;; https://github.com/immerrr/lua-mode
     34 
     35 ;; However, sessions are not yet working.
     36 
     37 ;;; Code:
     38 
     39 (require 'org-macs)
     40 (org-assert-version)
     41 
     42 (require 'ob)
     43 (require 'org-macs)
     44 (require 'cl-lib)
     45 
     46 (declare-function lua-shell "ext:lua-mode" (&optional argprompt))
     47 (declare-function lua-toggle-shells "ext:lua-mode" (arg))
     48 (declare-function run-lua "ext:lua" (cmd &optional dedicated show))
     49 
     50 (defvar org-babel-tangle-lang-exts)
     51 (add-to-list 'org-babel-tangle-lang-exts '("lua" . "lua"))
     52 
     53 (defvar org-babel-default-header-args:lua '())
     54 
     55 (defcustom org-babel-lua-command "lua"
     56   "Name of the command for executing Lua code."
     57   :version "26.1"
     58   :package-version '(Org . "8.3")
     59   :group 'org-babel
     60   :type 'string)
     61 
     62 (defcustom org-babel-lua-mode 'lua-mode
     63   "Preferred lua mode for use in running lua interactively.
     64 This will typically be `lua-mode'."
     65   :group 'org-babel
     66   :version "26.1"
     67   :package-version '(Org . "8.3")
     68   :type 'symbol)
     69 
     70 (defcustom org-babel-lua-hline-to "None"
     71   "Replace hlines in incoming tables with this when translating to lua."
     72   :group 'org-babel
     73   :version "26.1"
     74   :package-version '(Org . "8.3")
     75   :type 'string)
     76 
     77 (defcustom org-babel-lua-None-to 'hline
     78   "Replace `None' in lua tables with this before returning."
     79   :group 'org-babel
     80   :version "26.1"
     81   :package-version '(Org . "8.3")
     82   :type 'symbol)
     83 
     84 (defun org-babel-execute:lua (body params)
     85   "Execute a block of Lua code with Babel.
     86 This function is called by `org-babel-execute-src-block'."
     87   (let* ((session (org-babel-lua-initiate-session
     88 		   (cdr (assq :session params))))
     89          (result-params (cdr (assq :result-params params)))
     90          (result-type (cdr (assq :result-type params)))
     91 	 (return-val (when (and (eq result-type 'value) (not session))
     92 		       (cdr (assq :return params))))
     93 	 (preamble (cdr (assq :preamble params)))
     94          (full-body
     95 	  (org-babel-expand-body:generic
     96 	   (concat body (if return-val (format "\nreturn %s" return-val) ""))
     97 	   params (org-babel-variable-assignments:lua params)))
     98          (result (org-babel-lua-evaluate
     99 		  session full-body result-type result-params preamble)))
    100     (org-babel-reassemble-table
    101      result
    102      (org-babel-pick-name (cdr (assq :colname-names params))
    103 			  (cdr (assq :colnames params)))
    104      (org-babel-pick-name (cdr (assq :rowname-names params))
    105 			  (cdr (assq :rownames params))))))
    106 
    107 (defun org-babel-prep-session:lua (session params)
    108   "Prepare SESSION according to the header arguments in PARAMS.
    109 VARS contains resolved variable references."
    110   (let* ((session (org-babel-lua-initiate-session session))
    111 	 (var-lines
    112 	  (org-babel-variable-assignments:lua params)))
    113     (org-babel-comint-in-buffer session
    114       (mapc (lambda (var)
    115               (end-of-line 1) (insert var) (comint-send-input)
    116               (org-babel-comint-wait-for-output session))
    117 	    var-lines))
    118     session))
    119 
    120 (defun org-babel-load-session:lua (session body params)
    121   "Load BODY into SESSION."
    122   (save-window-excursion
    123     (let ((buffer (org-babel-prep-session:lua session params)))
    124       (with-current-buffer buffer
    125         (goto-char (process-mark (get-buffer-process (current-buffer))))
    126         (insert (org-babel-chomp body)))
    127       buffer)))
    128 
    129 ;; helper functions
    130 
    131 (defun org-babel-variable-assignments:lua (params)
    132   "Return a list of Lua statements assigning the block's variables."
    133   (mapcar
    134    (lambda (pair)
    135      (format "%s=%s"
    136 	     (car pair)
    137 	     (org-babel-lua-var-to-lua (cdr pair))))
    138    (org-babel--get-vars params)))
    139 
    140 (defun org-babel-lua-var-to-lua (var)
    141   "Convert an elisp value to a lua variable.
    142 Convert an elisp value, VAR, into a string of lua source code
    143 specifying a variable of the same value."
    144   (if (listp var)
    145       (if (and (= 1 (length var)) (not (listp (car var))))
    146           (org-babel-lua-var-to-lua (car var))
    147         (if (and
    148              (= 2 (length var))
    149              (not (listp (car var))))
    150             (concat
    151              (substring-no-properties (car var))
    152              "="
    153              (org-babel-lua-var-to-lua (cdr var)))
    154           (concat "{" (mapconcat #'org-babel-lua-var-to-lua var ", ") "}")))
    155     (if (eq var 'hline)
    156         org-babel-lua-hline-to
    157       (format
    158        (if (and (stringp var) (string-match "[\n\r]" var)) "[=[%s]=]" "%S")
    159        (if (stringp var) (substring-no-properties var) var)))))
    160 
    161 (defun org-babel-lua-table-or-string (results)
    162   "Convert RESULTS into an appropriate elisp value.
    163 If the results look like a list or tuple, then convert them into an
    164 Emacs-lisp table, otherwise return the results as a string."
    165   (let ((res (org-babel-script-escape results)))
    166     (if (listp res)
    167         (mapcar (lambda (el) (if (eq el 'None)
    168 				 org-babel-lua-None-to el))
    169                 res)
    170       res)))
    171 
    172 (defvar org-babel-lua-buffers '((:default . "*Lua*")))
    173 
    174 (defun org-babel-lua-session-buffer (session)
    175   "Return the buffer associated with SESSION."
    176   (cdr (assoc session org-babel-lua-buffers)))
    177 
    178 (defun org-babel-lua-with-earmuffs (session)
    179   (let ((name (if (stringp session) session (format "%s" session))))
    180     (if (and (string= "*" (substring name 0 1))
    181 	     (string= "*" (substring name (- (length name) 1))))
    182 	name
    183       (format "*%s*" name))))
    184 
    185 (defun org-babel-lua-without-earmuffs (session)
    186   (let ((name (if (stringp session) session (format "%s" session))))
    187     (if (and (string= "*" (substring name 0 1))
    188 	     (string= "*" (substring name (- (length name) 1))))
    189 	(substring name 1 (- (length name) 1))
    190       name)))
    191 
    192 (defvar lua-default-interpreter)
    193 (defvar lua-which-bufname)
    194 (defvar lua-shell-buffer-name)
    195 (defun org-babel-lua-initiate-session-by-key (&optional session)
    196   "Initiate a lua session.
    197 If there is not a current inferior-process-buffer in SESSION
    198 then create.  Return the initialized session."
    199   ;; (require org-babel-lua-mode)
    200   (save-window-excursion
    201     (let* ((session (if session (intern session) :default))
    202            (lua-buffer (org-babel-lua-session-buffer session))
    203 	   ;; (cmd (if (member system-type '(cygwin windows-nt ms-dos))
    204 	   ;; 	    (concat org-babel-lua-command " -i")
    205 	   ;; 	  org-babel-lua-command))
    206 	   )
    207       (cond
    208        ((and (eq 'lua-mode org-babel-lua-mode)
    209              (fboundp 'lua-start-process)) ; lua-mode.el
    210         ;; Make sure that lua-which-bufname is initialized, as otherwise
    211         ;; it will be overwritten the first time a Lua buffer is
    212         ;; created.
    213         ;;(lua-toggle-shells lua-default-interpreter)
    214         ;; `lua-shell' creates a buffer whose name is the value of
    215         ;; `lua-which-bufname' with '*'s at the beginning and end
    216         (let* ((bufname (if (and lua-buffer (buffer-live-p lua-buffer))
    217                             (replace-regexp-in-string ;; zap surrounding *
    218                              "^\\*\\([^*]+\\)\\*$" "\\1" (buffer-name lua-buffer))
    219                           (concat "Lua-" (symbol-name session))))
    220                (lua-which-bufname bufname))
    221           (lua-start-process)
    222           (setq lua-buffer (org-babel-lua-with-earmuffs bufname))))
    223        (t
    224 	(error "No function available for running an inferior Lua")))
    225       (setq org-babel-lua-buffers
    226             (cons (cons session lua-buffer)
    227                   (assq-delete-all session org-babel-lua-buffers)))
    228       session)))
    229 
    230 (defun org-babel-lua-initiate-session (&optional session _params)
    231   "Create a session named SESSION according to PARAMS."
    232   (unless (string= session "none")
    233     (error "Sessions currently not supported, work in progress")
    234     (org-babel-lua-session-buffer
    235      (org-babel-lua-initiate-session-by-key session))))
    236 
    237 (defvar org-babel-lua-eoe-indicator "--eoe"
    238   "A string to indicate that evaluation has completed.")
    239 
    240 (defvar org-babel-lua-wrapper-method
    241   "
    242 function main()
    243 %s
    244 end
    245 
    246 fd=io.open(\"%s\", \"w\")
    247 fd:write( main() )
    248 fd:close()")
    249 (defvar org-babel-lua-pp-wrapper-method
    250   "
    251 -- table to string
    252 function t2s(t, indent)
    253    if indent == nil then
    254       indent = \"\"
    255    end
    256    if type(t) == \"table\" then
    257       ts = \"\"
    258       for k,v in pairs(t) do
    259          if type(v) == \"table\" then
    260             ts = ts .. indent .. t2s(k,indent .. \"  \") .. \" = \\n\" ..
    261                t2s(v, indent .. \"  \")
    262          else
    263             ts = ts .. indent .. t2s(k,indent .. \"  \") .. \" = \" ..
    264                t2s(v, indent .. \"  \") .. \"\\n\"
    265          end
    266       end
    267       return ts
    268    else
    269       return tostring(t)
    270    end
    271 end
    272 
    273 
    274 function main()
    275 %s
    276 end
    277 
    278 fd=io.open(\"%s\", \"w\")
    279 fd:write(t2s(main()))
    280 fd:close()")
    281 
    282 (defun org-babel-lua-evaluate
    283     (session body &optional result-type result-params preamble)
    284   "Evaluate BODY as Lua code."
    285   (if session
    286       (org-babel-lua-evaluate-session
    287        session body result-type result-params)
    288     (org-babel-lua-evaluate-external-process
    289      body result-type result-params preamble)))
    290 
    291 (defun org-babel-lua-evaluate-external-process
    292     (body &optional result-type result-params preamble)
    293   "Evaluate BODY in external lua process.
    294 If RESULT-TYPE equals `output' then return standard output as a
    295 string.  If RESULT-TYPE equals `value' then return the value of the
    296 last statement in BODY, as elisp."
    297   (let ((raw
    298          (pcase result-type
    299            (`output (org-babel-eval org-babel-lua-command
    300 				    (concat preamble (and preamble "\n")
    301 					    body)))
    302            (`value (let ((tmp-file (org-babel-temp-file "lua-")))
    303 		     (org-babel-eval
    304 		      org-babel-lua-command
    305 		      (concat
    306 		       preamble (and preamble "\n")
    307 		       (format
    308 			(if (member "pp" result-params)
    309 			    org-babel-lua-pp-wrapper-method
    310 			  org-babel-lua-wrapper-method)
    311 			(mapconcat
    312 			 (lambda (line) (format "\t%s" line))
    313 			 (split-string
    314 			  (org-remove-indentation
    315 			   (org-trim body))
    316 			  "[\r\n]") "\n")
    317 			(org-babel-process-file-name tmp-file 'noquote))))
    318 		     (org-babel-eval-read-file tmp-file))))))
    319     (org-babel-result-cond result-params
    320       raw
    321       (org-babel-lua-table-or-string (org-trim raw)))))
    322 
    323 (defun org-babel-lua-evaluate-session
    324     (session body &optional result-type result-params)
    325   "Pass BODY to the Lua process in SESSION.
    326 If RESULT-TYPE equals `output' then return standard output as a
    327 string.  If RESULT-TYPE equals `value' then return the value of the
    328 last statement in BODY, as elisp."
    329   (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5)))
    330 	 (dump-last-value
    331 	  (lambda
    332 	    (tmp-file pp)
    333 	    (mapc
    334 	     (lambda (statement) (insert statement) (funcall send-wait))
    335 	     (if pp
    336 		 (list
    337 		  "-- table to string
    338 function t2s(t, indent)
    339    if indent == nil then
    340       indent = \"\"
    341    end
    342    if type(t) == \"table\" then
    343       ts = \"\"
    344       for k,v in pairs(t) do
    345          if type(v) == \"table\" then
    346             ts = ts .. indent .. t2s(k,indent .. \"  \") .. \" = \\n\" ..
    347                t2s(v, indent .. \"  \")
    348          else
    349             ts = ts .. indent .. t2s(k,indent .. \"  \") .. \" = \" ..
    350                t2s(v, indent .. \"  \") .. \"\\n\"
    351          end
    352       end
    353       return ts
    354    else
    355       return tostring(t)
    356    end
    357 end
    358 "
    359 		  (concat "fd:write(_))
    360 fd:close()"
    361 			  (org-babel-process-file-name tmp-file 'noquote)))
    362 	       (list (format "fd=io.open(\"%s\", \"w\")
    363 fd:write( _ )
    364 fd:close()"
    365 			     (org-babel-process-file-name tmp-file
    366                                                           'noquote)))))))
    367 	 (input-body (lambda (body)
    368 		       (mapc (lambda (line) (insert line) (funcall send-wait))
    369 			     (split-string body "[\r\n]"))
    370 		       (funcall send-wait)))
    371          (results
    372           (pcase result-type
    373             (`output
    374              (mapconcat
    375               #'org-trim
    376               (butlast
    377                (org-babel-comint-with-output
    378                    (session org-babel-lua-eoe-indicator t body)
    379                  (funcall input-body body)
    380                  (funcall send-wait) (funcall send-wait)
    381                  (insert org-babel-lua-eoe-indicator)
    382                  (funcall send-wait))
    383                2) "\n"))
    384             (`value
    385              (let ((tmp-file (org-babel-temp-file "lua-")))
    386                (org-babel-comint-with-output
    387                    (session org-babel-lua-eoe-indicator nil body)
    388                  (let ((comint-process-echoes nil))
    389                    (funcall input-body body)
    390                    (funcall dump-last-value tmp-file
    391                             (member "pp" result-params))
    392                    (funcall send-wait) (funcall send-wait)
    393                    (insert org-babel-lua-eoe-indicator)
    394                    (funcall send-wait)))
    395                (org-babel-eval-read-file tmp-file))))))
    396     (unless (string= (substring org-babel-lua-eoe-indicator 1 -1) results)
    397       (org-babel-result-cond result-params
    398 	results
    399         (org-babel-lua-table-or-string results)))))
    400 
    401 (defun org-babel-lua-read-string (string)
    402   "Strip single quotes from around Lua string."
    403   (org-unbracket-string "'" "'" string))
    404 
    405 (provide 'ob-lua)
    406 
    407 ;;; ob-lua.el ends here