LoginSignup
5
3

More than 1 year has passed since last update.

【TypeScript】状態で分岐できるパーサコンビネータを作った

Posted at

はじめに

TerrarioというTypeScript製のパーサコンビネータを書いたので紹介します。

ソースもGitHubで公開してます。
https://github.com/marihachi/terrario

作成の経緯

これまでJavaScriptやTypeScriptでパーサーを作成する時はPEG.jsを使っていました。
PEG.jsはササッとパーサーを書けるのでパーサーの試作としては便利なライブラリですが、
少し複雑な仕様を盛り込んだパーサーを書こうとすると力不足に感じていました。

こんな時

例えば、以下のように括弧をネストできる文法を考えた場合、

1+(2+(3+4))

素直に実装すると、括弧の中身は関数呼び出しとして表現されます(再帰下降パーサーの場合)。
あまりにも多く括弧が含まれているとスタックオーバーフローの原因になるため、ネストできる回数は制限したくなります。

このような要求をPEG.jsやその他知っているライブラリでは実現が難しそうでした。できたとしても、トリッキーな方法になると思います...。

複雑な仕様に対応したい

そこで、条件を使ったパース処理の分岐をサポートしたパーサジェネレータやパーサコンビネータを探しましたが、それらしいものが見つからなかったので作ってみました。

コンセプト

  • 状態を使ってパース処理を分岐できる。
    • 例: ネスト回数をカウントしてパース結果を変えるなど。
  • パーサーコンビネータとして作成する。
    • 小さい関数の組み合わせになるため、JavaScriptエンジンの最適化が効きやすいです。
  • APIをシンプルにする。
    • 各コンビネータの名前や構成はParsimmonというライブラリを参考にしました。

基本的なパーサーの例

hello worldという文字列を受け入れるパーサーです。

import * as T from 'terrario';

// build a parser
const parser = T.alt([
  T.str('hello'),
  T.str('world'),
  T.str(' '),
]).many(0);

// parse the input string
const input = 'hello world';
const result = parser.parse(input);

console.log(result);
// => { success: true, value: [ 'hello', ' ', 'world' ], index: 11 }
  • T.alt: いずれかにマッチ。優先度付き選択。
  • T.str: 文字列か正規表現で入力文字列にマッチ&消費。
  • parser.many(0): 0回以上の繰り返し。

JSONパーサーの作成例

JSONパーサーは100行程度で書けます。

おわりに

JavaScriptやTypeScriptでパーサーを作成する時は、Terrarioをぜひ試してみてください!

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3