カテゴリー別アーカイブ: ISO Pascal

GNU Pascalの整数ゼロ除算検出モジュール。

GNU Pascalでは、整数ゼロ除算はランタイムシステムでは検出せず、OSのアプリ異常になってしまうので、検出するモジュールを作成しました。プログラム上は整数演算ですが、整数のゼロ除算が行われると floating-point exception シグナル(SIGFPE)が発生します。これを拾って他のランタイムエラーのようにエラーメッセージを表示して終了します。
エラーメッセージは、GNU Pascalのソースコードに「floating point exception signal received (error #264)」があったので、これを使用します。GNU PascalでSIGFPEが整数のゼロ除算以外出ないのならば「integer division by 0 (error #712)」を採用したいところですが、そうであるかどうか分からないので前者にしました。ただし、他のランタイムエラーの様な例外発生アドレスは表示しません。
その他、他のランタイムエラーの挙動に合わせて以下の仕様です。

出力先:標準エラー出力(StdErr)
プログラムの終了コード:42

使い方は、DOWNLOAD-2のページから、sigfpe_1.0.zip をダウンロードし、任意のフォルダに展開します。
そのフォルダを、コンパイルオプション –unit-path で指定します。
以下のプログラムを DivTest.p、sigfpe_1.0.zip の展開フォルダを C:¥PG¥GNUPascal¥Lib とすると、次のようにコンパイルします(1行で入力)。

gpc --extended-pascal --executable-file-name --unit-path=C:¥PG¥GNUPascal¥Lib DivTest.p
program DivTest(Input, Output);

import SigFpe; { SigFpe をインポートする }

var
  A : Integer;
  B : Integer;
  C : Integer;
begin
  Write('A = ');
  ReadLn(A);
  Write('B = ');
  ReadLn(B);
  C := A div B;
  WriteLn('C = ', C:1);
  WriteLn('正常終了');
end.

実行して、B=で0を入力するとエラーメッセージを出力して終了します。OSのアプリ異常にはなりません。

C:¥PG¥GNUPascal¥DivTest>DivTest
A = 1
B = 0
DivTest.exe: floating point exception signal received (error #264)

C:¥PG¥GNUPascal¥DivTest> _

 

GNU Pascalの例外検出について。

Pascal を使用する場合、基本的な実行時の例外はランタイムシステムで検出して処理を中断してほしいと思います。C 言語ではOSが検知して異常終了しない限り、誤ったままの処理を継続してしまい、処理が不定になったり誤った結果を出力してしまうことがあります。その結果が誤っていることも気がつかないかもしれません。
基本的な実行時の例外は以下のものがあります。関数の戻り値などでプログラムが制御できないものです。
・整数のゼロ除算。
・整数のオーバーフロー。
・(配列などの)範囲オーバー。
・不定アドレスの参照。
・スタックオーバーフロー。
例外の検出と、その捕捉を追求するとAdaになると思います。
ISO Pascalには例外の捕捉(try..catchのような仕組み)はありませんが、ISO Pascalを実装しているGNU PascalのISO Extended Pascalモードで、ちゃんと検出するかどうかを調べてみました。
その結果、以下のようになりました。Windows、Linuxとも同じ結果です。
【整数のゼロ除算】
ダメ。
gpcランタイムで検出できず、プログラムの異常終了としてOSが検知。
【整数のオーバーフロー】
ダメ(不完全)。
整数の加算と、実数から整数の変換時のオーパーフローは検出するが、整数同士の乗算によるオーバーフローは検出せず、不正な値のまま処理を続行しています。
【配列の範囲オーバー】
OK。
【不正アドレス(nilポインター)のアクセス】
OK。
【スタックオーバーフロー】
ダメ。
gpcランタイムで検出できず、プログラムの異常終了としてOSが検知。

テストプログラムは以下のものを作成しました。ソースはExTest.pとます。
コンパイルは、次のようにコンパイルオプションを指定。(1行で入力)

gpc --extended-pascal --executable-file-name --io-checking --range-and-object-checking --pointer-checking --case-value-checking --stack-checking ExTest.p
{ 例外テストプログラム }
program ExTest(Input, Output);

var
  N: Integer;

procedure Menu;
begin
  WriteLn;
  WriteLn('*** テストケース ***');
  WriteLn('1) 整数のゼロ除算');
  WriteLn('2) 整数のオーバーフロー(加算)');
  WriteLn('3) 整数のオーバーフロー(乗算)');
  WriteLn('4) 整数のオーバーフロー(実数⇒整数)');
  WriteLn('5) 配列範囲オーバー');
  WriteLn('6) nilポインターアクセス');
  WriteLn('7) スタックチェック');
  WriteLn('9) 終了');
  Write('テストNo. (1..7, 9)?');
  ReadLn(N);
end;

procedure Test1;
var
  A, B, C: Integer;
begin
  A := 1;
  B := 0;
  WriteLn('C := A div B');
  C := A div B;               { ここで整数のゼロ除算 }
  WriteLn(A, ' div ', B, ' = ', C);
end;

procedure Test2;
var
  A: Integer;
begin
  WriteLn('A := MaxInt');
  A := MaxInt;
  WriteLn('A := A + 100');
  A := A + 100;               { ここで整数オーバフロー }
  WriteLn('A = ', A);
end;

procedure Test3;
var
  A: Integer;
begin
  WriteLn('A := MaxInt');
  A := MaxInt;
  WriteLn('A := A * 100');
  A := A * 100;               { ここで整数オーバフロー }
  WriteLn('A = ', A);
end;

procedure Test4;
var
  A: Integer;
begin
  WriteLn('A := MaxInt');
  A := MaxInt;
  WriteLn('A := Round(A * 100.0)');
  A := Round(A * 100.0);      { ここで整数オーバフロー }
  WriteLn('A = ', A);
end;

procedure Test5;
var
  V : array [1..10] of Integer;
  I : Integer;
begin
  for I := 1 to 11 do
  begin
    WriteLn('V[', I:2, '] := ', I:2);
    V[I] := I;              { I=11で範囲オーバー }
  end;
end;

procedure Test6;
var
  A: Integer;
  P: ^Integer;
begin
  WriteLn('P := nil');
  P := nil;
  WriteLn('P^ := 10');        { ここでnilアクセス }
  P^ := 10;
  WriteLn('A := P^');
  A := P^;
  WriteLn(' A = ', A:2);
end;

procedure Test7;
  procedure Test7a(D: Integer);
  var
    V : array [1..1000] of Integer;
  begin
    Write(D:5);
    D := D + 1;
    Test7a(D);                { 無限再帰呼び出しでスタック溢れ }
  end;
begin
  Test7a(1);
end;

begin
  repeat
    Menu;
    case N of
      1:  Test1;
      2:  Test2;
      3:  Test3;
      4:  Test4;
      5:  Test5;
      6:  Test6;
      7:  Test7;
    otherwise
      ; {何もしない}
    end;
  until N = 9;
end.

GNU Pascal コンパイラの設定(MinGW版)を修正しました。

Pascal 日和 ホームページ の「環境設定」メニューの「GNU Pascal コンパイラの設定(MinGW版)」を、最新のMinGW環境との組合わせで動作するように修正しました。MinGW環境の設定は、「環境設定」メニューの「MinGWの設定」ページを参照して下さい。
具体的には、gnupas.cmd の中で環境変数を設定することで対応しました。Dev+GNU Pascalの環境設定が参考になりました。
これにあわせて「環境設定」メニューの「MinGWの設定(GNU Pascal用)」は不要になったので削除しました。
また、GNU PascalのMinGW版とDev+GNU Pascal版で、gmupas.cmdが重複するので、Dev+GNU Pascal版の方を、gnupas2.cmd としました。

「環境設定」メニューの「共通設定」ページにある resetpath.cmd に、gnupas.cmd など他のコマンドプロンプトで設定している環境変数を削除する設定を追加しました。

各ページを開いたら、リフレッシュして最新ページを開いて下さい。

GNU Pascalコンパイラのインストールを新しく作成しました。

GNU Pascalと古いバージョンのMinGWとの組合わせは現実的では無くなったので、Dev-Pascal という Pascal 用の IDE と GNU Pascal コンパイラ、MinGW をセットにした 「Dev+GNU Pascal」のインストール手順を、Pascal 日和 ホームページ の「環境設定」メニューに新しく「GNU Pascal コンパイラの設定(Dev GNU Pascal版)」として作成しました。
PATHの設定をすれば、これまで通りコマンドプロンプトでのメイクも問題ありません。
「Dev+GNU Pascal」は、Dev Pascalの構文色分けは Free Pascal の構文が前提のようで、GNU Pascalを使用したときの色分けはいまいちです。

 

ホームページに「エディタ設定(PSPad)」ページを追加しました。

Pascal日和 ホームページの「環境設定」メニューに「エディタ設定(PSPad)」ページを追加しました。
PSPadのインストールと、Object Pascal(Free Pascal/Lazarus)、ISO Pascal、ADW Modula-2、GNAT(Ada)のための設定手順を載せました。ダウンロード1ページのPSPad用の設定ファイルを使用します。

エディタは、それぞれ好みやこだわりがあると思いますが、フリーなので試してみてください。

ホームページの「GNU Pascal コンパイラの設定」ページを大幅に修正しました。

最新版のMinGWをインストール(またはアップグレード)すると、GNU Pascal (gpc) で以下のようなリンクエラーが発生します。

/mingw/lib/crt2.o:crt1.c:(.text+0x1f1): undefined reference to `__chkstk_ms’
/mingw/lib/libmingwex.a(glob.o):glob.c:(.text+0x5e3): undefined reference to `__chkstk_ms’
/mingw/lib/libmingwex.a(glob.o):glob.c:(.text+0x690): undefined reference to `__chkstk_ms’
/mingw/lib/libmingwex.a(glob.o):glob.c:(.text+0x7e9): undefined reference to `__chkstk_ms’
/mingw/lib/libmingwex.a(glob.o):glob.c:(.text+0x82d): undefined reference to `__chkstk_ms’
/mingw/lib/libmingwex.a(glob.o):glob.c:(.text+0xc0d): more undefined references to `__chkstk_ms’ follow
collect2: ld returned 1 exit status

「_chkstk」が「__chkstk_ms」に変更になったのが要因だと思います。
そのため、「Pascal 日和 ホームページ」の「GNU Pascal コンパイラの設定」ページを大幅に修正しました。
ページ自体も「MinGWの設定(GNU Pascal用)」と「GNU Pascal コンパイラの設定(MinGW版)」に分けました。MinGWとGNU Pascalコンパイラは「C:\GPC」にマージしました。
問題が発生した方は、新しい設定を試みてください。

テキストエディタ「PSPad」用の設定ファイルをアップしました。

「ダウンロード1」ページで、テキストエディタ「PSPad」の書式設定ファイル(構文色分け)をアップしました。
メニューの「ダウンロード1」を選択すると開きます。
PSPadは海外(チェコ)のフリーのテキストエディタで、プログラム言語用の書式設定ファイルを自作できる機能を持っているので、ISO Pascal、ADW Modula-2、GNAT(Ada)の書式設定ファイルを作成してみました。
メニューなど日本語にも対応しています。
キーワード指定に正規表現が指定できないので、数値表現のバリエーションに対応できない部分があります。GNAT(Ada)については、'”‘、”’を使用したときも正しく表示できませんが、変数’属性は正しく表示できるのでGNAT(Ada)も対応しました。

gPad用の書式設定ファイルを更新しました。

テキストエディタ gPad が Version 2.1.0 にアップデートされ、文字列中のエスケープ文字の認識を無効化できるようになりました。Pascal, Modula-2, Ada の文字列中にはエスケープ文字はありません。
「ダウンロード1」ページに公開している gPad 用の書式設定ファイルをすべて gPad Version 2.1.0 対応に更新しました。 設定ファイルのバージョンは、すべて 1.3 に統一しましした。
gPad Version 2.1.0 では、書式設定の更新に不具合があるため、新しい設定を適用する前に、旧設定をエントリごと削除して、gPadを再起動してください。それから新しい設定をインポートすると不具合を回避できます。gPad の次のリリースでは改善されるようです。

IMPLEMENTATION … の日本語表現について。

Pascal日和 ホームページで、Pascalの「実装部」、Modula-2の「実装モジュール」という表現をそれぞれ「実現部」と「実現モジュール」にしました。「Modula-2プログラミング[改訂第3版]」の表現にあわせました。