fromElisp icon indicating copy to clipboard operation
fromElisp copied to clipboard

An Emacs Lisp reader in Nix.

#+TITLE: fromElisp

An Emacs Lisp reader in Nix. Yes, it's /necessary/.

#+begin_example nix-repl> :p fromElisp "(use-package lsp-mode :ensure :commands lsp)" [ [ "use-package" "lsp-mode" ":ensure" ":commands" "lsp" ] ] #+end_example

  • Functionality

    ~fromElisp~ provides four main functions: ~fromElisp~, ~parseElisp~, ~fromOrgModeBabelElisp~ and ~parseOrgModeBabelElisp~.

    • fromElisp /elisp/ (string)

      Takes a string argument and tries to convert the Elisp read from it to the closest equivalent data type in Nix.

      For example, #+begin_src emacs-lisp :tangle yes (use-package highlight-symbol :ensure t :hook (((python-mode emacs-lisp-mode nix-mode) . highlight-symbol-mode) ((python-mode emacs-lisp-mode nix-mode) . highlight-symbol-nav-mode)) :config (highlight-symbol-nav-mode) (setq highlight-symbol-idle-delay 0.5) (set-face-attribute 'highlight-symbol-face nil :background "dark cyan")) #+end_src is read as #+begin_src nix :tangle yes [[ "use-package" "highlight-symbol" ":ensure" true ":hook" [[[ "python-mode" "emacs-lisp-mode" "nix-mode" ] "highlight-symbol-mode" ] [[ "python-mode" "emacs-lisp-mode" "nix-mode" ] "highlight-symbol-nav-mode" ]] ":config" [ "highlight-symbol-nav-mode" ] [ "setq" "highlight-symbol-idle-delay" 0.5 ] [ "set-face-attribute" [ "quote" "highlight-symbol-face" ] [ ] ":background" "dark cyan" ]]] #+end_src

    • parseElisp /elisp/ (string)

      Takes a string argument and provides an AST with additional structural info, such as object type, line number and list depth.

      The AST provided for the code #+begin_src emacs-lisp :tangle yes (setq mouse-wheel-scroll-amount '(3 ((shift) . 1))) #+end_src would be #+begin_src nix :tangle yes [{ depth = 0; line = 1; type = "list"; value = [ { line = 1; type = "symbol"; value = "setq"; } { line = 1; type = "symbol"; value = "mouse-wheel-scroll-amount"; } { line = 1; type = "quote"; value = { depth = 1; line = 1; type = "list"; value = [ { line = 1; type = "integer"; value = 3; } { depth = 2; line = 1; type = "list"; value = [ { depth = 3; line = 1; type = "list"; value = [ { line = 1; type = "symbol"; value = "shift"; } ]; } { line = 1; type = "integer"; value = 1; } ]; } ]; }; } ]; }] #+end_src

    • fromOrgModeBabelElisp /text/ (string)

      ~fromElisp~, but for Org mode babel files. Runs ~fromElisp~ on the result of tangling all Elisp code blocks with ~:tangle yes~ set. Tangling is done internally, not by Org mode.

    • parseOrgModeBabelElisp /text/ (string)

      ~parseElisp~, but for Org mode babel files. Runs ~parseElisp~ on the result of tangling all Elisp code blocks with ~:tangle yes~ set. Tangling is done internally, not by Org mode.

*** Other functions

- *tokenizeElisp* /elisp/ (string)

  Takes a string argument and produces a list of tokens from the
  Elisp read from it. Used internally by ~fromElisp~ and ~parseElisp~.

- *tokenizeElisp'* /args/ (attrs)

  An alternate version of ~tokenizeElisp~ allowing the specification
  of the starting line number.

  /args/ is an attribute set with the following attributes:

  - /elisp/ (string, required)

    The string of Elisp to tokenize.

  - /startLineNumber/ (int, optional)

    The line number to use for the first line of /elisp/. Useful if
    /elisp/ is a block of code from a larger file and you want line
    numbers to be correct.

- *parseElisp'* /tokens/ (list)

  An alternate version of ~parseElisp~ which takes as its argument a
  list of tokens produced by ~tokenizeElisp~.

- *fromElisp'* /ast/ (list)

  An alternate version of ~fromElisp~ which takes as its argument an
  AST produced by ~parseElisp~.

- *tokenizeOrgModeBabelElisp* /text/ (string)

  Run tokenizeElisp' on all Elisp code blocks (with ~:tangle yes~
  set) from Org mode babel text /text/.

- *tokenizeOrgModeBabelElisp'* /defaultArgs/ (attrs) /text/ (string)

  An alternate version of ~tokenizeOrgModeBabelElisp~ which allows
  the specification of default arguments for Org babel
  headers. Currently only ~:tangle~ is supported.

- *parseOrgBabelElisp'* /defaultArgs/ (attrs) /text/ (string)

  An alternate version of ~parseOrgBabelElisp~ which allows
  the specification of default arguments for Org babel
  headers. Currently only ~:tangle~ is supported.

- *fromOrgBabelElisp'* /defaultArgs/ (attrs) /text/ (string)

  An alternate version of ~fromOrgModeBabelElisp~ which allows
  the specification of default arguments for Org babel
  headers. Currently only ~:tangle~ is supported.
  • Usage

    You can use ~fromElisp~ in your code in multiple ways.

*** fetchGit

You can use ~fetchGit~ without a ~rev~ attribute to try it out quickly
and then add the ~rev~ when you want reproducibility.

#+begin_src nix :tangle yes
  with (import (builtins.fetchGit {
    url = "https://github.com/talyz/fromElisp.git";
    ref = "master";
    # rev = "c13d6035666f36ca940db996f1dbaf83cb4e8453";
  }));
  # ... code ...
#+end_src

*** niv

If you use [[https://github.com/nmattia/niv][niv]] to manage your Nix dependencies, simply run

#+begin_src shell :tangle yes
  $ niv add talyz/fromElisp
#+end_src

to add ~fromElisp~ to your dependencies and import it as follows:

#+begin_src nix :tangle yes
  with (import (import ./nix/sources.nix).fromElisp {});
  # ... code ...
#+end_src

*** Git submodule

If you plan on contributing to ~fromElisp~ and want to do it from
your own source, you can import it as a [[https://git-scm.com/book/en/v2/Git-Tools-Submodules][Git submodule]].
  • Limitations

    • No Unicode support

      Nix doesn't support Unicode, so we can't either.

    • No evaluation

      This is a /reader/ and not an /evaluator/. You can make some pretty reasonable assumptions from the structure of the code, though: it's, for example, used in the [[https://github.com/nix-community/emacs-overlay][Emacs overlay]] to implement the functionality of ~emacsWithPackagesFromUsePackage~.