久しぶりに、Modula-2でプログラミング--その2 CmdLineモジュールの紹介-- ― 2023年01月23日 20:58
コマンドライン・パラメーターを読みとり、分解する仕組みを実現したモジュールを公開します。 Modula-2で作成しました。Modula-2では、コンパイル単位を「モジュール」と呼びます。モジュールは3種類あり、 「定義モジュール(DEFINITION MODULE)」、「実現モジュール(IMPLEMENTATION MODULE)」、 「モジュール(MODULE") です。
「定義モジュール」は、モジュールのインターフェイスを記述したもので、他のモジュールに公開する定数や手続きを記述します。
「実現モジュール」は、名前の通り、モジュールの機能を書き下したプログラムです。
「モジュール」は、いわゆるメインプログラムです。
まずは、定義モジュール"CmdArgs.def"です。
DEFINITION MODULE CmdArgs;
EXPORT QUALIFIED Argv,Argc;
VAR
Argc: CARDINAL;
PROCEDURE Argv(n: CARDINAL;VAR ArgString: ARRAY OF CHAR);
END CmdArgs.
モジュール"CmdArgs"が提供(EXPORT)する手続き"Argv"と変数"Argc"を定義しています。
これを事前コンパイルしておき、このモジュールを利用するモジュールをコンパイルする際に読み込まれ、
妥当性のチェックに使われます。
おつぎは、実現モジュール"CmdArgs.mod"です。
CmdArgs.mod
IMPLEMENTATION MODULE CmdArgs;
FROM ASCII IMPORT nul,ht,lf,cr;
FROM SYSTEM IMPORT ADR,WORD;
FROM OpSys IMPORT BdosFunctions,Bdos,CPMStringBuffer;
CONST
SPACE = ' ';
TAB = ht;
COMMA = ',';
SQUORT = "'";
DQUORT = '"';
EOS = nul;
MAXARGS = 20;
EMPTY = -1;
VAR
ArgList: ARRAY [0..MAXARGS-1] OF INTEGER;
CommandLine: CPMStringBuffer;
QChar: CHAR;
c,s: CARDINAL;
CommaFlag: BOOLEAN;
(* Argc: CARDINAL; defined in CmdArgs.Def *)
Msg: ARRAY [0..80] OF CHAR;
PROCEDURE skipBlanks();
BEGIN
WHILE isBlankChar(CommandLine.text[c]) DO
INC(c);
END;
END skipBlanks;
PROCEDURE skipSeparator();
BEGIN
WHILE isSeparator(CommandLine.text[c]) DO
INC(c);
END;
END skipSeparator;
PROCEDURE isBlankChar(ch: CHAR): BOOLEAN;
BEGIN
IF (ch = SPACE) OR (ch = TAB) THEN
RETURN TRUE;
END;
RETURN FALSE;
END isBlankChar;
PROCEDURE Argv(n: CARDINAL;VAR ArgString: ARRAY OF CHAR);
VAR
s,d: CARDINAL;
BEGIN
IF Argc < n THEN
ArgString[0] := EOS;
ELSIF ArgList[n] = EMPTY THEN
ArgString[0] := EOS;
ELSE
s := ArgList[n];
d := 0;
WHILE CommandLine.text[s] # EOS DO
ArgString[d] := CommandLine.text[s];
INC(s);INC(d);
END;
ArgString[d] := EOS;
END;
END Argv;
PROCEDURE nextQuort();
BEGIN
WHILE (CommandLine.text[c] # EOS)
AND (CommandLine.text[c] # QChar) DO
INC(c);
END;
END nextQuort;
PROCEDURE nextSeparator();
BEGIN
WHILE (CommandLine.text[c] # EOS)
AND (NOT isSeparator(CommandLine.text[c])) DO
INC(c);
END;
END nextSeparator;
PROCEDURE getOneArg(): CARDINAL;
VAR
s: CARDINAL;
BEGIN
IF CommandLine.text[c] # EOS THEN
IF isQuort(CommandLine.text[c]) THEN
QChar := CommandLine.text[c];
INC(c);
s := c;
nextQuort();
ELSE
s := c;
nextSeparator();
END;
CommaFlag := FALSE;
IF CommandLine.text[c] # EOS THEN
IF CommandLine.text[c] # COMMA THEN
CommandLine.text[c] := EOS;
INC(c);
skipBlanks();
END;
IF CommandLine.text[c] = COMMA THEN
CommaFlag := TRUE;
CommandLine.text[c] := EOS;
INC(c);
END;
END;
ELSE
s := 0;
END;
RETURN s;
END getOneArg;
PROCEDURE isQuort(ch: CHAR): BOOLEAN;
BEGIN
IF (ch = SQUORT) OR (ch = DQUORT) THEN
RETURN TRUE;
END;
RETURN FALSE;
END isQuort;
PROCEDURE isSeparator(ch: CHAR): BOOLEAN;
BEGIN
CASE ch OF
SPACE: RETURN TRUE;
| TAB: RETURN TRUE;
| COMMA: RETURN TRUE;
ELSE
RETURN FALSE;
END;
END isSeparator;
PROCEDURE getCmdLine();
VAR
BDOSCmd: RECORD
CASE BOOLEAN OF
TRUE: Func: BdosFunctions;
| FALSE: Cmd: WORD;
END;
END;
junk: WORD;
BEGIN
BDOSCmd.Func := prtStr;
Msg := 'arguments>$';
Bdos(BDOSCmd.Cmd,ADR(Msg),junk);
CommandLine.maxLen := CHR(255);
CommandLine.curLen := CHR(0);
BDOSCmd.Func := rdCBuf;
Bdos(BDOSCmd.Cmd,ADR(CommandLine),junk);
CommandLine.text[ORD(CommandLine.curLen)] := EOS;
BDOSCmd.Func := prtStr;
Msg[0] := cr; Msg[1] := lf; Msg[2] := '$';
Bdos(BDOSCmd.Cmd,ADR(Msg),junk);
FOR Argc := 0 TO MAXARGS-1 DO
ArgList[Argc] := EMPTY;
END;
Argc := 0;
c := 0;
CommaFlag := FALSE;
skipBlanks();
WHILE (CommandLine.text[c] # EOS) AND (Argc < MAXARGS) DO
ArgList[Argc] := getOneArg();
skipBlanks();
INC(Argc);
END;
IF (CommandLine.text[c] = EOS) AND CommaFlag THEN
ArgList[Argc] := c;
INC(Argc);
END;
END getCmdLine;
BEGIN
getCmdLine();
END CmdArgs.
手続き"getCmdLine()"は、このモジュールが初期化されるときに動きます。"getCmdLine()"は、プロンプト "arguments>"を表示し、引数を読みとります。その後、前回示したルールに従って引数を分解します。
変数"Argc"は、分解後の引数の数です。
手続き"Argv(n,Param)"は、"n"番目の引数(nilで終わる文字列)を "Param"にコピーして返します。 存在しない引数の時は、"Param"は、長さ0の文字列になります。第一引数は引数は、"n=0"です。
何の変哲のない下請け手続きで書き下しましたので、ロジックを簡単に追えると思います。
次回は、"CmdArgs"モジュールを使用した例を紹介します。
最近のコメント