Ace.js のシンタックスハイライターを自作する(実装編)

[Ace.js](http://ace.c9.io/) は、Webページに簡単に埋め込むことができるテキストエディタです。様々なプログラミング言語に対して、その記述をサポートする機能(シンタックスハイライトや自動インデントなど)が用意されており、Webページ上でのプログラミング用エディタの作成に適しています。

# この記事の目的
Ace.js には、様々な言語をサポートする機能が既に用意されています。しかし、もちろんすべての言語に用意されてはおらず、サポートに含まれない言語も存在します。特に、研究目的で作られたプログラミング言語のような、マイナーな言語はサポートされていません。ただ、Ace には言語サポート機能を自作できる仕組みが用意されています。この記事では、R-WHILE というプログラミング言語に対して、シンタックスハイライト機能を作成します。

## 実装編
[環境構築編](https://qiita.com/Yoshi0207/items/577e401a39baa35439e7)では、R-WHILEのシンタックスハイライト機能の実装に必要な `mode-rwhile.js` を作成するための環境を整えました。実装編では、`rwhile_highlight_rules.js` にシンタックスハイライト規則を記述し、実際に ace テキストエディタ上で R-WHILE のプログラムが色付けされるようにします。

# シンタックスハイライト機能を実装する
## R-WHILE の構文規則
R-WHILE の構文規則は次のようになります。

```BNF:
Variables   ∋ X, Y ::= X0 | X1 | ...
Expressions ∋ E, F ::= X | d | cons E F | hd E | tl E | =? E F
Patterns    ∋ Q, R ::= X | d | cons Q R
Commands    ∋ C, D ::= X ^= E | Q <= R | C; D
                      | if E then C else D fi F | from E do C loop D until F
Macro       ∋ M    ::= macro X ( X, ..., X ) C
Programs    ∋ P    ::= M* read X; C; write Y
```
Ace では、構文規則を厳密に定めることで、エラー表示など様々な恩恵を得ることができますが、この記事では、色付けのみ行います(すごく大変になるので)。ここで色付けするものは、`macro` や `cons` などのキーワードと、上の規則には載っていませんが、`'name` で定義される変数です。

## シンタックスハイライト規則を定義する
どのように色付けするのかが決まったので、実際に `rwhile_highlight_rules.js` にシンタックスハイライト規則を記述します。`rwhile_highlight_rules.js` は次のようになります。

```javascript:rwhile_highlight_rules.js
...

var RwhileHighlightRules = function() {
  // キーワード
  var keyword = ("cons|hd|tl|if|fi|from|until|show|then|else|do|loop|read|write|macro");
  
  // 組み込み定数
  var builtinConstants = ("nil");
  
  var keywordMapper = this.createKeywordMapper({
    "keyword": keyword,
    "constant.language": builtinConstants,
  }, "identifier");
  
  // regexp must not have capturing parentheses. Use (?:) instead.
  // regexps are ordered -> the first match is used
  this.$rules = {
    "start": [
      {
        token: "comment", // multi line comment
        regex: "\\(\\*",
        next: "comment"
      }, {
        token: keywordMapper, // String, Array, or Function: the CSS token to apply
        regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b", // String or RegExp: the regexp to match
      }, {
        token: "keyword.operator",
        regex: "\\^=|<=|=\\?"
      }, {
        token: "variable",
        regex: /'(?!\s|\.|;|,|\))/,
        next: "variable"
      }
    ],
    "variable": [
      {
        token: "variable",
        regex: /[a-zA-Z0-9](?=\s|\.|;|,|\))/,
        next: "start"
      }, {
        token: "variable",
        regex: /^\s*/,
        next: "start"
      }, {
        defaultToken: "variable"
      }
    ],
    "comment": [
      {
        token: "comment", // closing comment
        regex: "\\*\\)",
        next: "start"
      }, {
        defaultToken: "comment"
      }
    ]
  };
}
```

### 解説
- 初期状態が`start`で、そこから条件(regex)に従ってnext(`variable`,`comment`)に遷移するオートマトンです。
- `token`はCSSに関わります。例えば`token:"comment"`の場合、HTMLは以下のようになります。

```html
<span class="ace_support ace_comment"> 文字列 </span>
```

`ace_comment`クラスに対応する色が読み込むテーマのCSSで定義されています。また、各テーマにおいてtextmateというテキストエディタの命名規則に従う限り、対応するカラーが定義されています。なので、[Visual Studio CodeやAtomのシンタックスハイライト拡張機能を作る](https://qiita.com/maxfie1d/items/51af2984b7a628c41a94)の記事を参考に、各状態のクラスを決めました。

## 結果
[環境構築編](https://qiita.com/Yoshi0207/items/577e401a39baa35439e7)の方法で`mode-rwhile.js`を作成し、`ace.js`で読み込むと、正しく色付けされていることがわかります。
![シンタックスハイライトされている図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691673/e62c308a-56b7-13ac-78fd-9dc07f8ebbb9.png)

# 補足
- R-WHILE シンタックスハイライト機能のソースコードは[こちら](https://github.com/yokoyama-lab/R-WHILE_Syntax_Highlighter)から見られます。

# 参考サイト
- [Ace公式ドキュメント](https://ace.c9.io/)
- [Aceリポジトリ](https://github.com/ajaxorg/ace)
- [Visual Studio CodeやAtomのシンタックスハイライト拡張機能を作る](https://qiita.com/maxfie1d/items/51af2984b7a628c41a94)