on master help sign in
Merge pull request #24 from replit/rr-elisp-v2

Add support for Emacs Lisp. We use IELM.

commita67121f6023a0f277f7cd1c3596e712d44d90e8f authorRadon Rosborough <radon.neon@gmail.com> dateSat 8 Jun 2019, 17:37 (7 years ago) parent6108bc5
5 files changed+146−3
Dockerfile+4−2 Makefile+1−1 README.md+1 languages/elisp/main.go+62 prybar_assets/elisp/repl.el+78
Dockerfile+4−2
@@ -15,7 +15,8 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
1515 expect \
1616 nodejs \
1717 m4 \
18− software-properties-common
18+ software-properties-common \
19+ emacs-nox
1920
2021RUN wget http://launchpadlibrarian.net/309343864/libmozjs185-dev_1.8.5-1.0.0+dfsg-7_amd64.deb && \
2122 wget http://launchpadlibrarian.net/309343863/libmozjs185-1.0_1.8.5-1.0.0+dfsg-7_amd64.deb && \
@@ -56,7 +57,8 @@ RUN make \
5657 prybar-nodejs \
5758 prybar-julia \
5859 prybar-tcl \
59− prybar-ocaml
60+ prybar-ocaml \
61+ prybar-elisp
6062
6163ENV LC_ALL=C.UTF-8
6264
Makefile+1−1
@@ -16,4 +16,4 @@ test:
1616 ./run_tests
1717
1818clean:
19− @rm ./prybar-* ./languages/*/generated_*.go
19+ @rm -f ./prybar-* ./languages/*/generated_*.go
README.md+1
@@ -46,6 +46,7 @@ language which starts a Prybar-compatible REPL.
4646| Javascript (nodejs) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
4747| Julia | ✔ | ✗ | ✔ | ✔ | ✗ | ✔ |
4848| OCaml | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ |
49+| Emacs Lisp | ✔ | ✔ | ✔ | ✔ | ✗ | ✔ |
4950
5051## Build and run
5152
languages/elisp/main.gonew file+62
@@ -0,0 +1,62 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "github.com/replit/prybar/utils"
6+ "os"
7+ "os/exec"
8+ "path/filepath"
9+ "syscall"
10+)
11+
12+func Execute(config *utils.Config) {
13+
14+ emacs, err := exec.LookPath("emacs")
15+
16+ if err != nil {
17+ panic(err)
18+ }
19+
20+ execPath, err := os.Executable()
21+
22+ if err != nil {
23+ panic(err)
24+ }
25+
26+ runDir := filepath.Dir(execPath)
27+ replPath := filepath.Join(runDir, "prybar_assets", "elisp", "repl.el")
28+
29+ os.Setenv("PRYBAR_EVAL", config.Exp)
30+ os.Setenv("PRYBAR_EXEC", config.Code)
31+ os.Setenv("PRYBAR_PS1", config.Ps1)
32+
33+ if config.Quiet {
34+ os.Setenv("PRYBAR_QUIET", "1")
35+ } else {
36+ os.Setenv("PRYBAR_QUIET", "")
37+ }
38+
39+ // We only support one file, despite the fact that this
40+ // variable is a list.
41+ if len(config.Args) >= 1 {
42+ os.Setenv("PRYBAR_FILE", config.Args[0])
43+ } else {
44+ os.Setenv("PRYBAR_FILE", "")
45+ }
46+
47+ if !(config.Interactive || config.OurInteractive) {
48+ fmt.Fprintln(os.Stderr, "prybar-elisp: warn: non-interactive mode not implemented");
49+ }
50+
51+ if config.Ps2 != "... " {
52+ fmt.Fprintln(os.Stderr, "prybar-elisp: warn: ps2 not implemented");
53+ }
54+
55+ args := []string{"emacs", "-Q", "--load", replPath, "--eval", "(prybar-repl)"}
56+ err = syscall.Exec(emacs, args, os.Environ())
57+
58+ if err != nil {
59+ panic(err)
60+ }
61+
62+}
prybar_assets/elisp/repl.elnew file+78
@@ -0,0 +1,78 @@
1+(require 'ielm)
2+
3+(defun prybar-config (key)
4+ "Read a Prybar configuration variable from the environment.
5+KEY is a symbol, like `quiet'. Return the value of the
6+environment variable \"PRYBAR_QUIET\" (or analogously for other
7+KEYs). If the environment variable is unset, or its value is the
8+empty string, then return nil instead."
9+ (let* ((name (format "PRYBAR_%s" (upcase (symbol-name key))))
10+ (val (getenv name)))
11+ (and val (/= (length val) 0) val)))
12+
13+(defmacro with-prybar-config (vars &rest body)
14+ "Read Prybar configuration VARS and bind lexically while running BODY.
15+VARS is a list of symbols as could be passed to `prybar-config'.
16+Each symbol is prepended with `prybar-' and bound lexically to
17+the return value of `prybar-config'.
18+
19+VARS may also contain lists of the form (SYMBOL DEFAULT), not
20+just symbols. In this case, the behavior is the same except that
21+DEFAULT is used as the binding for the symbol if `prybar-config'
22+returns nil.
23+
24+For example, the following usage:
25+
26+ (with-prybar-config (eval (ps1 \"-->\"))
27+ ...)
28+
29+expands to:
30+
31+ (let ((prybar-eval (prybar-config 'eval))
32+ (prybar-ps1 (or (prybar-config 'ps1) \"-->\")))
33+ ...)"
34+ (declare (indent 1))
35+ `(let (,@(mapcar
36+ (lambda (var)
37+ (if (listp var)
38+ `(,(intern (format "prybar-%S" (nth 0 var)))
39+ (or (prybar-config ',(nth 0 var))
40+ ,(nth 1 var)))
41+ `(,(intern (format "prybar-%S" var))
42+ (prybar-config ',var))))
43+ vars))
44+ ,@body))
45+
46+(defun prybar-repl ()
47+ "Start a Prybar REPL at the top level. This mutates global state.
48+The REPL uses `ielm', which see. After version information is
49+printed and forms for immediate evaluation or execution are
50+handled, display the IELM buffer and return."
51+ (menu-bar-mode -1)
52+ ;; Disable bold and underline, from
53+ ;; <https://stackoverflow.com/a/2065305/3538165>.
54+ (mapc
55+ (lambda (face)
56+ (set-face-attribute face nil :weight 'normal :underline nil))
57+ (face-list))
58+ ;; IELM only supports PS1, not PS2.
59+ (with-prybar-config
60+ (eval exec (ps1 "--> ") quiet file)
61+ (setq ielm-prompt prybar-ps1)
62+ (with-current-buffer (get-buffer-create "*ielm*")
63+ (if prybar-quiet
64+ (setq ielm-header "")
65+ (insert (format "GNU Emacs %s\n" emacs-version)))
66+ (when prybar-file
67+ (condition-case e
68+ (load (expand-file-name prybar-file) nil 'nomessage)
69+ (error
70+ (insert (format "%s\n" (error-message-string e))))))
71+ (inferior-emacs-lisp-mode)
72+ (when prybar-exec
73+ (insert prybar-exec)
74+ (ielm-send-input 'for-effect))
75+ (when prybar-eval
76+ (insert prybar-eval)
77+ (ielm-send-input)))
78+ (pop-to-buffer-same-window "*ielm*")))