鱒身(Masu_mi)のブログ

知った事をメモする場所。

Lua字句解析 llex.* を読んだ

luaに演算子を追加したのだけど単に感想を書いただけだと何の意味もなくなってしまうので、 テーマ毎に整理しておく。 最初は字句解析器関係(llex.h, llex.c)について。

公開API

字句解析器はユーザーの文字列から字句列に変換する部分。 ヘッダファイル で公開されている関数は以下。

  • 基本的に構文解析器が呼ぶ
  • 字句解析処理の進行は構造体LexStateの変化に一致する
  • 実際の先読み字句取得などはLexStateのメンバで判断する

読んだところ以下の様な機能を提供している。

関数 処理
void luaX_init (lua_State *L) Lua仮想機械に予約語を登録
void luaX_setinput
(lua_State *L, LexState *ls, ZIO *z, TString *source)
字句解析器に解析対象設定
TString *luaX_newstring (LexState *ls,
const char *str, size_t l)
解析結果文字列を文字列定数として格納 格納先は構文解析器
void luaX_next (LexState *ls) 字句解析を進める(現在の字句)
void luaX_lookahead (LexState *ls) 先読み字句の解析を行ないlsに設定する
void luaX_lexerror (LexState *ls,
const char *msg, int token)
字句解析エラーをLua状態機械に伝える
void luaX_syntaxerror
(LexState *ls, const char *s)
構文解析エラーをLua状態機械に渡す (luaX_lexerrorをそのまま使ってる)
const char *luaX_token2str (LexState *ls,
int token)
字句に対応するLua文字列を取得

luaX_init詳細

Lua_Stateを予約語を受け付けられる状態にする。

  1. 予約語をLua文字列として生成する(luaX_tokens, RESERVED)
  2. luaX_fixを用いてガベージコレクションの対象から外す
  3. TString.txv.reserved にトークンの番号+1 を代入する (lu_byteにキャストしてだけど)

3つ目の具体的な目的がよくわかっていない。

luaX_setinput詳細

Lexerを初期化している

  1. 仮想マシンを保持
  2. 先読み字句を無しにする(まだ先読みしていない状態) TK_EOS
  3. メンバ(source)にソースコード格納
  4. デバッグ用?に現在の行数と最終行数を1に初期化
  5. ZIO(?)を格納

llex詳細

文字列から字句を判別して返却する

check_next

現在の文字が与えた文字集合に属するか判別し含まれる場合、確認する文字を1つ進める。 その際、次の字句の為にbufferに文字を追加する。

save

字句に対応する文字列を特定する為に字句解析最中に文字をバッファに保存する。

read_numeral, read_string, read_long_string

それぞれ対応する字句か判別すると思う。

その他

他にある関数は以下で詳細は追っていない。

  • txtToken
  • luaX_token2str
  • luaX_newstring
  • skip_sep
  • buffreplace
  • trydecpoint

字句の登録

字句の登録は配列 luaX_tokens で行なった。 対応する様に`enum RESERVED に字句種別を登録する必要がある。 llex が字句識別の本体なので他の真似をして追加した。 これらの事を行なったコミットが以下。

ref. https://github.com/masu-mi/lua-with-yorihijouni/commit/3b601929ee2d6fd5ba87e31ad7f8795abe4a7047

書いたときは普通にTK_WHILEより後ろにTK_LSなどを追加したが以下の記述があったので予約語として扱われる為には TK_WHILEより前に置くか下の記述のTK_WHILEを自分で追加した予約語に置き換える必要がある。

#define NUM_RESERVED>-(cast(int, TK_WHILE-FIRST_RESERVED+1))

これを怠ると何が不都合なのかはまだわかっていない。

  • ガベージコレクションの対象になる? そもそも文字列が生成されないので問題になるかわからない
  • 字句から予約語の文字列として引き当てに失敗するのかも知れない 前に書いた時は引き当てられていたけどそれは対応する意味情報として引き当てていただけ?

感想

字句解析は単純な処理だけあり読み易い。 ただLua仮想機械でエラーが把握するためにLua文字列の生成や メモリ・バッファの管理などが入り組んでしまう所もある。

字句解析処理はllexで決まっていて既にcodeになっているのに対応する予約語は 後からLua仮想機械に文字列として登録する仕掛けになっているの何でだろうって思った。

次は何処を読もうか迷っている。 順当には構文解析なのだけどLuaはLL(1)手書きなので想像はつく。 やはり, lcode, lvm, ldo あたりに手をつけた方が得る物が大きいだろうか。