🌴

自作プログラミング言語を初級者が1週間で作る方法 (1) Peggyの紹介

2022/12/05に公開

自作プログラミング言語を誰でも簡単に作れる方法を紹介します。プログラミング言語の仕組みを詳しく知らない初級者でも、自分が思い描くオリジナルの文法を持った言語を 1 週間程度で作ることができます。

必要なもの

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

方法

自作言語は JavaScript に変換して実行します。構文解析と JavaScript への変換には Peggy を用います[1]。Peggy にはオンライン版があり、ウェブブラウザー上ですぐに使うことができます。

Peggy の使い方

はじめに

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

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

Peggy オンライン版の表示は環境によって若干異なる可能性があります。

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

Peggy オンライン版の初期状態を見ると、自作言語のコードは 2 * (3 + 4) となっています。そして自作言語の実行結果は、このコードの計算結果である 14 となっています。この時点ですでに電卓レベルの自作言語が完成していることがわかります。

自作言語を作ってみる

左側のテキストエリアに 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 に変換しました。

計算結果を出力する

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 コードに変換することでとても簡単に自作言語を作ってその実行結果を得ることができます。次回は自作言語に二項演算子の連鎖と空白文字の無視と浮動小数点数の文法を付け加えてみます。

脚注
  1. Peggy は PEG を用いたパーサージェネレーターです。Peggy のオリジナルである PEG.js を用いても構いませんが、現在は更新が停止しているようです。 ↩︎

Discussion