ArgsImpl.mesa
Copyright Ó 1985, 1991 by Xerox Corporation. All rights reserved.
Bloomenthal, October 22, 1992 3:26 pm PDT
Michael Plass, November 22, 1991 5:19 pm PST
DIRECTORY Args, Commander, CommanderOps, Convert, IO, Rope;
ArgsImpl: CEDAR PROGRAM
IMPORTS Commander, CommanderOps, Convert, IO, Rope
EXPORTS Args
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Arg: TYPE ~ Args.Arg;
Public Procedures
NArgs: PUBLIC PROC [cmd: Commander.Handle] RETURNS [INTEGER] ~ {
RETURN[CommanderOps.Parse[cmd].argc-1];
};
GetRope: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [ROPE] ~ {
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
RETURN [IF argv.argc < nArg+2 OR nArg < 0 THEN NIL ELSE argv[nArg+1]];
};
ArgRope: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ {
a.rope ¬ GetRope[cmd, nArg];
a.ok ¬ a.rope # NIL;
};
ArgReal: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ {
r: ROPE ¬ GetRope[cmd, nArg];
a.ok ¬ r # NIL;
IF a.ok THEN a.real ¬ Convert.RealFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}];
};
ArgInt: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0] RETURNS [a: Arg] ~ {
r: ROPE ¬ GetRope[cmd, nArg];
a.ok ¬ r # NIL;
IF a.ok THEN a.int ¬ Convert.IntFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}];
};
ArgIntDef: PUBLIC PROC [cmd: Commander.Handle, nArg: INTEGER ¬ 0, defVal: INT]
RETURNS [a: Arg]
~ {
r: ROPE ¬ GetRope[cmd, nArg];
a.ok ¬ TRUE;
a.int ¬ IF r = NIL
THEN Convert.IntFromRope[r ! Convert.Error => {a.ok ¬ FALSE; CONTINUE}]
ELSE defVal;
};
ArgIntRange: PUBLIC PROC [cmd: Commander.Handle, n, min, max: INT]
RETURNS [arg: Arg]
~ {
arg ¬ ArgInt[cmd, n];
IF NOT arg.ok THEN RETURN;
IF arg.ok THEN arg.ok ¬ arg.int >= min AND arg.int <= max;
};
Error: PUBLIC ERROR [reason: ROPE] = CODE;
ArgsGetFromRope: PUBLIC PROC [input, format: ROPE, caseSensitive: BOOL ¬ FALSE]
RETURNS [Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg]
~ {
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17: Arg;
cmd: Commander.Handle ¬ CommanderOps.CreateFromStreams[];
cmd.commandLine ¬ input;
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17] ¬
ArgsGet[cmd, format, caseSensitive];
RETURN[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17];
};
ArgsGet: PUBLIC PROC [cmd: Commander.Handle, format: ROPE, caseSensitive: BOOL ¬ FALSE]
RETURNS [Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg]
~ {
maxNRet: INTEGER ~ 18;
maxNArgs: INTEGER ~ 100;
retArgs: ARRAY[0..maxNRet) OF Arg;
usedArgs: PACKED ARRAY [0..maxNArgs) OF BOOL ¬ ALL[FALSE];
nArgs: INTEGER ¬ MIN[maxNArgs, NArgs[cmd]];
formatLength: INTEGER ¬ Rope.Length[format];
argIndex: INTEGER ¬ 0;
formatIndex: INTEGER ¬ 0;
retIndex: INTEGER ¬ 0;
SetRets: enter at '% or ']; return at next '%, '[, '-, or end of format string.
SetRets: PROC ~ {
optional: BOOL ¬ Rope.Fetch[format, formatIndex] = '[;
formatIndex ¬ formatIndex+1;
WHILE formatIndex < formatLength AND retIndex < maxNRet DO
c: CHAR ¬ Rope.Fetch[format, formatIndex];
SELECT c FROM
'%, '[, '-, IO.SP => RETURN;
'b => retArgs[retIndex] ¬ [ok: TRUE, bool: TRUE];
'i, 's, 'r => {
WHILE argIndex < nArgs AND usedArgs[argIndex] DO
argIndex ¬ argIndex+1;
ENDLOOP;
IF argIndex >= nArgs THEN {
IF NOT optional THEN ERROR Error["Missing non-optional argument"];
formatIndex ¬ Rope.SkipTo[format, formatIndex, "-"];
RETURN
};
retArgs[retIndex] ¬ SELECT c FROM
'i => ArgInt[cmd, argIndex],
's => ArgRope[cmd, argIndex],
'r => ArgReal[cmd, argIndex],
ENDCASE => [];
usedArgs[argIndex] ¬ TRUE;
argIndex ¬ argIndex+1;
IF NOT retArgs[retIndex].ok THEN {
IF NOT optional THEN Error["Conversion error"];
formatIndex ¬ formatIndex+1;
LOOP;
};
};
ENDCASE => {
formatIndex ¬ Rope.SkipTo[format, formatIndex, "%[-"];
Error["Unrecognized delimiter or specifier"];
RETURN
};
formatIndex ¬ formatIndex+1;
retIndex ¬ retIndex+1;
ENDLOOP;
};
SkipTokens: enter at '% or ']; return at next '%, '[, '-, or end of format string.
SkipTokens: PROC ~ {
formatIndex ¬ formatIndex+1;
WHILE formatIndex < formatLength DO
SELECT Rope.Fetch[format, formatIndex] FROM
'i, 's, 'r, 'b => retIndex ¬ retIndex+1;
'%, '[, '- => RETURN;
ENDCASE => Error["Unrecognized delimiter or specifier"];
formatIndex ¬ formatIndex+1;
ENDLOOP;
};
WHILE formatIndex < formatLength DO     -- first, set hyphenated arguments:
SELECT Rope.Fetch[format, formatIndex] FROM
'%, '[ => SkipTokens[];
'- => {
key: ROPE;
n: INTEGER ¬ Rope.SkipTo[format, formatIndex, "[%"];
IF n = formatLength THEN Error["No matching specifier"];
key ¬ Rope.Substr[format, formatIndex, n-formatIndex];
formatIndex ¬ n;
argIndex ¬ 1;
WHILE argIndex <= nArgs DO
IF Rope.Equal[GetRope[cmd, argIndex-1], key, caseSensitive] THEN {
usedArgs[argIndex-1] ¬ TRUE;
SetRets[];
EXIT;
};
argIndex ¬ argIndex+1;
REPEAT
FINISHED => formatIndex ¬ Rope.SkipTo[format, formatIndex, "-%["];
ENDLOOP;
};
ENDCASE => {
formatIndex ¬ Rope.SkipTo[format, formatIndex, "-%["];
Error["Unrecognized delimiter"];
};
ENDLOOP;
argIndex ¬ retIndex ¬ formatIndex ¬ 0;    -- now set non-hyphenated arguments:
WHILE formatIndex < formatLength DO
SELECT Rope.Fetch[format, formatIndex] FROM
'- => {formatIndex ¬ Rope.SkipTo[format, formatIndex, "%"]; SkipTokens[]};
'%, '[ => SetRets[];
ENDCASE => {
formatIndex ¬ formatIndex+1;
Error["Unrecognized delimiter"];
};
ENDLOOP;
FOR n: INTEGER IN[0..nArgs) DO
IF NOT usedArgs[n] THEN Error["Extraneous argument"];
ENDLOOP;
RETURN[
retArgs[0], retArgs[1], retArgs[2], retArgs[3], retArgs[4], retArgs[5],
retArgs[6], retArgs[7], retArgs[8], retArgs[9], retArgs[10], retArgs[11],
retArgs[12], retArgs[13], retArgs[14], retArgs[15], retArgs[16], retArgs[17]];
};
Tests
ArgsTest: Commander.CommandProc ~ {
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF args.argc < 2
THEN RETURN[$Failure,
"usage: ArgsTest <test #> <argument list>, tests are:
 1: [argument list]
 2: [optional integer]
 3: [optional real]
 4: [optional real]
 5: [optional real +/or integer]
 6: [-switch <real>]"]
ELSE {
cmd.commandLine ¬ NIL;
FOR i: INTEGER IN [2..args.argc) DO
cmd.commandLine ¬ Rope.Cat[cmd.commandLine, " ", args[i]];
ENDLOOP;
SELECT Convert.IntFromRope[args[1] ! Convert.Error => GOTO Bad] FROM
1 => [result, msg] ¬ ArgsTest1[cmd];
2 => [result, msg] ¬ ArgsTest2[cmd];
3 => [result, msg] ¬ ArgsTest3[cmd];
4 => [result, msg] ¬ ArgsTest4[cmd];
5 => [result, msg] ¬ ArgsTest5[cmd];
6 => [result, msg] ¬ ArgsTest6[cmd];
ENDCASE => GOTO Bad;
};
EXITS Bad => RETURN[$Failure, "bad test #"];
};
General Test of ArgsGet:
ArgsTest1: Commander.CommandProc ~ {
I: PROC [a: Arg, r: ROPE] ~ {
IF NOT a.ok THEN RETURN;
IO.PutFL[cmd.out, "%g:\tbool: %g\tint: %g\treal: %g\trope: %g\n",
LIST[IO.rope[r], IO.bool[a.bool], IO.int[a.int], IO.real[a.real], IO.rope[a.rope]]];
IF NOT a.ok THEN IO.PutRope[cmd.out, "\tNOT ok\n"];
};
a, b, c, d, e, f, g, i, l, m, n: Arg;
[a, b, c, d, e, f, g, i, l, m, n] ¬
ArgsGet [cmd, "-a%b-b%b-c%b-d%r-e%i-f%r-g%s-i%s-l%s-m%s-n%b" !
Error => {msg ¬ reason; GOTO Bad}];
I[a,"a"];I[b,"b"];I[c,"c"];I[d,"d"];I[e,"e"];I[f,"f"];I[g,"g"];I[i,"i"];I[l,"l"];I[m,"m"];I[n,"n"];
EXITS Bad => RETURN[$Failure, msg];
};
Tests for Defaults:
ArgsTest2: Commander.CommandProc ~ {
ArgIntDef will return a default 3 if no command line argument is specified:
IO.PutF1[cmd.out, "arg = %g\n", IO.int[ArgIntDef[cmd, 0, 3].int]];
};
ArgsTest3: Commander.CommandProc ~ {
If there is a real argument specified, return it, otherwise return 1.2:
value: REAL ¬ IF ArgReal[cmd].ok THEN ArgReal[cmd].real ELSE 1.2;
IO.PutF1[cmd.out, "arg = %g\n", IO.real[value]];
};
ArgsTest4: Commander.CommandProc ~ {
Use the optional delimiter ("[") to find an argument; if no such argument, use 1.2:
a: Args.Arg;
[a] ¬ ArgsGet[cmd, "[r"];
IO.PutF1[cmd.out, "arg = %g\n", IO.real[IF a.ok THEN a.real ELSE 1.2]];
};
ArgsTest5: Commander.CommandProc ~ {
Use the optional delimiter for two optional arguments (argument order is fixed):
a, b: Args.Arg;
[a, b] ¬ ArgsGet[cmd, "[ri"];
IO.PutF[cmd.out, "a = %g, b = %g\n",
IO.real[IF a.ok THEN a.real ELSE 1.2],
IO.int[IF b.ok THEN b.int ELSE 3]];
};
ArgsTest6: Commander.CommandProc ~ {
Use a key specifier for two optional arguments:
a, b: Args.Arg;
[a, b] ¬ ArgsGet[cmd, "-a%r-b%i"];
IO.PutF[cmd.out, "a = %g, b = %g\n",
IO.real[IF a.ok THEN a.real ELSE 1.2],
IO.int[IF b.ok THEN b.int ELSE 3]];
};
Commander.Register["ArgsTest", ArgsTest, "\nArgsTest [argument list]."];
END.