速習・プログラミング言語 (構文定義による差分学習)

新たに今までとは別のプログラミング言語を学ぶ方向けに、効率のよい学習方法を紹介します。


# この方法の利点

最近、GoやPromelaという言語をグループで学ぶ機会があったのですが、構文定義から学ぶ私のやり方に興味をもってくれた方がいたので書いてみました。私のやり方では、まず学ぶ言語の「構文定義」を探し、そこからどんなプログラムをかけるか考えるというものです。

この「構文定義」とは、**コードをどのような構造で書くかを決めた規則のこと**です。このあと具体例を出しますが、この構文定義とは「if文はこう書くよ」といった規則だと思ってください。

そして、この構文定義を読むと、どのようなコードを書けるのか（あるいは書けないようにされているのか）をすぐに把握できるという利点があります。例えば、構文定義に「`async`」や「`await`」といった文字列が現れれば、非同期処理を同期的な見た目で扱う機能の存在を把握できます。他にも構文定義の量から、その言語がどれだけ多機能なのかを把握できます。

ただし、構文定義からわかるのは、あくまでどのような見た目のコードを書けるかということだけです。そのため、そのコードが実際にどのような意味をもつかについてはわかりません。そこで、構文定義を一通り眺めた後は、それぞれの構造の名前（例えば「if文」のような）から意味を推測するか、その名前で検索するといった学習が続きます。言い換えると、既存の知識との差分だけの学習で、ほぼ言語仕様の全容を把握できるということです[^3]。つまり、**この学習方法はさまざまな言語に触れた人ほど、学習速度をあげられるという特徴をもっています**。

[^3]: ここで把握できるのはあくまで仕様だけです。したがって、標準ライブラリやコミュニティの慣例、仕様の落とし穴などは把握できないという欠点があります。これを補うために有効な方法として、標準ライブラリの一覧を眺めたり、lint のような静的解析ツールのルールを読むという裏技があります。

これからこの方法を紹介していくのですが、いきなり現実の構文定義から出発すると難しすぎるので、ミニ言語の例からみていきましょう。


# 構文定義の読み方

ここでは次のような単純なコードを書けるミニ言語と、その構文定義を例に、構文定義がどのようなものなのかを説明していきます：

```
if a==b {
  // DO SOMETHING
}
```

なんとなく挙動のわかりそうなミニ言語ですね。では、このコードで使われている構文定義をみてみましょう。表の左に構文定義、右にその読み方を書いてみました。

| 構文[^1]   | 読み方 |
|:-------|:-------|
| `プログラム := 文*` | _プログラム_ は、複数の _文_ のまとまり。 |
| `文 := if文 │ コメント文` | _文_ は _if文_ か _コメント文_ のどちらか。 |
| `if文 := "if" 式 "{" 文* "}"` | _if文_ は `if` の後に _式_ があり、次に `{` が続いて、複数の _文_ のまとまりが続いて、`}` で終わる。例えば、 `if a == 0 { }` と書ける。 |
| `コメント文 := "//" [A-Z ]* "\n"` | _コメント文_ は `//` の後に英数大文字か空白が続いて改行で終わる。 |
| `式 := 比較式` | このミニ言語の _式_ とは _比較式_ のこと（現実的には四則演算や関数呼び出しなどがここに入る）。 |
| `比較式 := 変数名 "==" 変数名` | _比較式_ は _変数名_ の後に `==` が続き、_変数名_ で終わる。 |
| `変数名 := [a-z]+` | _変数名_ は、aからzまでの英小文字からなる文字列。 |

[^1]: この構文定義の記法は、正規表現からの連想でなんとなく読めるゆるふわなやつです。より形式化された記法では BNF とか ABNF とか EBNF とか色々ありますが、現実にはメジャーな言語で BNF に似たオレオレ記法がいろいろ登場するので、あまり厳密に考えなくて大丈夫です。

では、この構文定義から、先ほどのプログラムと構文定義の対応をみていきます。まず、コード全体は _プログラム_ です。

```text:Step1
<プログラム>
```

_プログラム_ は複数の _文_ からなるため、_プログラム_ を _文_ と複数を意味する `*` で書き換えます（_プログラム_ → _文\*_）。

```text:Step2
<文>*
```

この _文_ は _if文_ と _コメント文_ のいずれかと書き換えられますが、ここでは元のコードにあわせて1つの _if文_ で書き換えます（_文\*_ → _if文_）。

```text:Step3
<if文>
```

次に、_if文_ の構文定義にしたがって内容を展開しましょう（_if文_ → `if` _式_ `{` _文\*_ `}`）。

```text:Step4
if <式> {
  <文>*
}
```

ここで _式_ と _文_ を展開できますが、まず _式_ の方を展開してしまいましょう（_式_ → _変数名_ `==` _変数名_）。

```text:Step5
if <変数名>==<変数名> {
  <文>*
}
```

この _変数名_ ですが、元のコードを読むとそれぞれ `a` と `b` が対応します（_変数名_ → `a`、_変数名_ → `b`）。

```text:Step6
if a==b {
  <文>*
}
```

最後に複数の _文_ を1つの _コメント文_ で書き換えます（_文\*_ → _コメント文_）。

```text:Step7
if a==b {
  <コメント文>
}
```

元のコードにあうように内容を展開すると、復元できました（_コメント文_ → `//[a-z ]*\n`）。

```text:Step8
if a==b {
  // DO SOMETHING
}
```

ここまでの例で、構文定義がどのようなもので、そして実際のコードとどのように対応するのかがわかったと思います。では、実際の言語で同じように構文定義をみていきましょう。この題材として「サンプルコードの意味を把握する」と「適当なコードを書いてみる」の2つを解説します。


# サンプルコードの意味を把握する

今回は、次のような Go のコードの意味を調べてみましょう（このコードは[Goでテストを書いてみよう - golang.tokyo コードラボ](https://golangtokyo.github.io/codelab/go-greeting/?index=codelab#5)から引用しています）。このうち、`(f ClockFunc)` の部分がわからなかったとします：

```go
func (f ClockFunc) Now() time.Time {
        return f()
}
```

では、まず[Go の構文定義](https://golang.org/ref/spec)をみにいきましょう。そして、先ほどのコードのうち、特徴的なキーワードである「`func`」でページ内を検索します。

すると、300件の件数がヒットしますが、これを順番にみていきます。すると30番目ぐらいで次のような記述を見つけられます：

> More than one type may implement an interface. For instance, if two types S1 and S2 have the method set
> 
>     func (p T) Read(b Buffer) bool { return … }
>     func (p T) Write(b Buffer) bool { return … }
>     func (p T) Close() { … }

これを読む限り、どうやらメソッドと関連がありそうなことがわかりました。そこで、メソッドの宣言文を確認しにいきます。宣言は英語で「Declaration」なので、「Method declaration」に類似する項目を上部の索引から探します。

すると、すばり「[Method declarations](https://golang.org/ref/spec#Method_declarations)」という項目があるので、それをみにいきます。Method declarations の構文定義は次のようになっているようです：

```
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

IdentifierList = identifier { "," identifier } .
```

ここから、`(f ClockFunc)` はどうやら Receiver と呼ばれていて、`f` は Identifier（識別子）、`ClockFunc` は Type（型）と呼ばれていることがわかります。ここまでくればしめたもので、オブジェクト指向言語に触れたことがあれば、**[Receiver の意味するものを推測できます](https://www.d.umn.edu/~gshute/softeng/object-oriented.html)**。言い換えると、Swift/Python でいうところの `self`、Java/JavaScript でいうところの `this`、Ruby でいうところの `@` にあたるものだとあたりをつけられます。少し独特なのは、Receiver の仮引数名らしきもの（`f` にあたる部分）を自由に設定できそうなところです。しかし、同じように Receiver を自由に命名できる Python を触ったことがあれば、なんとなく納得できます。

この学習の過程が、まさしく他の言語との差分で学習しているという部分です！なんとなく雰囲気を掴めたでしょうか。

では、次の例をみてみましょう。


# 適当なコードを書いてみる

先ほどは、与えられたサンプルコードを読むために構文定義を使いました。今回は、コードを書く際に構文定義を使ってみます。この題材として、モデル検査用の言語 [Promela](http://spinroot.com/spin/Man/index.html) で、なにがしかのループ文を書いてみるとします[^2]。

[^2]: 実際には、何かでみたサンプルコードがまどろっこしい定義だったので、疑問に思って他の書き方を調べたという経緯でした。

それでは、Promela の構文定義をみにいきます（[Promela (Spin 6.1) の構文定義](http://spinroot.com/spin/Man/grammar.html)）。まず、ここから探すのは一番最初にどのような構文（トップレベルの構文と呼びます）を使えるかです。よくあるトップレベルの構文名は「Program」や「Package」などですが、Promela の構文定義にはそのような名前はないようです。

また、他にもよくあるパターンとして、文のまとまりがトップレベルの構文に含まれているというものがあります。例えば、if文やfor文などはプログラムの出だしから書けることが多いですが、これはトップレベルの構文が複数の文のまとまりだからです。そこで、文を意味する「Statement」でページ内検索してみると、次のように `stmnt` という構文定義が文を意味していそうなことがわかります：

```
stmnt	: IF options FI		/* selection */

    ...（省略）...

	| name ':' stmnt	/* labeled statement */

    ...（省略）...
```

ここから、この `stmnt` を含められる構文を再帰的に探します。これ以上遡れなくなる構文が、探しているトップレベルの構文にあたります。まず、`stmnt` を含められるのは `step` のようです[^4]：

[^4]: なお、`stmnt` 自身に `stmnt` を含められるようですが、これはトップレベルの構文を探す上では無視して構いません。理由を説明します。自分自身を含められるパターンには2つの可能性がありえます。(1) トップレベルの構文が自身を含めるパターン、(2) トップレベル出ない構文が自身を含めるパターン、です。今回重要視しているのは、`stmnt` がトップレベルの構文かどうかですが、`step` から `stmnt` を参照できる以上、`stmnt` がトップレベルの構文にはなりえません。そのため、パターンとしては (2) なので無視して構わないというわけです。

```
step    : stmnt	[ UNLESS stmnt ]
	| decl_lst
	| XR varref [',' varref ] *
	| XS varref [',' varref ] *
```

この `step` は、次の `sequence` に含められるようです：

```
sequence: step [ ';' step ] *
```

さらにこの `sequence` は `proctype` に含められることがわかります[^5]：

[^5]: なお、`proctype` 以外にも `stmnt` と `options` にも含められるようですが、先ほど `stmnt` はトップレベルの構文ではないということがわかっており、`options` も同様にトップレベルの構文ではないことがわかります。したがって、`proctype` のみが今のところのトップレベルの構文の候補になっています。

```
proctype: [ active ] PROCTYPE name '(' [ decl_lst ]')'
	  [ priority ] [ enabler ] '{' sequence '}'
```

そして、この `proctype` は `module` に含められるようです：

```
module	: proctype	/* proctype declaration */
	| init		/* init process       */
	| never		/* never claim        */
	| trace		/* event trace        */
	| utype		/* user defined types */
	| mtype		/* mtype declaration  */
	| decl_lst	/* global vars, chans */
```

この `module` は `spec` に含められるようです：

```
spec	: module [ module ] *
```

この `spec` は、その他のどの構文にも含められないようですから、これが探していたトップレベルの構文です！

このトップレベルの構文がわかれば、あとは前にミニ言語と同じような構文定義の書き換えでコードを記述できます。まずは、トップレベルの構文である `spec` を配置します：

```
<spec>
```

この `spec` は、少なくとも1つ以上の `module` で書き換えができるようです。`module` が何者なのかいまのところわかっていませんが、たかがループ文を書くのに複数のモジュールが必要になるとも考えづらいので、1つの `module` で書き換えます（`spec` → `module [ module ]*`）：

```
<module>
```

`module`にはさまざまなものを書けるようですが、今回は事前に `proctype` っぽいという情報を周囲のサンプルコードから掴んでいました（ずるい）。そのため、`proctype` で書き換えます（`module` → `proctype`）：

```
<proctype>
```

さて、この `proctype` が曲者で、次のように謎の属性（`active` やら `enabler` やら）を指定できるようですが、既存の知識ではさっぱり意味がわかりません：

```
proctype: [ active ] PROCTYPE name '(' [ decl_lst ]')'
	  [ priority ] [ enabler ] '{' sequence '}'
```

そこで、この `proctype` における `active` がどのような意味を持つか調べます。たまたま Promela の構文定義は構文名がリンクになっていたので、謎の [`active` のリンク](http://spinroot.com/spin/Man/active.html) へ飛んでみます。すると次のような解説が書かれていました：

<blockquote><dl>
<dt>NAME 
<dd>active - prefix for proctype declarations to instantiate an initial set of processes.
<dt>SYNTAX 
<dd><pre><code>active proctype name ( [ decl_lst ] ) { sequence } 
active '[' const ']' proctype name ( [ decl_lst ] ) { sequence }</code></pre></dd>
<dt>DESCRIPTION 
<dd>The keyword active can be prefixed to any proctype declaration to define a set of processes that are required to be active (i.e., running) in the initial system state. At least one active process must always exist in the initial system state. Such a process can also be declared with the help of the keyword init .
</dl></blockquote>

これを読む限り、なにやら `proctype` の初期状態を指示できるようです。そして、少なくとも1つは active な `proctype` がないといけないとも書いてありますから、今回は指定してみることにします（エラーが出たらまた考え直す方針）：

```
active proctype <name> ( [ <decl_list> ] ) [ priority ] [ enabler ] {
    <sequence>
}
```

同様に、`priority` や `enabler` を調べ、これが省略しても問題なさそうとわかるので、これらは省略することにします：

```
active proctype <name> ( [ <decl_list> ] ) {
    <sequence>
}
```

次に `name` を調べますが、これはどうやらただの名前で、特に解説もなさそうなのでどんなものでも指定できると推測して適当な名前にします：

```
active proctype P ( [ <decl_lst> ] ) {
    <sequence>
}
```

そして、残るは `decl_lst` です。この構文定義を調べると、次のように型名（`typename`）らしき構文なのがわかります：

```
decl_lst: one_decl [ ';' one_decl ] *
one_decl: [ visible ] typename  ivar [',' ivar ] *
```

ここから、C++でいうテンプレート宣言、Javaやその他の言語でいうジェネリクス宣言あたりを連想します。今回はただのループ文を書きたいだけですので、省略してみます：

```
active proctype P() {
    <sequence>
}
```

あとは、`sequence` にfor文を埋め込むだけです！ここから少し速をあげますが、`sequence` → `step` → `stmnt` → `FOR '(' range ')' '{' sequence '}'`（for文らしきもの）へと書き換えていきます：

```
active proctype P() {
    for ( <range> ) {
        <sequence>
    }
}
```

ここで、for文にリンクが貼られていることに気づくので、その内容を確認してみます。すると、次のように親切にも例文が載っていました！：

<blockquote><dl>
<dt>NAME 
<dd>for - deterministic iteration statement.
<dt>SYNTAX 
<dd><pre><code>for '(' varref ':' expr '..' expr ')' '{' sequence '}' 
for '(' varref in array ')' '{' sequence '}' 
for '(' varref in channel ')' '{' sequence '}'</code></pre></dd>
<dt>DESCRIPTION 
<dd>The for statement was added in Spin Version 6 to simplify writing common loops over a user-defined range of values, the elements of an array, or the messages currently stored in a channel. For statements are internally converted into the corresponding Promela code, with the first statement issued being an assignment statement. This means that for statements are always executable (the guard statement is an assignment), independent of what the guard of the sequence in the body of the for is. Execution could of course still block inside the body of the for statement.
<dt>EXAMPLES 
<dd>The simplest form of the for statement is to simply iterate the execution of a given sequence of statements for a range of values, which must consist of two expressions separated by the two character token '..'. For instance,
<pre><code>int i;

for (i : 1 .. 10) {
    printf("i = %d\n", i)
}</code></pre></dd>
</dl></blockquote>

ここから、どうやらループ変数の事前宣言が必要そうだということを把握できます。最終的には、次のようなコードになりました：

```promela
active proctype P() {
	int i

	for (i : 0 .. 10 ) {
		printf("%d", i)
	}
}
```

そして、恐る恐るこれを実行してみると、ちゃんと意図通りに実行できました！どうやらうまく書けていたようです。

ここ注目してほしいのは、私たちは Promela のチュートリアルを一切やらずにここまでのコードを書けたということです。チュートリアルをやれば、すでに知っている知識もたくさん出てくることを思えば、今回の学習過程はとても効率的なことがわかりますね。


# まとめ

このように、構文定義から言語を学ぶことで、既存の知識からの差分だけで言語を効率的に学習できます。このように、構文定義に興味を持って新しい言語を学んでみてはいかがでしょうか。

なお、構文定義の応用などもっと知りたい！という方には「[事前知識なしで理解する 静的検査のいろは](https://speakerdeck.com/orgachem/introduction-about-static-analysis-without-previous-knowledge)」というスライドをオススメしておきます（自薦）。

最後に、主要な言語（主観）の構文定義を付録として残しておきます。


# 付録: 主要な言語の構文定義

- [C# 6.0 の構文定義](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/lexical-structure)
- [C++17 の構文定義](http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf)（標準仕様の閲覧は有料なので無料な最終ドラフトの方を参照）
- [Erlang 21.0 の構文定義](http://erlang.org/doc/reference_manual/users_guide.html)
- [Go の構文定義](https://golang.org/ref/spec)
- [Haskell 2010 の構文定義](https://www.haskell.org/onlinereport/haskell2010/haskellch10.html)
- [Java 11 の構文定義](https://docs.oracle.com/javase/specs/jls/se11/html/jls-19.html)
- [JavaScript (ECMAScript 2015) の構文定義](https://www.ecma-international.org/ecma-262/6.0/ECMA-262.pdf)
- [Kotlin 1.3 の構文定義](https://kotlinlang.org/docs/reference/grammar.html)
- [Python 3.7.1 の構文定義](https://docs.python.org/3/reference/lexical_analysis.html)
- [Ruby 2.5.0 の構文定義](https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2fprogram.html)
- [Scala 2.12 の構文定義](https://www.scala-lang.org/files/archive/spec/2.12/13-syntax-summary.html)
- [Scheme の構文定義](https://www.scheme.com/tspl2d/grammar.html)
- [Swift 4.2 の構文定義](https://docs.swift.org/swift-book/ReferenceManual/AboutTheLanguageReference.html)