構文解析用言語ParserScriptの紹介

:::note warn
現在、ParserScriptは非推奨です。
:::


私は趣味でプログラミング言語を作っていますが、特に構文解析器に頭を悩ませてきました。そんな時、
 
 「別にBNFの文法のように書かずにもっとプログラミング言語またはアセンブリのように書けば良いのでは」
 
　という考えを思いつきました。
　今回はC#言語で作成した構文解析用言語（アセンブリとも言える）ParserScriptの文法をBNFと一緒にまとめてみました。
 
　ParserScriptのロゴ（適当に作りました）
![IMG_0341.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3805626/8b4025f9-2b35-8672-4556-2d933c8e9a01.jpeg)



# ParserScriptができること
　ParserScriptができることは以下の二つです。
　・文法が間違っていないかのチェック
　・文法の解析、及び抽象構文木の作成
　つまりは、機械語にコンパイルされる言語は
```plaintext
プログラム
    │
 字句解析
    │
 構文解析
    ├──────────┐
 アセンブリ　    │
    ├──────────┘
  機械語
    │
 実行マシン
```
　となるのを
```plaintext
プログラム
    │
ParserSript
    ├──────────┐
 アセンブリ    　│
    ├──────────┘
  機械語
    │
 実行マシン
```
　で、できます。
　予想ですが
```plaintext
 プログラム
     │
ParserScript
     │
  実行マシン
```
　詳しくしたやつ（訳あって英語です）↓
```plaintext
Program
    │
┌ParserScript──────┐
│                  │
│  LexicalAnalysis │
│        │         │
│     Parsing      │
│        │         │
│     Assembly     │
│        │         │
│  MachineLanguage │
│                  │
└───┬──────────────┘
    │
 ExecutionMachine
```
　字句解析、構文解析、アセンブリ化、機械語化、全てをParserScritpで実装できるかもしれません。疑い深いかもしれませんが、次に紹介するParserScriptの文法を見ると本当にできる可能性があるということがわかります。
# 文法
　この言語の予約語を紹介します。
```prs:.prs
# '#'をつけるとその行はコメント扱いになる

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"
```prs:.prs
match "a"
```
BNF: NUMBER
```prs:.prs
number
```
BNF: STRING
```prs:.prs
string
```
BNF: [ "a" ]
```prs:.prs
record
match "a"
down now
# 以下のコードも可
# nomatch?
#     break
# end
# downを使うときはmatch?またはnomatch?の中に入れるのを推奨
```
BNF: { "a" }
```prs:.prs
record
set
match "a"
match?
    jump
end
nomatch?
    down now
end
```
BNF: ( "a" | "b" )
```prs:.prs
record
match "a"
nomatch?
    break
    record          #ここのrecordは省略しても良い
    match "b"
end
```
BNF: <...>
```prs:.prs
call ...
```
  

  これらを使って
```BNF:Grammar
<fact> ::= [ "-" ] [ "0." ] NUMBER | "(" <expr> ")"
<expr> ::= <fact> { ( "+" | "-" ) <fact> }
```
をParserScriptで書くと
```prs:Grammar.prs
# 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を開発しているため、使わないことをお勧めします。