One neat feature of Factor is the ability to create and deploy programs as compiled binaries -- both CLI (command-line) or UI (graphical) applications.
I thought it might be fun to build the cat command-line program in Factor, and show how it can be deployed as a binary. From the man pages:
The cat utility reads files sequentially, writing them to the standard output. The file operands are processed in command-line order. If file is a single dash ('-') or absent, cat reads from the standard input.
We'll start by creating the
cat vocabulary. You can either create the
cat.factor file yourself, or use tools.scaffold to do it for you:
( scratchpad ) USE: tools.scaffold ( scratchpad ) "cat" scaffold-work Creating scaffolding for P" resource:work/cat/cat.factor" ( scratchpad ) "cat" vocab edit
Begin the implementation by listing some imports and a namespace:
USING: command-line kernel io io.encodings.binary io.files namespaces sequences strings ; IN: cat
Printing each line from a stream is easy using the each-line word (flushing after each write to match the behavior of
: cat-lines ( -- ) [ write nl flush ] each-line ;
I chose to treat files (which might be text or binary) as binary, reading and writing 1024 bytes at a time. We check that the file exists, printing an error if not found:
: cat-stream ( -- ) [ 1024 read dup ] [ >string write flush ] while drop ; : cat-file ( path -- ) dup exists? [ binary [ cat-stream ] with-file-reader ] [ write ": not found" write nl flush ] if ;
Given a list of files, with a special case for "-" (to read from standard input), we can
cat each one:
: cat-files ( paths -- ) [ dup "-" = [ drop cat-lines ] [ cat-file ] if ] each ;
Finally, we need an entry point that checks if command-line arguments have been provided:
: run-cat ( -- ) command-line get [ cat-lines ] [ cat-files ] if-empty ; MAIN: run-cat
Using the deploy-tool:
( scratchpad ) "cat" deploy-tool
Click "Save" to persist the deploy settings into a
deploy.factor file, and "Deploy" to create a binary. You should see output like the following:
Deploying cat... Writing vocabulary manifest Preparing deployed libraries Stripping manual memory management debug code Stripping destructor debug code Stripping stack effect checking from call( and execute( Stripping specialized arrays Stripping startup hooks Stripping default methods Stripping compiler classes Finding megamorphic caches Stripping globals Compressing objects Compressing quotations Stripping word properties Stripping symbolic word definitions Stripping word names Clearing megamorphic caches Saving final image
And your binary should be in the same directory as your Factor installation (in a
cat.app sub-directory on the Mac).
$ ls -hl cat.app/Contents/MacOS/cat -rwxr-xr-x 1 user staff 421k Aug 21 11:11 cat.app/Contents/MacOS/cat* $ cat.app/Contents/MacOS/cat hello, world hello, world ^D
The code for this is on my Github.