これまで
シェルもどきoreshellを自作している。
シェルは入力した文字列を読み取って、コマンドと引数群に分割してプロセス生成に渡している。
現在の実装では入力文字列を単純に空白で分割している。
空白文字を含んでいるファイル名/パス名(例:「cp \ oge "h ge"」)を扱えるようにするために字句解析を導入する。
以下は入力された文字列「cp \ oge "h ge"」を字句解析する直前を表した図である。
PとSは、文字を読み取るためのポインタである。この2つのポインタは入力された文字列の先頭から末尾に向かって移動する。
以降で、入力された文字列をこの2つのポインタを使って読み取る様子を説明する。
ステートマシンは開始するとすぐに「lexText状態」に遷移する。
Pの現在位置に文字「c」を見つけたので「lexString状態」に遷移する。
「lexString状態」では、バックスラッシュ文字、クォーテーション文字、空白文字、EOFのいずれかが見つかるまで
を繰り返す。
見つけたらSの位置からPの位置までの間の文字群を切り出す。
Pの現在位置に文字「c」を見つけた。
Pを次に進める。
Pの現在位置に文字「p」を見つけた。
Pを次に進める。
Pの現在位置に文字「 (空白)」を見つけた。
空白文字が見つかったので、Sの位置からPの位置までの間の文字群を切り出す。
SをPの位置まで進める。
「lexText状態」に戻る。
Pの現在位置に文字「 (空白)」を見つけたので「lexWhitespace状態」に遷移する。
「lexWhitespace状態」では、空白以外の文字が見つかるまで
を繰り返す。
見つけたらSの位置からPの位置までの間の文字群を切り出す。
Pの現在位置に文字「 (空白)」を見つけた。
Pを次に進める。
Pの現在位置に文字「\」を見つけた。
空白以外の文字が見つかったので、Sの位置からPの位置までの間の文字群を切り出す。
SをPの位置まで進める。
「lexText状態」に戻る。
Pの現在位置にバックスラッシュ文字を見つけたので「lexEscapeChar状態」に遷移する。
「lexEscapeChar状態」では、
を行う。
そのあとSの位置からPの位置までの間の文字群を切り出す。
Pを次に進める。(移動後位置の文字は「 (空白)」)
Pを次に進める。(移動後位置の文字は「o」)
Sの位置からPの位置までの間の文字群を切り出す。
SをPの位置まで進める。
「lexText状態」に戻る。
Pの現在位置に文字「o」を見つけたので「lexString状態」に遷移する。
「lexString状態」では、前述の通りバックスラッシュ文字、クォーテーション文字、空白文字、EOFのいずれかが見つかるまで
を繰り返す。
見つけたらSの位置からPの位置までの間の文字群を切り出す。
Pの現在位置に文字「o」を見つけた。
Pを次に進める。
Pの現在位置に文字「g」を見つけた。
Pを次に進める。
Pの現在位置に文字「e」を見つけた。
Pを次に進める。
Pの現在位置に文字「 (空白)」を見つけた。
空白文字が見つかったので、Sの位置からPの位置までの間の文字群を切り出す。
SをPの位置まで進める。
「lexText状態」に戻る。
一つ目の区切りの「 (空白)」の切り出しと同じなので説明は省略。
Pの現在位置に文字「"」を見つけたので「lexQuotedString状態」に遷移する。
「lexQuotedString状態」では、次のクォーテーション文字が見つかるまで
を繰り返す。
見つけたらSの位置からPの位置までの間の文字群を切り出す。(ただし両端のクォート文字も切り出しに含める)
Pの現在位置に文字「"」を見つけた。
Pを次に進める。
Pの現在位置に文字「h」を見つけた。
Pを次に進める。
Pの現在位置に文字「 (空白)」を見つけた。
Pを次に進める。
Pの現在位置に文字「g」を見つけた。
Pを次に進める。
Pの現在位置に文字「e」を見つけた。
Pを次に進める。
Pの現在位置に文字「"」を見つけた。
Pを次に進める。
次のクォーテーション文字が見つかったので、Sの位置からPの位置までの間の文字群を切り出す。
SをPの位置まで進める。
「lexText状態」に戻る。
Pの位置が文字列全体の長さよりはみ出た場合、文字列全体の終端を検知したとみなす。
終端マークを切り出す。
字句解析を完了し、トークン群を切り出すことができた。
しかし、切り出したトークン群をそのままシェル内のプロセス生成に渡すことはできない。
ここで説明した通り、プロセス生成に渡したい文字列の配列になるように、トークン群から要らない要素を削除、要素同士の連結をして再構成する必要がある。
今回の例だと、
また、エスケープ処理した文字列リテラルから先頭のエスケープ文字「\」、または前後のクオート文字「"」「'」を撤去する。
実装の該当箇所は以下の通り。