web-dev-qa-db-ja.com

パーサーはトークンパターンをどのように検索しますか?

パーサーがマークダウンのようにトークンパターンを検索する方法を説明できますか?

ネストされたパターンが含まれるとすぐに、中括弧パターン[]()のみに一致するものを思いつくかもしれません。

例えばこのようなもので

foo [**baz**](baz) qux

トークナイザーはおそらくexplodesこれらのトークンへの文字列

"foo ", "[", "**", "baz", "**", "]", "(", "baz", ")", " qux"

そしてそれをパーサーに渡してパターンを認識し、それがリンクであり、中括弧が一致し、さらにラベル内の太字スタイルを理解します。

それはある種の状態マシンだと思いますが、実際にはそうしますthink[が発生するとすぐに何かが意味される可能性があるため、トークンを格納し、後続のトークンが一致しない場合この状態を破棄し、区切りトークンを通常のリテラルに変えます。つまり、(の終了後に]がなかった場合は、前に戻って他のすべての意味を変更する必要がありました。複雑すぎると思いますか?

私がそれを見ると、実装するのは簡単だったように見えますが、もしinventのアルゴリズムが必要な場合、それはできませんでした。

5
t3chb0t

見たところ簡単に実装できたようですが、アルゴリズムを考案しようとしてもできませんでした。

ありがたいことにいくつかの人々はすでに持っています:)

  1. https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form
  2. https://en.wikipedia.org/wiki/Context-free_grammar
  3. https://en.wikipedia.org/wiki/Recursive_descent_parser

それは複雑なトピックであり、そこにはたくさんありますが、簡単に要約すると:

トークナイザーが文字列をトークンに分解するのは正しいことです。次に、パーサーは文法を内部に定義し、通常はBNFのようなものを使用して、断片を合わせます。

"foo ", "[", "**", "baz", "**", "]", "(", "baz", ")", " qux"

次のような文法で解析できます:

line = <rounds> | <squares> | <markdown_stuff> | <line>
rounds = '(' <line> ')'
squares = '[' <line> ']'
markdown_stuff = <italics> | <bold> | <Word_text>
italics = '*' <Word_text> '*' 
bold = '**' <Word_text> '**'
Word_text = <Word_char> | <Word_char> <Word_text>
Word_char= 'a', 'b', 'c', 'd'... etc 'A', 'B', 'C', etc '0', '1', '2', '3', etc '_'

再帰的であることに注意してください。 Word_textのようないくつかのルールは直接自分自身を参照し、<line>のような他のルールは<line>を参照するものを参照します。 (python language doc はそのような例でいっぱいです)

あなたの言語のための文法を作った後、あなたは例えば書くでしょう。それを「読み取る」ための再帰的降下パーサー。または、より一般的には、YACCやANTLRなどのツールを使用して、文法に基づいてパーサーを直接作成します。

ステートマシンについて:YACCは、パーサーを巨大なステートテーブルの観点から実装していると思います。使って久しぶりです。

3
Pod