私はしばしばパーサーコンビネーターとは対照的にレクサー / パーサーで作業し、解析でクラスを取ったことがない人を見て、バイナリデータの解析について尋ねます。通常、データはバイナリであるだけでなく、コンテキスト依存でもあります。これは基本的に、1種類のトークン(バイト用トークン)のみを持つことになります。
レクサー/パーサーによるバイナリデータの解析が、解析クラスを受講していないが、理論に基づいたCS学生にとって十分に明確である理由を誰かが説明できますか?
私はしばしばパーサーコンビネーターとは対照的にレクサー / パーサーで作業し、解析でクラスを取ったことがない人を見て、バイナリデータの解析について尋ねます。通常、データはバイナリであるだけでなく、コンテキスト依存でもあります。これは基本的に、1種類のトークン(バイト用トークン)のみを持つことになります。
レクサー/パーサーによるバイナリデータの解析が、解析クラスを受講していないが、理論に基づいたCS学生にとって十分に明確である理由を誰かが説明できますか?
回答:
原則として、何も悪いことはありません。
実際には、
私が知っているほとんどの非テキストデータ形式はコンテキストフリーではないため、一般的なパーサージェネレーターには適していません。最も一般的な理由は、プロダクションが存在しなければならない回数を示す長さフィールドがあることです。
明らかに、コンテキストに依存しない言語を使用しても、パーサージェネレーターの使用が妨げられることはありません。言語のスーパーセットを解析し、セマンティックルールを使用して必要なものに減らします。結果が決定的である場合、そのアプローチは非テキスト形式に使用できます。問題は、ほとんどのバイナリ形式で任意のデータを埋め込むことができるため、同期するカウント以外のものを見つけることです。長さフィールドは、それがいくらであるかを示します。
次に、パーサーからのフィードバックを使用して、手動で作成されたレクサーでそれを処理できるようなトリックの再生を開始できます(たとえば、Cのlex / yacc処理は、そのようなトリックを使用してtypedefを処理します)。しかし、それから2番目のポイントに行きます。
ほとんどの非テキストデータ形式は非常に単純です(たとえコンテキストに依存していなくても)。上記のカウントが無視される場合、言語は通常のLL1であり、最悪の場合はLL1であるため、手動の解析手法に適しています。また、再帰降下などの手動解析手法では、カウントの処理が簡単です。
データを3つのカテゴリに分類しましょう。人間が読めるデータ(通常はテキスト、書籍からプログラムまでさまざまです)、コンピューターが読むことを目的としたデータ、およびその他のデータ(画像または音声の解析)です。
最初のカテゴリでは、コンピューターで使用できるものに処理する必要があります。人間が使用する言語は一般にパーサーで比較的うまくキャプチャできるため、通常はパーサーを使用します。
3番目のカテゴリのデータの例は、テキストに解析したい本のページのスキャン画像です。このカテゴリでは、ほとんどの場合、入力に関する非常に具体的な知識が必要です。したがって、入力を解析するには特定のプログラムが必要です。標準の解析技術では、ここまで行くことはできません。
あなたの質問は、2番目のカテゴリに関するものです。バイナリのデータがある場合、それはほとんどの場合、別のコンピュータープログラム向けのコンピュータープログラムの製品です。これはすぐに、データのフォーマットがその作成を担当するプログラムによって選択されることも意味します。
コンピュータープログラムは、ほとんどの場合、明確な構造を持つ形式でデータを生成します。入力を解析する場合、基本的に入力の構造を把握しようとしています。バイナリデータの場合、この構造は一般的に非常にシンプルであり、コンピューターで簡単に解析できます。
言い換えると、通常、すでに構造がわかっている入力の構造を把握するのは少し無駄です。解析は無料ではないので(時間がかかり、プログラムが複雑になります)、これがバイナリデータでレクサー/パーサーを使用するのが「とても間違っている」理由です。
LANGSEC: Language-theoretic Security
興味深い視点を提供します。記事の1つは、「奇妙なマシン」:システムの入力処理機能を形成する既知の形式のアドホックパーサーについて語っています。実際に意図したとおりに機能しない場合があります。誤った仮定により、欠陥のあるマシンは、特別に細工された入力が与えられると予期しない状態遷移を実行し、不可能な計算を実行します。これにより、攻撃ベクトルが作成されます。正式な文法を使用すると、証明可能な正しいアルゴリズムが得られます。
(+ a (* b (- c d)) e)
a b c d - * + e +
。通常の数学表記は、Lisp(より多くの括弧を必要としますが、可変アリティを無料で取得するため、大きなアリティを使用して式を表現するために必要なシンボルが少ない)またはRPL(括弧を必要としない)よりも冗長性があります。このような冗長性は、コンピューターにほとんど役立ちません。データにエラーがある場合、エラー修正ロジックは通常、データの機能的意味から分離されます。たとえば、任意のそれらが何を表すかに関係なく、バイトシーケンス。
バイナリ形式は通常、コンパクトになるように設計されています。つまり、コンテキストなしの文法で表現できるバランスの取れた括弧など、単純な言語機能はほとんどありません。さらに、データのバイナリ表現が正規である、つまり各オブジェクトの単一の表現を持っていると便利なことがよくあります。これは、括弧などの冗長な機能を除外します。冗長性が少ないことによるもう一つの賞賛に値しない結果は、すべての入力が構文的に正しい場合、エラーチェックを節約することです。
バイナリデータの非自明なパーサーに対するもう1つの要因は、多くのバイナリ形式が、オーバーヘッドの少ない一定のメモリで動作するのが好きな低レベルコードによって解析されるように設計されていることです。要素の任意の繰り返しを許可することに適用可能な場合、固定サイズが優先されます。左から右へのパーサーが最初にオブジェクトに適切な量のメモリを割り当て、次にオブジェクトの表現を読み取ることを可能にするTLVなどの形式。左から右への解析は、中間バッファーなしでデータを処理することができるため、利点です。