catch.jp blog

デジタルをわかりやすく。

2014-7-20

yaccによるWebスクリプト言語

Category: Programming   

※「catch.jp-wiki」から移動したページです。

Part1.でJavascript電卓ができたので、次は簡単なスクリプト言語を作ってみます。スクリプト言語の開発に必要な基本的な技術を確認するために、ものすごーく簡単なスクリプト言語を作ります。

このページでは、kmyaccをツールとして、Javascriptで動くものを作ります。構文木などは作らないで、別のプログラミング言語へ変換だけやります。

とりあえず、SaruMane Script(猿真似スクリプト)という名前を付けることにしました。

スキャナーを作る

スクリプト言語では、電卓と違って、文字列やコメントなどを扱うので、まずスキャナー(字句解析)を作り直します。 構文解析に先立って、コードの文字の切り出しと分類を行うのが、このスキャナー。字句解析とかレキシカルアナライザー(Lexical analyzer)と呼ばれます。

電卓では、yaccのサンプル定義ファイルに、スキャナーのコードが埋め込まれていましたが、それを差し替えます。

とりあえず、ここでは、スキャナーが動作するサンプルページを用意しました。

文字の判別には、正規表現を使っています。

元の電卓用スキャナーとyaccの実装スタイルに合わせて、グローバル変数(!)を使っています。

スキャナーの動作

スキャナは、コードを次のように分類して構文解析に送ります。

  • IDENT
    • 役割:識別子(変数名や関数名)
    • 規則:最初の文字が、英文字(a-zとA-Z)にアンダーバー(_)。その後ろに英数字
  • NUMBER
    • 役割:数値
    • 規則:小数点も使える。
  • STRING
    • 役割:文字列
    • 規則:ダブルクォーテーション(")かシングルクォーテーション(')で囲む
  • COMMENT
    • 役割:コード中のコメント(注釈)。
    • 規則:「//」
  • 記号
    • 計算記号やカッコなど

次のように入力した場合

123abc"xyz"//comment

次のように表示されます。

-1 : 123abc"xyz"//comment,token : 123,yylex : 257
-1 : abc"xyz"//comment,token : abc,yylex : 258
-1 : "xyz"//comment,token : xyz,yylex : 259
-1 : //comment,token : //comment,yylex : COMMENT1

先頭は改行位置(改行記号がない場合は「-1」)。そのあと、現在解析している文字、切り出し結果、分類結果と続きます。

なお、コメント文を判別できるようにしています。通常、コメント文は無視して捨てるけれど、この簡易スクリプト言語は変換器なので残すようにしています。

スキャナーとパーサーを組み合わせる

動作確認のため、スキャナーで分類した結果を素通しするような構文解析器(parser:パーサー)を作ってみました。

program:/* empty */
       | program stmt { setAnswer(outputs); }
       ;

stmt: NUMBER  { outputs += "NUMBER: " + yylval + "<br> ";}
| IDENT { outputs += "IDENT: " + yylval + "<br> ";}
| STRING { outputs += "STRING: " + yylval + "<br> ";}
| COMMENT { outputs += "COMMENT: " + yylval + "<br> ";}
| '\n' { outputs += "(CR)<br> "; }
| error { $$ = ""; }
;

スキャナーが分類した結果は、構文解析側で判断して、分類とデータを表示します。

たとえば

123abc"xyz"//456

と入力すると、

NUMBER: 123
IDENT: abc
STRING: xyz
COMMENT: //456

と表示されます。

プログラムっぽいものを書けるようにする

次は、パーサーを拡張して、代入文と関数と引数を書けるようにします。 これで、プログラムっぽいものが、記述できるようになります。

たとえば、次のように入力すると

a=200
x()
y(100)
z(1,2,3,4)
a=1*(2+3)
b=x(100)
c=200 //comment

次のように、そのまま表示されます。

a=200
x()
y(100)
z(1,2,3,4)
a=1*(2+3)
b=x(100)
c=200
//comment

入力したプログラムを、いったんスキャナーでバラバラにして、それをパーサーで組み立てなおしているのです。

ここまでの段階で対応しているのは、次の構文です。

  • 関数呼び出し funcall
    • 関数名(引数) //引数あり
    • 関数名() //引数なし
  • 代入文 assign
    • 変数名=数式
  • コメント文 「//」から行末まで

  • 引数 args

    • 数式1, 数式2・・・引数3
    • 数式1
  • 数式 expr

    • 足し算, 引き算, 掛け算, 割り算, 余り
    • カッコによる優先順位付け, 関数呼び出し
  • 基本データ構造 primary

    • 識別子(変数名, 関数名)
    • 数値
    • 文字列 「””」か「' '」で囲んだもの。エスケープ記号とか効かない

構文ルールに合致しない場合は、「syntax error」と表示されます。

たとえば、次のように、文字だけ書いても、関数呼び出しでも代入文でもコメント文でもないため、「syntax error」と表示されます。

abc

ここまでの、ソースファイルは次のようになっています。

Processing.jsっぽく書けるようにする

ここで作っている簡易スクリプト言語は、ターゲット言語をProcessing.jsにしています。

Processing.jsは、ビジュアルデザイン用プログラミング言語Processing(プロセッシング)のWeb版です(JavascriptベースのProcessing)。グラフィックアニメーションやユーザーによる操作が取り扱いやすくなっています。

Processing.jsによる、アニメーションサンプルはこんな感じです。

サンプル > mouse_move

簡易スクリプト言語の主な機能は、次のような感じにします。

  • 命令や関数などはProcessing.jsとおんなじ。
  • 末尾のセミコロン(;)は不要。
  • If文とかFor文とかWhile文とかも、(とりあえず)なし。Processing.jsだと、なしでもそれらしいプログラムが作れるので。
  • クラス定義はあるけれど、クラス生成は(今のところ)なし。

で、こんなふうに末尾のセミコロンなしのプログラムを変換すると

void setup() {
  size(250, 250)
  frameRate(20)
}
void draw() {
  background(0)
  ellipse(width/2, height/2, mouseX, mouseY)
}

こんなふうに、Processinng.jsに変換されます。

void setup() { size(250, 250);
  frameRate(20);
}
void draw() { background(0);
  ellipse(width/2, height/2, mouseX, mouseY);
}

サンプルページとソースファイルは次のようになっています。

これだけでは、機能的には特別うれしいところはありませんが、独自文法のスクリプト言語の作るもとになるはず。

Processing.jsに変換して実行する

ついに、できました!

変換したコードをProcessing.jsに渡して実行します。

とりあえず、サンプルページのRunボタンをクリック。それから、右側の黒色エリアにマウスポインタを合わせると、その位置に合わせて楕円が描かれます。そのすぐ下には、変換後のスクリプトが表示されます。

動作上の制約

非常に簡易的なスクリプト言語なので、次のような注意が必要です。

ブロックコメントに非対応

「/* */」を使った、複数行にまたがるコメントは、今のところ使えません。

デバッグツールによるデバッグ

スクリプトは、変換して直接実行しているので、デバッグツールを適用できません。

変換後のスクリプトを、Processing.jsに直接貼り付けるともう少しやりやすくなるかも。

グローバル変数の衝突回避

SaruMane Scriptは、いくつかグローバル変数を使っています(yyyBuffer、yyyToken、yylval)。特別な名前にしてあるので、衝突することは、ほとんどないと思いますが、念のため注意してください。

グローバル変数を使っていると、他のプログラムと衝突するので、Javascriptの場合は変換プログラムをクロージャーにするとか、対策しなくちゃならないなー。と思っていたけれど、調べてみたら、ちょっと手間がかかるので、後回しにしました。


Part1.yaccによるJavascript電卓に戻る

Part3.日本語っぽいWebスクリプト言語に進む

ソース

参考資料