cwal

whcl: whclsh
Login

whcl: whclsh

(⬑Table of Contents)

whclsh is the canonical whcl shell application for running whcl code from batch scripts or an interactive REPL. It acts as client to the whcl library interface and is not part of the library.

Some docs are still TODO.

Future topics include, but are not limited to:

Intro

whclsh is a client application of libwhcl which serves several purposes:

Building whclsh

In the canonical build tree, whclsh is built as part of the normal whcl build process.

However, the preferred distribution of whcl is the amalgamation build, a small set of standalone C source and header files.

In short, building the shell requires only:

$ cc -c -I. cliapp.c    # utility code required by...
$ cc -c -I. whclsh.c    # the main shell app
$ cc -c -I. libwhcl.c   # the libwhcl amalgamation build
$ cc -o whclsh cliapp.o whclsh.o libwhcl.o

That's all there is to it, but that will create a build with no interactive editing support, so it's not helpful for interactive use. For interactive mode, the REPL part can use either:

To add one of those, define either CLIAPP_ENABLE_READLINE=1 or CLIAPP_ENABLE_LINENOISE=1 when compiling cliapp.c and link to the appropriate libraries: -lreadline or -llinenoise, noting that readline also requires -lncurses on many, if not most, platforms. Like the amalgamation build, cliapp.c responds to the HAVE_CONFIG_H compilation macro and will, if it's set to any value, include "config.h". Thus the line-editing macros may optionally be set that way.

(Patches to add support for other interactive editing APIs are welcomed!)

With one of those in place, the shell will start up in interactive mode and the platform's EOF character, entered on an empty line, will exit the shell. (On Unix systems that's ctrl-D.)

Invoking the Shell

whclsh -? or --help provide a complete list of options for the shell, but the more useful/common ones are listed here:

Environment Variables which Influence whclsh

whcl[ARGV]

whcl[ARGV] is an array holding all flags passed to whclsh after a -- flag. Such flags are intended for use only by scripts. The array holds all arguments in unparsed form.

Its flags property is an object which holds boolean-style flags: -a, --b, etc. It treats a single leading dash and two leading dashes identically and strips them from the flag property's name. Flags which have a value are required to be a single token with a = between the flag and its value. The token may be quoted. Note that if the same flag is given more than once, the last one wins, so if a script requires such a feature it will need to process the list directly from the ARGV object. Also note that property storage is unordered, so the order of the properties in the flags object is effectively arbitrary.

Its nonFlags property holds a list of all non-flag arguments, in the order they are provided.

For example:

First with no flags:

$ whclsh -e 'echo "ARGV:" [whcl.ARGV to-json 2]' \
  -e 'echo "Flags:" whcl.ARGV.flags' \
  -e 'echo "Non-flags:" whcl.ARGV.nonFlags'
ARGV: []
Flags: {}
Non-flags: []

And with flags:

$ whclsh -e 'echo "ARGV:" [whcl.ARGV to-json 2]' \
  -e 'echo "Flags:" whcl.ARGV.flags.' \
  -e 'echo "Non-flags:" whcl.ARGV.nonFlags' \
 -- -a -b=c --c=d e f g --h --i="Hi there"
ARGV: [
  "-a",
  "-b=c",
  "--c=d",
  "e",
  "f",
  "g",
  "--h",
  "--i=Hi there"
]
Flags: {"c": "d", "b": "c", "a": true, "i": "Hi there", "h": true}
Non-flags: ["e", "f", "g"]

Installed APIs

whclsh installs all of the APIs found in the API index. When running in interactive mode or when passed the --whcl.shell flag, it adds an object named whcl[shell] with the following APIs:

Extending whclsh (Without Modifying It)

The file whclsh_extend.c exists as a way for clients to add functionality to whclsh without having to edit the shell application. To build this option in, add the macro WHCLSH_EXTEND when compiling whclsh.c and then link whclsh_extend.o to the resulting application. The source tree comes with a commented copy of that file demonstrating and explaining how to plug in new features.

As the library matures, loadable module support will also be added (ported over from s2), allowing C-level features to be built as DLLs and loaded at runtime.

There's no Place Like WHCL_HOME

This section covers a convention for how one developer installs whcl. This convention is in no way "official" nor baked into the language. It's just proven to be convenient over the years with whcl's sibling languages.

In short, it's proven helpful to define an environment variable, WHCL_HOME, which points to the whcl "home" directory - where all of its utility code, loadable modules, etc., are kept.

Assuming ~/bin is in one's path, a wrapper script such as the following should be placed there and named whclsh:

#!/bin/sh
# whclsh wrapper script. Configure it for the local
# system and drop it in your PATH somewhere...
: ${WHCL_HOME:="$HOME/whcl"}
: ${WHCLSH_INIT_SCRIPT:="${WHCL_HOME}/whclsh.whcl"}
: ${WHCLSH_HISTORY_FILE:="${WHCL_HOME}/whclsh.history"}
: ${WHCL_IMPORT_PATH:=".:${WHCL_HOME}"}
: ${WHCL_MODULE_PATH:=".:${WHCL_HOME}/mod"}
: ${WHCL_REQUIRE_PATH:="."}
for rp in "${PWD}/require.d" "${WHCL_HOME}/require.d"; do
    if [ -d "${rp}" ]; then
        WHCL_REQUIRE_PATH="${WHCL_REQUIRE_PATH}:${rp}"
    fi
done
export WHCL_HOME WHCL_IMPORT_PATH \
       WHCL_MODULE_PATH WHCL_REQUIRE_PATH \
       WHCLSH_INIT_SCRIPT WHCLSH_HISTORY_FILE
exec "${WHCL_HOME}/whclsh" "$@"

The real whclsh binary itself is then copied to $WHCL_HOME/whclsh.