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 installed Cozenage binary, and print when it was compiled.

-l and --library

Preload Scheme and/or Cozenage 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 (scheme 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 scheme source file:

(import (scheme file))
(import (scheme 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 Scheme 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 Scheme 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.8.9
  Press <Ctrl+d> or type 'exit' to quit

-->

The ‘-->’ is the cozenage prompt, at which you can type in Scheme 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: ‘…’.

--> (+ 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!"
-->

Whether Cozenage was built with GNU readline, or the *BSD and macOS provided libedit, a history of expressions typed into the prompt will be generated, and can be accessed and cycled through using the up and down arrows. This history is also written to a file for persistence between runs of the REPL. Currently, the file is written to ~/.cozenage_history, but at some point the history file name and location will become a configurable option.

Libedit provides rudimentary tab-completion, but this will only work for filepaths.

If built with GNU Readline, this file path tab-completion is available, but Cozenage will also provide extended tab completion for all defined procedures and special forms at startup. Simply type in the first few characters of the desired name. If there is no ambiguity, the rest of the name 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

To exit the REPL, type <Ctrl-D>, or simply type ‘exit’ at the prompt and press enter.