Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
;; This file:
;;   http://angg.twu.net/emlua/emlua-repl.el.html
;;   http://angg.twu.net/emlua/emlua-repl.el
;;           (find-angg "emlua/emlua-repl.el")
;; https://raw.githubusercontent.com/edrx/emlua/main/emlua-repl.el
;;           https://github.com/edrx/emlua/blob/main/emlua-repl.el
;; Author: Eduardo Ochs <eduardoochs@gmail.com>
;; Version: 2022mar28
;; License: GPL2
;;
;; See: <https://github.com/edrx/emlua>.


;; Introduction
;; ============
;; This file implements `eepitch-emlua' and several variants of it -
;; i.e., it implements "several different `eepitch-emlua's".
;;
;; All these `eepitch-emlua's pitch things to the emlua buffer, and
;; this GENERALLY means that they talk to a Lua interpreter via
;; `emlua-dostring' and then use the emlua buffer as a kind of log of
;; the communication. This screenshot shows how this is _typically_
;; used:
;;
;;   http://angg.twu.net/IMAGES/2022eepitch-emlua-0.png
;;
;; BUT: the emlua buffer is just a log buffer, and to keep the code
;; simple I keep it in fundamental mode. This means that RET there is
;; not special: typing, say,
;;
;;   print(2+3)
;;
;; after a prompt in the emlua buffer and then typing RET does not
;; send the "print(2+3)" to the Lua interpreter, or anywhere - the RET
;; just inserts a "\n".
;;
;; Remember that `eepitch-shell' sets up a target buffer running an
;; "inferior shell". See:
;;
;;   (find-eev-quick-intro "6. Controlling shell-like programs")
;;   (find-enode "Major Modes" "inferior shell process")
;;   (find-enode "Interactive Shell")
;;
;; Here are the first two paragraphs of the page "Interactive Shell":
;;
;;   To run a subshell interactively, type ‘M-x shell’. This creates
;;   (or reuses) a buffer named ‘*shell*’, and runs a shell subprocess
;;   with input coming from and output going to that buffer. That is
;;   to say, any terminal output from the subshell goes into the
;;   buffer, advancing point, and any terminal input for the subshell
;;   comes from text in the buffer. To give input to the subshell, go
;;   to the end of the buffer and type the input, terminated by <RET>.
;;   
;;   By default, when the subshell is invoked interactively, the
;;   ‘*shell*’ buffer is displayed in a new window, unless the current
;;   window already shows the ‘*shell*’ buffer. This behavior can be
;;   customized via ‘display-buffer-alist’.
;;
;; In this file the notion "inferior Lua" will be kept deliberately
;; vague: the meaning of "sending (something) the the inferior Lua"
;; will depend on the context, and _can be modified at will_. In most
;; cases it means:
;;
;;   send [this] to the REPL running in the Lua interpreter showing
;;   [this] in the log buffer, and then show in the log buffer the
;;   answer of the Lua interpreter and the next prompt
;;
;; but the details of this "send" can change, and the REPL can change
;; - for example, Technomancy is experimenting with REPLs based on
;; coroutines, and I am making some experiments with REPLs that send
;; text with properties back to Emacs - and in same cases we perform
;; tests using fake REPLs that always answer the same string.
;;
;;
;; `emlua-do'
;; ==========
;; One of the main building blocks of this file is the function
;; `emlua-do', whose semantics can also be changed. In principle
;;
;;   (emlua-do '(foo bar))
;;
;; evals `(foo bar)' inside the emlua buffer, but this can mean
;; something full of add-ons, like "make sure that the emlua buffer
;; exists, is initialized, and is shown in a visible window, and run
;; `(foo bar)' there with the point at the end of the buffer".
;;
;; Right now the behavior of `emlua-do' is changed by defalias-ing it
;; to different functions.


;; An older description:
;; ---------------------
;; Note: this is my N-th attempt (for N big!) of rewriting this file
;; to make its code easy to understand... but it still needs more
;; rewrites, and lots of explanations and tests. In particular, 1) I
;; need to rename some functions to make clear that we are
;; implementing a kind of an "inferior process" running Lua, 2) I need
;; to explain that the buffer "*emlua*" is just a log of our
;; interactions with our "inferior Lua" via `eepitch-emlua-esend' -
;; lines typed there are not automatically sent to the inferior Lua,
;; 3) it _seems_ that the functions that end in "-bw" are not needed,
;; 4) only a few functions defined in eepitch.el use
;;
;;   (setq eepitch-line 'eeepitch-line-<suffix>)
;;
;; to set up alternative ways to send lines to the target. I did not
;; remember the details of how they worked, and I improvised a lot,
;; using functions with bad names and weird constructs... I need to
;; clean up that code A LOT. There are some examples of "official"
;; functions that use alternative `eepitch-line's here:
;;
;;   (find-eev "eepitch.el" "other-terms")
;; 
;; See also:
;; 
;;   (find-eev "eepitch.el" "eepitch-this-line")



;; «.faces»			(to "faces")
;; «.find-emluabuffer»		(to "find-emluabuffer")
;; «.emlua-do»			(to "emlua-do")
;; «.emlua-insert»		(to "emlua-insert")
;; «.eepitch-emlua-fake1»	(to "eepitch-emlua-fake1")

;; «.esend»			(to "esend")
;; «.eepitch-emlua»		(to "eepitch-emlua")
;; «.emlua-dostring+»		(to "emlua-dostring+")




;; To test this, compile emlua.cpp, then run:
;;
;;   (add-to-list 'load-path default-directory)
;;   (require 'emlua-repl)
;;   (emlua-init-so)
;;   (emlua-init-dofiles)
;;   (emlua-init-newrepl)
;;
;; and then run the test blocks in this file.

(require 'eepitch)
(require 'emlua-data)
(require 'emlua-init)





;;;  _____                   
;;; |  ___|_ _  ___ ___  ___ 
;;; | |_ / _` |/ __/ _ \/ __|
;;; |  _| (_| | (_|  __/\__ \
;;; |_|  \__,_|\___\___||___/
;;;                          
;; «faces»  (to ".faces")
;;
(defface emlua-prompt-face
  '((((background dark))  :foreground "RoyalBlue3")
    (((background light)) :foreground "RoyalBlue3"))
  "")

(defface emlua-user-input-face
  '((((background dark))  :foreground "orange1")
    (((background light)) :foreground "DarkOrange"))
  "")

(defface emlua-output-face
  '((((background dark))  :foreground "bisque")
    (((background light)) :foreground "SaddleBrown"))
  "")

(defface emlua-error-face
  '((((background dark))  :foreground "red")
    (((background light)) :foreground "red"))
  "")





;;;   __ _           _       _            __  __           
;;;  / _(_)_ __   __| |     | |__  _   _ / _|/ _| ___ _ __ 
;;; | |_| | '_ \ / _` |_____| '_ \| | | | |_| |_ / _ \ '__|
;;; |  _| | | | | (_| |_____| |_) | |_| |  _|  _|  __/ |   
;;; |_| |_|_| |_|\__,_|     |_.__/ \__,_|_| |_|  \___|_|   
;;;                                                        
;; «find-emluabuffer»  (to ".find-emluabuffer")

(defun emlua-buffer ()
  "Return the *emlua* buffer, or nil if it doesn't exist."
  (get-buffer "*emlua*"))

(defun emlua-buffer-initial-prompt ()
  (propertize "Emlua:\n>>> " 'face 'emlua-prompt-face))

(defun find-emluabuffer ()
  "Go to the *emlua* buffer, and make sure it's initialized."
  (if (emlua-buffer)
      (find-ebuffer "*emlua*")
    (find-ebuffer "*emlua*")
    (insert (emlua-buffer-initial-prompt))))

;; Other basic tools:
;;
(defun emlua-window ()
  "Return a window with the *emlua* buffer, or nil if it doesn't exist."
  (get-buffer-window "*emlua*"))

(defun emlua-buffer-kill ()
  (if (emlua-buffer) (ee-kill-buffer (emlua-buffer))))



;;;                 _                       _       
;;;   ___ _ __ ___ | |_   _  __ _        __| | ___  
;;;  / _ \ '_ ` _ \| | | | |/ _` |_____ / _` |/ _ \ 
;;; |  __/ | | | | | | |_| | (_| |_____| (_| | (_) |
;;;  \___|_| |_| |_|_|\__,_|\__,_|      \__,_|\___/ 
;;;                                                 
;; «emlua-do»  (to ".emlua-do")

(defun emlua-do-b (code)
  (find-emluabuffer)
  (goto-char (point-max))
  (eval code))

(defun emlua-do-bs (code)
  (save-excursion
    (find-emluabuffer)
    (goto-char (point-max))
    (eval code)))

(defun emlua-do-w (code)
  (if (not (emlua-window))
      (find-2a nil '(find-emluabuffer)))
  (save-selected-window
    (select-window (emlua-window))
    (goto-char (point-max))
    (eval code)))

(defalias 'emlua-do 'emlua-do-bs)



;;;  _                     _   
;;; (_)_ __  ___  ___ _ __| |_ 
;;; | | '_ \/ __|/ _ \ '__| __|
;;; | | | | \__ \  __/ |  | |_ 
;;; |_|_| |_|___/\___|_|   \__|
;;;                            
;; «emlua-insert»  (to ".emlua-insert")

(defun emlua-insert (str &optional face)
  (emlua-do `(insert ,(propertize str 'face face))))

(defun emlua-insert-prompt (prompt)
  (emlua-insert prompt 'emlua-prompt-face))

(defun emlua-insert-user-input (line)
  (emlua-insert (concat line "\n") 'emlua-user-input-face))

(defun emlua-insert-output (output)
  (emlua-insert output 'emlua-output-face))

(defun emlua-insert-error (err)
  (emlua-insert err 'emlua-error-face))


'("This is a test block:
* (emlua-buffer-kill)
* (defalias 'emlua-do 'emlua-do-w)
* (emlua-do nil)
* (emlua-insert-user-input "foo")
* (emlua-insert-output "bar\n")
* (emlua-insert-prompt ">-> ")

--")




;;;   __       _        _ 
;;;  / _| __ _| | _____/ |
;;; | |_ / _` | |/ / _ \ |
;;; |  _| (_| |   <  __/ |
;;; |_|  \__,_|_|\_\___|_|
;;;                       
;; «eepitch-emlua-fake1»  (to ".eepitch-emlua-fake1")

(defun eepitch-line-emlua-fake1 (line)
  (eepitch-eval-at-target-window
   `(progn (emlua-insert-user-input ,line)
	   (emlua-insert-output "(emlua-fake1 output)\n")
	   (emlua-insert-prompt ">-> "))))

(defun eepitch-emlua-fake1 ()
  (interactive)
  (defalias 'emlua-do 'emlua-do-b)
  (prog1 (eepitch '(find-emluabuffer))
    (setq eepitch-line 'eepitch-line-emlua-fake1)))


'("This is a test block:
* (eepitch-emlua-fake1)
* (eepitch-kill)
* (eepitch-emlua-fake1)
foo
bar

--")




;;;                           _ 
;;;   ___  ___  ___ _ __   __| |
;;;  / _ \/ __|/ _ \ '_ \ / _` |
;;; |  __/\__ \  __/ | | | (_| |
;;;  \___||___/\___|_| |_|\__,_|
;;;                             
;; «esend»  (to ".esend")

(defvar eepitch-emlua-out nil
  "The results of the last call to `eepitch-emlua-esend0'.")

(defun eepitch-emlua-esend0 (line)
  "Run REPL:esend(LINE) and save the results in `eepitch-emlua-out'."
  (setq eepitch-emlua-out
	(emlua-dostring
	 (emlua-format "return REPL:esend(%s)" line))))

(defun eepitch-emlua-esend1 ()
  "Insert the contents of `eepitch-emlua-out' in the right way."
  (if (stringp eepitch-emlua-out)
      (emlua-insert-error (concat eepitch-emlua-out "\n"))
    (if (= 0 (length eepitch-emlua-out))
	"eepitch-emlua-out is []: do nothing"
      (emlua-insert-output (aref eepitch-emlua-out 0)))))

(defun eepitch-emlua-eprompt ()
  "Insert the result of REPL:eprompt() at the end of the *emlua* buffer."
  (emlua-insert-prompt
   (aref (emlua-dostring "return REPL:eprompt()") 0)))


'("This is a test block:
* (defalias 'emlua-do 'emlua-do-w)
* (emlua-buffer-kill)
* (find-2a nil '(find-emluabuffer)))
* (emlua-insert-user-input "= 22+33")
* (eepitch-emlua-esend0    "= 22+33")
* (eepitch-emlua-esend1)
* (eepitch-emlua-eprompt)

--")








;;;                  _ _       _                          _             
;;;   ___  ___ _ __ (_) |_ ___| |__         ___ _ __ ___ | |_   _  __ _ 
;;;  / _ \/ _ \ '_ \| | __/ __| '_ \ _____ / _ \ '_ ` _ \| | | | |/ _` |
;;; |  __/  __/ |_) | | || (__| | | |_____|  __/ | | | | | | |_| | (_| |
;;;  \___|\___| .__/|_|\__\___|_| |_|      \___|_| |_| |_|_|\__,_|\__,_|
;;;           |_|                                                       
;;
;; «eepitch-emlua»  (to ".eepitch-emlua")

(defun eepitch-line-emlua (line)
  (eepitch-eval-at-target-window
   `(progn (emlua-insert-user-input ,line)
	   (eepitch-emlua-esend0    ,line)
	   (eepitch-emlua-esend1)
	   (eepitch-emlua-eprompt))))

(defun eepitch-emlua ()
  "Setup eepitch-ing to an inferior Lua.
The \"inferior Lua\" is not a process associated to the *emlua*
buffer - it is a Lua interpreter. We exchange data with it using
`emlua-dostring', and we use the *emlua* buffer as a log of our
communications with it. See the source of `eepitch-line-emlua' to
understand what happens when we send a line to the inferior Lua."
  (interactive)
  (defalias 'emlua-do 'emlua-do-b)
  (prog1 (eepitch '(find-emluabuffer))
    (setq eepitch-line 'eepitch-line-emlua)))



'("This is a test block:
* (eepitch-emlua)
* (eepitch-kill)
* (eepitch-emlua)
print(2 + 3)
print(2 +
      3 +
      4)
= 2,
  3,
  4
= 2 + nil
= EdrxEmacsRepl
PPP(EdrxEmacsRepl)
PPPV(EdrxEmacsRepl.__index)

--")






;;;      _           _        _                   
;;;   __| | ___  ___| |_ _ __(_)_ __   __ _   _   
;;;  / _` |/ _ \/ __| __| '__| | '_ \ / _` |_| |_ 
;;; | (_| | (_) \__ \ |_| |  | | | | | (_| |_   _|
;;;  \__,_|\___/|___/\__|_|  |_|_| |_|\__, | |_|  
;;;                                   |___/       
;;
;; «emlua-dostring+»  (to ".emlua-dostring+")
;; This is like emlua-dostring, but it treats the result as elisp code
;; that has to be eval-ed. Note: this is just a demo! Please redefine
;; this function and reuse its name!

(defun emlua-dostring+ (luacode)
  (let ((results (emlua-dostring luacode)))
    (if (stringp results)
	(error "%s" results)
      (let ((result (aref results 0)))
	(eval (read result))))))

;; Tests: (emlua-dostring+ "return '(+ 22 33)'")
;; Tests: (emlua-dostring+ "return '(insert \"\\n;; HELLO\")'")
;; Tests: (emlua-dostring+ "return  -- no return values")
;; Tests: (emlua-dostring+ "!!! a syntax error !!!")





(provide 'emlua-repl)


;; Local Variables:
;; coding:  utf-8-unix
;; End: