From 432c518f73e3619fe8b79dd01de2e5a2f50faac3 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Wed, 19 Oct 2022 23:08:35 -0300 Subject: bin/cl: Add first working version, integrated with Makefile and etc/sh/rc --- Makefile | 17 ++- bin/cl | 296 +++++++++++++++++++++++++++++++++++++++++++++++++ etc/lisp-cli/init.lisp | 26 +++++ etc/sh/rc | 1 + 4 files changed, 336 insertions(+), 4 deletions(-) create mode 100755 bin/cl diff --git a/Makefile b/Makefile index c1723b3..7384fb2 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,11 @@ pod2man = \ derived-assets = \ - $(pod2man) \ - $(XDG_CONFIG_HOME)/ssh/id_rsa.pub \ - $(XDG_DATA_HOME)/common-lisp/source \ - $(XDG_DATA_HOME)/euandreh/e.list.txt \ + $(pod2man) \ + $(XDG_CONFIG_HOME)/ssh/id_rsa.pub \ + $(XDG_DATA_HOME)/common-lisp/source \ + $(XDG_DATA_HOME)/euandreh/e.list.txt \ + $(XDG_DATA_HOME)/lisp-cli/clisp.image \ @@ -29,6 +30,14 @@ $(XDG_CONFIG_HOME)/ssh/id_rsa.pub: $(XDG_DATA_HOME)/euandreh/e.list.txt: ~/Documents/txt/ opt/aux/gen-e-list.sh sh opt/aux/gen-e-list.sh > $@ +$(XDG_DATA_HOME)/lisp-cli/clisp.image: $(XDG_CONFIG_HOME)/lisp-cli/init.lisp bin/cl + cl \ + -I clisp \ + -v \ + -e '(ql:quickload :trivial-dump-core)' \ + -e '(trivial-dump-core:dump-image "$@")' \ + -e '(uiop:quit)' + check-shellcheck: diff --git a/bin/cl b/bin/cl new file mode 100755 index 0000000..1b9a659 --- /dev/null +++ b/bin/cl @@ -0,0 +1,296 @@ +#!/bin/sh +set -eu + + +uuid() { + od -xN20 /dev/urandom | + head -n1 | + awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' +} + +tmpname() { + echo "${TMPDIR:-/tmp}/cl.tmpfile.$(uuid)" +} + +mkstemp() { + name="$(tmpname)" + touch "$name" + echo "$name" +} + +escape_name() { + printf '%s' "$1" | + sed 's|"|\\"|g' | + printf '(load "%s")\n' "$(cat -)" +} + + +IMPLEMENTATIONS=' +abcl +allegro +ccl +clasp +clisp +cmucl +ecl +jscl +mkcl +sbcl +' + +usage() { + cat <<-'EOF' + Usage: + cl [-e EXP] [-f FILE] [-p] [-M IMAGE] [-I IMPL] [-n] [-v] [FILE...] + cl -l + cl -h + EOF +} + +help() { + cat <<-'EOF' + + + Options: + -e EXP an sexp to be evaluated (can be given more than once) + -f FILE FIXME + -p print the value of the last given expression + -M IMAGE load the given Lisp image + -m disable looking for the default image under $XDG_DATA_HOME + -I IMPL use the given implementation (default: $LISP_CLI_IMPL) + -n skip loading the implementation's init file + -v verbose mode + -l list the known types of implementations + -h, --help show this message + + FILE FIXME + + + Lauch the desired Lisp implementation, properly adapting the given + CLI options. + + When the implementation is not explicited on the command line, and + the $LISP_CLI_IMPL environment variable in unset, implementations are + searched for alphabetically in $PATH, untill one is found, otherwise + an error is emitted. + + The supported implementations are: + EOF + + + for i in $IMPLEMENTATIONS; do + printf '%s\n' "$i" + done + + cat <<-'EOF' + + + Examples: + + Launch CLISP REPL, when $LISP_CLI_IMPL is set to 'clisp': + + $ cl + + + Lauch SBCL REPL, with the given Lisp image loaded, skipping the + loading of the '.sbclrc' file: + + $ cl -n -Isbcl sbcl.image + + + Run file1.lisp with ABCL: + + $ cl -Iabcl file1.lisp + + + Process STDIN: + + $ cat <<-EOS > process.lisp + + EOS + $ cat f1.txt f2.txt | cl -p process.lisp + + + Print a value on all types of implementations: + + $ for i in `cl -l`; do cl -I$i -pe 'call-arguments-list'; done + EOF +} + + +for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac +done + +NO_RC=false +SCRIPT="$(mkstemp)" +LISP_CLI_RC="${XDG_CONFIG_HOME:-$HOME/.config}/lisp-cli/init.lisp" +VERBOSE=false +IMAGE='' +SKIP_DEFAULT_IMAGE=false +IMPL="${LISP_CLI_IMPL:-}" +while getopts 'e:f:pM:mI:nvlh' flag; do + case "$flag" in + e) + printf '%s\n' "$OPTARG" >> "$SCRIPT" + ;; + f) + escape_name "$OPTARG" >> "$SCRIPT" + ;; + M) + IMAGE="$OPTARG" + ;; + m) + SKIP_DEFAULT_IMAGE=true + ;; + I) + IMPL="$OPTARG" + ;; + n) + NO_RC=true + ;; + v) + VERBOSE=true + ;; + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac +done +shift $((OPTIND - 1)) + +INTERACTIVE=true +for f in "$@"; do + INTERACTIVE=false + escape_name "$f" >> "$SCRIPT" +done + +MAIN="$(mkstemp)" +if [ "$NO_RC" = false ] && [ -e "$LISP_CLI_RC" ]; then + escape_name "$LISP_CLI_RC" > "$MAIN" +fi + +if [ "$INTERACTIVE" = true ]; then + escape_name "$SCRIPT" >> "$MAIN" +else + cat <<-EOF >> "$MAIN" + (handler-case + (progn + (load "$SCRIPT" + :verbose NIL + :print NIL) + (uiop:quit 0)) + (error (e) + (format *error-output* "~&~%ERROR: ~a" e) + (uiop:quit 1))) + EOF +fi + +if [ -z "$IMPL" ]; then + for i in $IMPLEMENTATIONS; do + if command -v "$i" > /dev/null; then + IMPL="$i" + break + fi + done + if [ -z "$IMPL" ]; then + printf 'Could not find any implementation in $PATH.\n' >&2 + exit 2 + fi +fi + +DEFAULT_IMAGE="${XDG_DATA_HOME:-$HOME/.local/share}/lisp-cli/$IMPL.image" +if [ "$SKIP_DEFAULT_IMAGE" = false ] && [ -z "$IMAGE" ] && + [ -e "$DEFAULT_IMAGE" ]; then + IMAGE="$DEFAULT_IMAGE" +fi + +case "$IMPL" in + abcl) + exit 4 + ;; + allegro) + exit 4 + ;; + ccl) + exit 4 + ;; + clasp) + exit 4 + ;; + clisp) + ARGS="-ansi -i $MAIN" + if [ -n "$IMAGE" ]; then + set -- -M "$IMAGE" + fi + if [ "$NO_RC" = true ]; then + ARGS="$ARGS -norc" + fi + if [ "$VERBOSE" = false ]; then + ARGS="$ARGS -q -q" + else + set -x + fi + exec clisp $ARGS "$@" + ;; + cmucl) + exit 4 + ;; + ecl) + exit 4 + ;; + jscl) + exit 4 + ;; + mkcl) + exit 4 + ;; + sbcl) + ARGS="--disable-debugger --script $MAIN" + if [ "$VERBOSE" = false ]; then + ARGS="$ARGS --noinform" + fi + if [ "$NO_RC" = true ]; then + ARGS="$ARGS --no-sysinit --no-userinit" + fi + if [ -n "$IMAGE" ]; then + set -- --core "$IMAGE" + fi + exec sbcl $ARGS "$@" + ;; + *) + printf 'Unsupported implementation: "%s".\n\n' "$IMPL" >&2 + usage >&2 + exit 2 + ;; +esac + +exit + +# https://www.cliki.net/cl-launch +# buildapp +# https://github.com/memleaks/clbuild +# https://www.cliki.net/roswell +# https://github.com/shinmera/cl-all +# https://fare.livejournal.com/184127.html +# https://github.com/rolpereira/trivial-dump-core +# FIXME: enable readline from CL itself +when competing with roswell, add completion +FIXME: implement the example from help string diff --git a/etc/lisp-cli/init.lisp b/etc/lisp-cli/init.lisp index 0f01d57..062446e 100644 --- a/etc/lisp-cli/init.lisp +++ b/etc/lisp-cli/init.lisp @@ -3,3 +3,29 @@ (user-homedir-pathname)))) (when (probe-file quicklisp-init) (load quicklisp-init))) + +(defun load-once (p) + (let ((k (intern + (concatenate 'string + (string :ql/) + (string p)) + "KEYWORD"))) + (unless (member k *features*) + (ql:quickload p) + (pushnew k *features*)) + k)) + +(mapcar #'load-once + (list + :cl-ppcre + :cffi + :trivial-dump-core + :named-readtables + :rstring)) + +(mapcar (lambda (p) + (pushnew (concatenate 'string p "/") cffi:*foreign-library-directories* + :test #'equal)) + (cl-ppcre:split ":" (uiop:getenv "LIBRARY_PATH"))) + +(load-once :cl-fswatch) diff --git a/etc/sh/rc b/etc/sh/rc index 0f0ed5d..d5801b8 100644 --- a/etc/sh/rc +++ b/etc/sh/rc @@ -87,6 +87,7 @@ export LEX=flex export LDFLAGS='-flto' export LISP='sbcl --eval' export N_PROCS GUILE_EFFECTIVE_VERSION +export LISP_CLI_IMPL=clisp add_prefix() { export GUILE_LOAD_PATH="$1/share/guile/site/$GUILE_EFFECTIVE_VERSION${GUILE_LOAD_PATH:+:}${GUILE_LOAD_PATH:-:}" -- cgit v1.2.3