Dev Study
← 解説「状態機械という読み方 — 1 文字読んで、状態を変えるか、トークンを出すか」に戻る

サンプルコードで身につける: 状態機械という読み方 — 1 文字読んで、状態を変えるか、トークンを出すか

解説で学んだ概念を、3つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

14 つの状態で <p>…</p> を切る

Data / TagOpen / EndTagOpen / TagName の 4 だけので、「今の状態」と「今の文字」の 2 つで次にすることが決まるというの形を、 State と二重の でそのまま書いています。実行すると <P>a < b<BR></p> が StartTag <p>、Char 'a' ' ' '<' ' ' 'b'、StartTag <br>、EndTag </p>、Eof の順に出て、タグ名が小文字に揃い「a < b」の < が文字になることが分かります。ただしこの素朴版は「<」の次がタグ名でないとき 2 文字をその場で出して戻るので <<p> のような入力では崩れ、次のサンプルの で直します。

Rust

2reconsume: 今の文字をもう一度読む

HTML Standard 13.2.5 の各状態がよく使う「状態を切り替えて今の文字を再消費する(reconsume)」を、consume() の reconsume フラグと reconsume_in(state) で実装しています。実行すると読んだ文字と状態が 1 行ずつトレースされ、a <<p>x</p> で TagOpen が 2 つ目の '<' を読んで Char '<' を出した後、Data が同じ '<' をもう一度読む行に「同じ文字をもう一度」と付きます。TagOpen → TagName、EndTagOpen → TagName でも同じ印が出て、文字を捨てずに次の状態へ渡せていることが分かります。

Rust

3実務: 同じ型で CSV の引用符を読む

引用符つき CSV( 4180 の形)も FieldStart / Unquoted / Quoted / AfterQuote の 4 状態で同じように書けます。Quoted の中ではカンマも改行もただの文字で、AfterQuote で次が '"' なら 1 つの '"'、そうでなければ閉じ引用符だったので今の文字を Unquoted で読み直します(continue で i を進めない = reconsume)。実行すると "Yamada, Taro" がカンマを含んだまま 1 フィールド、"He said ""hi""" が He said "hi"、"two\nlines" が改行を含んだ 1 フィールドとして 3 レコードに分かれ、split(',') では壊れる入力が読めています。

Rust

図やコードに出てくる用語(タップで説明)