はじめに
たまには娯楽でプログラムを書きたいなと思い、今年の Advent Calendar では文言(wenyan‑lang)に挑戦してみることにしました。
数年前に下記のような文言の記事をいくつか見かけて、漢文調のプログラミング言語を一度触ってみたかったのです。
今回の記事では文言で書いた簡単なプログラムの紹介と、どんなことを考えながらプログラムを書いたのかのふりかえりをします。
文言についての説明は、本記事では割愛します。
公式サイト( https://wy-lang.org/ )や上記記事をご覧いただければと思います。
今回書いたプログラムの紹介
今回書いたプログラムがこちらです。
吾有一術名之曰「金字塔」。欲行是術。必先得一數。曰「段」。乃行是術曰。
有數零名之曰「甲」。
恆為是。若「段」小於「甲」者乃止也。
吾有一言名之曰「行」。
有數零名之曰「乙」。
減「段」以「甲」名之曰「左空」。
恆為是。若「左空」不大於「乙」者乃止也。
加「行」以「「一」」。昔之「行」者今其是矣。
加「乙」以一。昔之「乙」者今其是矣。
云云。
乘「甲」以二減其以一名之曰「金字」。
有數零名之曰「丙」。
恆為是。若「丙」不小於「金字」者乃止也。
加「行」以「「金」」。昔之「行」者今其是矣。
加「丙」以一。昔之「丙」者今其是矣。
云云。
乘「段」以二減其以「乙」減其以「丙」名之曰「右空」。
恆為是。若「右空」不大於一者乃止也。
加「行」以「「一」」。昔之「行」者今其是矣。
減「右空」以一。昔之「右空」者今其是矣。
云云。
夫「行」。書之。
加「甲」以一。昔之「甲」者今其是矣。
云云。
是謂「金字塔」之術也。
有數十名之曰「段」。
施「金字塔」於「段」。
wenyan -cでJavaScriptにコンパイルした結果をインデントしたものがこちらです。
var 金字塔=_=>{};
金字塔=段=>{
var 甲=0;
while(true){
if (段<甲){
break;
};
var 行="";
var 乙=0;
const _ans1=段-甲;
var 左空=_ans1;
while(true){
if (左空<=乙){
break;
};
const _ans2=行+"一";
行=_ans2;
const _ans3=乙+1;
乙=_ans3;
};
const _ans4=甲*2;
const _ans5=_ans4-1;
var 金字=_ans5;
var 丙=0;
while(true){
if (丙>=金字){
break;
};
const _ans6=行+"金";
行=_ans6;
const _ans7=丙+1;
丙=_ans7;
};
const _ans8=段*2;
const _ans9=_ans8-乙;
const _ans10=_ans9-丙;
var 右空=_ans10;
while(true){
if (右空<=1){
break;
};
const _ans11=行+"一";
行=_ans11;
const _ans12=右空-1;
右空=_ans12;
};
const _ans13=行;
console.log(_ans13);
const _ans14=甲+1;
甲=_ans14;
};
};
var 段=10;
const _ans15=金字塔(段);
実行後のコンソール出力結果がこちらです。

漢文調のプログラムを見ると最初はびっくりしますが、文言のプログラム、JavaScriptへのコンパイル結果、コンソール出力結果を比較すると、なんとなく読めてくるような気がします。
ちなみに、今回書いたプログラムファイルをwenyan -rでレンダリングしたものがこちらです。

漢文の古文書みたいで格好良いですね。
どんなことを考えながら書いたのかのふりかえり
ここからは、文言を使って簡単なプログラムを書いてみるにあたり、どんなことを考えながら進めたのかをふりかえっていきます。
触ったことのないプログラミング言語で何か書いてみるという経験が新鮮で、中々面白い体験でした。
何ができそうか確認してみる
まずはローカル環境を構築し、どうやって動かすのか、何ができるのかを確認しました。
インストールコマンドはREADMEを参照し、こちらのVSCode用プラグインも入れました。
サンプルプログラムを叩いてみる
READMEを参照し、下記を試してみます。
wenyan examples/helloworld.wy
examples配下にサンプルプログラムが沢山入っているのを見つけ、色々実行してみます。
ここで、標準入力を受け取らずにコンソールへ何かを出力するプログラムなら、サンプルプログラムを見ながら書けそうだなと当たりを付けます。
コマンドのオプションを確認する
wenyanコマンドを実行して、どんなオプションがあるかを確認します。
,_ ,_
\/ ==
/\ []
WENYAN LANG 文言 Compiler v0.3.4
Usage: wenyan [options] [files...]
Options:
-v, --version Output the version
-l, --lang <lang> Target language, can be "js", "py" or "rb" (default: "js")
-c, --compile Output the compiled code instead of executing it
-e, --eval <code> Evaluate script
-i, --interactive Interactive REPL
-o, --output [file] Output compiled code or executing result to file
-r, --render Outputs renderings
--roman [method] Romanize identifiers. The method can be "pinyin", "baxter" or "unicode"
--strict Enable static typechecking (default: false)
--allowHttp Allow to import from http (default: false)
--dir <path> Directory to importing from, seprates with comma(,)
--no-outputHanzi Don't convert output to hanzi
--log <file> Save log to file
--title <title> Override title in rendering
-h, --help Display help
wenyan -cで文言のコードがJavaScriptにコンパイルできることを確認します。
トラブルシューティングに使えそうなので覚えておきます。
wenyan -rで例のSVGが生成できることを確認します。
格好良いので覚えておきます。
どんなプログラムを書くか考える
サンプルプログラムとwenyanコマンドのオプションを確認したところで、どんなプログラムを書くか考えます。
とりあえず繰り返しを使って何かを出力するプログラムを書いてみることにします。
FizzBuzzも九九も既にあったので、for文で三角形を表示することにしました。
そういえば、金字塔という言葉は元々ピラミッドを指していたなと思い出します。
金の字を三角形に出力することにします。
更に簡単なプログラムを書いてみる
まずは最終的に書きたいものより更に簡単なプログラムを書いてみることにしました。
吾有一術名之曰「金字塔」。欲行是術。必先得一數。曰「段」。乃行是術曰。
有數零名之曰「甲」。
恆為是。若「段」小於「甲」者乃止也。
有數零名之曰「乙」。
吾有一言。名之曰「行」。
恆為是。若「乙」不小於「甲」者乃止也。
加「行」以「甲」。昔之「行」者今其是矣。
加「乙」以一。昔之「乙」者今其是矣。
云云。
夫「行」。書之。
加「甲」以一。昔之「甲」者今其是矣。
云云。
是謂「金字塔」之術也。
有數九名之曰「段」。
施「金字塔」於「段」。
wenyan -cでJavaScriptにコンパイルした結果をインデントしたものがこちらです。
var 金字塔=_=>{};
金字塔=段=>{
var 甲=0;
while(true){
if (段<甲){
break;
};
var 乙=0;
var 行="";
while(true){
if (乙>=甲){
break;
};
const _ans1=行+甲;
行=_ans1;
const _ans2=乙+1;
乙=_ans2;
};
const _ans3=行;
console.log(_ans3);
const _ans4=甲+1;
甲=_ans4;
};
};
var 段=9;
const _ans5=金字塔(段);
参考にした記述
上記のプログラムを書くにあたり、まずはfor文的な処理をサンプルプログラムの中から探すところから始めました。
九九のプログラムが参考になると思い、multiplication_table.wyを参照します。
吾有一術名之曰「乘算口訣」。是術曰。
有數一名之曰「甲」。
恆為是。若「甲」不小於十者乃止也。
有數一名之曰「乙」。
加「甲」以「乙」名之曰「丙」。
恆為是。若「乙」不小於「丙」者乃止也。
乘「甲」以「乙」名之曰「果」。
若「果」不小於十者吾有三言。曰「乙」曰「甲」曰「果」書之。
若非吾有四言。曰「乙」曰「甲」曰「「得」」曰「果」書之也。
加「乙」以一。昔之「乙」者今其是矣。
云云。
加「甲」以一。昔之「甲」者今其是矣。
云云。
是謂「乘算口訣」之術也。
施「乘算口訣」。
上記プログラムから以下のように読み取りました。
-
恆為是から云云までが繰り返しのブロックになっていそう -
若から者乃止也までが終了条件になっていそう-
若に「もし」の意味があると漢文で習った記憶があるので、ifだろう -
止也とあるので終了条件だろう
-
-
不小於は大小比較だろう -
加「甲」以一。昔之「甲」者今其是矣。で変数甲のインクリメント処理をしている -
吾有一術名之曰「乘算口訣」。是術曰。から是謂「乘算口訣」之術也。までで関数乘算口訣を定義しているみたい -
施で定義した関数を呼び出せるようだ
他にも参考になる書き方がないか見ていると、draw_heart.wyで次のような書き方を見つけます。
吾有一術名之曰「畫心」。
欲行是術。必先得一言。曰「心語」。
施「畫心」於「「琉璃梳子撫青絲。畫心牽腸癡不癡。」」。
欲行是術。必先得一言。で引数を定義できるようなので、折角なら使ってみることにします。
また、READMEを眺めているうちにチートシートを見つけます。
こちらも適宜参照しました。
冒頭のプログラムを書く
プロトタイプの簡単なプログラムが実行できた後、最初にご紹介したプログラムに改良しました。
いきなり文言で書くと混乱しそうだったので、事前にJavaのプログラムを用意しました。
public class Pyramid {
public static void main(String[] args) {
for(int i = 1; i <= 9; i++) {
for(int j = 1; j <= 9 - i; j++) {
System.out.print(" ");
}
for(int j = 1; j < 2 * i; j++) {
System.out.print(i);
}
System.out.println("");
}
}
}
Javaのプログラムを見ながら文言で書いていったのですが、次のような点で苦戦しました。
- 半角スペースの入れ方が分からない
-
「「" "」」で行けるかと思ったのですが、「「「「 」」」」に置換されてしまい上手くいきませんでした - 半角スペースの代わりに
「「一」」を使うこととし、合わせて右側に「「一」」を出力する処理を書き足しました
-
- 大小記号で混乱
- 不等号記号の代わりに
小於大於不小於不大於の組み合わせで求める処理にするのに苦戦しました
- 不等号記号の代わりに
- 変数名に混乱
- 最初は
甲乙丙丁戊……と命名していたのですが、変数が増えるにつれて混乱してきました - 日頃から十干を使い慣れているのでなければ、甲乙丙丁位で止めて、極力変数名を考えた方が覚えやすいなと思いました……
- 最初は
おわりに
今回は文言(wenyan‑lang)でピラミッド(金字塔)を出力するプログラムに挑戦してみました。
見慣れないプログラミング言語だと、練習用のプログラムを書くのにも色々なところで躓いて、小さな発見が沢山ありました。
皆様も頭の体操に是非!
