Pascal p5c / p5x を試してみたい

# はじめに
標準 Pascal を実行できる環境が失われつつあるので、**Pascal-P5** の実行環境を作ったりしていました。

Pascal-P5 は Scott A. Moore 氏による標準 Pascal 水準 0 に対応した Pascal コンパイラです。Pascal で書かれており、GPC (GNU Pascal) でコンパイルできるようになっています。

 - [割と簡単に '標準 Pascal' を試してみたい (Qiita)](https://qiita.com/ht_deko/items/41e95154e8da2f901698)
 - [Pascal-P シリーズについて (Qiita)](https://qiita.com/ht_deko/items/adab81f28295a124e9eb)

P シリーズでは `PCOM` が Pascal ソースコードを中間ファイル形式 (疑似アセンブラソース) に変換し、`PINT` が 中間ファイルをアセンブルして疑似機械語に変換したもの (メモリ上に生成) を実行しています。

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

で、Pascal-P5 を調べていると目に付くんですよ、**Pascal p5c / p5x** が。

# Pascal p5c / p5x

Pascal p5c / p5x は Trevor Blight 氏による Pascal コンパイラで、Pascal-P5 がベースになっています。

 - [pascal p5c (SourceForge)](https://sourceforge.net/projects/pascal-p5c/)
 - [pascal p5c 最新版 (SourceForge)](https://sourceforge.net/p/pascal-p5c/code/HEAD/tree/)

p5c / p5x は Pascal-P5 と異なり、P コードコンパイラではありません。プログラムとしては `PCOM` に相当するものしかなく、疑似アセンブラソースの代わりに gcc のソースコードを吐き出します。これにより、実行形式ファイルを生成する事が可能になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/69d02678-c6bf-0f8c-9176-9ce528b75ae8.png)

:::note
その昔 **p2c** という C -> Pascalトランスレータがあり、これを使った **p2cc** というコンパイラがありました。C コンパイラを使って実行形式ファイルを得るという手段は同じなのですが、やっている事が少々異なります。

p5c / p5x はトランスレータではないため、元の Pascal ソースとはかけ離れた C ソースが出力されます。このため、p5c / p5x を Pascal から C 言語への移植に使う用途には向いていません。
:::

## Pascal p5c
**Pascal p5c** は標準 Pascal 水準 1 に対応した Pascal コンパイラです。

 - [pascal p5c wiki (SourceForge)](https://sourceforge.net/p/pascal-p5c/wiki/Home/)

C のプリプロセッサが使えるので、インクルードファイルを使う事ができます。

```pascal
program testReal(output);

#include "sys.inc.pas"

begin
   {field width is real digits + sign + '.' + exp width}
   writeln('the biggest real is ', REAL_MAX:REAL_DIGITS+7);
end.
```

## Pascal p5x
**Pascal p5x** は p5c の派生版で、様々な機能拡張が行われています。Pascal p5c のソースコード中にサブフォルダとして含まれています。

 - [p5x Extensions (SourceForge)](https://sourceforge.net/p/pascal-p5c/wiki/using%20the%20p5x%20compiler/)

拡張された機能は次の通りです。

 - 識別子に `_` と `$` が利用可能
 - 行コメント `//` が利用可能
 - 任意の宣言順序
 - 定数式のサポート
 - コマンドラインパラメータのサポート (`argc()`, `argv()`)
 - 拡張された case 文 (選択肢定数に部分範囲式が利用可能、**otherwise** 部の追加)
 - 2 つ目のパラメータを持つ `succ()` と `pred()` [^1]
 - 定義済み定数 (`maxreal`, `minreal`, `epsreal`, `maxchar`) [^1]
 - 外部ファイルのアサイン `Assign()`
 - ファイルに追記するための `Extend()` [^1]
 - べき乗演算子 `**`
 - 緩和された定数文字列の代入と比較
 - 文字列をダブルクォーテーションで囲むことが可能
 - 変数のための **external** 指令
 - 関数及び手続きのための **inline** 指令
 - 16進値を表すためのプレフィクス `$`
 - ビット操作関数群
 - 定義済みの timestamp レコードと `GetTimeStamp()` [^1]
 - プログラムを終了させるための `halt()`

## Pascal p5c / p5x コンパイラの入手

コンパイラはソースコードとして提供されているので、これをビルドする必要があります。

GPC が動作する環境が整っていれば、スクリプト `gen` でコンパイラが生成されます。

```
$ ./gen --notest
```

うまくビルドできない場合には、環境変数 `PATH` と `LIBRARY_PATH` の設定を見直してみてください。

:::note
`--notest` を付けないと、ビルド後にテストが走ります。
:::

### p5c の入手

MinGW と WSL2 (ubuntu) でビルドしてみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/e521b34f-0558-8bb6-74bb-81d0dc209f24.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/6a1cede3-36fb-978f-84ed-bdddea45424b.png)

:::note
MinGW でビルドすると `P5C.EXE` という実行形式ファイルが生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/c7332706-de72-f700-8cdb-111f117a15d4.png)
:::

p5c を Windows で動作させるなら、次の 8 ファイルを集めておくと便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/2cc84ade-f4d3-5ccc-915f-fa4ce764a0d6.png)

| ファイル| 説明 |
|:---|:---|
| p5c.exe         | p5c コンパイラ |
| p5c.h           | p5c ヘッダファイル |
| p5c.c           | p5c コンパイラ C 言語ソース |
| assert.inc.pas  | インクルードファイル: アサーション  |
| clib.inc.pas    | インクルードファイル: 標準 C 関数 |
| cstr.inc.pas    | インクルードファイル: 標準 C 文字列  |
| guide.txt       | ドキュメント: p5c ガイド |
| README          | ドキュメント: p5c Readme  |

:::note
各ファイルの説明については `README` に記載があります。
:::

### p5x の入手
こちらも MinGW と WSL2 (ubuntu) でビルドしてみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/9e199357-af7e-0a49-7ed8-29f23032f89b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/1a55854b-8ac6-342c-8139-2763a822b5b8.png)

MinGW の方はテストエラーになっていましたが、コンパイラ自体は生成されていました。

:::note
MinGW でビルドすると `P5X.EXE` という実行形式ファイルが生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/97f3952f-9998-1941-9ba4-1012da608a58.png)
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/6d674d70-acba-6b20-2396-9d0f135fd81b.png)

p5x を Windows で動作させるなら、次の 10 ファイルを集めておくと便利です。

| ファイル| 説明 |
|:---|:---|
| p5x.exe         | p5x コンパイラ |
| p5x.h           | p5x ヘッダファイル |
| p5x.c           | p5x コンパイラ C 言語ソース |
| assert.inc.pas  | インクルードファイル: アサーション  |
| clib.inc.pas    | インクルードファイル: 標準 C 関数 |
| cstr.inc.pas    | インクルードファイル: 標準 C 文字列  |
| string.inc.pas  | インクルードファイル: 文字列ライブラリ  |
| console.inc.pas | インクルードファイル: Turbo Pascal の crt ユニットに似たライブラリ |
| guide.txt       | ドキュメント: p5x ガイド |
| README          | ドキュメント: p5c Readme  |

:::note
各ファイルの説明については `README` に記載があります。
:::

## Hello,world.

p5c / p5x で `Hello,world.` を出力するプログラムを作ってみます。

Pascal のソースコードはこんな感じです。

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

### p5c の場合
p5c が出力したソースコードは必ず `p5c.h` をインクルードするので、gcc の include フォルダにコピーしておくと便利です。

コンパイルは `p5c <Pascalソースファイル> <Cソースファイル>` です。

```
C:\WORK\P5C>p5c hello.pas hello.c
P5c Pascal compiler vs. 1.2


     1       program Hello(output);
     2       begin
     3         Writeln('Hello,world.');
     4       end.

Errors in program: 0
Warnings in program: 0
```

`hello.c` が出力されました。トランスレータが吐いたソースではない事が解ります。

```c:hello.c
/*
 *  c file Generated by P5c Pascal compiler v 1.2
 */
#define MEMDEBUG
#include <p5c.h>
#if __P5C_H__ - 0 != 100
#error "compiler incompatible with p5c.h"
#endif
struct _PcleanupInf *_Phead=NULL;
static void _Pclose_file(text *const $fp) {
 if($fp->f != NULL) {
  if($fp->name!=NULL && $fp->flags==-2) putc('\n', $fp->f);
  fclose($fp->f);
 }
} // _Pclose_file()
static text output_1 = { .name="output", .flags=-1 };
static void _Pexit(void) {
if(output_1.flags==-2)  putc('\n', output_1.f);
while(_Phead!=NULL){
  _Phead->cuFunc();_Phead=_Phead->link;};
} //_Pexit()
int main( int argc, char* argv[] ) {
output_1.f =  stdout;
#if defined(__WIN32__)
setvbuf(stdout, NULL, _IONBF, 0);
#endif
atexit(_Pexit);
({
text *const $fp = &output_1;
/* const string, len is 12 */
fprintf( $fp->f, "%s", "Hello,world." );
$fp->flags = -2;
fprintf( $fp->f, "\n");
$fp->flags = -1;
});
_Preport_mem(_Proot);
exit(EXIT_SUCCESS);
} /* main() */
```

`hello.c` を gcc でコンパイルして `hello.exe` を生成し、それを実行してみます。

```
C:\WORK\P5C>gcc -o hello hello.c -lm

C:\WORK\P5C>hello.exe
Hello,world.
```

:::note
Linux 等の場合には gcc でのコンパイルまで行う `pc` スクリプトと、生成された実行形式ファイルの実行まで行う `r` スクリプトが利用可能です。
:::

### p5x の場合

p5x も p5c の時同様です。ヘッダファイル (`p5x.h`) をコピーしておくのを忘れないようにしてください。

## ん？

p5c / p5x 共に、コンパイラのソースコードは `pcom.pas` です。もちろん内容はそれぞれで異なりますが、これをコンパイルして `PCOM(.EXE)` が得られます。これが p5c / p5x コンパイラですね。

その証拠にビルドしたフォルダにある `PCOM(.EXE)` を実行すると p5c / p5x コンパイラと表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/402d0376-e59d-ff84-328c-0635de14794d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/15deb150-da3a-26a5-6224-a06eb96ab094.png)

では、`P5C(.EXE)` や `P5X(.EXE)` は `PCOM(.EXE)` をリネームしたものなのでしょうか？実はそうではありません。

ビルドスクリプトは `pcom.pas` を GPC でコンパイルし、`PCOM(.EXE) (p5c / p5x)` を得た後、この `PCOM(.EXE) (p5c / p5x)` で `pcom.pas` を再度コンパイルし、`p5c.c` / `p5x.c` を得て、それを gcc でコンパイルして `P5C(.EXE)` / `P5X(.EXE)` を生成しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/a976c15d-4a80-796f-955c-cdcf5efe1475.png)

ビルドフォルダには `p5c.c` / `p5x.c` が残っていますので、p5c / p5x コンパイラを得るには gcc だけがあればいい事になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/f9057e33-8883-8162-f7e2-4e4c01ebd52e.png)

:::note
この用途のための `p5c-good.c` がビルドフォルダにあるのですが、p5x フォルダの `p5c-good.c` は p5x ではなく p5c コンパイラの C ソースファイルです。`p5x.c` を得るには、GPC によるビルドを一度行う必要があります。
:::

### つまりは gcc 環境があれば p5c / p5x コンパイラが得られるのか？
「はい、そうです！」...ってだけでは面白くないので **『Embarcadero Dev-C++』** で試してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/7219ea46-7501-4f09-76fc-12884b29b3ad.png)

 - [Embarcadero Dev-C++ (Embarcadero)](https://www.embarcadero.com/jp/free-tools/dev-cpp)
 - [Embarcadero Dev-C++ を日本語化しようとすると文字化けする件 (Qiita)](https://qiita.com/ht_deko/items/7de6f946e4b718fd4ca4)

環境に `p5c.h` をコピーするのが面倒だったので、ソースコードを改変してコンパイルしてみました。

```diff_c
/*
 *  c file Generated by P5c Pascal compiler v 1.2
 */
- #include <p5c.h>
+ #include "p5c.h"
#if __P5C_H__ - 0 != 100
#error "compiler incompatible with p5c.h"
#endif
```

普通に p5c コンパイラが得られました。64bit gcc コンパイラでコンパイルしたので、この Pascal コンパイラも 64bit コンパイラです。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/9312bba2-4ae8-25b4-726f-d8d71f4a9792.png)

p5x コンパイラもビルドできました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/c034167b-1191-caaa-f7c0-88c75f87ba5d.png)

p5x コンパイラが得られました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/b9568ca7-4f5e-f37a-8b52-abfa1908c3d3.png)


# おわりに
これで GPC が使えなくなったとしても標準 Pascal を試す事ができそうです。
ヨカッタ、ヨカッタ。 


[^1]: Extended Pascal の拡張です。