ChrysaLisp features a powerful command-line environment that extends the
familiar concept of Unix-like pipes into a distributed computing paradigm.
This allows for complex data processing and system building tasks to be
orchestrated across multiple VP (Virtual Processor) nodes in a network. This
document explores the core concepts, key components, options processing, and
provides detailed examples using the grep, forward, and make commands.
-
Individual Command Applications:
-
Most command-line tools in ChrysaLisp are implemented as Lisp scripts (e.g.,
cmd/grep.lisp,cmd/make.lisp). -
They typically follow the standard model of reading from standard input (stdin), writing to standard output (stdout), and writing errors to standard error (stderr).
-
They accept command-line arguments, which are parsed using a common options processing library.
-
When a command application is invoked, it runs as a ChrysaLisp task.
-
-
Piping (
|or!):-
Like Unix shells, ChrysaLisp allows the stdout of one command to be "piped" to the stdin of another. For example:
cat file.txt | grep "pattern" | sort. -
This creates a pipeline where data flows sequentially through a series of processing stages.
-
The
|is the ditrubuted operator,!is the pining operator.
-
-
Distribution:
-
Pipeline Distribution: Each command in a pipeline can potentially run as a separate task on a different VP node in the ChrysaLisp network. The system handles the transparent routing of data (stdout/stdin) between these distributed tasks.
-
Task Distribution within Commands: Some commands, particularly those designed for parallel processing (like
makeorgrepin certain modes), can themselves spawn multiple worker tasks that may be distributed across available nodes to perform sub-operations in parallel.
-
The distributed pipe functionality is primarily managed by components found in
lib/task/pipe.inc and lib/task/cmd.inc.
-
pipe-split cmdline_string -> list_of_commands(fromlib/task/pipe.inc):-
This crucial Lisp function is responsible for parsing a raw command-line string into a list of individual command segments.
-
It correctly handles quoted arguments, ensuring that pipe symbols (
|) or other special characters within quotes are treated as part of an argument and not as pipeline separators. -
For example,
pipe-split "grep -e \"hello | world\" file.txt | sort"would likely produce("grep -e \"hello | world\" file.txt" "sort").
-
-
The
PipeLisp Class (fromlib/task/pipe.inc):-
This class is the Lisp-level abstraction for creating and managing a single, potentially distributed, pipeline.
-
Constructor
(Pipe cmdline_string [&optional user_select_mailboxes]):-
Takes the full command line string (e.g., "cmd1 | cmd2").
-
Uses
pipe-splitto break it into individual commands. -
For each command segment:
-
It constructs the path to the command's Lisp script (e.g., "cmd/cmd1.lisp").
-
It calls
(open-pipe list_of_command_scripts), which launches each command as a new ChrysaLisp task. These tasks can be scheduled on any available node.open-pipereturns a list ofnet_ids, which are the stdin mailboxes for each launched command task. -
Wiring the Pipeline: This is the most complex part. The constructor then iterates backwards through the launched tasks. For each task
NandN-1:-
It sends an initialization message to task
N. This message tells taskNwhat mailbox to use for its stdout (which will be the stdin mailbox of taskN+1, or a final output mailbox for the last command) and what mailbox to use for its stderr. -
Task
Nacknowledges this setup by sending its actual stdout mailbox ID back. -
This acknowledged stdout ID of task
Nis then used as the target stdout for taskN-1. This process effectively connectsstdoutof taskN-1tostdinof taskN.
-
-
The
Pipeobject keeps track of the stderr streams of all commands in the pipeline and the final stdout stream of the last command.
-
-
-
Methods:
-
:poll(): Checks if there's any output available from any of the stderr streams or the final stdout stream, or from anyuser_select_mailboxes. -
:read(): Reads available data from the pipeline (either an error message from a command's stderr or data from the final stdout). Returns:tif a user-supplied mailbox (fromuser_select) has data. Returns:nilif the pipe is closed. -
:write(string_data): Writesstring_datato the stdin of the first command in the pipeline. -
:close(): Initiates a graceful shutdown of the pipeline, waiting for all stderr streams to report they have stopped.
-
-
-
pipe-run cmdline_string [&optional output_function](fromlib/task/pipe.inc):- A convenience wrapper that creates a
Pipeinstance for the givencmdline_string, reads all data from its final stdout until the pipe closes, and callsoutput_function(defaulting toprint) for each chunk of data received.
- A convenience wrapper that creates a
-
pipe-farm jobs_list [&optional retry_timeout](fromlib/task/cmd.inc):-
Purpose: To execute a list of independent command-line jobs in parallel, distributing them across available nodes using a farm of worker tasks. This is different from the
Pipeclass, which manages a sequential flow of data through stages. -
Worker Task: It uses a generic worker script,
lib/task/cmd.lisp. -
Operation:
-
It creates a
Localfarm (similar to theFarmclass described inlib/task/farm.inc). -
The
createcallback for the farm launches instances oflib/task/cmd.lispon available nodes. -
dispatch-jobsends individual command strings from thejobs_listto available worker tasks. The message includes a reply mailbox and a unique key. -
The
lib/task/cmd.lispworker executes the received command (usingpipe-runinternally, so each job can itself be a pipeline), captures its stdout/stderr, prefixes it with the key, and sends it back. -
pipe-farmcollects these results. -
It includes fault tolerance: if a worker task becomes unresponsive (detected by a timer and the farm's
:refreshmechanism), its job is requeued by thedestroycallback and reassigned.
-
-
This is ideal for batch processing tasks that can run independently, like compiling multiple files or searching multiple files.
-
-
lib/task/cmd.lisp(Generic Command Worker):-
The worker script launched by
pipe-farmand potentially by other task-spawning mechanisms. -
It waits for a message containing a command string, a reply key, and a reply mailbox.
-
It executes the command string using
(pipe-run command_string output_to_string_stream_lambda). -
It captures all output (stdout and stderr from
pipe-runif it errors) into a string. -
It prepends the
reply_key(followed by a newline) to this captured output string. -
It sends the combined string back to the specified reply mailbox.
-
It has an inactivity timeout; if it doesn't receive a job for a period, it exits.
-
ChrysaLisp command-line applications use a standardized library for parsing options.
-
optlist_definition: Each command defines a list that specifies its valid options. An entry in this list typically looks like:-
'( (list_of_aliases) handler_or_help_string ) -
Example:
'( (("-h" "--help") "This help text.") (("-f" "--file") handle_file_option_func) )
-
-
options stdio_obj optlist_definition -> list_of_non_option_args | :nil:-
This is the main parsing function.
-
It takes the application's
:stdioobject (to get command-line arguments viastdio-get-args) and itsoptlist_definition. -
It uses
options-split(which respects quoted arguments) to tokenize the raw argument string. -
It iterates through the tokenized arguments:
-
If an argument starts with
-, it's treated as an option. -
options-findis used to look up the option in theoptlist_definition. If not found, it typically defaults to looking for-h. -
If the handler part of the found option entry is a string, it's printed as help text (using
options-print), and parsing usually stops (returning:nil). -
If the handler is a Lisp function, that function is called with the remaining arguments and the option string itself. The handler function is responsible for consuming any values associated with the option (e.g., the filename for
-o <filename>) from the remaining arguments list and must return the updated list of remaining arguments.
-
-
Arguments not starting with
-are collected as non-option arguments (e.g., file paths). -
It returns the list of collected non-option arguments, or
:nilif help was displayed or a parsing error occurred.
-
-
grep(cmd/grep.lisp)-
Purpose: Searches for patterns in text.
-
Options Processing: Defines an extensive
usagelist for options like-e pattern,-f(file mode),-w(whole words),-r(regexp mode),-c(coded pattern),-m(markdown mode). Handler lambdas in theusagelist set global flags (e.g.,file_flag,regexp_flag) or thepatternvariable. -
Core Logic:
-
Uses
(query pattern words_flag regexp_flag)to get a search engine instance (SubstrorRegexp) and processed pattern/metadata. -
grep-stream: Reads a stream line by line, applies(. search :match? line pattern meta), and prints matching lines. Handles-m(markdown mode) by skipping "```" code blocks. -
grep-file: Reads a file line by line. If a match is found, prints the filename and stops for that file. Also handles-m.
-
-
Distribution:
-
If
-f(file mode) is used with multiple files,grepusespipe-farm. It constructs newgrepcommand lines for each input file (e.g.,grep -c -f -e <coded_pattern> <filepath>) and passes these topipe-farm. The-cflag ensures the pattern is passed in an encoded way to the workergrepinstances. Each workergrepthen runs in file mode on its assigned file and prints the filename if it matches. The maingrepcollects these filenames. -
If not in
-fmode, it processes files sequentially or stdin directly.
-
-
-
forward(cmd/forward.lisp)-
Purpose: Scans Lisp source files for forward references to functions or macros.
-
Options Processing: Has a simple
usagelist, primarily for-h. -
Core Logic (
workfunction):-
Performs a two-pass scan of a single Lisp file.
-
Pass 1: Collects all
(defun ...)and(defmacro ...)definitions intodefs_map(name -> line number) and all potential function/macro uses intouses_map(name -> list of usage line numbers) using regex matching. -
Pass 2: Iterates through
uses_map. For each used name, if it's indefs_map, it checks if any usage line number is less than its definition line number. If so, it prints a forward reference warning.
-
-
Distribution:
-
If multiple files are specified (or read from stdin),
forwardusespipe-farm. -
It constructs new
forwardcommand lines for each input file (e.g.,forward <filepath>). -
Each worker
forwardinstance processes its assigned file using theworkfunction and prints any warnings. The mainforwardcollects and prints this output.
-
-
-
make(cmd/make.lisp)-
Purpose: The primary build tool for ChrysaLisp; compiles VP files, creates boot images, generates documentation.
-
Options Processing: Defines a
usagelist for targets likeall,boot,platforms,docs,it(all-inclusive),test. -
Core Logic (Dispatch): The
mainfunction parses these target flags and calls appropriate underlying functions, most of which are imported fromlib/asm/asm.inc(e.g.,make,make-all,remake-all-platforms) or defined locally (make-docs). -
Distribution:
-
Compilation (
compilefunction fromlib/asm/asm.inc): The corecompilefunction (called by variousmaketargets) uses aLocalfarm (very similar topipe-farm) to distribute the compilation of individual.vpfiles across available nodes. Worker tasks for compilation are instances oflib/asm/asm.lisp. -
Documentation Generation (
make-docssubcommand):-
The
make-docsfunction withincmd/make.lispitself usespipe-farmto execute other command-line tools (likegrep -h,cat -h, etc.) with the-hoption to capture their help output for inclusion in thecommands.mddocumentation. -
The scanning of source files for doc strings is done sequentially by the main
make-docsprocess.
-
-
Cross-Platform Builds (
make-platforms, etc.): These functions iterate through supported CPU/ABI combinations and invoke the single-platform build functions, effectively distributing the build process for different targets if the underlyingcompileuses the farm for each.
-
-
-
Layered Distribution: ChrysaLisp demonstrates multiple layers of distribution:
-
Pipeline Stages: The
Pipeclass can distribute stages of acmd1 | cmd2pipeline to different nodes. -
Independent Job Farming:
pipe-farm(and theLocalfarm used bycompile) distributes a set of independent jobs (command executions or file compilations) to a pool of worker tasks running on various nodes. -
Internal Task Spawning by Commands: A command executed as part of a pipeline or by
pipe-farmcan itself be a complex application that spawns its own worker tasks for internal parallelism (e.g., a hypothetical parallel sorting command).
-
-
Network Transparency: The underlying messaging system (
:sys_mail) and task management (:sys_task,open-task) abstract away the network details, allowing tasks to communicate vianet_ids regardless of their physical location on the VP network. -
Local/FarmClasses: These Lisp-level classes (fromlib/task/local.incorlib/task/farm.inc) provide a convenient abstraction for managing a pool of worker tasks, including launching, monitoring (via timestamps and:refresh), and restarting them.
-
PipeClass: If a command in the pipeline fails to launch duringPipeconstruction, it attempts to send abort signals to already launched commands. ThePipe's:readmethod can detect when a stream component (stderr/stdout) has closed, allowing the controlling application to react. -
pipe-farm/LocalFarm: Thedestroycallback (which requeues jobs) and the:refreshmethod (which detects timeouts and triggers restarts) provide a degree_ of fault tolerance for worker tasks. -
lib/task/cmd.lisp(Worker): Uses(catch ...)aroundpipe-runto capture errors from the executed command and send them back as part of the result string. It also has an inactivity timeout to self-terminate if orphaned. -
Individual Commands: Often print error messages to stderr or return specific exit codes/statuses, which can be captured if they are run within a
pipe-runmanaged bylib/task/cmd.lisp.
The ChrysaLisp distributed pipe system, coupled with its command-line
applications and options processing, provides a remarkably flexible and
powerful environment for both interactive use and complex, automated system
building tasks. The ability to seamlessly distribute pipeline stages or
parallelize independent jobs across a network, combined with robust options
parsing and fault-tolerant farm management, makes it a sophisticated
platform. Commands like grep, forward, and especially make serve as
excellent examples of how these distributed capabilities are leveraged to
perform significant work efficiently.