Langphilia! / KL1 / klex, 2002-07-13

klex: KL1 lexer

GNU autoconf, automake, flex を使って KL1 項の字句解析器を作ってみる。


使ったもの

とりあえず使ってみる

  1. mkdir klex; cd klex
    ソースディレクトリを作って、そこに移動する。
  2. emacs klex.l
    ソースファイル klex.l を書く。 emacs が *.l を Lisp ファイルと認識するので *.lex としたいけど、 automake が *.lex を Lex ソースファイルと認識してくれなかった。
  3. autoscan
    すると、configure.scan が生成される。
  4. emacs configure.in
    生成された configure.scan を参考にして、 autoconf のソースファイル configure.in を書く。
  5. emacs Makefile.am
    適当に automake のソースファイル Makefile.am を書く。
  6. aclocal
    すると、autoconf (automake?) に必要な aclocal.m4 が生成される。 aclocal は autoconf の一部であるべきなので、将来はなくなると言う噂。
  7. autoconf
    すると、configure.in から configure が生成される。
  8. automake
    すると、必要なものがないと言われる。
  9. automake --add-missing
    すると、COPYING, INSTALL, install-sh, missing, mkinstalldirs が自動的に補完される。というわけで GPL に従います。 でもまだ、AUTHORS, ChangeLog, NEWS, README がないと言われる。
  10. emacs AUTHORS
    とりあえず自分の名前とメールアドレスを書く。
  11. touch ChangeLog NEWS README
    とりあえず中身なしのファイルを作っておく。
  12. automake
    すると、configure.in と Makefile.am から Makefile.in が生成される。
  13. ./configure
    すると、Makefile.in から Makefile が生成される。
  14. make
    すると、Makefile に基づいて flex が klex.l から klex.c を生成し、 gcc が klex.c から klex を生成する。
  15. make dist
    すると、klex-0.3.tar.gz が生成される。

ソースファイル

-- 以降は本ページ用のコメントなので、ソースファイルには書かないでね。

configure.in

AC_INIT(klex.l)              -- ソースファイル
AM_INIT_AUTOMAKE(klex, 0.2)  -- プログラム名とバージョン

AC_PROG_CC                   -- gcc を使う
AM_PROG_LEX                  -- flex を使う

AC_OUTPUT(Makefile)          -- Makefile を作らせる

Makefile.am

bin_PROGRAMS = klex      -- 実行ファイル
klex_SOURCES = klex.l    -- ソースファイル
klex_LDADD = @LEXLIB@    -- flex の共有ライブラリを使う

klex.l

/* klex.l -- lexer for KL1
 * Copyright 2002, TAKAGI Yusuke.
 */

%{

/* 普通は flex と bison を一緒に使うので、それらの間のインタフェイス yylex()
 * の結果であるトークン型も bison に生成させ、#include "klparse.h" するみたい。
 * 今回は flex しか使わないので、トークン型も main() も klex.l の中で定義する。
 */
enum token {
  T_EOF,             -- ファイルの終りに到達したら yylex() は 0 を返す
  T_NONE = 'n',      -- 空白類とコメント
  T_VAR = 'v',       -- 変数トークン
  T_ATOM = 'a',      -- 記号トークン
  T_INT = 'i',       -- 整数トークン
  T_FLOAT = 'f',     -- 浮動小数点数トークン
  T_STRING = 's' };  -- 文字列トークン
                     -- 一文字のトークンは文字コードそのまま

%}

SPACE  [ \f\n\r\t\v]
LOWER  [a-z]         -- 小文字クラス
UPPER  [A-Z_]        -- 大文字クラス
ALNUM  [0-9a-zA-Z_]  -- 英数字クラス
SEPARATOR [,(){}\[\]]  -- 区切り文字

CBLOCK  [/][*]+([^*/]*[*]+)+[/]  -- ブロックコメントの正則表現
CLINE   [%][^\n]*[\n]            -- 行コメントの正則表現

VAR  {UPPER}{ALNUM}*             -- 変数トークンの正則表現

ATOM   {LOWER}{ALNUM}*            -- 記号の正則表現
SPE    [~+\-*/\^<>=`:.?@#$&]+  -- 特殊記号アトムの正則表現
ONE    [!\|;]                     -- 一文字アトムの正則表現

SIGN  [+-]?
DEC   [0-9]+
INT    {SIGN}{DEC}
FLOAT  {SIGN}{DEC}[.]{DEC}([e]{SIGN}{DEC})?  -- 空白を入れてはいけません。

STRING  \"([^\"\n\\]|\\\n|\\.)*\"

%%

/* 空白類とコメントを読み飛ばす。 */
{SPACE}+
{CBLOCK}
{CLINE}

{SEPARATOR}  { return yytext[0]; }  /* 記号ではない一文字のトークン */

{VAR}   { return T_VAR; }  /* 変数トークン */

{ATOM}|{SPE}|{ONE}   { return T_ATOM; }  /* 記号トークン */

{INT}    { return T_INT; }
{FLOAT}  { return T_FLOAT; }

{STRING}  { return T_STRING; }

.  { return T_NONE; }

%%

extern int main(int argc, char** argv)
{
  for(;;){
    enum token token = yylex();

    if(token == T_EOF) break;

    printf("<%c>%s\n", token, yytext, token);
  }
}

Copyright 2002, TAKAGI Yusuke.