久しぶりに、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"モジュールを使用した例を紹介します。
最近のコメント