月別アーカイブ: 2014年4月

Free Pascal で日本語処理(3)

Free PascalコンパイラにソースコードのエンコーディングがUTF-8であることを認識させる方法は以下の3つがあります。

① コンパイル時に、コンパイラオプションの -FcUTF8 を指定する。
② ソースコード中にディレクティブの {$CODEPAGE UTF8} または {$CODEPAGE UTF-8}
を指定する。

③ ソースコードを BOM付きのUTF-8エンコーディングで保存する。

①~③はどれか1つで有効です。組み合わせても問題はありません。
①,②のソースコードのエンコーディングは、BOM無しのUTF-8でOKです。BOM付きのUTF-8にすると③も有効になります。

ソースコードをBOM無しのUTF-8エンコーディングで保存し、①のコンパイルオプションも②のディレクティブも指定しない場合、コンパイラはソースコードに対してUTF-8という認識はしません。

UTF-8のBOM(byte order mark)とは、ファイルの先頭3バイトに、$EF,$BB,$BF を付けたものです。③では、コンパイラがこの3バイトを認識してUTF-8と判断します。
UTF-8に対応したテキストエディタでは、ファイル作成時または保存時に、UTF-8(BOM付き)とUTF-8(BOM無し)の指定ができるはずです。もしくは、テキストエディタのオプションでBOMを付けるか付けないかの指定があるかもしりません。
BOM付きのUTF-8を「UTF-8」、BOM無しのUTF-8を「UTF-8N」と指定するエディタもあります。


先の投稿の「Free Pascal で日本語処理(1)」と「Free Pascal で日本語処理(2)」で、ソースコードをUTF-8で保存してくださいというのは、BOM無しのUTF-8エンコーディングで保存することを前提としています。その上で、UTF-8の認識を {$CODEPAGE UTF8} で切り替えるようにしています。事前説明が漏れていました。

Lazarus 1.2.2 がリリースされました。

Lazarus 1.2.2 が出ました。
http://www.lazarus.freepascal.org
Free Pascal 2.6.4 ベースになりました。(Lazarus 1.2.0はFree Pascal 2.6.2ベースです)

Win64ユーザは、Win32 + 64bitクロス環境を使用することが推奨されることは変りません。
Free Pascal 2.8.0 でWin64の問題は対応されるようです。

Lazarus 1.2.2 の Win32版とx64クロスコンパイラは以下からダウンロードできます。
http://sourceforge.net/projects/lazarus/files/Lazarus%20Windows%2032%20bits/Lazarus%201.2.2/

インストール手順や使い方は Lazarus 1.2.0 と同じです。
インストール手順は、Pascal日和ホームページにあります。1.2.0を1.2.2に読み替えて下さい。

Free Pascal で日本語処理(2)

今回は、文字列定数のエンコーディングについて投稿します。

正しく文字列定数と文字列リテラルを扱うには、UTF-8でソースコードを作成し、{$CODEPAGE UTF8}ディレクティブを指定する必要があります。

まず、{$CODEPAGE UTF8}を指定しないプログラム(KnjTest04.pp)。

program KnjTest04;
{$MODE OBJFPC}{$H+}
const
  C = '漢字文字列&SBCS123ハンカクカナ';
begin
  WriteLn('Length(C) : ', Length(C));
end.

 

KnjTest04.pp をSJISで保存してコンパイル&実行すると以下の様に表示します。

Length(C) : 24

KnjTest04.pp をUTF-8で保存してコンパイル&実行すると以下の様に表示します。

Length(C) : 41

※どちらもコンパイルオプションで -FcUTF8 の指定はしないでください。

この違いは何かというと、コンパイラはソースコードのエンコーディングが何であるかは分かりません。
プログラムを構成するキーワードが ASCIIコードであればよく、コメントや、’文字列’は単なる1バイトのコード(ASCIIにだけではなく8bitコード対応)の列でしかありません。
そのため、定数 C は、ソースに書かれているコードをそのまま文字の列として表すことになります。{$H+}を指定しているので、AnsiString型に相当します。
SJISでは、’漢’,’字’,’文’,’字’,’列’は、それぞれ2バイトの文字で構成され、その他は1バイトの文字で構成されています。
SJISでの C の長さは、5×2+14=24です。
UTF-8では、’漢’,’字’,’文’,’字’,’列’,’ハ’,’ン’,’カ’,’ク’,’カ’,’ナ’は、それぞれ3バイトの文字で構成され、その他は1バイトの文字で構成されています。
UTF-8での C の長さは、11×3+8=41です。

これは、特殊なことではなく、C コンパイラでも同じです。

次に、{$CODEPAGE UTF8}を指定したプログラム(KnjTest05.pp)。

program KnjTest05;
{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}
const
  C = '漢字文字列&SBCS123ハンカクカナ';
begin
  WriteLn('Length(C) : ', Length(C));
end.

 

KnjTest05.pp をUTF-8で保存してコンパイル&実行すると以下の様に表示します。

Length(C) : 19

この場合、Length(C)は指定した文字列の文字数となっています。
コンパイラは、{$CODEPAGE UTF8}によってエンコーディングがUTF-8であることを知らされるため、’漢’,’字’,’文’,’字’,’列’,’ハ’,’ン’,’カ’,’ク’,’カ’,’ナ’を1文字として認識してくれます。(旧バージョンのFree Pascalではどこまで対応しているか分かりません。2.6.xベースの話です)

では、このとき C の型はどうなるでしょうか?(文字列リテラルの型は何か)

C を型付き定数として、AnsiString型にしてみます(KnjTest06.pp)。

program KnjTest06;
{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}
const
  C : AnsiString = '漢字文字列&SBCS123ハンカクカナ';
begin
  WriteLn('Length(C) : ', Length(C));
end.

 

KnjTest06.pp をUTF-8で保存してコンパイルすると以下の様にコンパイルエラーとなります。

KnjTest06.pp(5,63) Error: Unicodechar/string constants cannot be converted to ansi/shortstring at compile-time

これは、Unicode文字(列)をAnsi文字(列)に変換できませんと言っています。
つまり、{$CODEPAGE UTF8}を指定した場合、文字列リテラル’漢字文字列&SBCS123ハンカクカナ’は、Unicode文字列型ということになります。

AnsiString型からUnicodeString型に変えてみます(KnjTest07.pp)。

program KnjTest07;
{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}
const
  C : UnicodeString = '漢字文字列&SBCS123ハンカクカナ';
begin
  WriteLn('Length(C) : ', Length(C));
end.

 

KnjTest07.pp をUTF-8で保存してコンパイル&実行すると以下の様に表示します。

Length(C) : 19

コンパイルは成功し、結果はKnjTest05.pp と同じになりました。

最後に、WideString型とAnsiString型を加えてみます(KnjTest08.pp)。

program KnjTest08;
{$MODE OBJFPC}{$H+}
{$CodePage UTF8}
const
  C : UnicodeString = '漢字文字列&SBCS123ハンカクカナ';
  W : WideString    = '漢字文字列&SBCS123ハンカクカナ';
  X : UnicodeString = 'ABCDEFG';
  A : AnsiString    = 'ABCDEFG';
begin
  WriteLn('Length(C) : ', Length(C));
  WriteLn('Length(W) : ', Length(W));
  WriteLn('Length(X) : ', Length(X));
  WriteLn('Length(A) : ', Length(A));
end.

 

KnjTest08.pp をUTF-8で保存してコンパイル&実行すると以下の様に表示します。

Length(C) : 19
Length(W) : 19
Length(X) : 7
Length(A) : 7

WideString型はUnicodeString型と同じように扱えます。
シングルバイト文字列(マルチバイト文字列を構成しない文字コード)の場合はUnicodeString型(WideString型)でもAnsiString型でも大丈夫です。シングルバイト文字列のリテラルはAnsiString型ということです。

Free Pascal で日本語処理(1)

今回は、Free Pascalで日本語を処理する場合のソースコードのエンコーディングについて投稿します。
これらはオフィシャルな情報ではなく、個人の見解です。参考情報として扱ってください。

結論からいいますと、日本語を容易に扱うには次の2点を行います。

  1.  ソースコードはUTF-8で作成する。
  2.  ソースコード中に{$CODEPAGE UTF8}ディレクティブを指定する。
    {$CODEPAGE UTF8}を指定せず、コンパイラでオブション -FcUTF8 を指定しても同じです。

理由を以下に示します。

Free Pascalでソースコードを記述する場合、UTF-8か、システム標準のエンコーディングになると思います。
UTF-8はFree PascalとLazarusの既定のエンコーディングです。

シングルバイトのASCIIコード(#00-#7F)はUTF-8でも変らないので英語では気になりませんが、日本語などマルチバイト文字を使用する場合は、どのエンコーディングをどの場面で使用するか考慮する必要があります。正しく理解するとFree PascalとLazarusで日本語対応アプリの作成が容易になります。
最近のLinuxはUTF-8がシステム標準となっているものが多いと思いますので、ソースコードの記述はUTF-8だけで良いと思いますが、日本語Windowsの場合、システムのエンコーディングがSJIS(CP932:コードページ932)なので、UTF-8とSJISのどちらで記述してもI/O処理はSJISになるようにする必要があります。プログラム内でのマルチバイト文字とワイド文字の変換も正しく行われるようにする必要があります。
Lazarusの場合は、ソースのエンコードとシステムのエンコードに加えGUIコンポーネントのエンコードについても考慮が必要です。今回は、Free Pascalの範囲で説明します。

※マルチバイト文字(列)

文 字:Char, AnsiChar
文字列:String、AnsiString, UTF8String
1文字は1~数バイトの可変。ASCII文字は1バイトで漢字などは複数バイトで表現されます。
{$H+}が指定されると、StringとAnsiStringは同じになります。
UTF8StringもAnsiStringと同じです。以下のように定義されています。
type UTF8String = type AnsiString;
どちらも区別なく任意のエンコーディングの文字列を格納します。
相互の代入処理においても何の変換も発生しません。

※ワイド文字(列)

文 字:WideChar, UnicodeChar
文字列:WideString, UnicodeString
1文字が2バイトのUTF-16LEエンコーディング文字です。(BMPの場合)
UnicodeStringは参照カウントで管理され、WideStringは参照カウントはありません。

Free Pascalのソースコードについては次の3パターンについて考えます。

① SJISで記述する。この場合{$ENCODING ・・・}は使用できない。
② UTF-8で記述する。{$CODEPAHE UTF8}または -FcUTF8 は指定しない。
③ UTF-8で記述する。{$CODEPAHE UTF8}または -FcUTF8 を指定する。

テストプログラムは以下のものを使用します。
エンコードを指定して保存ができるエディタを使用して下さい。

①のプログラムは、ファイル名 KnjTest01.pp、{$CODEPAGE UTF8}削除、SJISで保存。
②のプログラムは、ファイル名 KnjTest02.pp、{$CODEPAGE UTF8}削除、UTF-8で保存。
③のプログラムは、ファイル名 KnjTest03.pp、UTF-8で保存。

コンパイルは、 fpc  <ファイル名>  でOKです。

program KnjTestxx;  { xxを変えてテストする。01, 02, 03 }

{ ソースコードはSJIS(CP932)とUTF-8を使用する }

{$MODE OBJFPC}{$H+}
{$CODEPAGE UTF8}        { ③のみ使用。①, ②では削除する。}

const
  C = '漢字文字列&SBCS123ハンカクカナ';

var
  A : AnsiString;
  U : UnicodeString;
  W : WideString;

procedure WriteStr(N: Integer);
begin
  WriteLn('*** CASE ', N, ' ***');
  WriteLn('C : ', C);
  WriteLn('A : ', A);
  WriteLn('U : ', U);
  WriteLn('W : ', W);
end;

begin
  { テストケース 1 }
  A := '漢字文字列&SBCS123ハンカクカナ';
  U := '漢字文字列&SBCS123ハンカクカナ';
  W := '漢字文字列&SBCS123ハンカクカナ';
  WriteStr(1);
  { テストケース 2 }
  A := C;
  U := C;
  W := C;
  WriteStr(2);
  { テストケース 3 }
  U := A;
  W := A;
  WriteStr(3);
  { テストケース 4 }
  WriteLn;
  Write('A = '); ReadLn(A);
  Write('U = '); ReadLn(U);
  Write('W = '); ReadLn(W);
  WriteStr(4);
end.

 

各実行結果は、次のようになります。A=, U=, W= の入力のところでは、「漢字文字列&SBCS123ハンカクカナ」を入力。

【① KnjTest01】

C:¥PG¥FreePascal¥KnjTest>KnjTest01
*** CASE 1 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : ?????¶????&SBCS123??¶?¶?
W : ?????¶????&SBCS123??¶?¶?
*** CASE 2 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : ?????¶????&SBCS123??¶?¶?
W : ?????¶????&SBCS123??¶?¶?
*** CASE 3 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ

A = 漢字文字列&SBCS123ハンカクカナ
U = 漢字文字列&SBCS123ハンカクカナ
W = 漢字文字列&SBCS123ハンカクカナ
*** CASE 4 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ

ワイド文字列に対して、文字列リテラルの代入(CASE 1)や定数の代入(CASE 2)でデータが異常となります。
このときのUとWは、UTF-16でもUTF-8でもありません。
ワイド文字列への代入では、右辺値がシステムのエンコードであることが前提です。日本語Windowsの場合はSJISのMBCSが前提となります。
すでに変数に格納されている文字列は、システムのエンコードとして認識します(CASE 3)。ソースコードの文字列リテラルは漢字であろうとシングルバイト(SBCS)のエンコードとみなされ、1バイトずつワイド文字に変換されます。’漢’は、SJISで $8A-$BF ですが、このSJISコードをUTF-16に変換するのでは無く、$8Aと$BFがそれぞれUTF-16に変換され、$8A-$00,$BF-$00となってしまいます。
A := 文字列リテラル の場合は、MBCSかSBCSか関係なくそのまま変数に転送されるので、ソースコードのエンコーディングのままのコード列となります。
U := A, W := A では、Aの内容をシステムのエンコードとして解釈し、SJISのMBCSをUTF-16に変換するため正しい(期待する)UTF-16になります。

I/O処理(CASE 4)では、ワイド文字列(U,W)とSJIS(CP932)の変換が行われています。

SJIS(CP932)でプログラムを書いていたらうまくいきそうだと思うのですが、上記のような落とし穴があります。
もし、{$CODEPAGE CP932}があればこの問題もうまくいくかも知れませんが、現在のところ{$CODEPAFE UTF8}しか用意されていません。

【② KnjTest02】

C:¥PG¥FreePascal¥KnjTest>KnjTest02
*** CASE 1 ***
C : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
A : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
U : ???????????????&SBCS123????????¶?????¶???
W : ???????????????&SBCS123????????¶?????¶???
*** CASE 2 ***
C : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
A : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
U : ???????????????&SBCS123????????¶?????¶???
W : ???????????????&SBCS123????????¶?????¶???
*** CASE 3 ***
C : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
A : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
U : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
W : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・

A = 漢字文字列&SBCS123ハンカクカナ
U = 漢字文字列&SBCS123ハンカクカナ
W = 漢字文字列&SBCS123ハンカクカナ
*** CASE 4 ***
C : 貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ

基本的には①と同じ事が起こっています。
CASE 1 と CASE 2 の「貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・」(C,A)は「漢字文字列&SBCS123ハンカクカナ」のUTF8表示です。コマンドプロンプトがSJIS環境のため、化けて表示されているだけです。
ワイド文字への文字列リテラル代入も①同様、’漢’は、UTF8で $E6-$BC-$A2 なので、1バイトずつ変換され、$E6-$00,$BC-$00,$A2-$00となっています。

CASE 3 の U := A, W := A では、UTF8の表示と同じように見えますが、3バイトずつのUTF-8コードを2バイトずつのSJISコードとして解釈し、それぞれをワイド文字にしたのでコマンドプロンプトで文字化けしたのではなく、データ佐野ものが「貍「蟄玲枚蟄怜・&SBCS123・奇セ晢スカ・ク・カ・・」となっています。UTF8の文字列をコマンドプロンプトに表示したときも同様に2バイトずつのSJIS(CP932)として該当文字を表示しているので同じに見えます。

やはり、ワイド文字の問題が残るのでソースコードをUTF-8に下だけではだめです。

CASE 4 のI/O処理は、ソースコードのエンコーディングは関係ないので、①と同じく正しく変換されます。

【③ KnjTest03】

C:¥PG¥FreePascal¥KnjTest>KnjYest03
*** CASE 1 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ
*** CASE 2 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ
*** CASE 3 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ

A = 漢字文字列&SBCS123ハンカクカナ
U = 漢字文字列&SBCS123ハンカクカナ
W = 漢字文字列&SBCS123ハンカクカナ
*** CASE 4 ***
C : 漢字文字列&SBCS123ハンカクカナ
A : 漢字文字列&SBCS123ハンカクカナ
U : 漢字文字列&SBCS123ハンカクカナ
W : 漢字文字列&SBCS123ハンカクカナ

この場合は、すべて期待通りの表示となります。
コースコードをUTF-8にするだけではなく、{$CODEPAGE UTF8}も指定すると、ソースコードがUTF-8で書かれていることをコンパイラに知らせます。-FcUTF8 コンパイラオプションも同様です。
この場合、ソースコードに記述された文字列リテラルもUTF-8で書かれていると認識されます。String, AnsiString, UTF8Stringの変数の中身はシステムのエンコーディングであることが前提なので、文字列リテラルを UTF-8 から SJIS に変換して格納してくれます。文字列リテラルで設定された定数(C)もSJISとして処理されます。{$CODEPAGE UTF-8}が指定されると、文字列リテラルはUTF-16になります。
そのため、②では失敗していた CASE 1/2/3 の U := A, W := A もSJISとUTF-16の変換になり正しく処理されます。正しいワイド文字列になります。

②の様に{$CODEPAGE UTF-8}が無いとコンパイラはソースコードのエンコーディングが分かりません。そのため文字列リテラルを使用するときにいかなる変換も行わないわけです。

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> _

 

XDS Modula-2の例外検出について。

XDS Modula-2 のランタイムシステムは、以下のすべてを検出します。
【整数のゼロ除算】
【整数のオーバーフロー】
【配列の範囲オーバー】
【不正アドレス(nilポインター)のアクセス】
【スタックオーバーフロー】

XDS 2.51、XDS 2.6 beta 2 ともにクリアです。

ちなみに、ADW Modula-2 では、配列の範囲オーバーは検出しませんでした。他のチェックオプションはありましたが、なぜか範囲チェックのオプション設定自体が見当たりません。

ExTest.p の Modula-2 版のソースを以下に示します。ソースはExTest.modとます。
コンパイラのチェックも厳しく、単純に例外が予測できるコードを書くとコンパイル時にエラーとなるので、若干変えています。また、Pascal版もModula-2版も、メニューは繰返し表示するようにしていますが、実際には例外の捕捉はしていないので、例外検出でプログラムが終了します。ここではプログラムが終了してしまうことが問題では無く、終了しないことを問題としています。Free PascalとXDS Modula-2/ADW Modula-2では例外の捕捉を追加し、処理を継続させることができます。
コンパイルは、XDS Modula-2 をインストールし、次のようにします。

xc =make ExTest.mod
(* 例外テストプログラム *)
MODULE ExTest;

FROM STextIO  IMPORT WriteString, WriteLn, SkipLine;
FROM SWholeIO IMPORT WriteInt, ReadInt;
FROM RealMath IMPORT round;
FROM SYSTEM   IMPORT CAST;

VAR
    N : INTEGER;

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

PROCEDURE IntZero(): INTEGER;
BEGIN
    RETURN 0;
END IntZero;

PROCEDURE MaxInt(): INTEGER;
BEGIN
    RETURN MAX(INTEGER);
END MaxInt;

PROCEDURE Test1;
VAR
    A, B, C: INTEGER;
BEGIN
    A := 1;
    B := IntZero();
    C := A DIV B;                       (* ここで整数のゼロ除算 *)
    WriteInt(A, 0);
    WriteString(' div ');
    WriteInt(B, 0);
    WriteString(' = ');
    WriteInt(C, 0);
    WriteLn;
END Test1;

PROCEDURE Test2;
VAR
    A: INTEGER;
BEGIN
    WriteString('A := MAX(INTEGER)');
    WriteLn;
    A := MaxInt();
    WriteString('A := A + 100');
    WriteLn;
    A := A + 100;                       (* ここで整数オーバフロー *)
    WriteString('A = ');
    WriteInt(A, 0);
    WriteLn;
END Test2;

PROCEDURE Test3;
VAR
    A: INTEGER;
BEGIN
    WriteString('A := MAX(INTEGER)');
    WriteLn;
    A := MaxInt();
    WriteString('A := A * 100');
    WriteLn;
    A := A * 100;                       (* ここで整数オーバフロー *)
    WriteString('A = ');
    WriteInt(A, 0);
    WriteLn;
END Test3;

PROCEDURE Test4;
VAR
    A: INTEGER;
BEGIN
    WriteString('A := MAX(INTEGER)');
    WriteLn;
    A := MaxInt();
    WriteString('A := A * 100');
    WriteLn;
    A := round(CAST(REAL, A) * 100.0);  (* ここで整数オーバフロー *)
    WriteString('A = ');
    WriteInt(A, 0);
    WriteLn;
END Test4;

PROCEDURE Test5;
VAR
    V: ARRAY [1..10] OF INTEGER;
    I: INTEGER;
BEGIN
    FOR I := 1 TO 11 DO
        WriteString('V[');
        WriteInt(I, 2);
        WriteString('] = ');
        WriteInt(I, 2);
        WriteLn;
        V[I] := I;                      (* I=11で範囲オーバー *)
    END;
END Test5;

PROCEDURE Test6;
VAR
    A: INTEGER;
    P: POINTER TO INTEGER;
BEGIN
    WriteString('P := NIL');
    WriteLn;
    P := NIL;
    WriteString('P^ := 10');            (* ここでNILアクセス *)
    WriteLn;
    P^ := 10;
    WriteString('A := P^');
    WriteLn;
    A := P^;
    WriteInt(A, 0);
    WriteLn;
END Test6;

PROCEDURE Test7;
    PROCEDURE Test7a(D: INTEGER);
    VAR
        V: ARRAY [1..1000] OF INTEGER;
    BEGIN
        WriteInt(D, 8);
        INC(D);
        Test7a(D);
    END Test7a;
BEGIN
    Test7a(1);
END Test7;

BEGIN
    REPEAT
        Menu;
        CASE N OF
              1: Test1;
            | 2: Test2;
            | 3: Test3;
            | 4: Test4;
            | 5: Test5;
            | 6: Test6;
            | 7: Test7;
        ELSE
            ; (* 何もしない *)
        END;
    UNTIL N = 9;
END ExTest.

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

Free Pascal のランタイムシステムは、以下のすべてを検出します。
【整数のゼロ除算】
【整数のオーバーフロー】
【配列の範囲オーバー】
【不正アドレス(nilポインター)のアクセス】
【スタックオーバーフロー】

GNU Pascalの例外検出について。」のプログラムをFree Pascalコンパイラで次のようにコンパイルします。ソースはExTest.ppとます。

fpc -Mobjfpc -Crtoi ExTest.pp

また、program ExTest(Input, Output); の後に、uses SysUtils; を追加すると、例外発生時にはエラー番号ではなくメッセージが表示されます。
try .. catch .. end を使用して、例外を捕捉することもできます。

以上を考えると、特に処理系の指定がない場合は、GNU Pascal よりも Free Pascal を使用した方が、安全なプログラムが作れると思います。
ISO Standard PascalやISO Extended Pascalの使用が求められる場合は、GNU Pascalという選択肢もあると思いますが、その時は、検出されない実行時例外があることを意識してください。整数のオーバーフローについては、一度実数計算をしてからround()関数で、整数かをして検出させるなどの工夫をするのも有効です。
どちらにしても、通常はコンパイラオプションやソース中のディレクティブなどで、各種例外のチェック機能を有効にするようにしてください。

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 など他のコマンドプロンプトで設定している環境変数を削除する設定を追加しました。

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