Skip to content

DEADB17/ob-janet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Janet language support in Emacs Org-mode

Support for evaluating Janet code in org mode. See the Working with source code section in the org manual.

ob-janet.el is based on ob-racket.

Installation

  1. Install Org mode
  2. Install Janet
  3. Download ob-janet.el and place it in your load-path
  4. When using use-package add the following to your init.el file:
    (use-package ob-janet
      :after org
      :pin manual
      :config
      (append '((janet . t)) org-babel-load-languages))
        
  5. Optionally install janet-mode to edit Janet code.

Usage examples

In addition to the header arguments common to all languages, ob-janet has some specific ones to control how code is evaluated.
  • :cmd set to janet by default.

    Determines which Janet executable and any switches that will be used when evaluating the code.

  • :eval-file Not set by default.

    When requiring modules that are relative to the code block the evaluation needs to happen in a specific path instead of org-babel-temporary-directory.

    :eval-file FILENAME Can be used to specify where the code block is written and evaluated.

    If :eval-file “” is used, the name is taken from #+NAME and janet is used as the extension. :output-dir can be used to specify the directory.

No header arguments

#+begin_src janet
  (def str-1 "hello")
  (def str-2 "world")
  (def all (string/join [str-1 str-2] ", "))
  (string/ascii-upper all)
#+end_src

Outputs:

HELLO, WORLD

:results output

#+begin_src janet :results output
 (map pp
      [nil true false

       # Symbols
       'symbol
       'kebab-case-symbol
       'snake_case_symbol
       'my-module/my-fuction
       '*****
       '!%$^*__--__._+++===~-crazy-symbol
       '*global-var*
       '你好

       # Keywords
       :keyword :range :0x0x0x0 :a-keyword :: :

       # Numbers
       0 +0.0 -10_000 16r1234abcd 0x23.23 1e10 1.6e-4
       7r343_111_266.6&+10 # a base 7 number in scientific notation. evaluates to 1.72625e+13 in base 10

       # Strings
       "This is a string."
       "This\nis\na\nstring."
       "This
       is
       a
       string."
       ``
       This
       is
       a
       string
       ``

       # Buffers
       @"" @"Buffer." @``Another buffer``

       # Tuples
       (do 1 2 3) ['do 1 2 3]

       # Arrays
       @(:one :two :three) @[:one :two :three]

       # Structs
       {}
       {:key1 "value1" :key2 :value2 :key3 3}
       {[1 2 3] [4 5 6]}
       {@[] @[]}
       {1 2 3 4 5 6}

       # Tables
       @{}
       @{:key1 "value1" :key2 :value2 :key3 3}
       @{[1 2 3] [4 5 6]}
       @{@[] @[]}
       @{1 2 3 4 5 6}

       # Splice
       ;["one" 2 ['III]]
      ])
#+end_src

Outputs:

nil
true
false
symbol
kebab-case-symbol
snake_case_symbol
my-module/my-fuction
*****
!%$^*__--__._+++===
-crazy-symbol
*global-var*
你好
:keyword
:range
:0x0x0x0
:a-keyword
::
:
0
0
-10000
305441741
35.1367
1e+10
0.00016
1.72625e+13
"This is a string."
"This\nis\na\nstring."
"This      is      a      string."
"      This\n      is\n      a\n      string\n      "
@""
@"Buffer."
@"Another buffer"
3
(do 1 2 3)
@[:one :two :three]
@[:one :two :three]
{}
{:key2 :value2 :key3 3 :key1 "value1"}
{(1 2 3) (4 5 6)}
{@[] @[]}
{5 6 3 4 1 2}
@{}
@{:key2 :value2 :key3 3 :key1 "value1"}
@{(1 2 3) (4 5 6)}
@{@[] @[]}
@{5 6 3 4 1 2}
"one"
2
(III)

Lists – :results value is implied

#+NAME: a-list
#+begin_src janet :results list
  '(Hello Wonderful World)
#+end_src

Outputs:

  • Hello
  • Wonderful
  • World

Variables and tables – :results value is implied

#+begin_src janet :results table :var input=a-list
  [input nil input]
#+end_src

Outputs:

HelloWonderfulWorld
HelloWonderfulWorld

Debug

#+begin_src janet :var x=a-list :debug t
  (print x)
  (print "Hello World")
#+end_src

Outputs:

(pp (do (def x '(Hello Wonderful World))
(print x)
(print "Hello World")))

File

:results output is implicitly set.

#+NAME: code
#+begin_src janet :file-ext janet
  (defn a-func []
    (print "Hello World"))
#+end_src

Creates the file code.janet in the current directory with this content:

(defn a-func []
  (print "Hello World"))

The content of the file is not evaluated.

Eval-file

:results output is implicitly set to get the result of the evaluation.

#+NAME: eval-file
#+begin_src janet :eval-file ""
  (import ./code) # Created in the `File` sample
  (code/a-func)
#+end_src

Creates the file eval-file.janet in the current directory with this content:

(import ./code) # Created in the `File` sample
(code/a-func)

And outputs:

Hello World

Source code

The contents of ob-janet.el are extracted from this file. To re-generate the code, open this file in an Emacs buffer and M-x org-babel-tangle. The complete source will be in exported to ob-janet.el.

Main (Public functions)

org-babel-execute:janet

(defun org-babel-execute:janet (body params)
  "Evaluate a `janet' code block.  BODY and PARAMS.

Some custom header arguments are supported to control the
evaluation.  These are:

- :cmd which allows to set the Janet executable and the switches
  on each code block.

- :debug which outputs the body before passing it to the
  interpreter.

- :eval-file FILENAME which writes the body to FILENAME and then
  evaluates the result.  When FILENAME is equal to \"\" it is
  derived from the code-block name."
  (let ((vars        (org-babel--get-vars params))
        (prologue    (alist-get :prologue params))
        (epilogue    (alist-get :epilogue params))
        (cmd         (alist-get :cmd params "janet"))
        (result-type (alist-get :result-type params))
        (ext         (alist-get :file-ext params "janet"))
        (file        (alist-get :file params))
        (eval-file   (alist-get :eval-file params))
        x-body)

    (when (eq "" eval-file)
      (setq eval-file (alist-get :file
                                 (org-babel-generate-file-param
                                  (nth 4 (org-babel-get-src-block-info))
                                  (cons (cons :file-ext ext) params)))))

    (setq x-body (if (or vars prologue epilogue)
                     (ob-janet--wrap-body body vars prologue epilogue)
                   body))

    (when (and (not file)
               (not eval-file)
               (string= result-type "value"))
      (setq x-body (format org-babel-function-wrapper:janet x-body)))

    (if (assq :debug params)
        x-body
      (if file
          (with-temp-file file (insert x-body))
        (let* ((temp (or eval-file
                         (org-babel-temp-file "ob-" (concat "." ext))))
               (result (progn (with-temp-file temp (insert x-body))
                              (org-babel-eval (concat cmd " " temp) ""))))
          (org-babel-reassemble-table
           (org-babel-result-cond (alist-get :result-params params)
             result
             (ob-janet--table-or-string result))
           (org-babel-pick-name (alist-get :colname-names params)
                                (alist-get :colnames params))
           (org-babel-pick-name (alist-get :rowname-names params)
                                (alist-get :rownames params))))))))

org-babel-prep-session:janet

(defun org-babel-prep-session:janet (session params)
  "Not implemented.  SESSION and PARAMS are discarded."
  (error "`janet` presently does not support sessions"))

Auxiliary (Private functions)

org-babel-function-wrapper:janet

(defvar org-babel-function-wrapper:janet
  "(pp (do %s))"
  "Janet code to print value of body.")

ob-janet–table-or-string

(defun ob-janet--table-or-string (results)
  "Convert RESULTS into an appropriate elisp value.
If RESULTS look like a table, then convert them into an Emacs-lisp table,
otherwise return the results as a string."
  (let ((res (org-babel-script-escape (string-trim results))))
    (if (listp res)
        (mapcar
         (lambda (el)
           (if (equal el 'nil)
               org-babel-janet-nil-to el))
         res)
      res)))

ob-janet–wrap-body

(defun ob-janet--wrap-body (body vars prologue epilogue)
  "Wraps BODY VARS, PROLOGUE and EPILOGUE if present.
Returns the wrapped body as a string."
  (let ((var-defs nil))
    (when (> (length vars) 0)
      (setq var-defs (ob-janet--vars-to-values vars)))
    (mapconcat #'identity
               (append
                (when prologue (list (ob-janet--expand-fmt pro)))
                var-defs
                (list body)
                (when epilogue (list (ob-janet--expand-fmt epi))))
               "\n")))

ob-janet–vars-to-values

(defun ob-janet--vars-to-values (vars)
  "Convers VARS to a string of janet code.
VARS are wrapped with def."
  (mapcar (lambda (var)
            (concat
             "(def"
             (format " %s " (car var))
             (format (if (listp (cdr var)) "'%S" "%S") (cdr var))
             ")"))
          vars))

ob-janet–expand-fmt

(defun ob-janet--expand-fmt (fmt &optional params)
  "Expands a format list `FMT', and return a string.
PARAMS
Substitutes symbols according to the `params` alist.
The `fmt` argument may also be a string, in which
case it is returned as is."
  (if (stringp fmt)
      fmt
    (mapconcat
     (lambda (x)
       (cond
        ((stringp x) x)
        ((eq x 'ln) "\n")
        ((eq x 'quot) "\"")
        ((eq x 'apos) "\'")
        ((symbolp x)
         (let ((p (cdr (assq x params))))
           (unless p
             (error "Key %s not in %S" x params))
           (format "%s" p)))
        (t (error "Expected string or symbol: %S" fmt))))
     fmt "")))

Custom options

(defcustom org-babel-janet-hline-to "nil"
  "Replace hlines in incoming tables with this when translating to janet."
  :group 'org-babel
  :version "27.0.50"
  :package-version '(Org . "9.3.4")
  :type 'string)

(defcustom org-babel-janet-nil-to 'hline
  "Replace 'nil' in janet tables with this before returning."
  :group 'org-babel
  :version "27.0.50"
  :package-version '(Org . "9.3.4")
  :type 'symbol)

Defaults

Default header arguments.

(defvar org-babel-default-header-args:janet
  '((:cmd . "janet"))
  "Default arguments when evaluating a Janet source block.
Defaulting `:cmd' to `janet'.")

ob-janet.el

;;; ob-janet.el --- Janet language support in Emacs Org-mode  -*- lexical-binding: t; -*-

;; Copyright (C) 2020 DEADB17

;; Author: DEADB17
;; Version: 1.0.0
;; Created: 2020-02-09
;; Keywords: literate programming, janet
;; Homepage: https://github.com/DEADB17/ob-janet

;; This file is not part of GNU Emacs

;;; License:

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Support for evaluating janet code in org-mode
;; See https://orgmode.org/manual/Working-with-source-code.html

;; Requirements:

;; - Janet, see http://janet-lang.org/
;; - janet-mode https://github.com/ALSchwalm/janet-mode

;;; Code:

(require 'ob)

;; add janet to languages supported by org
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("janet" . "janet"))

<<custom-options>>

<<defaults>>

<<auxiliary>>

<<main>>

(provide 'ob-janet)

;;; ob-janet.el ends here

Releases

No releases published

Packages

No packages published