Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/SquircleSpace/shcl
SHell in Common Lisp
https://github.com/SquircleSpace/shcl
common-lisp shell
Last synced: 8 days ago
JSON representation
SHell in Common Lisp
- Host: GitHub
- URL: https://github.com/SquircleSpace/shcl
- Owner: SquircleSpace
- License: apache-2.0
- Created: 2016-10-14T02:52:45.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2021-05-11T07:03:39.000Z (over 3 years ago)
- Last Synced: 2024-05-19T05:36:12.145Z (6 months ago)
- Topics: common-lisp, shell
- Language: Common Lisp
- Size: 865 KB
- Stars: 311
- Watchers: 16
- Forks: 16
- Open Issues: 17
-
Metadata Files:
- Readme: README.org
- License: LICENSE.txt
Awesome Lists containing this project
README
#+BEGIN_COMMENT
Copyright 2017 Bradley JensenLicensed 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 athttp://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 JensenSHCL 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_EXAMPLENow lay your eyes on a lisp function participating in a pipeline!
#+BEGIN_EXAMPLE
shcl> : ,(shcl/core/debug:graph-dependencies) | dot -Tpng > graph.png
#+END_EXAMPLEThe =#$= 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 #) :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 # #) :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_EXAMPLEYou 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_EXAMPLEIf 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_EXAMPLECongratulations! 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_EXAMPLEOkay, actually, that kind of went off the rails.