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 にも含まれてる内容なので、インクルードしてもまったくの無駄。

YYPARSE_PARAM とかいうそれっぽいマクロの使いドコロ

最初は %parse-param ではなくこっちを使ってたのですが、 yyerror() とかの APIシグネチャにはまったくノータッチを決め込むので、無用の長物です。