Bison/Flex による再入可能字句/構文解析器
いろいろ試行錯誤があったので結論とかをまとめる。
使う API など
Flex 側
Flex が提供する
- `--header-file' オプション
- 他のソースから yylex_init_extra() とかを使うため
- YY_DECL マクロ
- Bison が yylex() のシグネチャを決めつけてくるので歩み寄る
- `%option reentrant' ディレクティブ
- 基本
- yyget_extra() API
- パーサオブジェクトを Flex 側から触る
電卓プログラムからの抜粋 - calc.lex:
%{
#include "./calc.tab.h"
#define YY_DECL int yylex(YYSTYPE* yylval, void* yyscanner)
#define PARSE_STATE yyget_extra(yyscanner)
int yywrap(yyscan_t s){ return 1; }
%}
%option reentrant
%%
// ...
%%
Bison 側
- YYLEX_PARAM マクロ
- パーサオブジェクトから yyscan_t 構造体を渡す用
- `%pure_parser' ディレクティブ
- 基本
- `%parse-param' ディレクティブ
- yyparse() やら yyerror() やらにパーサオブジェクトを渡すため
電卓プログラムからの抜粋 - calc.y:
%{ #define YYDEBUG 1 #include "./parse_state.h" #define YYLEX_PARAM p->scanner void yyerror(struct parse_state *p, const char *msg){ parser_error(p, msg); } %} %pure_parser %parse-param { struct parse_state *p } /* other directives */ %% /* rules */ %%
ハマったところ
Flex の出力したヘッダは、 Flex ソースでインクルードしてはいけない
なんかよく分かりませんが、内部用っぽいマクロをヘッダの最後で #undef しているので、まともに動かなくなります。
parse_state 構造体の定義のために yyscan_t を定義したヘッダが要るな -> parse_state.h で Flex ヘッダをインクルードする ->間接的に Flex ソースにもインクルードされる -> 動かない
結論を言うと、 yyscan_t は 単なる void* の typedef なので、 void* を使えばよいです。
電卓プログラムからの抜粋 - parse_state.h:
#ifndef _PARSE_STATE_H_ #define _PARSE_STATE_H_ struct parse_state { void *scanner; /* ... 他のメンバ ... */ }; void parser_init(struct parse_state *); void parser_destroy(struct parse_state *); void parse(struct parse_state *); #endif /* _PARSE_STATE_H_ */
↑の関数の実装
#include <stdio.h> #include "./parse_state.h" #include "./lex.yy.h" void parser_init(struct parse_state *p){ yylex_init_extra(p, &(p->scanner)); /* ... 他の初期化 ... */ } void parser_destroy(struct parse_state *p){ yylex_destroy(p->scanner); /* ... 他の後始末 ... */ } void parse(struct parse_state *p){ /* 必要に応じて yyset_in() などで設定 */ if (yyparse(p) != 0) { fprintf(stderr, "error occured\n"); } }
Bison の出力したヘッダは、 Bison ソースでインクルードしなくてもよい
トークンと %union の宣言だけで、しかも Bison の出力する .c にも含まれてる内容なので、インクルードしてもまったくの無駄。