最新言語「P#」の環境を作る

# はじめに

P#とは、Microsoftが開発中のプログラミング言語です。

非同期＆イベント駆動型という特徴を持ち、非同期処理のテストを容易にすることに重点を置いているようです。
Machine（オートマトン）、State（状態）、Event（状態遷移トリガ）を記述することによりプログラムを構築するのですが、注目すべきはこの言語、**文法がC#の拡張**であり、**.NET Framework上で動作する**のです。
つまり、「C#コードはP#コードである」という論理が成り立ちます。
夢がひろがりんぐですね。

# 環境構築

公式Githubはこちら。

https://github.com/p-org/PSharp

執筆時点でのP#のバージョンは1.2.4です。

*2017/9/2追記 バージョン1.3.2で検証済みです。*

公式Githubには色々と文書があるので、環境構築は簡単かなーと思いきや、文書は古い内容なのか、それ通りにやってハマったのでメモします。

## PSharpの入手

公式から任意のフォルダにクローンします。

```bash:git
> git clone https://github.com/p-org/PSharp.git
```

これでPSharpフォルダ以下にソリューションが生成されます。

## ビルド

公式には`dotnet restore`を使えとか、build.ps1でビルドしろとか書かれていますが、うまく行きませんでした。
しかし、Visual Studio Community 2017 でPSharp.slnを開き、単純にビルドすれば通ります。
これで、`PSharp/bin/net46/`以下にDLLやコンパイラなどが生成されます。

## P#プロジェクトの準備

P#をコンパイルするにはプロジェクトが必要です。
C#コンソールアプリのプロジェクトを作りましょう。
このとき、ターゲットフレームワークは「.NET Framework 4.6」を指定してください。

プロジェクトを作成したら、参照設定が必要です。
`PSharp/bin/net46`以下のこのDLLを参照してください。

```text:参照の追加
Microsoft.PSharp.dll
```

そして、以下の2ファイルを用意します。

```csharp:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.PSharp;

namespace PSharpSample
{
    class Program
    {
        static void Main(string[] args)
        {
            PSharpRuntime.Create().CreateMachine(typeof(Server));
            Console.ReadLine();
        }
    }
}

```

```csharp:Machines.psharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PSharpSample
{
	event Ping; // Client sends this event to the Server
	event Pong; // Server sends this event to the Client
	event Unit; // Event used for local transitions
    
	// Event used for configuration, can take a payload
	event Config (target: machine);
    
	machine Server
	{
		machine client;
        
		start state Init
		{
			entry
			{
				// Instantiates the Client
				this.client = create(Client);
				// Sends event to client to configure it
				send(this.client, Config, this);
				raise(Unit); // Sends an event to itself
			}

			on Unit goto Active; // Performs a state transition
		}
        
		state Active
		{
			on Ping do async
			{
				// Sends a Pong event to the Client
				Console.WriteLine("Receive Ping");
				await Task.Delay(1000);
				Console.WriteLine("Send Pong");
				send(this.client, Pong);
			}
		}
	}
	
	machine Client
	{
		machine server;
		
		start state Init
		{
			on Config do Configure; // Handles the event
			on Unit goto Active; // Performs a state transition
		}
    
		void Configure()
		{
			// Receives reference to Server
			this.server = (trigger as Config).target;
			raise(Unit); // Sends an event to itself
		}
  
		state Active
		{
			entry
			{
				Console.WriteLine("Send Ping");
				send(this.server, Ping);
			}

			on Pong do SendPing;
		}
		
		async Task SendPing()
		{
			// Sends a Ping event to the Server
			Console.WriteLine("Receive Pong");
			await Task.Delay(1000);
			Console.WriteLine("Send Ping");
			send(this.server, Ping);
		}
	}
}

```

以上の2ファイルが用意できたら、Machines.psharpのプロパティをいじりましょう。

Machines.psharp -> プロパティ -> ビルドアクション -> コンパイル

に設定します。
これをしないと次のコンパイルが通りません。

以上ができたら、Visual Studioのウィンドウを一旦閉じます。

## コンパイル

Developer Command Prompt for VS 2017を開いてください。
そうしたら、`PSharp/bin/net46`へ移動しましょう。
そして、以下のコマンドを実行します。

```bash:コンパイル
> PSharpCompiler.exe /s:${SOLUTION_PATH}
```

上記の`${SOLUTION_PATH}`には、先程準備したプロジェクトのソリューションを指定してください。
これでコンパイルが通るはずです。
EXEができているので、実行してみてください。

```text:出力
Send Ping
Receive Ping
Send Pong
Receive Pong
Send Ping
Receive Ping
...
```

## Visual Studio上でビルド

*2017/9/2追記 本節の内容を変更しました。*

Visual Studio上でビルドを行なうには、まずテキストエディタを使ってプロジェクトの`.csproj`ファイルを開きます。
以下のようなXMLファイルですね。

```xml:PSharpSample.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
  ...
```

これに`Import`タグを2つ追加します。

```xml:PSharpSample.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  ↓これを追加
  <Import Project="$(PSharpBinaries)\PSharp.targets" />
  ↓これも追加
  <Import Project="$(PSharpBinaries)\PSharp.vs2017.targets" />
  <PropertyGroup>
  ...
```

上記の`$(PSharpBinaries)`には、`PSharp\bin\net46`の絶対パスを記述します。

これができたら、再度プロジェクトを開いてください。
もう一度ビルドアクションの設定をします。
今度は、

Machines.psharp -> プロパティ -> ビルドアクション -> PSharp

が選択できるようになっているので、これに設定します。
あとはVisual Studioによるビルドが通るようになっているので、開発が容易になります。

# おわりに

公式サイトには、IntelliSenseなどの記述もあるのですが、私の環境ではどうもうまくいきませんでした。

ここで紹介させていただいた内容はすぐに変更される恐れがありますので、ご注意願います。

ともあれ、これでP#の開発が可能になりました。
Git内にはサンプルも含まれていますので、是非試してみてください。