割と簡単に '標準 Pascal' を試してみたい

# はじめに
Delphi を使っていて、何が **標準 Pascal** (Standard Pascal) に準拠していて何が準拠していないのかふと知りたくなることがあります。ISO/IEC 7185 の仕様を読めばいいのですが、実際にコードを実行して試したい事がよくあります。

そんな時は [ideone](https://qiita.com/ht_deko/items/aba9e0b6a6e8d003d34e#ideonecom) で **GNU Pascal (gpc)** を使ったり、同じく **Free Pascal (fpc)** で `標準 Pascal 互換モード` にしてみたりするのですが、これらは純粋な標準 Pascal ではないので、場合によっては検証にならなかったりします。 

 - [標準 Pascal の規格票と解説書を読んでみる (Qiita)](https://qiita.com/ht_deko/items/091661d6fa3d72700a56)
 - [ 標準 Pascal 範囲内での Delphi 入門 (Qiita)](https://qiita.com/ht_deko/items/e2796788c65c2cda1de5)

# 純粋な '標準 Pascal' を求めて
Windows 環境で最も簡単に実行できる標準 Pascal は Scott A. Moore 氏が書いた (改修した) **Pascal-P5** だと思われます (P4 まではサブセット)。

 - [PASCAL-P5 (SourceForge)](https://sourceforge.net/projects/pascalp5/)
 - [PASCAL-P5 Files (SourceForge)](https://sourceforge.net/projects/pascalp5/files/)
 - [PASCAL-P5 master/ (SourceForge)](https://sourceforge.net/p/pascalp5/code/ci/master/tree/)

PASCAL-P シリーズは P-CODE コンパイラです。PCOM / PINT の 2 つのコマンドがあり、PCOM がソースコードを中間ファイル形式にコンパイルし、それを PINT が実行します。PCOM がコンパイラで PINT がインタプリタという訳です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/09fe1546-14fe-5639-3da6-d9f792e8d9e0.png)

より正確に言えば、PCOM が Pascal ソースコードを中間ファイル形式 (疑似アセンブラソース) に変換し、PINT が 中間ファイルをアセンブルして疑似機械語に変換したもの (メモリ上に生成) を実行しています。

なので、理屈的には PINT さえ移植できればどこでもプログラムを実行できます。え？ **Java っぽい？**　え？ **.NET っぽい？**　え？ **VB っぽい？** そうですか...

 - [Pascal-P シリーズについて (Qiita)](https://qiita.com/ht_deko/items/adab81f28295a124e9eb)
 - [pコードマシン (Wikipedia)](https://ja.wikipedia.org/wiki/P%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9E%E3%82%B7%E3%83%B3)

## ■ ISO 標準 Pascal を取り巻く状況
Pascal-P5 は ISO 標準 Pascal の仕様でコンパイルされる前提になっていますが、現在では ISO 標準 Pascal を準備する事自体が難しい状況になっています。**ISO 標準 Pascal を試すために ISO 標準 Pascal が必要**という「鶏が先か卵が先か」という状況です。

**GNU Pascal (gpc)** は ISO 標準 Pascal の仕様を含んでいますが、32bit 環境しかないために最近の Linux のパッケージには含まれていないことが多いです。Windows 環境への導入も割と面倒です。

**Free Pascal (fpc)** はマルチプラットフォームで導入も簡単なのですが、Turbo Pascal 寄りであり、ISO 標準 Pascal 互換に近づけるには `{$mode ISO}` を指定してコンパイルするか、コンパイルスイッチ `-Miso` を付ける必要があります。

幸い、Pascal-P5 のアーカイブには Windows 用の実行可能ファイル (EXE) が含まれているため、Windows 環境ならば割と簡単に ISO 標準 Pascal を試すことができます [^7]。
 
## ■ Window で Pascal-P5 を動かす

### 環境設定
PASCAL-P5 のアーカイブを解凍したら、bin フォルダにある **PCOM.EXE** と **PINT.EXE** を適当な場所にコピーします。これが PASCAL-P5 の本体です [^6]。

さて、PCOM は `prd` という名前のファイルをソースコードとみなし、コンパイルして `prr` という中間形式ファイルを出力します。そして PINT は `prd` という名前のファイルを中間形式ファイルとみなして実行します...どちらも入力ファイルが `prd` で出力ファイルが `prr` です。名前決め打ちなんですよ、面倒ですね。

これを簡単にするバッチファイルもアーカイブの bin フォルダにあるのですが、何故かバッチファイルに cp や mv が使われていて Cygwin や MinGW がないと動作しません。

 - [Cygwin](https://www.cygwin.com/)
 - [Cygwin (Wikipedia)](https://ja.wikipedia.org/wiki/Cygwin)
 - [MinGW (Wikipedia)](https://ja.wikipedia.org/wiki/MinGW)

### スクリプト (バッチ) ファイル
面倒なので Cygwin が不要なようにバッチファイルを書きました。以下の 4 つのファイルを pcom.exe / pint.exe と同じ場所に保存してください。

**・setpath.bat**
カレントディレクトリを環境変数 PATH に設定するバッチファイルです。

```bat:setpath.bat
@echo off
cls
set PATH=%CD%;%PATH%
echo %PATH%
```

**・compile.bat**
ソースファイルをコンパイルして中間形式ファイルを出力するバッチファイルです。

```bat:compile.bat
@echo off
cls

if not "%1"=="" goto paramok
echo *** Error: Missing parameter
goto exit

:paramok
if exist "%1.pas" goto fileexists
if exist "%1.dpr" goto fileexists
echo *** Error: Missing %1.pas file
goto exit

:fileexists
if "%2"=="" goto continue
if exist "%2" goto continue
echo *** Error: Missing %2 input file
goto exit

:continue
echo.
echo Compiling and running %1
echo.
if exist "%1.pas" copy %1.pas prd > nul
if exist "%1.dpr" copy %1.dpr prd > nul
pcom
move prr %1.p5 > nul

:stop
if exist prd del prd

:exit
```

**・run.bat**
中間形式ファイルを解釈して実行するバッチファイルです。

```bat:run.bat
@echo off
cls

if not "%1"=="" goto paramok
echo *** Error: Missing parameter
goto exit

:paramok
if exist "%1.p5" goto fileexists
echo *** Error: Missing %1.p5 file
goto exit

:fileexists
if not "%2"=="" goto useinputfile
copy %1.p5 prd > nul
goto run

:useinputfile
rem The input file, if it exists, gets put on the end of the intermediate
echo ? >> %1.p5
type %1.p5 %2 > prd

:run
pint
if "%3"=="" goto stop
copy prr %3 > nul

:stop
if exist prd del prd
if exist prr del prr

:exit
```

**・p5.bat**
ソースコードファイルをコンパイルして実行するバッチファイルです。

```bat:p5.bat
@echo off
cls

if not "%1"=="" goto paramok
echo *** Error: Missing parameter
goto stop

:paramok
if exist "%1.pas" goto fileexists
if exist "%1.dpr" goto fileexists
echo *** Error: Missing %1.pas file
goto stop

:fileexists
if "%2"=="" goto continue
if exist "%2" goto continue
echo *** Error: Missing %2 input file
goto stop

:continue
echo.
echo Compiling and running %1
echo.
if exist "%1.pas" copy %1.pas prd > nul
if exist "%1.dpr" copy %1.dpr prd > nul
pcom
move prr %1.p5  > nul
if not "%2"=="" goto useinputfile
copy %1.p5 prd  > nul
goto run

:useinputfile
rem The input file, if it exists, gets put on the end of the intermediate
echo ? >> %1.p5
type %1.p5 %2 > prd

:run
pint
if "%3"=="" goto stop
copy prr %3 > nul

:stop
if exist prd del prd
if exist prr del prr

:exit
```

### PASCAL-P5 の使い方 (Windows)
まず、サンプルプログラムを用意します。アーカイブの `sample-programs` フォルダにもサンプルがありますが、今回は FizzBuzz を用意してみました。

```pascal:FizzBuzz.pas
program FizzBuzz(output);
var
  i: Integer;
begin
  for i:=1 to 100 do
    begin
      if ((i mod 3) + (i mod 5)) = 0 then
        Writeln('Fizz Buzz')
      else if (i mod 3) = 0 then
        Writeln('Fizz')
      else if (i mod 5) = 0 then
        Writeln('Buzz')
      else
        Writeln(i);
    end;
end.
```

まず、PASCAL-P5 (とバッチファイル) を格納したフォルダへ移動します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/6cd56c7b-efdc-99d7-ecd6-1bea783052c8.png)

アドレスバーに `CMD` と入力し〔Enter〕キーを押し、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/e3b25c9e-3ecd-98e3-b349-a23071587280.png)

カレントディレクトリでコマンドプロンプトを開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/54d82a13-150b-1227-61e9-d276272f865d.png)

先程の FizzBuzz.pas が違うフォルダにあるのであれば `setpath` を実行して PASCAL-P5 にパスを通しておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/22a32f4d-8875-2e17-e46a-52882ae2a7e4.png)

`compile fizzbuzz` としてコンパイルします。ここでの拡張子の入力は不要ですが、ソースファイル名は *.pas である必要があります [^5]。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/4d1379a5-fb99-d9e6-3360-13fd08ee76fa.png)

正しくコンパイルされると `fizzbuzz.p5` ファイルが生成されます。これが中間形式ファイルです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/8368b9cd-dd54-4f11-1446-f1f1b6feeb5e.png)

`run fizzbuzz` として実行します。拡張子は不要です。一旦中間形式ファイルができてしまえば、実行の都度コンパイルする必要はありません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/ea99261b-f64f-e164-ddde-e9c58139fb9e.png)

`p5 fizzbuzz` とすると、コンパイルと実行を連続して行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/77c5fd1d-aa08-b27e-3017-97b256ef0748.png)

### 外部ファイルの扱い方

**PASCAL-P5** では、任意の外部ファイルを扱えませんが、2 つの外部テキストファイルを扱う事ができます。但し理屈的に、プログラムで書き出したファイルを同じプログラムで読み込めないという制約があります。

| 外部ファイル | 説明 |
|:---|:---|
| **prd** | 読み込み専用の外部ファイル |
| **prr** | 書き込み専用の外部ファイル |

**PASCAL-P5** のコンパイラである **PCOM** は、次のような動作になっています。

 - `prd` という名前のファイルをソースファイル (Pascal) として扱う。
 - `prr` という名前でコンパイルされた中間ファイルを出力する。

**PASCAL-P5** のインタプリタである **PINT** は、次のような動作になっています。

 - `prd` という名前のファイルを中間形式ファイルとして扱う。
 - `prr` という名前で出力ファイルを出力する。

少しややこしいのですが、コンパイラ (PCOM) もインタプリタ (PINT) も入力ファイル `prd` と、出力ファイル `prr` という 2 つの外部ファイルしか扱えないという事です。

インタプリタ実行用バッチファイル `run.bat` は外部ファイルを扱えるようになっています。

```
run.bat "プログラム (中間形式ファイル)" <"入力ファイル" <"出力ファイル">>
```

入力ファイルがある場合、それを中間形式ファイルの後ろにマージします。

#### ・ファイル入力
例えば次のファイルがあったとします。

```text:hello.txt
Hello,world.
BlaisePascal
```

次のプログラムは入力ファイルを標準出力に出力します。

```pascal:ReadFile.pas
program ReadFile(output, prd);
var
  C: Char;
begin
  Reset(prd);
  while not Eof(prd) do
    begin
      while not Eoln(prd) do
        begin
          Read(prd, C);
          Write(C);
        end;
      Readln(prd);
      Writeln;  
    end;
end.
```

次のようにすると入力ファイル `hello.txt` を受け取ります。

```
run ReadFile hello.txt
```

但し、中間ファイルとマージしているため、中間ファイルの内容も表示してしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/bd69afff-f47e-cf82-087b-05f5e261be4e.png)

バッチファイルは中間ファイルと入力ファイルをマージする時に `?` を挟み込んでいますから、これを目印に中間ファイルを読み飛ばす事は可能です。

```pascal:ReadFile
program ReadFile(output, prd);
label
  1;
var
  C: Char;
begin
  Reset(prd);
  { Skip Intermediate File }
  while not Eof(prd) do
    begin
      Read(prd, C);
      if C = '?' then
        begin
          Readln(prd);
          goto 1;  
        end;  
    end;   
1:
  while not Eof(prd) do
    begin
      while not Eoln(prd) do
        begin
          Read(prd, C);
          Write(C);
        end;
      Readln(prd);
      Writeln;  
    end;
end.
```

今度は入力ファイルだけが処理されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/f4e608db-9e4b-b439-2872-70920b34bc21.png)

#### ・ファイル出力
次のプログラムはテキストファイルを出力します。

```pascal:WriteFile.pas
program WriteFile(prr);
begin
  Rewrite(prr);
  Writeln(prr, 'Hello,world.');
  Writeln(prr, 'BlaisePascal');
end.
```

次のようにすると入力ファイルなし (NUL) で、`hello.txt` を出力します。

```
run WriteFile NUL hello.txt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/f15b3afd-b02c-961c-309d-3dcf19482d4e.png)

```text:hello.txt
Hello,world.
BlaisePascal
```


## ■ pcom と pint をソースコードからビルドする際の注意事項

独自ビルドする場合には、`doc` フォルダにある **the_p5_compiler.pdf** に軽く目を通しておいてください。本記事の手順はドキュメント記載の手順と異なる場合があります。


コンパイルが通り、`pcom` を単独で実行した際に、次のような表示になるのであれば、後述するソースコードの修正が必要です。

```
P5 Pascal compiler vs. 1.3
Pascal-P5 complies with the requirements of level 0 of ISO/IEC 7185.

i
i Pascal intermediate file Generated by P5 Pascal compiler vs. 1.3
i
o a-b-c+d+e-f-g-h-i-j-k-l+m-n-o-p-q-r+s-t-u+v+w-x-y-z-
```

`i i i o...` と続くこのファイルはコンパイラが吐く中間形式ファイルで、これをファイルではなく標準出力 (画面) に出力してしまっています。

:::note warn
最新版 (v1.4) のソースは <font color="Crimson">GPC 以外でコンパイルする事が困難</font>になっています (GPC も一緒に置いてありますが...)。
:::

:::note info
v1.3 のアーカイブが DL できなくなってしまったようなので、https://ayera.dl.sourceforge.net/project/pascalp5/pascal-p5.tar.gz を https://ht-deko.com/software/pascal-p5.tar.gz にミラーしておきました。
:::


### FPC でビルドする/した際の注意点

FPC のバージョンによっては `EOF()` の解釈が標準 Pascal と異なるため、2 つの問題が発生します。

 1. ソースコードのピリオドの後に任意の 1 バイトが必要。
 2. PINT が `End of file` のエラーを出力する事がある。

前者は次のコードでエラーが出ます。

```pascal:hello.pas
program Hello(Output);
begin
  Writeln('Hello, world.');
end.
```

```
Error numbers in listing:
-------------------------
  6  Illigal symbol
 22  '.' expected
```
ピリオドの後に任意の一文字があれば問題を回避できますので、ピリオドの後で改行しておくといいでしょう。CP/M ファイルのように、ファイル終端として 0x1A を追加してもいいと思います。

後者は `PINT.PAS` を一部書き換える必要があります。2332 行付近にある次のコードを

```pascal:pint.pas
                              { FPC has an error where it indicates EOF 1 byte 
                                before the end of the file. A workaround is to
                                comment the EOF check out, but note that this
                                is not permanent, since it compromises error
                                checking. }
                              if eof(bfiltable[fn]) then
                                errori('End of file              ');
```

コメントアウトします。

```pascal:pint.pas
                              { FPC has an error where it indicates EOF 1 byte 
                                before the end of the file. A workaround is to
                                comment the EOF check out, but note that this
                                is not permanent, since it compromises error
                                checking. }
{
                              if eof(bfiltable[fn]) then
                                errori('End of file              ');
}
```

:::note info
Windows 11 の WSL2 (Ubuntu 20.04.3 LTS) にインストールした fpc (3.0.4+dfsg-23 [2019/11/25] for x86_6) では EOF() の問題は発生しませんでした。
:::

:::note info
現状 (2021/10 現在) では、Windows / macOS / Linux で修正箇所は同一になっていると思います。
:::

## ■ Windows の場合 (fpc でコンパイルする場合)

Windows 環境で **Free Pascal (fpc)** を用いてソースコードから Pascal-P5 をビルドする手順です。

Windows 10 1803 以降で作業する事を想定しています。FreePascal (または Lazarus) は適当な場所にインストールしておいてください。

### 環境設定
アーカイブを DL して解凍しておきます。

```
C:\> mkdir c:\tmp
C:\> cd C:\tmp
C:\tmp> curl -OL https://ht-deko.com/software/pascal-p5.tar.gz
C:\tmp> tar -zxvf pascal-p5.tar.gz
C:\tmp> cd Pascal-p5\source
C:\tmp\Pascal-p5\source> 
```

### コンパイル
アーカイブに最初から入っているバイナリと同じ挙動にするにはソースコードの修正が必要なようです。

**note:** 当方の環境には Lazarus がインストールされており、それに含まれる fpc をコンパイルに使ったのですが、バージョンは `3.0.4 [2018/5/19]` でした。`{$mode ISO}` は効くようですが、外部ファイルとの関連付けに assign() が必要な感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/51104e66-622d-289d-c77a-ee03f26da294.png)


**・pcom.pas の修正**
任意のエディタで pcom.pas を開いてください。

```
C:\> micro pcom.pas
```

Assign の処理を追加します (Line: 5986 付近)。追加場所は `(*initialize*)` で検索してください。

```pascal:pcom.pas
  write('P5 Pascal compiler vs. ', majorver:1, '.', minorver:1);                                      
  if experiment then write('.x');                                                                     
  writeln;                                                                                            
  writeln('Pascal-P5 complies with the requirements of level 0 of ISO/IEC 7185.');                    
  writeln;        
                                                                                   
  (*initialize*)                                                                                      
  (************)  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加
  initscalars; initsets; inittables;                                                                 
```

**・pint.pas の修正**
任意のエディタで pint.pas を開いてください。

```
C:\> micro pcom.pas
```

Assign の処理を追加します (Line: 2432 付近)。追加場所は `P5 Pascal interpreter` で検索してください。

```pascal:pint.pas
  write('P5 Pascal interpreter vs. ', majorver:1, '.', minorver:1);
  if experiment then write('.x');
  writeln;
  writeln;
  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加

  { !!! remove this next statement for self compile }
  {elide}rewrite(prr);{noelide}
```

修正が終わったら fpc.exe にパスを通します。

```
C:\tmp\Pascal-p5\source> set path=<fpc.exe のある場所>;%PATH%
```

コンパイルします。

```
C:\tmp\Pascal-p5\source> fpc -Miso -v0 pcom.pas
C:\tmp\Pascal-p5\source> fpc -Miso -v0 pint.pas
```

**pcom.exe** と **pint.exe** が生成されていると思うので、これをコピーして使います。


## ■ Windows の場合 (gpc でコンパイルする場合)
Windows 環境で **GNU Pascal (gpc)** を用いてソースコードから Pascal-P5 をビルドする手順です。

MinGW と GPC の環境設定を行います。[『Pascal 日和』](https://lazpas.e-hiyori.org/hp1/) さんの記事を参考にしてください。

 - [共通設定 (Pascal 日和)](https://lazpas.e-hiyori.org/hp1/kankyou_kyoutsuu.html)
 - [MinGW 設定 (Pascal 日和)](https://lazpas.e-hiyori.org/hp1/mingw_setup.html)
 - [GNU Pascal の設定 (MinGW版) (Pascal 日和)](https://lazpas.e-hiyori.org/hp1/compiler_gnupascal_mingw.html)

:::note
GPC の環境設定がうまくいかない場合には IDE (Dev-Pascal) 付きの `Dev+GNU Pascal 1.9.4.13 + GPC 20070904 + Mingw runtimes + GCC (3.4.5) support files` をインストールしてみてください。

 - [https://www.gnu-pascal.de/binary/mingw32/](https://www.gnu-pascal.de/binary/mingw32/)
:::

### 環境設定
アーカイブを DL して解凍しておきます。

```
C:\> mkdir c:\tmp
C:\> cd C:\tmp
C:\tmp> curl -OL https://ht-deko.com/software/pascal-p5.tar.gz
C:\tmp> tar -zxvf pascal-p5.tar.gz
C:\tmp> cd Pascal-p5
```

### コンパイル
設定が正しく完了していれば、ドキュメントにあるように、

```
C:\tmp\Pascal-p5> setpath
C:\tmp\Pascal-p5> configure
C:\tmp\Pascal-p5> make
```

と実行するだけです。実行ファイルは `Pascal-p5\bin` フォルダに生成されます。

最新版のソースだと `cc.exe` がなくて `spew.c` がコンパイルできないかもしれません。**spew.exe** が不要なら放置で、必要なら MinGW の bin フォルダにある gcc.exe をコピーして cc.exe にリネームしたものを置いておくといいでしょう。

## ■ macOS の場合 (fpc でコンパイルする場合)
macOS 環境で **Free Pascal (fpc)** を用いてソースコードから Pascal-P5 をビルドする手順です。

事前に Xcode はインストールされているものとします。当方の環境は macOS High Sierra (10.13.6) / Mac Mini (Mid 2011) です。

### 環境設定

まずはパッケージャ **Homebrew** を導入します。

 - [Homebrew](https://brew.sh/index_ja)

サイトの説明にあるように、ターミナルで以下のスクリプトを実行します。

```bash
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

Homebrew の導入が終わったら `fpc` をインストールします。

```bash
$ brew install fpc
```

PASCAL-P5 のアーカイブを DL して解凍します。

```
$ cd /tmp
$ curl -OL https://ht-deko.com/software/pascal-p5.tar.gz
$ tar -zxvf pascal-p5.tar.gz
$ cd Pascal-p5/source/
```

### コンパイル
Windows 用バイナリと同じ挙動にするにはソースコードの修正が必要なようです。

**note:** Homebrew でインストールされた fpc は `3.0.4 [2018/10/2]` でした。`{$mode ISO}` は効くようですが、外部ファイルとの関連付けに assign() が必要な感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/7468bc75-853b-199e-8bc4-986c0f7c208a.png)

**・pcom.pas の修正**
任意のエディタで pcom.pas を開いてください。

```
$ nano pcom.pas
```

Assign の処理を追加します (Line: 5986 付近)。追加場所は `(*initialize*)` で検索してください。

```pascal:pcom.pas
  write('P5 Pascal compiler vs. ', majorver:1, '.', minorver:1);                                      
  if experiment then write('.x');                                                                     
  writeln;                                                                                            
  writeln('Pascal-P5 complies with the requirements of level 0 of ISO/IEC 7185.');                    
  writeln;        
                                                                                   
  (*initialize*)                                                                                      
  (************)  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加
  initscalars; initsets; inittables;                                                                 
```

**・pint.pas の修正**
任意のエディタで pint.pas を開いてください。

```
$ nano pint.pas
```

Assign の処理を追加します (Line: 2432 付近)。追加場所は `P5 Pascal interpreter` で検索してください。

```pascal:pint.pas
  write('P5 Pascal interpreter vs. ', majorver:1, '.', minorver:1);
  if experiment then write('.x');
  writeln;
  writeln;
  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加

  { !!! remove this next statement for self compile }
  {elide}rewrite(prr);{noelide}
```

**・コンパイル**
pcom.pas と pint.pas を Free Pascal でコンパイルします。`{$mode ISO}` オプションを付けます。

```
$ fpc -Miso -v0 -Px86_64 pcom.pas
$ fpc -Miso -v0 -Px86_64 pint.pas
```

エラーが出るようなら、環境変数 `MACOSX_DEPLOYMENT_TARGET` にバージョンを明示的に指定してコンパイルします。

```
$ MACOSX_DEPLOYMENT_TARGET=10.13 fpc -Miso -v0 -Px86_64 pcom.pas
$ MACOSX_DEPLOYMENT_TARGET=10.13 fpc -Miso -v0 -Px86_64 pint.pas
```

バイナリを ~/p5/ にコピーします [^4]。

```
$ mkdir ~/p5
$ cp pcom pint ~/p5/
```

もうソースコードは不要です。

```
$ cd /tmp
$ rm -Rf Pascal-P5/
$ rm -f pascal-p5.tar.gz
```

### スクリプトファイル
スクリプトファイルを適当な場所 (~\p5) に作成します。

```bash
$ cd ~/p5/
```

スクリプトファイルは [Linux と共通](#スクリプトファイル)です。compile / run / p5 を作成してください。


**・パーミッション変更**
スクリプトを実行できるようにパーミッションを変更しておきます。

```
$ chmod 755 compile run p5
```

### PASCAL-P5 の使い方 (macOS)
Linux の場合と同じです。まずは hello.pas を `~/p5/` に作成します。

```
$ nano hello.pas
```

```pascal:hello.pas
program Hello(Output);
begin
  writeln('Hello, world.');
end.
```

```
$ ./compile hello
$ ./run hello
```

または

```
$ ./p5 hello
```

でコンパイルして実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/909b01c5-80df-7e10-5076-eeef215bf54b.png)

あまり macOS に詳しくないのでスクリプトは適当でないかもしれません。

## ■ macOS の場合 (gpc でコンパイルする場合)

2021/10 現在、macOS (64bit) で動作する **GNU Pascal (gpc)** は存在しません。

:::note info
macOS は 10.15 Catalina 以降、32bit アプリケーションが動作しません。
:::

## ■ Linux の場合 (FPC でコンパイルする場合)
Linux 環境で **Free Pascal (fpc)** を用いてソースコードから Pascal-P5 をビルドする手順です。

アーカイブの `\pascal-p5.tar\Pascal-p5\gpc\linux_X86` に Ubuntu (x86) 用のバイナリが一応含まれてはいるのですが、32bit バイナリなので WSL とかでは動作しません。

### 環境設定
Free Pascal をインストールしておきます。

```
$ sudo apt-get install fpc
```

PASCAL-P5 のアーカイブを解凍します。

```
$ cd /tmp
$ curl -OL https://ht-deko.com/software/pascal-p5.tar.gz
$ tar -zxvf pascal-p5.tar.gz
$ cd Pascal-p5/source/
```

### コンパイル
Windows 用バイナリと同じ挙動にするにはソースコードの修正が必要かもしれません。

**Note:** 検証に使った WSL 用の `3.0.0+dfsg-2 [2016/01/28]` では以下の修正が必要でした。少なくともプログラムヘッダに対しては `{$mode ISO}` が効かない感じです。

**Note:** WSL をアップデートして `3.0.4+dfsg-18ubuntu2 [2018/08/29]` で試してみたら、ソースコードの修正は一切不要でした。プログラムヘッダに対して `{$mode ISO}` が効くようで、標準 Pascal と同じ解釈になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/41e2c06a-a564-fcd4-eb99-e470d7d515e4.png)

**Note:** Windows 11 の WSL2 上の fpc `3.0.4+dfsg-23 [2019/11/25]` で試してみたら、`{$mode ISO}` は効くようですが、外部ファイルとの関連付けに assign() が必要な感じです。修正点は macOS の場合と同じです (pcom / pint 共に 1. の修正は不要)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/eef5d1fc-6584-af29-24a9-0df5913bc2d8.png)

**・pcom.pas の修正**
任意のエディタで pcom.pas を開いてください。

```
$ nano pcom.pas
```

　1. `{elide}` の行を削除します (Line: 394)。`!!! remove` で検索するとみつかります。この修正は fpc のバージョンによっては不要です。

```pascal:pcom.pas
    { !!! remove this statement for self compile }
    {elide}prd,prr: text;{noelide}       { output code file } // <- この行を削除
```

　2. Assign の処理を追加します (Line: 5986 付近)。追加場所は `(*initialize*)` で検索してください。

```pascal:pcom.pas
  write('P5 Pascal compiler vs. ', majorver:1, '.', minorver:1);                                      
  if experiment then write('.x');                                                                     
  writeln;                                                                                            
  writeln('Pascal-P5 complies with the requirements of level 0 of ISO/IEC 7185.');                    
  writeln;        
                                                                                   
  (*initialize*)                                                                                      
  (************)  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加
  initscalars; initsets; inittables;                                                                 
```

**・pint.pas の修正**
任意のエディタで pint.pas を開いてください。

```
$ nano pint.pas
```

　1. `{elide}` の行を削除します (Line: 317)。`!!! remove` で検索するとみつかります。この修正は fpc のバージョンによっては不要です。

```pascal:pint.pas
      { !!! remove this next statement for self compile }                                                 
      {elide}prd,prr     : text;{noelide}(*prd for read only, prr for write only *)  // <- この行を削除
```

　2. Assign の処理を追加します (Line: 2432 付近)。追加場所は `P5 Pascal interpreter` で検索してください。

```pascal:pint.pas
  write('P5 Pascal interpreter vs. ', majorver:1, '.', minorver:1);                                   
  if experiment then write('.x');                                                                     
  writeln;                                                                                            
  writeln;                                                                                                                                                                                                
  
  {$ifdef fpc}        // <- 追加
  assign(prd, 'prd'); // <- 追加
  assign(prr, 'prr'); // <- 追加
  {$endif}            // <- 追加

  { !!! remove this next statement for self compile }                                                 
  {elide}rewrite(prr);{noelide}       
```

**・コンパイル**
pcom.pas と pint.pas を Free Pascal でコンパイルします。`{$mode ISO}` オプションを付けます。

```
$ fpc -Miso -v0 pcom.pas
$ fpc -Miso -v0 pint.pas
```

~/p5/ にバイナリをコピーしておきます [^3]。

```
$ mkdir ~/p5
$ cp pcom pint ~/p5/
```

もうソースコードは不要です。

```
$ cd /tmp
$ rm -Rf Pascal-P5/
$ rm -f pascal-p5.tar.gz
```

### スクリプトファイル
スクリプトファイルを適当な場所 (~\p5) に作成します。Windows 用と同じ使い方ができるスクリプトファイルです。

```
$ cd ~/p5/
```

**・compile**
ソースファイルをコンパイルして P-CODE 形式ファイルを出力するスクリプトファイルです。

```bash:compile
#!/bin/bash
#
# Compile file in batch mode using FPC Pascal.
#
# Runs a compile with the input and output coming from/
# going to files.
#
# Execution:
#
# Compile <file>
#
# <file> is the filename without extention.
#
# The files are:
#
# <file>.pas - The Pascal source file
# <file>.p5  - The intermediate file produced
# <file>.err - The errors output from the compiler
#
# Note that the l+ option must be specified to get a full
# listing in the .err file (or just a lack of l-).
#

if [ -z "$1" ]
then
   echo "*** Error: Missing parameter"
   exit 1
fi

if [ ! -f $1.pas ]
then
   echo "*** Error: Missing $1.pas file"
   exit 1
fi

cp $1.pas prd
rm -f $1.p5
./pcom | tee $1.err
rm -f prd

#
# The status of the compile is not returned, so convert a non-zero
# error message to fail status
#
grep -q "Errors in program: 0" $1.err
rc=$?
if [[ $rc != 0 ]] ; then

    rm -f prr
    exit 1

fi
#
# Move the prr file to <file.p5>
#
rm -f $1.p5 $1.err prd
mv prr $1.p5
```

**・run**
P-CODE 形式ファイルを解釈して実行するスクリプトファイルです。

```bash:run
#!/bin/bash
#
# Run a Pascal file in batch mode using FPC Pascal
#
# Runs a Pascal intermediate in batch mode.
#
# Execution:
#
# run <file>
#
# <file> is the filename without extention.
#
# The files are:
#
# <file>.p5  - The intermediate file
# <file>.inp - The input file to the program
#

if [ -z "$1" ]
then
   echo "*** Error: Missing parameter"
   exit 1
fi

if [ ! -f $1.p5 ]
then
   echo "*** Error: Missing $1.p5 file"
   exit 1
fi

cp $1.p5 prd
if [ -f $1.inp ]
then
  ./pint < $1.inp
else
  ./pint
fi

rm -f prd prr
```

**・p5**
ソースコードファイルをコンパイルして実行するスクリプトファイルです。

```bash:p5
#!/bin/bash
#
# Compile with P5 using FPC Pascal
#
# Execute with:
#
# p5 <file>
#
# Where <file> is the name of the source file without
# extention. The Pascal file is compiled and run.
# Any compiler errors are output to the screen. Input
# and output to and from the running program are from
# the console, but output to the prr file is placed
# in <file>.out.
# The intermediate code is placed in <file>.p5.
#

if [ -z "$1" ]
then

   echo "*** Error: Missing parameter"
   exit 1

fi

if [ ! -f $1.pas ]
then

   echo "*** Error: Missing $1.pas file"
   exit 1

fi

echo
echo Compiling and running $1
echo
cp $1.pas prd
./pcom | tee $1.err
rm -f prd

grep -q "Errors in program: 0" $1.err
rc=$?
if [[ $rc != 0 ]] ; then

    rm -f prr
    exit 1

fi

rm -f $1.p5 $1.err prd
mv prr $1.p5
cp $1.p5 prd
./pint
rm -f prd prr
```

**・パーミッション変更**
スクリプトを実行できるようにパーミッションを変更しておきます。

```
$ chmod 755 compile run p5
```

### PASCAL-P5 の使い方 (Linux)
hello.pas を `~/p5/` に作成します。

```
$ nano hello.pas
```

```pascal:hello.pas
program Hello(Output);
begin
  writeln('Hello, world.');
end.
```

```
$ ./compile hello
$ ./run hello
```

または

```
$ ./p5 hello
```

でコンパイルして実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/0f92d61a-7c72-f7e0-8a3e-52aca811e320.png)

あまり Linux に詳しくないのでスクリプトは適当でないかもしれません。

## ■ Linux の場合 (gpc でコンパイルする場合)
Linux 環境で **GNU Pascal (gpc)** を用いてソースコードから Pascal-P5 をビルドする手順です。

GNU Pascal の RPM は以下にあります。RPM が使えるディストリビューションなら、パッケージとして gpc が登録されていなくても導入はそれほど難しくありません。

 - [GNU Pascal (rpm.pbone.net)](http://rpm.pbone.net/index.php3?stat=3&search=gpc&srodzaj=3)

| rpm | 説明 |
|:--|:--|
| [gpc-20070904*.x86_64.rpm](http://rpm.pbone.net/index.php3?stat=3&srodzaj=4&dl=100&search=gpc-20070904%2Ax86_64.rpm) | 64bit Linux 用の RPM |
| [gpc-20070904*.i686.rpm](http://rpm.pbone.net/index.php3?stat=3&srodzaj=4&dl=100&search=gpc-20070904%2Ai686.rpm) | 32bit Linux 用の RPM |

CentOS 6 (32bit) の VM があったので試してみました。

```
$cd /tmp
$curl -OL <目的の RPM>
$rpm -ivh gpc*
```

無事 GNU Pascal を導入できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/2a51d6cc-1763-023a-4cbc-8a10215889e4.png)

### 環境設定
アーカイブを DL して解凍しておきます。

```
$ cd /tmp
$ curl -OL https://ht-deko.com/software/pascal-p5.tar.gz
$ tar -zxvf pascal-p5.tar.gz
$ cd Pascal-p5/source/
```

### コンパイル
pcom.pas と pint.pas を GNU Pascal でコンパイルします。ソースコードの修正は不要です。

```
$gpc --classic-pascal-level-0 --no-warnings --transparent-file-names --no-range-checking -o pcom pcom.pas
$gpc --classic-pascal-level-0 --no-warnings --transparent-file-names --no-range-checking -o pint pint.pas
```

**pcom** と **pint** が生成されていると思うので、これをコピーして使います。スクリプトは fpc の場合と同じです。

**Note:** `Pascal-p5` フォルダで `./setpath` `.configure` `make` すると `Pascal-p5/bin/` フォルダに pcom と pint が生成されます。スクリプトも `Pascal-p5/bin/` フォルダにあるものがそのまま使えますが、改行コードが CR+LF なので、**nkf** などを使って LF に変換しないとエラーになります。

```bash
$sudo yum install nkf

$cd /tmp/Pascal-p5/bin/
$nkf -Lu --overwrite compile run p5
```

また、`Pascal-p5/bin/` にあるスクリプトは pcom / pint にパスが通ってる前提なのと、compile と run は結果が画面にエコーされないのと、run は入力ファイルがなくても *.inp ファイルを要求するので注意が必要です。

```bash
$export PATH="/tmp/Pascal-p5/bin/:$PATH"
$./compile hello
$cat hello.err
$touch hello.inp
$./run hello
$cat hello.lst
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/559c2d28-808c-a90f-5701-bfce9403b026.png)


## ■ X68000 の場合
今回の趣旨からはちょっと外れますが X68000 (Human68K) だと **Pure Pascal** が標準 Pascal ですね。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/dfc040a5-747c-1e3d-5087-a4bc98b159aa.png)

 - [Pure Pascal (DEKO のアヤシいお部屋)](https://ht-deko.com/pure.html)

## ■ Delphi で Pascal-P5 をビルド

Delphi でビルドできたので、別記事にまとめました。

 - [Delphi で Pascal-P5 v1.3 をビルドする (Qiita)](https://qiita.com/ht_deko/items/d40399477ff5a8823a33)
 - [Delphi で Pascal-P5 v1.4 をビルドする (Qiita)](https://qiita.com/ht_deko/items/0a3a4a7613aa1b6c5296)

これで 64bit バイナリが生成できますね。

## ■ Pascal IDE (Windows)
Pascal-P5 を簡単に扱えるようにするための IDE を作りました。

 - [P_IDE (GitHub)](https://github.com/ht-deko/P_IDE/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/4a006ebf-c800-b31c-ce61-fcbab9861ee8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/af50409a-76af-d5a5-6516-6a589e3cbbd3.png)

Linux とかだとシェルスクリプトで作れそうな IDE ですが、こんなものでもあると便利です。

**See also:**

 - [Turbo Pascal 3.0.x の使い方 (Qiita)](https://qiita.com/ht_deko/items/ec212f5cc17cbe5f718b)
 - [テキストエディタ Micro を Delphi 互換のキーバインドで使う (Qiita)](https://qiita.com/ht_deko/items/c47fb602d090e5a2bfa8)
 - [Windows Terminal を DOS IDE ランチャーとして使う (Qiita)](https://qiita.com/ht_deko/items/3fe90b85616ee9ea2198)


# おわりに
とりあえず、目的は達成されました！めでたしめでたし...。

## K&R の K の方へ
K&R の **K の方**が **「Why Pascal is Not My Favorite Programming Language (何故 Pascal はお気に入りのプログラミング言語ではないのか)」** なんてのを書いていましたが Turbo Pascal や Delphi...それ以前の Apple の Pascal ですらその問題の多くは解決しています (好みの問題は残りますが)。

 - [Why Pascal is Not My Favorite Programming Language](http://www.lysator.liu.se/c/bwk-on-pascal.html)
 - [taro-nishinoの日記： Pascalが私の好きな言語でない理由 (スラド)](https://srad.jp/~taro-nishino/journal/499164/)

先の論文が書かれたのは 1981 年ですが、

 - 1983: Pascal 最初の ISO 規格化 / Turbo Pascal 1.0 / Apple Lisa Clascal
 - 1985: Apple による Object Pascal
 - 1989: Turbo Pascal 5.5 (Object Pascal)
 - 1990: ISO 標準 Pascal 改訂
 - 1991: ISO 拡張 Pascal 規格化
 - 1993: Pascal へのオブジェクト拡張 (ドラフト)
 - 1995: Delphi 1.0 (Object Pascal)

それ以降で (Object) Pascal が進化していないハズもなく。

例えば、論文に書かれていたループの問題についてですが、標準 Pascal だと

```pascal:loop.pas
program loop(output);
begin
  while True do
    begin
      break;
    end;
end.
``` 
このような `break` を使ったループの脱出はできません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/def857d7-5688-d9bb-13b8-87c482aafd91.png)

Delphi とかだと普通にできます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/1db4593f-8899-acea-b3fc-19e5cee25c19.png)

## Linux の L の方へ
Linux の **L の方**の Pascal 嫌いという話についても古い Pascal に基づくものなので的外れな指摘もあるかと思います。

 - [Linux: Using goto In Kernel Code (kerneltrap.org: Internet Archive)](https://web.archive.org/web/20051128093253/http://kerneltrap.org/node/553/2131)

例えば
> Of course, in stupid languages like Pascal, 
> where labels cannot be descriptive, goto's can be bad.

```pascal:labeltest.pas
program labeltest(output);
label 
  jump;
begin
  goto jump;
  writeln('Hello');
jump:
  writeln('World');
end.
```

確かに標準 Pascal だとラベルに任意の名前を付ける事はできず、符号なしの 4 桁以下の整数を指定する必要がありますが、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/a7279d4f-63e5-41bf-6393-3c27bde1201d.png)

Delphi ならラベルに識別子が使えます (符号なしの整数も使えます)。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/0ee560ec-b404-b366-f5ec-907bc01301fd.png)

 - [宣言と文（Delphi）(DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%AE%A3%E8%A8%80%E3%81%A8%E6%96%87%EF%BC%88Delphi%EF%BC%89#goto_.E6.96.87)

古い Pascal しか知らないと**「Pascal...面倒くさいよね！」**という意見が出てしまうのもわからなくはないですが、Delphi 等のモダンな (Object) Pascal が面倒くさい訳ではありません。

## そして最新版の Delphi

[![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/da73efc2-1762-7a52-094a-91b3ef379d1d.png)
](https://www.embarcadero.com/jp/products/delphi/starter)

 - [Delphi Community Edition (Embarcadero)](https://www.embarcadero.com/jp/products/delphi/starter)

**魔改造 Pascal** である Delphi には無償版 (Community Edition) もあるので、K の方や L の方が言ってた事が今でも正しい指摘なのかどうかをぜひ試してみてください。

... Delphi 使いは逆に標準 Pascal を触ってみて Delphi の有難みを知ってください (w


[^3]: 当初は /usr/bin/ へコピーするようにしていましたが、Windows や macOS と同じ構成にするために ~/p5/ にバイナリとスクリプトを集めるように変更しました。
[^4]: /usr/bin/ へコピーする予定でしたが、[最近の macOS はウルサクなった](https://qiita.com/iwaseasahi/items/9d2e29b02df5cce7285d)みたいなのでこうしました。
[^6]: GNU Pascal でコンパイルされているようです。最新版のツリーには最新版でコンパイルされた EXE が含まれていないかもしれません (古いバイナリなら gpc\windows_X86 にあります)。
[^7]: Pascal-P5 の fpc / gpc フォルダには Linux (Intel x86) 用のバイナリも含まれているのですが 32bit バイナリなので、例えば WSL の Ubuntu 等では実行できません。
[^5]: バッチファイルは *.dpr も許容するようにしてあります。