7

テキストエリアの変更イベントに応答する bbcode -> html コンバーターがあります。現在、これは一連の正規表現を使用して行われており、多くの病的なケースがあります。私はいつもこの文法の鉛筆を削りたいと思っていましたが、ヤクの毛を剃りたいとは思いませんでした. しかし...最近、PEGパーサー生成のかなり完全な実装のように見えるpegjsに気付きました。文法の大部分を指定しましたが、これが本格的なパーサーの適切な使用法であるかどうか疑問に思っています。

私の具体的な質問は次のとおりです。

  1. 私のアプリケーションは、できることを HTML に変換し、残りを生のテキストとして残すことに依存しているため、構文エラーで失敗する可能性のあるパーサーを使用して bbcode を実装することは理にかなっていますか? 例:[url=/foo/bar]click me![/url]終了タグの終了ブラケットが入力されると、確実に成功することが期待されます。しかし、その間にユーザーは何を見るでしょうか? 正規表現を使用すると、一致しないものを無視して、プレビュー用の通常のテキストとして扱うことができます。正式な文法では、解析ツリーから HTML を作成することに依存しているため、これが可能かどうかわかりません。解析に失敗するのは何ですか?

  2. どこで変換を行う必要があるのか​​ わかりません。正式な lex/yacc ベースのパーサーでは、ノード タイプを示すヘッダー ファイルとシンボルを使用します。pegjs では、ノード テキストを含むネストされた配列を取得します。pegjsで生成されたパーサーのアクションとして翻訳したコードを出力できるのですが、パーサーとエミッターを組み合わせるのがコード臭そうです。ただし、 を呼び出すとPEG.parse.parse()、次のような結果が返されます。

[
       [
          "[",
          "img",
          "",
          [
             "/",
             "f",
             "o",
             "o",
             "/",
             "b",
             "a",
             "r"
          ],
          "",
          "]"
       ],
       [
          "[/",
          "img",
          "]"
       ]
    ]

次のような文法が与えられます:

document
   = (open_tag / close_tag / new_line / text)*

open_tag
   = ("[" tag_name "="? tag_data? tag_attributes? "]")


close_tag
   = ("[/" tag_name "]") 

text
   = non_tag+

non_tag
   = [\n\[\]]

new_line
   = ("\r\n" / "\n")

もちろん、私は文法を省略していますが、あなたはその考えを理解しています. したがって、配列の配列には、所有しているノードの種類を示すコンテキスト情報がなく、パーサーが既にこれを行っていると思っていても、文字列の比較を再度行う必要があります。コールバックを定義し、アクションを使用して解析中にそれらを実行することは可能だと思いますが、それを行う方法について Web 上で入手できる情報はほとんどありません。

私は間違った木を吠えていますか?正規表現スキャンに戻り、解析を忘れるべきですか?

ありがとう

4

3 に答える 3

4

最初の質問(不完全なテキストの文法):

あなたは付け加えられます

incomplete_tag = ("[" tag_name "="? tag_data? tag_attributes?)
//                         the closing bracket is omitted ---^

after open_tagを変更documentし、最後に不完全なタグを含めるように変更します。秘訣は、常に解析するために必要なすべてのプロダクションをパーサーに提供することですが、有効なものが最初に来ます。incomplete_tagその後、ライブ プレビュー中は無視できます。

2 番目の質問(アクションを含める方法):

式の後にいわゆるアクションを記述します。アクションは中括弧で囲まれた Javascript コードであり、pegjs 式の後、つまりプロダクションの途中でも使用できます!

実際に{ return result.join("") }は、pegjs は単一の文字に分割されるため、ほとんどの場合、次のようなアクションが必要です。また、複雑なネストされた配列を返すこともできます。したがって、私は通常、アクションを小さく保つために、文法の先頭にある pegjs 初期化子にヘルパー関数を記述します。関数名を慎重に選択すると、アクションは自己文書化されます。

例については、 Python スタイルのインデントの PEG を参照してください。免責事項:これは私の答えです。

于 2012-07-24T13:12:08.883 に答える
3

最初の質問ですが、ライブ プレビューは難しいと言わざるを得ません。入力が「進行中」であることをパーサーが理解できないというあなたが指摘した問題は正しいです。Peg.js はどの時点でエラーが発生したかを教えてくれるので、その情報を取得して数単語戻って再度解析するか、終了タグが欠落している場合は最後に追加してみてください。

質問の 2 番目の部分は簡単ですが、その後の文法は見栄えがよくありません。基本的には、すべてのルールにコールバックを配置することです。たとえば、

text
   = text:non_tag+ {
     // we captured the text in an array and can manipulate it now
     return text.join("");
   }

現時点では、これらのコールバックを文法にインラインで記述する必要があります。私は今、仕事でこのようなことをたくさんやっているので、それを修正するために peg.js にプルリクエストを出すかもしれません。しかし、いつこれを行う時間を見つけられるかわかりません。

于 2012-07-23T20:07:02.397 に答える
1

Try something like this replacement rule. You're on the right track; you just have to tell it to assemble the results.

text = result:non_tag+ { return result.join(''); }

于 2012-07-24T11:58:51.457 に答える