Module: command-args Synopsis: Generic command processing utility Author: Roman Budzianowski Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define constant $option-prefixes = select ($os-name) #"win32" => #['/', '-']; otherwise => #['-']; end; define constant $option-parameter-separator = select ($os-name) #"win32" => ":"; otherwise => "="; end; define abstract class () constant slot command-id :: , required-init-keyword: id:; end class ; define method print-object (o :: , stream :: ) => () format(stream, "{command: %s args: %s}", o.command-id, o.command-arguments.key-sequence) end; define class () constant slot command-arguments :: = make(
); slot command-function :: , init-keyword: function:; constant slot command-hidden? :: , required-init-keyword: hidden?:; constant slot command-description :: , required-init-keyword: description:; constant slot command-documentation :: , required-init-keyword: documentation:; end; define method make (class == , #rest all-keys, #key, #all-keys) => (command :: ) apply(make, , all-keys) end method make; define method command-name (command :: ) => (name :: ) as-uppercase(as(, command-id(command))) end method command-name; define class () constant slot command-alias :: , required-init-keyword: alias:; end class ; define method command-function (alias :: ) => (function :: ) let command = command-alias(alias); command-function(command) end method command-function; define method command-arguments (alias :: ) => (arguments ::
) let command = command-alias(alias); command-arguments(command) end method command-arguments; define method command-hidden? (alias :: ) => (hidden? :: ) let command = command-alias(alias); command-hidden?(command) end method command-hidden?; define constant = ; // = limited(, of: ); define class () constant slot command-argument-flags :: false-or(), required-init-keyword: flags:; constant slot command-argument-keyword :: , required-init-keyword: keyword:; constant slot command-argument-usage :: false-or() = #f, init-keyword: usage:; constant slot command-argument-value? :: = #f, init-keyword: value?:; end class; define method print-object (o :: , stream :: ) => () format(stream, "command-arg flags: %s key: %s", o.command-argument-flags, o.command-argument-keyword) end; define variable *all-arguments* = make(
); define class () constant slot illegal-option-command :: , required-init-keyword: command:; constant slot illegal-option :: , required-init-keyword: option:; end; define class () constant slot illegal-argument :: , required-init-keyword: argument:; end; define method print-object (o :: , stream :: ) => () format(stream, "Illegal option %s to command: %s", o.illegal-option, o.illegal-option-command) end; define method print-object (o :: , stream :: ) => () format(stream, "Illegal argument: %s", o.illegal-argument) end; define class () constant slot missing-value-argument :: , required-init-keyword: argument:; end; define method print-object (o :: , stream :: ) => () format(stream, "Missing value for argument: %s", o.missing-value-argument.command-argument-flags) end; define class () constant slot illegal-syntax-command :: , required-init-keyword: command:; end; define method print-object(o :: , stream :: ) => () format(stream, "Illegal syntax for command: %s", o.illegal-syntax-command) end; // arguments come to being independently of the commands define method register-argument(argument :: ) => (arg :: ); if(argument.command-argument-flags) map(method(f) *all-arguments*[as(, f)] := argument end, argument.command-argument-flags) end; *all-arguments*[argument.command-argument-keyword] := argument end; define function find-argument(arg :: ) => (a :: ); block() *all-arguments*[arg]; exception() signal(make(, option: as(, arg))); end; end; define macro command-argument-definer { define command-argument ?:name } => { register-argument(make(, flags: #f, keyword: ?#"name")) } end macro; define macro command-flag-definer { define command-flag ?:name } => { register-argument(make(, flags: #(?"name"), keyword: ?#"name")) } end macro; define macro command-argument-spec-definer { define command-argument-spec ?args:* end } => { register-argument(make(, ?args)) } end macro; define function is-option?(flag :: ) => (yes :: ); member?(flag.first, $option-prefixes) end; define function parse-option(option :: ) => (option :: , param :: ) let parameterized? = subsequence-position(option, $option-parameter-separator); if (parameterized?) values(copy-sequence(option, end: parameterized?), copy-sequence(option, start: parameterized? + 1)); else values(option, ""); end if; end function; define function build-command-call(command :: , #rest arguments) => (parameter-list :: ); let len = arguments.size; let parameter-list = #(); // go through the flags first // bare-argument has to be last for(i from 0 below len, while: is-option?(arguments[i])) let arg-string = copy-sequence(arguments[i], start: 1); let (arg-string :: , param :: ) = parse-option(arg-string); let arg-symbol = as(, arg-string); let arg = element(*all-arguments*, arg-symbol, default: #f); let command-arg = arg & element(command.command-arguments, arg.command-argument-keyword, default: #f); unless(command-arg) //format-out("arg: %s all-args: %s\n", arg, key-sequence(*all-arguments*)); error(make(, command: command, option: arguments[i])) end; parameter-list := pair(as-keyword(arg.command-argument-keyword), parameter-list); if(arg.command-argument-value?) i := i + 1; if(i = len | is-option?(arguments[i])) error(make(, argument: arg)); else parameter-list := pair(arguments[i], parameter-list); end; else parameter-list := pair(if (param.empty?) #t else param end, parameter-list); end; finally for (j from i below len) let command-arg = arguments[j]; let arg = element(command.command-arguments, j - i, default: #f); unless(arg) //format-out("finally command: %s arg: %s\n", command, command-arg); error(make(, command: command, option: command-arg)) end; parameter-list := pair(as-keyword(arg.command-argument-keyword), parameter-list); parameter-list := pair(command-arg, parameter-list); end for; end; reverse!(parameter-list) end; define method print-usage (c :: ) format(*standard-error*, "\nusage: %s [flag]* value\nLegal flags:", c.command-id); for (arg in c.command-arguments) format(*standard-error*, arg.command-argument-usage); end for; end method;