re2c-正規表現コンパイラ

文字のストリームから特定のトークンを抽出するタスクは非常に一般的です。 多くの場合、正規表現で構成された字句解析プログラムの助けを借りて解決されます。 多くのアナライザーは、プログラムコードを生成するという原則に基づいて構築されており、プログラムコードは正規表現のロジックを実装しています。 実際、これは正規表現言語をプログラミング言語コードにコンパイルしたものです。



たとえば、 flexはそのようなアナライザーの1つです。 古いが、長年にわたって証明されています。



私はフレックスをたくさん使いました。良い面と悪い面の両方がありますが、概して、文句を言う必要はありませんでした。



しかし、昨日、興味深いプロジェクトre2cに出会いました。 実際、このことについて、数分で字句解析器をひざの上で書くことができます。





文字列からいくつかのコマンド、整数、小数を選択する必要があるとします。 flexを発見するか、次のように書くことができます:



#include <stdio.h> #include <stdlib.h> enum { CMD, INT, FLOAT, SPACE, END }; int scan(char** p, char** lex) { char* marker; if (lex) *lex = *p; /*!re2c re2c:define:YYCTYPE = "unsigned char"; re2c:define:YYCURSOR = *p; re2c:define:YYMARKER = marker; re2c:yyfill:enable = 0; re2c:yych:conversion = 1; re2c:indent:top = 1; "GET"|"PUT"|"EXIT" { return CMD; } [0-9]+ { return INT; } [0-9]+ '.' [0-9]* { return FLOAT; } [ \t]+ { return SPACE; } [^] { return END; } */ } int main(int argc, char* argv[]) { char *p, *last; int token; if (argc < 2) return 1; p = argv[1]; while ((token = scan(&p, &last)) != END) { int sz = p - last; switch (token) { case CMD: printf("Command: '%.*s'\n", sz, last); break; case INT: printf("Number: '%.*s'\n", sz, last); break; case FLOAT: printf("Float: '%.*s'\n", sz, last); break; } } return 0; }
      
      





そしてそれだけです!



コメント「/ *!Re2c」と「* /」で制限された行の間の「スキャン()」関数ですべての魔法が発生することは明らかです。



そのため、re2cはコードをプログラムテキストに直接埋め込む正規表現コンパイラです。



re2cを介してソースを実行する場合:



  re2c.exe -is test.re2c >test.c
      
      





次に、これを取得します。



 /* Generated by re2c 0.13.5 on Tue Apr 19 21:08:57 2011 */ #include <stdio.h> #include <stdlib.h> enum { CMD, INT, FLOAT, SPACE, END }; int scan(char** p, char** lex) { char* marker; if (lex) *lex = *p; { unsigned char yych; yych = (unsigned char)**p; if (yych <= '9') { if (yych <= 0x1F) { if (yych == '\t') goto yy8; goto yy10; } else { if (yych <= ' ') goto yy8; if (yych <= '/') goto yy10; goto yy6; } } else { if (yych <= 'F') { if (yych == 'E') goto yy5; goto yy10; } else { if (yych <= 'G') goto yy2; if (yych == 'P') goto yy4; goto yy10; } } yy2: yych = (unsigned char)*(marker = ++*p); if (yych == 'E') goto yy24; yy3: { return END; } yy4: yych = (unsigned char)*(marker = ++*p); if (yych == 'U') goto yy23; goto yy3; yy5: yych = (unsigned char)*(marker = ++*p); if (yych == 'X') goto yy18; goto yy3; yy6: ++*p; if ((yych = (unsigned char)**p) == '.') goto yy13; if (yych <= '/') goto yy7; if (yych <= '9') goto yy16; yy7: { return INT; } yy8: ++*p; yych = (unsigned char)**p; goto yy12; yy9: { return SPACE; } yy10: yych = (unsigned char)*++*p; goto yy3; yy11: ++*p; yych = (unsigned char)**p; yy12: if (yych == '\t') goto yy11; if (yych == ' ') goto yy11; goto yy9; yy13: ++*p; yych = (unsigned char)**p; if (yych <= '/') goto yy15; if (yych <= '9') goto yy13; yy15: { return FLOAT; } yy16: ++*p; yych = (unsigned char)**p; if (yych == '.') goto yy13; if (yych <= '/') goto yy7; if (yych <= '9') goto yy16; goto yy7; yy18: yych = (unsigned char)*++*p; if (yych == 'I') goto yy20; yy19: *p = marker; goto yy3; yy20: yych = (unsigned char)*++*p; if (yych != 'T') goto yy19; yy21: ++*p; { return CMD; } yy23: yych = (unsigned char)*++*p; if (yych == 'T') goto yy21; goto yy19; yy24: ++*p; if ((yych = (unsigned char)**p) == 'T') goto yy21; goto yy19; } } int main(int argc, char* argv[]) { char *p, *last; int token; if (argc < 2) return 1; p = argv[1]; while ((token = scan(&p, &last)) != END) { int sz = p - last; switch (token) { case CMD: printf("Command: '%.*s'\n", sz, last); break; case INT: printf("Number: '%.*s'\n", sz, last); break; case FLOAT: printf("Float: '%.*s'\n", sz, last); break; } } return 0; }
      
      





怖い はい、コードは手動編集用ではありませんが、必須ではありません。



コンパイルします:



  re2c.exe -is test.re2c >test.c && cl test.c
      
      





以下を開始します。



  test "GET 123.0 12344 PUT 10."
      
      





結果:



  Command: 'GET' Float: '123.0' Number: '12344' Command: 'PUT' Float: '10.'
      
      





彼らが言うように、速く、安くて陽気な。 re2cを完全にマスターするには、ドキュメントの 1 ページのみを読む必要があります



ちなみに、re2cを簡単に使用できるということは、複雑なアナライザーを作成できないという意味ではありません。 配布には、 CおよびRexxトークンの文法の例があります。



re2cフラグを使用すると、 「if / else」ではなく「switch / case」に基づいてコードを生成できます。 選択は、コンパイラが最適化するコードを理解することに基づいて行う必要があります。



私が理解しているように、re2cによって生成されたアナライザーは、非常に高速であるべきです。 同じflex'a、ANTLR'aまたはSpirit'aに対して測定することは興味深いでしょう。



ほとんどトピックに関する投稿:




All Articles