From d6722861ecb5d9e2520345fc942a94825b14ef8b Mon Sep 17 00:00:00 2001 From: George Mauer Date: Fri, 15 May 2020 12:20:39 -0500 Subject: [PATCH 01/14] fix racket indentation and parens which I had totally broken --- ob-racket.el | 72 +++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index f281bda..ca9770b 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -25,8 +25,6 @@ ;; org-babel functions for racket evaluation ;; -;; -- Provide -- - (provide 'ob-racket) ;; -- Requires -- @@ -54,39 +52,39 @@ (defun org-babel-execute:racket (body params) "Executes a Racket code block." ;; Round up the stuff we need - (let* ((parsed-params (ob-racket--parse-params params))) - (expanded-body (org-babel-expand-body:racket body params)) - (result-type (nth 0 parsed-params)) - (lang (nth 1 parsed-params)) - (vars (nth 2 parsed-params)) - (temp-file (make-temp-file "ob-racket-") + (let* ((parsed-params (ob-racket--parse-params params)) + (expanded-body (org-babel-expand-body:racket body params)) + (result-type (nth 0 parsed-params)) + (lang (nth 1 parsed-params)) + (vars (nth 2 parsed-params)) + (temp-file (make-temp-file "ob-racket-"))) ;; Build script in temporary file (with-temp-file temp-file (cond ;; Results type is "value" - run in let form - ((equal result-type 'value)))))) - (let ((vars-string) - (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " ")) - (insert (format "#lang %s\n(let (%s)\n%s)") - lang - vars-string - expanded-body + ((equal result-type 'value) + (let ((vars-string + (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) + (insert (format "#lang %s\n(let (%s)\n%s)" + lang + vars-string + expanded-body)))) ;; Results type is "output" - run as script - ((equal result-type 'output)))) - (let ((vars-string - (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) - (insert (format "#lang %s\n%s\n%s") - lang - vars-string - body + ((equal result-type 'output) + (let ((vars-string + (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) + (insert (format "#lang %s\n%s\n%s" + lang + vars-string + body)))) ;; Unknown result type?? - (t (error "Invalid result type: %s" result-type))) + (t (error "Invalid result type: %s" result-type)))) ;; Run script with Racket interpreter, delete temp file, and return output (with-temp-buffer - (prog2)) - (call-process org-babel-command:racket nil (current-buffer) nil temp-file) - (buffer-string)) - (delete-file temp-file)) + (prog2 + (call-process org-babel-command:racket nil (current-buffer) nil temp-file) + (buffer-string) + (delete-file temp-file))))) (defun org-babel-prep-session:racket (session params) (error "Racket does not currently support sessions.")) @@ -96,14 +94,14 @@ (defun ob-racket--parse-params (params) "Processes and parses parameters for an Org Babel code block. The results are returned as a list." - (let ((processed-params (org-babel-process-params params)))) - (result-type nil) - (lang nil) - (vars nil + (let ((processed-params (org-babel-process-params params)) + (result-type nil) + (lang nil) + (vars nil)) (dolist (processed-param processed-params) - (let ((key (car processed-param)) (value (cdr processed-param)))))) - (cond - ((equal key :result-type) (setq result-type value)) - ((equal key :lang) (setq lang value)) - ((equal key :var) (push value vars) - (list result-type lang vars)))) + (let ((key (car processed-param)) (value (cdr processed-param))) + (cond + ((equal key :result-type) (setq result-type value)) + ((equal key :lang) (setq lang value)) + ((equal key :var) (push value vars))))) + (list result-type lang vars))) From 97bf3aeabb8633fa125984fb0d56c2067a8292ba Mon Sep 17 00:00:00 2001 From: George Mauer Date: Fri, 15 May 2020 14:13:32 -0500 Subject: [PATCH 02/14] racket executable now customizable --- ob-racket.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index ca9770b..46551e2 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -38,8 +38,10 @@ (defvar org-babel-default-header-args:racket '((:lang . "racket")) "A list of default header args for Racket code blocks.") -(defvar org-babel-command:racket "/usr/bin/racket" - "The path to the Racket interpreter executable.") +(defcustom org-babel-command:racket "/usr/bin/racket" + "The path to the Racket interpreter executable." + :group 'org-babel + :type 'string) ;; -- Babel Functions -- From 394c3ad087887a0e28a03e93630c3dc1d1fb2d70 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Fri, 29 May 2020 21:16:13 -0500 Subject: [PATCH 03/14] proper indenttion --- ob-racket.el | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index 46551e2..6a186e4 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -55,38 +55,41 @@ "Executes a Racket code block." ;; Round up the stuff we need (let* ((parsed-params (ob-racket--parse-params params)) - (expanded-body (org-babel-expand-body:racket body params)) - (result-type (nth 0 parsed-params)) - (lang (nth 1 parsed-params)) - (vars (nth 2 parsed-params)) - (temp-file (make-temp-file "ob-racket-"))) + (expanded-body (org-babel-expand-body:racket body params)) + (result-type (nth 0 parsed-params)) + (lang (nth 1 parsed-params)) + (vars (nth 2 parsed-params)) + (requires-statements (nth 3 parsed-params)) + (temp-file (make-temp-file "ob-racket-"))) ;; Build script in temporary file (with-temp-file temp-file (cond ;; Results type is "value" - run in let form ((equal result-type 'value) - (let ((vars-string - (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) - (insert (format "#lang %s\n(let (%s)\n%s)" - lang - vars-string - expanded-body)))) + (let ((vars-string + (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) + (insert (format "#lang %s\n%s\n(let (%s)\n%s)" + lang + requires-statements + vars-string + expanded-body)))) ;; Results type is "output" - run as script ((equal result-type 'output) - (let ((vars-string - (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) - (insert (format "#lang %s\n%s\n%s" - lang - vars-string - body)))) + (let ((vars-string + (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) + (insert (format "#lang %s\n%s\n%s\n%s" + lang + requires-statements + vars-string + body)))) ;; Unknown result type?? (t (error "Invalid result type: %s" result-type)))) ;; Run script with Racket interpreter, delete temp file, and return output (with-temp-buffer (prog2 - (call-process org-babel-command:racket nil (current-buffer) nil temp-file) - (buffer-string) - (delete-file temp-file))))) + (call-process org-babel-command:racket nil (current-buffer) nil temp-file) + (buffer-string) + (delete-file temp-file))))) (defun org-babel-prep-session:racket (session params) (error "Racket does not currently support sessions.")) From 49068dc9e69ddc970981ec708038831e48895a26 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Fri, 29 May 2020 22:13:17 -0500 Subject: [PATCH 04/14] :require header works --- README.org | 100 ++++++++++++++++++++++++++++----------------------- ob-racket.el | 21 ++++++----- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/README.org b/README.org index 037ae46..c202868 100644 --- a/README.org +++ b/README.org @@ -1,63 +1,75 @@ * Org-Mode Babel Support for Racket -This Emacs module enables support for the [[https://racket-lang.org][Racket programming language]] in Emacs' -[[http://orgmode.org/worg/org-contrib/babel/][Org-mode Babel]]. This allows executing Racket code blocks directly from Org mode, -embedding the results of those code blocks in your Org files, and even chaining -the result from one code block into another code block. See the [[http://orgmode.org/worg/org-contrib/babel/intro.html][Babel intro]] for -more details on what's possible. + This Emacs module enables support for the [[https://racket-lang.org][Racket programming language]] in Emacs' + [[http://orgmode.org/worg/org-contrib/babel/][Org-mode Babel]]. This allows executing Racket code blocks directly from Org mode, + embedding the results of those code blocks in your Org files, and even chaining + the result from one code block into another code block. See the [[http://orgmode.org/worg/org-contrib/babel/intro.html][Babel intro]] for + more details on what's possible. ** Example Usage -This example shows how to add a code block implementing the classic recursive -factorial. With point inside the code block, press =C-c C-c= to execute the -block. The result will be inserted immediately beneath it. + This example shows how to add a code block implementing the classic recursive + factorial. With point inside the code block, press =C-c C-c= to execute the + block. The result will be inserted immediately beneath it. -#+BEGIN_SRC org - ,#+BEGIN_SRC racket :var input=10 - (define (factorial n) - (if (= n 1) - 1 - (* n (factorial (sub1 n))))) - (factorial input) - ,#+END_SRC + #+BEGIN_SRC org + ,#+BEGIN_SRC racket :var input=10 + (define (factorial n) + (if (= n 1) + 1 + (* n (factorial (sub1 n))))) + (factorial input) + ,#+END_SRC - ,#+RESULTS: - : 3628800 -#+END_SRC + ,#+RESULTS: + : 3628800 + #+END_SRC ** Supported Header Arguments -- :results :: Can be set to either =value= or =output=. If set to =value=, the - code block will be wrapped in a (let ...) form, and only the result of the form - will recorded. If set to =output=, the code block will be run as a script, and - all standard output will be recorded. Defaults to =value=. -- :lang :: Specifies the language used in the =#lang= directive at the beginning - of the script. Defaults to "racket". -- :var :: Allows defining a variable for use in the block. If using the =value= - output type, the variable will be passed to the wrapping function as an argument. - Otherwise, it will be defined at the top level of the script using a =(define ...)= - form. + - :results :: Can be set to either =value= or =output=. If set to =value=, the + code block will be wrapped in a (let ...) form, and only the result of the form + will recorded. If set to =output=, the code block will be run as a script, and + all standard output will be recorded. Defaults to =value=. + - :lang :: Specifies the language used in the =#lang= directive at the beginning + of the script. Defaults to "racket". + - :var :: Allows defining a variable for use in the block. If using the =value= + output type, the variable will be passed to the wrapping function as an argument. + Otherwise, it will be defined at the top level of the script using a =(define ...)= + form. + - :require :: Allows you to use require statements. Because the way =:results + value= works is by wrapping everything in a =let=, you cannot normally use + a =require= statement (these have to be top-level). To help, =:require + racket/date= will generate =(require racket/date)= outside the =let= block. + Multiple =require= statements can be used and they may or may not be + quoted. While not strictly necessary for =:results output=, will work + anyways to keep with the convention. + ** Installation -- Install =ob-racket.el= in your Emacs [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html#Lisp-Libraries][load path]] -- Add the following to your =.emacs.d= file: + - Install =ob-racket.el= in your Emacs [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html#Lisp-Libraries][load path]] + - Add the following to your =.emacs.d= file: -#+BEGIN_SRC emacs-lisp - ;; Enable Racket in Org-mode Babel - (org-babel-do-load-languages - 'org-babel-load-languages - '((racket . t))) -#+END_SRC + #+BEGIN_SRC emacs-lisp + ;; Enable Racket in Org-mode Babel + (org-babel-do-load-languages + 'org-babel-load-languages + '((racket . t))) + #+END_SRC -- If your Racket interpreter is installed in a non-standard location (anywhere - other than =/usr/bin/racket=), also add the following to your =.emacs.d= file: + - If your Racket interpreter is installed in a non-standard location (anywhere + other than =/usr/bin/racket=), also add the following to your =.emacs.d= file: -#+BEGIN_SRC emacs-lisp - ;; Set path to racket interpreter - (setq org-babel-command:racket "/path/goes/here") -#+END_SRC + #+BEGIN_SRC emacs-lisp + ;; Set path to racket interpreter + (setq org-babel-command:racket "/path/goes/here") + #+END_SRC ** Author -Chris Vig (chris@invictus.so) + Chris Vig (chris@invictus.so) + +*** Contributors + + - [[http://georgemauer.net][George Mauer]] diff --git a/ob-racket.el b/ob-racket.el index 6a186e4..fd5a281 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -59,27 +59,28 @@ (result-type (nth 0 parsed-params)) (lang (nth 1 parsed-params)) (vars (nth 2 parsed-params)) - (requires-statements (nth 3 parsed-params)) - (temp-file (make-temp-file "ob-racket-"))) + (requires (nth 3 parsed-params)) + (temp-file (make-temp-file "ob-racket-")) + (requires-string (mapconcat (lambda (r) (format "(require %s)" r)) requires "\n"))) ;; Build script in temporary file (with-temp-file temp-file (cond ;; Results type is "value" - run in let form ((equal result-type 'value) (let ((vars-string - (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) - (insert (format "#lang %s\n%s\n(let (%s)\n%s)" + (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) + (insert (format "#lang %s \n%s \n(let (%s) \n%s)" lang - requires-statements + requires-string vars-string expanded-body)))) ;; Results type is "output" - run as script ((equal result-type 'output) (let ((vars-string (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) - (insert (format "#lang %s\n%s\n%s\n%s" + (insert (format "#lang %s \n%s \n%s \n%s" lang - requires-statements + requires-string vars-string body)))) ;; Unknown result type?? @@ -102,11 +103,13 @@ returned as a list." (let ((processed-params (org-babel-process-params params)) (result-type nil) (lang nil) + (requires nil) (vars nil)) (dolist (processed-param processed-params) (let ((key (car processed-param)) (value (cdr processed-param))) (cond ((equal key :result-type) (setq result-type value)) ((equal key :lang) (setq lang value)) - ((equal key :var) (push value vars))))) - (list result-type lang vars))) + ((equal key :var) (push value vars)) + ((equal key :require) (push value requires))))) + (list result-type lang vars requires))) From fe7c12d25e8d38768bdac420be8d204240f2ec02 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Sat, 4 Jul 2020 00:01:52 -0500 Subject: [PATCH 05/14] can use custom racket langs --- ob-racket.el | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index fd5a281..a6e8cfa 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -56,33 +56,34 @@ ;; Round up the stuff we need (let* ((parsed-params (ob-racket--parse-params params)) (expanded-body (org-babel-expand-body:racket body params)) - (result-type (nth 0 parsed-params)) - (lang (nth 1 parsed-params)) - (vars (nth 2 parsed-params)) - (requires (nth 3 parsed-params)) + (result-type (cadr (assoc 'result-type parsed-params))) (temp-file (make-temp-file "ob-racket-")) - (requires-string (mapconcat (lambda (r) (format "(require %s)" r)) requires "\n"))) - ;; Build script in temporary file + (requires-string (mapconcat (lambda (r) (format "(require %s)" r)) + (cadr (assoc 'requires parsed-params)) + "\n"))) (with-temp-file temp-file (cond ;; Results type is "value" - run in let form ((equal result-type 'value) - (let ((vars-string - (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) vars " "))) + (let ((vars-string (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) + (cadr (assoc 'vars parsed-params)) + " "))) (insert (format "#lang %s \n%s \n(let (%s) \n%s)" - lang + (cadr (assoc 'racket-lang parsed-params)) requires-string vars-string expanded-body)))) ;; Results type is "output" - run as script ((equal result-type 'output) - (let ((vars-string - (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) vars "\n"))) - (insert (format "#lang %s \n%s \n%s \n%s" - lang - requires-string - vars-string - body)))) + (let* ((vars-string (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) + (cadr (assoc 'vars parsed-params)) + "\n")) + (output (format "#lang %s \n%s \n%s \n%s" + (cadr (assoc 'racket-lang parsed-params)) + requires-string + vars-string + expanded-body))) + (insert output))) ;; Unknown result type?? (t (error "Invalid result type: %s" result-type)))) ;; Run script with Racket interpreter, delete temp file, and return output @@ -104,12 +105,19 @@ returned as a list." (result-type nil) (lang nil) (requires nil) - (vars nil)) + (vars nil) + (racket-lang "racket")) (dolist (processed-param processed-params) - (let ((key (car processed-param)) (value (cdr processed-param))) + (let ((key (car processed-param)) + (value (cdr processed-param))) (cond ((equal key :result-type) (setq result-type value)) ((equal key :lang) (setq lang value)) ((equal key :var) (push value vars)) - ((equal key :require) (push value requires))))) - (list result-type lang vars requires))) + ((equal key :require) (push value requires)) + ((equal key :racket-lang) (setq racket-lang value))))) + `((lang ,lang) + (result-type ,result-type) + (vars ,vars) + (requires ,requires) + (racket-lang ,racket-lang)))) From 6dc0fb7e0c0da26e63e8b976b45c95e90afb6163 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Wed, 8 Jul 2020 08:37:05 -0500 Subject: [PATCH 06/14] fix ob-racket to allow usage of other languages! --- ob-racket.el | 74 +++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index a6e8cfa..df5b607 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -38,6 +38,14 @@ (defvar org-babel-default-header-args:racket '((:lang . "racket")) "A list of default header args for Racket code blocks.") +(defun ob-racket--format-require (requirement) + "Format require statement for Racket" + (format "(require %s)" requirement)) + +(defun ob-racket--format-var (var-name var-value) + "Format variable assignment statement for racket" + (format "(define %s (quote %s))" var-name var-value)) + (defcustom org-babel-command:racket "/usr/bin/racket" "The path to the Racket interpreter executable." :group 'org-babel @@ -45,47 +53,41 @@ ;; -- Babel Functions -- -(defun org-babel-expand-body:racket (body params) +(defun org-babel-expand-body:racket (body unparsed-params) "Expands the body of a Racket code block." - ;; Currently we don't do any expansion for tangled blocks. Just return - ;; body unmodified as specified by the user. - body) + (let* ((lines-of-body (split-string body "\n")) + (first-line (car lines-of-body)) + (params (ob-racket--parse-params unparsed-params)) + (output-lines-stack '())) + (cl-flet* ((out (l) (add-to-list 'output-lines-stack l)) + (out-headers () + (when-let* ((requires (assoc 'requires params))) + (out (mapconcat 'ob-racket--format-require + (cadr requires) + "\n"))) + (when-let* ((vars (cadr (assoc 'vars params)))) + (out (mapconcat (lambda (var) (ob-racket--format-var (car var) (cdr var))) + vars + "\n"))))) + ;; actually not sure this next part is necessary + (cond ((string-match "#lang" first-line) + (out first-line) + (out-headers)) + (t + (out "#lang racket") + (out-headers) + (out first-line))) + (dolist (l (cdr lines-of-body)) + (out l)) + (string-join (reverse output-lines-stack) "\n")))) + (defun org-babel-execute:racket (body params) "Executes a Racket code block." - ;; Round up the stuff we need - (let* ((parsed-params (ob-racket--parse-params params)) - (expanded-body (org-babel-expand-body:racket body params)) - (result-type (cadr (assoc 'result-type parsed-params))) - (temp-file (make-temp-file "ob-racket-")) - (requires-string (mapconcat (lambda (r) (format "(require %s)" r)) - (cadr (assoc 'requires parsed-params)) - "\n"))) + (let* ((expanded-body (org-babel-expand-body:racket body params)) + (temp-file (make-temp-file "ob-racket-"))) (with-temp-file temp-file - (cond - ;; Results type is "value" - run in let form - ((equal result-type 'value) - (let ((vars-string (mapconcat (lambda (var) (format "[%s (quote %s)]" (car var) (cdr var))) - (cadr (assoc 'vars parsed-params)) - " "))) - (insert (format "#lang %s \n%s \n(let (%s) \n%s)" - (cadr (assoc 'racket-lang parsed-params)) - requires-string - vars-string - expanded-body)))) - ;; Results type is "output" - run as script - ((equal result-type 'output) - (let* ((vars-string (mapconcat (lambda (var) (format "(define %s (quote %s))" (car var) (cdr var))) - (cadr (assoc 'vars parsed-params)) - "\n")) - (output (format "#lang %s \n%s \n%s \n%s" - (cadr (assoc 'racket-lang parsed-params)) - requires-string - vars-string - expanded-body))) - (insert output))) - ;; Unknown result type?? - (t (error "Invalid result type: %s" result-type)))) + (insert expanded-body)) ;; Run script with Racket interpreter, delete temp file, and return output (with-temp-buffer (prog2 From 4f31727ef3229c9282f35f31e619a3c7f65ed881 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Wed, 8 Jul 2020 17:11:26 -0500 Subject: [PATCH 07/14] cleanup --- README.org | 18 ++++++++++++++++-- ob-racket.el | 9 +++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/README.org b/README.org index c202868..32891e7 100644 --- a/README.org +++ b/README.org @@ -25,14 +25,28 @@ : 3628800 #+END_SRC + + This example shows how you can use custom languages. To run this you will need [[file:20200704153240-beautiful_racket.org][Beautiful Racket]] installed. + #+begin_src org + ,#+BEGIN_SRC racket :results output + #lang reader stacker-demo/stacker + 4 + 8 + + + 3 + * + ,#+END_SRC + + ,#+RESULTS: + : 36 + #+end_src + ** Supported Header Arguments - :results :: Can be set to either =value= or =output=. If set to =value=, the code block will be wrapped in a (let ...) form, and only the result of the form will recorded. If set to =output=, the code block will be run as a script, and all standard output will be recorded. Defaults to =value=. - - :lang :: Specifies the language used in the =#lang= directive at the beginning - of the script. Defaults to "racket". - :var :: Allows defining a variable for use in the block. If using the =value= output type, the variable will be passed to the wrapping function as an argument. Otherwise, it will be defined at the top level of the script using a =(define ...)= diff --git a/ob-racket.el b/ob-racket.el index df5b607..ff00a0e 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -107,8 +107,7 @@ returned as a list." (result-type nil) (lang nil) (requires nil) - (vars nil) - (racket-lang "racket")) + (vars nil)) (dolist (processed-param processed-params) (let ((key (car processed-param)) (value (cdr processed-param))) @@ -116,10 +115,8 @@ returned as a list." ((equal key :result-type) (setq result-type value)) ((equal key :lang) (setq lang value)) ((equal key :var) (push value vars)) - ((equal key :require) (push value requires)) - ((equal key :racket-lang) (setq racket-lang value))))) + ((equal key :require) (push value requires))))) `((lang ,lang) (result-type ,result-type) (vars ,vars) - (requires ,requires) - (racket-lang ,racket-lang)))) + (requires ,requires)))) From 30d4da6f836c6b2f3eb04d0617fae277ac404826 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Thu, 9 Jul 2020 09:53:22 -0500 Subject: [PATCH 08/14] ensure lines are not truncated, fix bug that deduped lines --- ob-racket.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index ff00a0e..f7d778c 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -58,8 +58,9 @@ (let* ((lines-of-body (split-string body "\n")) (first-line (car lines-of-body)) (params (ob-racket--parse-params unparsed-params)) - (output-lines-stack '())) - (cl-flet* ((out (l) (add-to-list 'output-lines-stack l)) + (output-lines-stack '()) + (print-length nil)) ;; Used by the format function which is invoked inside string formatting + (cl-flet* ((out (l) (push l output-lines-stack)) (out-headers () (when-let* ((requires (assoc 'requires params))) (out (mapconcat 'ob-racket--format-require From 332c30fb854bffa6fd2382d13d23ca98ffec705b Mon Sep 17 00:00:00 2001 From: George Mauer Date: Sun, 9 Aug 2020 15:42:05 -0500 Subject: [PATCH 09/14] fix formatting of vars --- ob-racket.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ob-racket.el b/ob-racket.el index f7d778c..b9e4e0a 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -44,7 +44,7 @@ (defun ob-racket--format-var (var-name var-value) "Format variable assignment statement for racket" - (format "(define %s (quote %s))" var-name var-value)) + (format "(define %s (quote %S))" var-name var-value)) (defcustom org-babel-command:racket "/usr/bin/racket" "The path to the Racket interpreter executable." From 71aebf60afcc2aa592c464af36caaa496143307a Mon Sep 17 00:00:00 2001 From: George Mauer Date: Sun, 9 Aug 2020 15:44:50 -0500 Subject: [PATCH 10/14] remove parsing for unused lang header --- ob-racket.el | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index b9e4e0a..3f034e0 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -106,7 +106,6 @@ returned as a list." (let ((processed-params (org-babel-process-params params)) (result-type nil) - (lang nil) (requires nil) (vars nil)) (dolist (processed-param processed-params) @@ -114,10 +113,8 @@ returned as a list." (value (cdr processed-param))) (cond ((equal key :result-type) (setq result-type value)) - ((equal key :lang) (setq lang value)) ((equal key :var) (push value vars)) ((equal key :require) (push value requires))))) - `((lang ,lang) - (result-type ,result-type) + `((result-type ,result-type) (vars ,vars) (requires ,requires)))) From 1dd509803128c2ae6c4377c4c238928a797d4a56 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Sun, 29 Nov 2020 23:04:54 -0600 Subject: [PATCH 11/14] can now use cusotm lanaguages via the :adjacent-file header feature --- README.org | 96 ++++++++++++++++++++++++++++++++++++++++++++-------- ob-racket.el | 32 +++++++++++++----- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/README.org b/README.org index 32891e7..b2e7031 100644 --- a/README.org +++ b/README.org @@ -25,21 +25,81 @@ : 3628800 #+END_SRC - + This example shows how you can use custom languages. To run this you will need [[file:20200704153240-beautiful_racket.org][Beautiful Racket]] installed. #+begin_src org - ,#+BEGIN_SRC racket :results output - #lang reader stacker-demo/stacker - 4 - 8 - + - 3 - * - ,#+END_SRC - - ,#+RESULTS: - : 36 - #+end_src + ,#+BEGIN_SRC racket :results output + #lang reader stacker-demo/stacker + 4 + 8 + + + 3 + ,,* + ,#+END_SRC + + ,#+RESULTS: + : 36 + #+end_src + +*** Custom Languages + To best implement and test a custom language we have to be able to emit the + language implementation in a known location next to its usage. We can do + this by naming a block and then referencing via the ~:adjacent-file~ header. + + + #+name: stacker-reader-expander.rkt + #+begin_src racket :eval no :noweb strip-export :tangle + #lang br/quicklang + + (define (read-syntax path port) + (define src-lines (port->lines port)) + (define src-datums (format-datums '(handle ~a) src-lines)) + (define module-datum `(module stacker-mod "./stacker-reader-expander.rkt" + ,@src-datums)) + (datum->syntax #f module-datum)) + (provide read-syntax) + + (define-macro (stacker-module-begin HANDLE-EXPR ...) + #'(#%module-begin + HANDLE-EXPR ... + (display (first stack)))) + (provide (rename-out [stacker-module-begin #%module-begin])) + + + (define stack empty) + + (define (pop-stack!) + (define item (first stack)) + (set! stack (rest stack)) + item) + + (define (push-stack! item) + (set! stack (cons item stack))) + + (define (handle [x #f]) + (when x + (cond + [(number? x) (push-stack! x)] + [(or (equal? + x) + (equal? * x)) + (define op-result (x (pop-stack!) (pop-stack!))) + (push-stack! op-result)]))) + (provide handle + *) + #+end_src + + This will now work! + + #+begin_src racket :adjacent-file stacker-reader-expander.rkt + #lang reader "./stacker-reader-expander.rkt" + 4 + 8 + + + 3 + ,* + #+end_src + + #+RESULTS: + : 36 ** Supported Header Arguments @@ -51,14 +111,20 @@ output type, the variable will be passed to the wrapping function as an argument. Otherwise, it will be defined at the top level of the script using a =(define ...)= form. - - :require :: Allows you to use require statements. Because the way =:results + - :require :: =DEPRECATED= Allows you to use require statements. Because the way =:results value= works is by wrapping everything in a =let=, you cannot normally use a =require= statement (these have to be top-level). To help, =:require racket/date= will generate =(require racket/date)= outside the =let= block. Multiple =require= statements can be used and they may or may not be quoted. While not strictly necessary for =:results output=, will work anyways to keep with the convention. - + - :adjacent-file :: Should be the name of another (presumably Racket) block + in the same document. This block source will be expanded and emitted + adjacent to the main file during execution. This is especially useful for + implementing languages in one block and testing them in another. Note that + at the moment, multiple ~:adjacent-file~ blocks are not supported. + + ** Installation diff --git a/ob-racket.el b/ob-racket.el index 3f034e0..322b3fa 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -46,6 +46,11 @@ "Format variable assignment statement for racket" (format "(define %s (quote %S))" var-name var-value)) +(defun ob-racket--expand-named-src-block (blockname) + (save-excursion + (goto-char (org-babel-find-named-block blockname)) + (org-babel-expand-src-block))) + (defcustom org-babel-command:racket "/usr/bin/racket" "The path to the Racket interpreter executable." :group 'org-babel @@ -53,11 +58,10 @@ ;; -- Babel Functions -- -(defun org-babel-expand-body:racket (body unparsed-params) +(defun org-babel-expand-body:racket (body params) "Expands the body of a Racket code block." (let* ((lines-of-body (split-string body "\n")) (first-line (car lines-of-body)) - (params (ob-racket--parse-params unparsed-params)) (output-lines-stack '()) (print-length nil)) ;; Used by the format function which is invoked inside string formatting (cl-flet* ((out (l) (push l output-lines-stack)) @@ -83,18 +87,25 @@ (string-join (reverse output-lines-stack) "\n")))) -(defun org-babel-execute:racket (body params) +(defun org-babel-execute:racket (body unparsed-params) "Executes a Racket code block." - (let* ((expanded-body (org-babel-expand-body:racket body params)) - (temp-file (make-temp-file "ob-racket-"))) - (with-temp-file temp-file - (insert expanded-body)) + (let* ((params (ob-racket--parse-params unparsed-params)) + (main-src (org-babel-expand-body:racket body params)) + (temporary-file-directory (file-name-as-directory (make-temp-file "ob-racket-" 'make-directory-only))) + (main-filename (concat temporary-file-directory (make-temp-name "ob-racket") "rkt"))) + (dolist (blockname (cadr (assoc 'adjacent-files params))) + (let ((filename (concat temporary-file-directory blockname)) + (src (ob-racket--expand-named-src-block blockname))) + (with-temp-file filename + (insert src)))) + (with-temp-file main-filename + (insert main-src)) ;; Run script with Racket interpreter, delete temp file, and return output (with-temp-buffer (prog2 - (call-process org-babel-command:racket nil (current-buffer) nil temp-file) + (call-process org-babel-command:racket nil (current-buffer) nil main-filename) (buffer-string) - (delete-file temp-file))))) + (delete-directory temporary-file-directory 'recursive))))) (defun org-babel-prep-session:racket (session params) (error "Racket does not currently support sessions.")) @@ -107,6 +118,7 @@ returned as a list." (let ((processed-params (org-babel-process-params params)) (result-type nil) (requires nil) + (adjacent-files nil) (vars nil)) (dolist (processed-param processed-params) (let ((key (car processed-param)) @@ -114,7 +126,9 @@ returned as a list." (cond ((equal key :result-type) (setq result-type value)) ((equal key :var) (push value vars)) + ((equal key :adjacent-file) (push value adjacent-files)) ((equal key :require) (push value requires))))) `((result-type ,result-type) (vars ,vars) + (adjacent-files ,adjacent-files) (requires ,requires)))) From ec8b73e1b36a01a4bb3ab3ce646c2cabe1f72702 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Wed, 2 Dec 2020 17:22:01 -0600 Subject: [PATCH 12/14] Readme was hiding header on custom lang demo --- README.org | 100 ++++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/README.org b/README.org index b2e7031..d724a64 100644 --- a/README.org +++ b/README.org @@ -47,60 +47,64 @@ this by naming a block and then referencing via the ~:adjacent-file~ header. - #+name: stacker-reader-expander.rkt - #+begin_src racket :eval no :noweb strip-export :tangle - #lang br/quicklang - - (define (read-syntax path port) - (define src-lines (port->lines port)) - (define src-datums (format-datums '(handle ~a) src-lines)) - (define module-datum `(module stacker-mod "./stacker-reader-expander.rkt" - ,@src-datums)) - (datum->syntax #f module-datum)) - (provide read-syntax) - - (define-macro (stacker-module-begin HANDLE-EXPR ...) - #'(#%module-begin - HANDLE-EXPR ... - (display (first stack)))) - (provide (rename-out [stacker-module-begin #%module-begin])) - - - (define stack empty) - - (define (pop-stack!) - (define item (first stack)) - (set! stack (rest stack)) - item) - - (define (push-stack! item) - (set! stack (cons item stack))) - - (define (handle [x #f]) - (when x - (cond - [(number? x) (push-stack! x)] - [(or (equal? + x) - (equal? * x)) - (define op-result (x (pop-stack!) (pop-stack!))) - (push-stack! op-result)]))) - (provide handle + *) + #+begin_src org + #+name: stacker-reader-expander.rkt + ,#+begin_src racket :eval no :noweb strip-export :tangle + #lang br/quicklang + + (define (read-syntax path port) + (define src-lines (port->lines port)) + (define src-datums (format-datums '(handle ~a) src-lines)) + (define module-datum `(module stacker-mod "./stacker-reader-expander.rkt" + ,@src-datums)) + (datum->syntax #f module-datum)) + (provide read-syntax) + + (define-macro (stacker-module-begin HANDLE-EXPR ...) + #'(#%module-begin + HANDLE-EXPR ... + (display (first stack)))) + (provide (rename-out [stacker-module-begin #%module-begin])) + + + (define stack empty) + + (define (pop-stack!) + (define item (first stack)) + (set! stack (rest stack)) + item) + + (define (push-stack! item) + (set! stack (cons item stack))) + + (define (handle [x #f]) + (when x + (cond + [(number? x) (push-stack! x)] + [(or (equal? + x) + (equal? * x)) + (define op-result (x (pop-stack!) (pop-stack!))) + (push-stack! op-result)]))) + (provide handle + *) + ,#+end_src #+end_src This will now work! - #+begin_src racket :adjacent-file stacker-reader-expander.rkt - #lang reader "./stacker-reader-expander.rkt" - 4 - 8 - + - 3 - ,* + #+begin_src org + ,#+begin_src racket :adjacent-file stacker-reader-expander.rkt + #lang reader "./stacker-reader-expander.rkt" + 4 + 8 + + + 3 + ,* + ,#+end_src + + #+RESULTS: + : 36 #+end_src - #+RESULTS: - : 36 - ** Supported Header Arguments - :results :: Can be set to either =value= or =output=. If set to =value=, the From ac7d6040292dcf3a13f18746bdef30c9ec98bbaa Mon Sep 17 00:00:00 2001 From: George Mauer Date: Mon, 4 Jan 2021 12:53:42 -0600 Subject: [PATCH 13/14] Can render multiple adjacent files --- README.org | 8 +++++--- ob-racket.el | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.org b/README.org index d724a64..b18c31a 100644 --- a/README.org +++ b/README.org @@ -126,10 +126,12 @@ in the same document. This block source will be expanded and emitted adjacent to the main file during execution. This is especially useful for implementing languages in one block and testing them in another. Note that - at the moment, multiple ~:adjacent-file~ blocks are not supported. - - + at the moment, multiple ~:adjacent-file~ blocks are not supported. You can + however pass a white-space-separated list to this argument eg + ~:adjacent-file foo1.rkt foo2.rkt~ where there are blocks named both + ~foo1.rkt~ and ~foo2.rkt~ in the same document. + ** Installation - Install =ob-racket.el= in your Emacs [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html#Lisp-Libraries][load path]] diff --git a/ob-racket.el b/ob-racket.el index 322b3fa..5ea1b98 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -126,7 +126,7 @@ returned as a list." (cond ((equal key :result-type) (setq result-type value)) ((equal key :var) (push value vars)) - ((equal key :adjacent-file) (push value adjacent-files)) + ((equal key :adjacent-file) (setq adjacent-files (split-string value "\s+"))) ((equal key :require) (push value requires))))) `((result-type ,result-type) (vars ,vars) From 1e2d8269be00929fb326b633a40972a688de8f87 Mon Sep 17 00:00:00 2001 From: George Mauer Date: Sun, 10 Oct 2021 20:41:25 -0500 Subject: [PATCH 14/14] set current working directory properly when calling our racket scripts. Actually use an extension than just an rkt suffix --- ob-racket.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ob-racket.el b/ob-racket.el index 5ea1b98..2383bb5 100644 --- a/ob-racket.el +++ b/ob-racket.el @@ -92,7 +92,7 @@ (let* ((params (ob-racket--parse-params unparsed-params)) (main-src (org-babel-expand-body:racket body params)) (temporary-file-directory (file-name-as-directory (make-temp-file "ob-racket-" 'make-directory-only))) - (main-filename (concat temporary-file-directory (make-temp-name "ob-racket") "rkt"))) + (main-filename (concat temporary-file-directory (make-temp-name "ob-racket") ".rkt"))) (dolist (blockname (cadr (assoc 'adjacent-files params))) (let ((filename (concat temporary-file-directory blockname)) (src (ob-racket--expand-named-src-block blockname))) @@ -103,7 +103,8 @@ ;; Run script with Racket interpreter, delete temp file, and return output (with-temp-buffer (prog2 - (call-process org-babel-command:racket nil (current-buffer) nil main-filename) + (let ((default-directory temporary-file-directory)) + (call-process org-babel-command:racket nil (current-buffer) nil main-filename)) (buffer-string) (delete-directory temporary-file-directory 'recursive)))))