How to run Cozenage

Cozenage provides both a file runner for evaluating code in external files and a fully-featured interactive REPL which allows for experimentation and quick discovery. These two methods of running Cozenage will be treated in order, after a short introduction to command-line flags which affect the operation of both.

Cozenage command line flags

-h and --help

Display short usage details to the screen, ie: print the information we are discussing here.

-V and --version

Print the version number of the currently running Cozenage binary, and print when it was compiled.

-l and --library

Preload Cozenage ‘base’ libraries at startup. Accepts a mandatory comma-delimited list of libraries to load. This is a convenience flag which is identical to manually loading libraries using the (import (base foo)) syntax. Valid arguments to this flag are:

bits, cxr, file, math, random, system, and time.

Using the file runner

To evaluate code in an external file, simply pass the filename as argument on the command line:

$ cozenage file.scm

By convention, Scheme source files typically use scm or ss suffixes. Cozenage will print a warning to the standard error stream when it detects a file without one of these two suffixes, but will attempt to run the file anyway. Cozenage will only run one external file at a time. If you want to run multiple unrelated files in sequential order, use your shell’s facilities for this:

$ cozenage file1.scm && cozenage file2.scm && cozenage file3.scm

Or perhaps more idiomatically:

$ for file in file1.scm file2.scm file3.scm; do
      cozenage ${file}
  done

If you want to load definitions from one or more files for use in a final script, you can use the (load) procedure. In your script file:

(load "file1.scm")
(load "file2.scm")
(load "file3.scm")

;;; ...and the rest of the script...

And now these definitions are available when you run the script:

$ cozenage script.scm

You can use the -l or --library flag as a convenience to pre-load libraries:

$ cozenage -l file,system script.scm

This is functionally equivalent to adding (import) expressions at the top of your source file:

(import (base file))
(import (base system))
;;; etc

Tip

Other than printing runtime errors to the standard error stream, file runner mode does not generate any output that is not explicitly generated by the code in the source file itself. Success or failure of the run can be determined by inspecting the output of echo $?.

You can pass flags and arguments to the source file you are running and these will be made available through the (command-line) procedure which returns them as a list of strings. As an example, let’s say we have these definitions in a file:

(write (command-line))
(newline)

Notice the results of running the file, given this invocation:

$ cozenage cli.scm -- --foo --bar --baz somefile.txt
("cli.scm" "--foo" "--bar" "--baz" "somefile.txt")

Note in particular, that you have to explicitly add ‘--’ AFTER the Scheme file name so that getopt() knows to stop parsing the arguments as args to Cozenage itself, and ensure they are interpreted as flags and arguments to the Scheme file.

Using the REPL

Most modern interpreted languages provide a REPL for interactive use. The idea of a REPL was first described by John McCarthy, the inventor of the original LISP language in his 1960 paper named “Recursive functions of symbolic expressions and their computation by machine”. The interactive loop he described is the direct ancestor of every REPL that exists today.

REPLs are a very powerful tool which allow for interactive ‘conversations’ with the interpreter, in order to experiment and try out various code expressions without the overhead of the edit-compile-test workflow required with most compiled languages.

Running Cozenage with no file arguments will start the program in REPL mode:

$ cozenage
  Cozenage version 0.11.0
  Press <Ctrl+d> or type '(exit)' to quit

-->

The ‘-->’ is the cozenage prompt, at which you can type in expressions to be immediately evaluated when you press enter. The REPL also provides multiline input using a simple heuristic that tracks if the parentheses are balanced. If there are more left parentheses than right parentheses in the current expression, pressing enter will continue on to the next line and print a secondary prompt: ‘…’. The secondary prompt will also be printed if you press enter while inside a string literal, that is, no closing quotation mark is detected.

--> (+ 2 4)
  6
--> (define (fact n)
  ...   (if (= n 1) 1
  ...       (* n (fact (- n 1)))))
  <lambda 'fact'>
--> (fact 5)
  120
-->

Note that you can type multiple expressions in a single line, and they will be evaluated in sequence:

--> (import (scheme write))
  #true
--> (display "Hello, World!") (newline)
  "Hello, World!"
-->

REPL History

The REPL maintains a buffer containing the history of entered expressions. This history can be cycled through using the up and down arrow keys. Currently, the size of the buffer is fixed at 500 items, but at some point this will become a configurable option.

Upon closing Cozenage, the history will be written to a file for persistence between runs. On startup, the history is read from the file to populate the runtime buffer. If the $XDG_STATE_HOME environment variable is set, the history file will be located at $XDG_STATE_HOME/cozenage/history. If this variable is not set, the history will be written to ~/.local/state/cozenage/history. This is a plain-text (UTF8) encoded file that can be inspected and edited with any text editor.

Tab Completion

Cozenage provides tab-completion for convenience. There are two kinds of completions that will be deployed depending on context. If inside a string literal the filename completer will be activated, using filenames relative to the CWD. In all other contexts, Cozenage provides extended tab completion for all defined procedures and special forms at startup.

To use tab completion, simply type in the first few characters of the desired name. If there is no ambiguity, the rest of the name, or the rest of the name up until a point of ambiguity, will be auto-completed at the prompt immediately upon pressing the tab key. If there are multiple possible completions, pressing the tab key a second time will display the options to the screen.

--> (def[tab]ine   ;;; The REPL auto-completes the 'define' special form
--> (make-[tab][tab]
make-bytevector make-list       make-string     make-vector
--> (make-b[tab]ytevector

REPL Navigation and Ctrl sequences

The left and right arrow keys will position the cursor backwards and forwards in the text buffer by one character. As mentioned already, the up and down arrow keys will cycle backwards and forwards through the expression history buffer. Ctrl + A will position the cursor at the start of the text buffer, and Ctrl + E will position the cursor at the end. These operations are non-destructive. That is, they will only position the cursor, leaving the contents of the buffer untouched. Ctrl + U will delete all text in the buffer from the cursor position to the start. Likewise, Ctrl + K will delete all text from the cursor position to the end.

While at the primary prompt (-->) the key combinations Ctrl + c and Ctrl + G act identically, and will discard the current text buffer and print a new prompt. At the secondary prompt (...) (ie: in multiline input) their behaviors are subtly different: Ctrl + C will discard ONLY the current line, to allow for corrections. Pressing Ctrl + G will discard the entire multiline buffer, and print a new prompt.

To exit Cozenage, press Ctrl + D (EOF) when the text buffer is empty (ie: Press Ctrl + C first if necessary). Another way to exit is to simply use the exit builtin procedure:

--> (exit)

Either of these methods will save the command history, and perform a clean shut down.