近況 2021-07-31

今月の活動 (ミローネ言語、パーサの無限ループバグなど)

ミローネ言語

https://github.com/vain0x/milone-lang

LSPサーバーを動かしながらライブラリコードを書いたりした。 LSPサーバーやパーサ、型検査器などの不具合がたくさんみつかった。

無限ループバグ vs. OOMキラー

パーサが無限ループするバグがあった。 発生するとループの中でメモリを確保するせいでメモリがなくなり、システムごと固まってしまう。 マウスもキーボードもすべて反応しないから、電源を長押しして強制終了するしかない。

Ubuntu (Linux) にはOOMキラーという機能があって、メモリが足りなくなったときにメモリ消費量の多いプロセスを強制終了させてくれるらしい。有効にしてみたが、改善しなかった。

参考: kernel - OOM killer not working? - Ask Ubuntu

earlyoomというやつを入れたら解決した。少し固まった後にプロセスを強制終了させてくれた。

rfjakob/earlyoom: earlyoom - Early OOM Daemon for Linux

不完全なコードをコンパイルすること

パーサのループは、次のようなコードで発生していた。

    match cond with
    | A -
//      ^
    | _ -> ()

A の引数パターンをパースするときに、次のトークンが - なので、-1 のような負数のパターンがあるはずだとパーサが判断する。 実際には - の後ろに数値リテラルがないので、エラーを報告する。 そして次の引数パターンをパースしようとする。 「- の直前で引数パターンをパースする」状態に戻ってきたので、無限ループになる。

この問題に関していえば - を読み飛ばすようにすれば解決する。 一般化していえば、「エラーを報告する際に次のトークンを読み飛ばす」ことにすれば無限ループ系のバグは起こりづらくなる (という印象がある)。 繰り返しをパースする場合は、繰り返しの先頭に戻る前に、1つ以上のトークンを読むように気をつけよう。

これは -> の - を押したタイミングで発生していた。 ビルドコマンドを実行するとき、このレベルで構文的に不正なコードをコンパイラに渡すことはないから、いままで発覚していなかったんだと思う。

LSPサーバーに常にコンパイルさせながらコードを書いていると、こういう「書いている途中のコード」をコンパイルする機会が増える。

例えばmatch式のアームの本体が空のときは構文エラーになる。 それに巻き込まれて後続のコードも構文エラーになることが多い。 アームの本体を書く段階で、後方で定義された関数が (定義部分が構文エラーに巻き込まれてるから?) 入力補完に出てこなかったりする。

    match cond with
    | _ ->
        // 入力補完にfが出てこない

let f () = // 構文エラーのせいでこのlet式はパースされてない
    ...

その他

関連記事