All Projects → SquircleSpace → Shcl

SquircleSpace / Shcl

Licence: apache-2.0
SHell in Common Lisp

Programming Languages

shell
77523 projects

#+BEGIN_COMMENT Copyright 2017 Bradley Jensen

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #+END_COMMENT

#+TITLE: SHCL: Shell Meets Common Lisp #+AUTHOR: Brad Jensen

SHCL is

  1. a very customizable shell made with secret alien technology, and
  2. an unholy union of POSIX Shell and Common Lisp.

SHCL is more than just a shell. It is a mutual embedding of POSIX Shell and Common Lisp. Behold Common Lisp embedded in POSIX shell embedded in Common Lisp! Notice that the Common Lisp form embedded in the shell expression can access the lexical environment. #+BEGIN_EXAMPLE (let ((rld "rld")) (capture (:stdout) #$ echo Hello ,(concatenate 'string "Wo" rld) #$)) ; => "Hello World" #+END_EXAMPLE

Now lay your eyes on a lisp function participating in a pipeline! #+BEGIN_EXAMPLE shcl> : ,(shcl/core/debug:graph-dependencies) | dot -Tpng > graph.png #+END_EXAMPLE

The =#$= reader macro isn't just some hack that constructs a string to be evaluated by a "real" shell. The =#$= reader macro fully parses the shell expression and constructs an equivalent Common Lisp form. SHCL IS the "real" shell!

#+BEGIN_EXAMPLE SHCL/CORE/LISP-INTERPOLATION> (macroexpand-1 '#$ if true; then echo woo; fi #$) (SHCL/CORE/SHELL-FORM:SHELL-IF (SHCL/CORE/SHELL-FORM:SHELL-RUN (WITH-FD-STREAMS NIL (EXPANSION-FOR-WORDS (LIST #<NAME "true">) :EXPAND-ALIASES T :EXPAND-PATHNAME-WORDS T :SPLIT-FIELDS NIL)) :ENVIRONMENT-CHANGES NIL :FD-CHANGES NIL) (SHCL/CORE/SHELL-FORM:SHELL-RUN (WITH-FD-STREAMS NIL (EXPANSION-FOR-WORDS (LIST #<NAME "echo"> #<NAME "woo">) :EXPAND-ALIASES T :EXPAND-PATHNAME-WORDS T :SPLIT-FIELDS NIL)) :ENVIRONMENT-CHANGES NIL :FD-CHANGES NIL)) T #+END_EXAMPLE

  • Building SHCL

SHCL is only really tested against SBCL and CCL, but it should be portable to other lisp compilers. Be aware that ECL is known to be problematic because it tries to reap child processes automatically.

First, you'll need to install some dependencies. To start with, you'll need Clang and libedit. There's also some Common Lisp dependencies that need to be taken care of: SBCL, Quicklisp, and cffi-grovel. If you're new to building Common Lisp projects, you might want to let [[https://github.com/roswell/roswell][Roswell]] set up your lisp environment for you.

#+BEGIN_EXAMPLE

Set up Clang, libedit, and Roswell

make LISP='ros -s cffi-grovel run --' #+END_EXAMPLE

You can skip Roswell if you want. Just make sure that you set LISP to a command that runs SBCL with Quicklisp and cffi-grovel loaded. For example,

#+BEGIN_EXAMPLE

Set up Clang, libedit, SBCL, and Quicklisp

QUICKLISP_SETUP=~/quicklisp/setup.lisp # or wherever you installed quicklisp make LISP="sbcl --no-userinit --load "$QUICKLISP_SETUP" --eval '(ql:quickload :cffi-grovel)'" #+END_EXAMPLE

If you use the Nix package manager, building SHCL is super easy! SHCL has a =default.nix= file, so you just need to run =nix-build=. #+BEGIN_EXAMPLE nix-build #+END_EXAMPLE

Congratulations! You built SHCL! If you try to run =shcl= you'll probably find that it doesn't work because it can't find =libshcl-support=. As part of the build, SHCL produces a shared library named (you guessed it!) =libshcl-support=. That library needs to be installed somewhere that the dynamic linker can find it. So, go ahead and use =sudo make install= to install SHCL and its support library! Don't forget to set the =PREFIX= to something you're happy with. Alternatively, you can just use the =run-shcl= script included in the repository. =run-shcl= just adds =$(pwd)= to the dynamic linker's search path before invoking =./shcl=.

Note: if you build SHCL using =nix-build=, then you don't have to worry about =libshcl-support=. SHCL will know how to find it!

  • Example Usage

I don't know what you're expecting to see here. Its a POSIX-like shell. You can do (almost) all your normal POSIX shell stuff in it.

#+BEGIN_EXAMPLE shcl> echo foobar foobar shcl> { echo foobar ; echo baz ; echo blip ; } | tail -n 1 blip shcl> shcl-enable-lisp-syntax shcl> if [ ,(+ 1 2 3) = ,(* 2 3) ]; then

echo woah wait what fi woah wait what shcl> shcl-repl shcl (lisp)> (define-builtin upcase () (loop :for line = (read-line standard-input nil :eof) :until (eq line :eof) :do (format "A%" (string-upcase line))) 0) UPCASE shcl (lisp)> ^D shcl> { echo ahhh ; echo what is going on ; } | upcase AHHH WHAT IS GOING ON #+END_EXAMPLE

Okay, actually, that kind of went off the rails.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].