🌴

Peggyで自作言語を作って遊ぶ (1) Peggyの紹介

2022/12/05に公開約3,700字

自作プログラミング言語をなるべく手軽かつ簡単に作る方法を紹介します。また、実際に自作プログラミング言語を作ってみます。

必要なもの

ウェブブラウザーが必要です。そのほかのソフトウェアや開発環境のインストールは不要です。このページを閲覧しているということは準備が完了しています。

方法

自作言語は JavaScript にトランスパイルして実行します。構文解析と JavaScript への変換には PEG を用いたパーサージェネレーターである Peggy を用います。Peggy にはオンライン版があり、ウェブブラウザー上ですぐに使うことができます。

Peggy の使い方

はじめに

Peggy のオンライン版に行きます。

Peggy オンライン版の画面にはテキストエリアが二つあります。一つ目のテキストエリアには Peggy のコードを書きます。二つ目のテキストエリアには自作言語のコードを書きます。Download the parser code のテキストボックスは使いません。

Peggy オンライン版の表示は環境によって若干異なる可能性があります。私はウェブブラウザーの設定でフォントを Consolas に変えています。

画面の左側のテキストエリアには Peggy のコードを書きます。右上のテキストエリアには自作言語のコードを書きます。右下の Output の領域には自作言語のコードを Peggy で処理した結果が表示されます。

使ってみる

左側のテキストエリアに Peggy のコードを書きます。まずは

Program = [0-9]+

とします。これは Program が 1 文字以上の数字であると定義しています。この状態だと右側のテキストエリアに書かれている自作言語のコード 2 * (3 + 4)Program の定義を満たさないので、次のようにパースに失敗します。

Line 1, column 2: Expected [0-9] or end of input but " " found.

自作言語のコードを 12345 に変えると Program の定義を満たすのでパースに成功します。

Input parsed successfully. Input: 12345, Output: ['1', '2', '3', '4', '5']

出力が ["1", "2", "3", "4", "5"] という配列になりました。Program の定義が [0-9]+ なので複数個の [0-9] にマッチする可能性があります。このとき Peggy はマッチした複数個の [0-9] を配列にして出力します。この出力を今から別の形に書き換えたいと思います。

配列の中身をすべて連結して一つの文字列にします。JavaScript では

["ab", "hijk", "pqr"].join("")

とすると配列の中身がすべて連結されて一つの文字列 "abhijkpqr" を得ます。あるいは

["ab", "hijk", "pqr"].reduce((acc, x) => acc + x, "")

としても同じ結果を得ます。

Peggy は JavaScript を使って出力を書き換えることができます。定義の後ろに {} をつけて

Program
  = nums:[0-9]+ { return nums.reduce((acc, x) => acc + x, ""); }

のようにします。{} 内では任意の JavaScript コードを実行することができます。この Peggy のコードは

  1. Program[0-9]+ と定義する。
  2. その [0-9]+ の部分に nums という名前を付ける。
  3. {} 内の JavaScript コードで nums から値を作り、それを return で出力する。

というものになっています。

そうすると次のように "12345" が出力されます。

Input: 12345, Output: '12345'

文法を加える

自作言語に足し算と引き算の文法を加えます。Peggy のコードを次のようにします。

Program
  = left:Integer op:Operator right:Integer {
      return left + " " + op + " " + right;
    }

Operator
  = "+"
  / "-"

Integer
  = nums:[0-9]+ {
      return nums.reduce((acc, x) => acc + x, "");
    }

Peggy は一番上に書かれている定義を起点にします。このコードは Program の定義が起点になります。そして定義を見やすくするために OperatorInteger といった補助的な定義を作りました。コードの内容を詳しく見てみます。

  • Program の定義は Integer Operator Integer です。
  • Operator の定義は "+" / "-" です。Peggy では / は「または」を表します。したがって Operator の定義は "+" または "-" です。
  • Integer の定義は 1 文字以上の数字です。

自作言語のコードを 7+2398-4 などにするとパースに成功します。

Input: 7+23, Output: '7 + 23'

上記の例では、Integer Operator Integer という文法に従った自作言語のコード 7+23 を JavaScript のコード 7 + 23 に変換しました。自作言語の文法は自分で好きなように決めることができます。適切な変換処理を書けばそれを JavaScript のコードに変換できます。

計算結果を出力する

Program の一つ上に Start を作り、変換後の JavaScript コードを eval() で実行するようにします。そうすると実行結果を出力することができます。

Start
  = p:Program {
      return eval(p);
    }

Program
  = left:Integer op:Operator right:Integer {
      return left + " " + op + " " + right;
    }

Input: 7+23, Output: 30

これで足し算と引き算ができる言語が完成しました。この程度だとまだプログラミング言語とよべるようなものではありませんが、これに掛け算、割り算、浮動小数点数、真偽値、if 式、関数、変数、レコード、リストなどを付け足していけば本格的なプログラミング言語になります。

まとめ

Peggy を用いた自作言語の作り方を紹介しました。自作言語のコードを直接 JavaScript コードに変換することでとても手軽かつ簡単に自作言語を作ってその実行結果を得ることができます。次回は自作言語に二項演算子の連鎖と空白文字の無視と浮動小数点数の文法を付け加えてみます。

Discussion

ログインするとコメントできます