パーサmemo 続き
わかった。パーサを返す関数だ。
こいつを使うことで、新たな文法を追加したり、yaccみたいに別文法を用意したりすることなく、拡張性が高く、それなりに読みやすいパーサを結構簡単に書くことができるのだ。多分。(超適当)
実装する側としても難しい部分は何も無い。効率は…かなり悪い気もするけど。
各々のパーサは、(パースに成功したか? . パースした結果は何か)という、コンスを返す。
まず、単一の「何か」をパースできるパーサを返す関数、'token'。
function ident_action( tok ) { // ... 識別子に会ったときの動作 } // TOK_IDENT をパースするだけのパーサを返す。 var id_token_parser = token( TOK_IDENT, ident_action ); // これでパース。 id_token_parser( lex_queue );
んで、「並び」をパースできるパーサを返す関数、'sequence'。
// これで、「TOK_IDENT がふたつ続くもの」をパースできるパーサができる。 var ident_seq_parser = sequence ( [id_token_parser,id_token_parser] ); // パース実行。 ident_seq_parser( lex_queue );
「選択」をパースできるパーサを返す関数、'select'。
// '(' ')' でくくられた識別子 var parened_token = sequence( [token(TOK_LPAREN), id_token_parser, token(TOK_RPAREN)] ); // '(' ')' でくくられた識別子、もしくは、ふたつ続く識別子をパースするパーサ var parened_or_2id_parser = select( [ parened_token, ident_seq_parser ] ); // パース実行 parened_or_2id_parser( lex_queue );
「任意の連続」をパースできる'star'。
// , をパース var comma_parser = token(TOK_COMMA); // "( ident" をパース var func_arg1 = sequence( [token(TOK_LPAREN),id_token_parser] ); // , ident , ident ... をパース var func_argrest = star( sequence([comma_parser, id_token_parser]) ); // ( ident, ident, ident ... ) をパース var func_arg_parser = sequence( [func_arg1,func_argrest] ); // パース実行 func_arg_parser( lex_queue );
これでやってみよう。