現在、ParserScriptは非推奨です。
私は趣味でプログラミング言語を作っていますが、特に構文解析器に頭を悩ませてきました。そんな時、
「別にBNFの文法のように書かずにもっとプログラミング言語またはアセンブリのように書けば良いのでは」
という考えを思いつきました。
今回はC#言語で作成した構文解析用言語(アセンブリとも言える)ParserScriptの文法をBNFと一緒にまとめてみました。
ParserScriptができること
ParserScriptができることは以下の二つです。
・文法が間違っていないかのチェック
・文法の解析、及び抽象構文木の作成
つまりは、機械語にコンパイルされる言語は
プログラム
│
字句解析
│
構文解析
├──────────┐
アセンブリ │
├──────────┘
機械語
│
実行マシン
となるのを
プログラム
│
ParserSript
├──────────┐
アセンブリ │
├──────────┘
機械語
│
実行マシン
で、できます。
予想ですが
プログラム
│
ParserScript
│
実行マシン
詳しくしたやつ(訳あって英語です)↓
Program
│
┌ParserScript──────┐
│ │
│ LexicalAnalysis │
│ │ │
│ Parsing │
│ │ │
│ Assembly │
│ │ │
│ MachineLanguage │
│ │
└───┬──────────────┘
│
ExecutionMachine
字句解析、構文解析、アセンブリ化、機械語化、全てをParserScritpで実装できるかもしれません。疑い深いかもしれませんが、次に紹介するParserScriptの文法を見ると本当にできる可能性があるということがわかります。
文法
この言語の予約語を紹介します。
# '#'をつけるとその行はコメント扱いになる
match "a"
# matchはその次の文字列をパースする文字列の最初からマッチさせる
number
# 十進数字と一致
string
# 十進数字、空白、改行以外と一致
record
# または、や0または1回一致、0回以上一致の時に使う
ast fact
# このastからlastまでを一つの文法規則としてみなす
# callを使って呼び出さない限り、中身は実行されない
last
# 文法規則の最後に付ける
call fact
# callの後の文法規則の最初の行に行き、lastにきたらこのcallの行に戻る
break
# いらなくなったrecordを消す
down true
# 0回一致の時に使う
# trueの部分はfalseやnowでも良い
match?
# 今までの文法規則に一致していなければendへ行く
nomatch?
# 今までの文法規則に一致していればendへ行く
set
# jumpを使用しこの行に行き、setを含めて実行する
jump
# 対応するsetへ行く
push "a"
# 構文解析時にpushの後の文字列を結果に追加する
これが、ParserScriptの予約語です。
BNFをParserScriptに書き換える
ParserScriptは文法がとても難しいので、BNFとの対応表を書きたいと思います。
BNF: "a"
match "a"
BNF: NUMBER
number
BNF: STRING
string
BNF: [ "a" ]
record
match "a"
down now
# 以下のコードも可
# nomatch?
# break
# end
# downを使うときはmatch?またはnomatch?の中に入れるのを推奨
BNF: { "a" }
record
set
match "a"
match?
jump
end
nomatch?
down now
end
BNF: ( "a" | "b" )
record
match "a"
nomatch?
break
record #ここのrecordは省略しても良い
match "b"
end
BNF: <...>
call ...
これらを使って
<fact> ::= [ "-" ] [ "0." ] NUMBER | "(" <expr> ")"
<expr> ::= <fact> { ( "+" | "-" ) <fact> }
をParserScriptで書くと
# exprから解析するため
call expr
# <fact> ::= [ "-" ] [ "0." ] NUMBER | "(" <expr> ")"
ast fact
# ( "a" | "b" )
record
# [ "-" ]
record
match "-"
down true
# [ "0." ]([ "-" ]と同じ)
record
match "0."
down true
# NUMBER
number
# "(" <expr> ")"
nomatch?
break
record
match "("
call expr
match ")"
end
last
# <expr> ::= <fact> { ( "+" | "-" ) <fact> }
ast expr
# <fact>
call fact
# { ( "+" | "-" ) <fact> }
record
set
# ( "+" | "-" )
record
match "+"
nomatch?
break
record
match "-"
end
call fact
match?
jump
end
nomatch?
down now
end
last
となります。
終わりに
BNFとは違い難しい言語ですが、ParserScript独自の機能を持っています。この言語の調整が終わり次第、GitHubにて組み込み用を公開するので、ぜひ使ってください(公開した時に、追加されている機能があると思います)。
現在、書きやすくかつ柔軟なコードを書けるUCCScriptを開発しているため、使わないことをお勧めします。