21

単純な文法をPEG.js プレイグラウンドに入力することで、PEG に頭を悩ませようとしています。

例 1:

  • 入力:"abcdef1234567ghijklmn8901opqrs"
  • 望ましい出力:["abcdef", "1234567", "ghijklmn", "8901", "opqrs"]

  • 実際の出力:["abcdef", ["1234567", ["ghijklmn", ["8901", ["opqrs", ""]]]]]

この例はほとんど機能しますが、結果の配列を 100 万レベルにネストしないように PEG.js を取得できますか? concat()どこかの代わりに使うのがコツjoin()だと思いますが、場所がわかりません。

start
  = Text

Text
  = Numbers Text
  / Characters Text
  / EOF

Numbers
  = numbers: [0-9]+ {return numbers.join("")}

Characters
  = text: [a-z]+ {return text.join("")}

EOF
  = !.

例 2:

例 1 と同じ問題とコードですが、Characters ルールを次のように変更すると、同じ結果が得られると予想されます。

Characters
  = text: (!Numbers .)+ {return text.join("")}

結果の出力は次のとおりです。

[",a,b,c,d,e,f", ["1234567", [",g,h,i,j,k,l,m,n", ["8901", [",o,p,q,r,s", ""]]]]]

これらの空の一致がすべて取得されるのはなぜですか?

例 3:

最後の質問。これはまったく機能しません。どうすればそれを機能させることができますか?ボーナス ポイントとして、効率性に関するヒントはありますか? たとえば、可能であれば再帰を避けるべきですか?

優れた PEG チュートリアルへのリンクもいただければ幸いです。( http://www.codeproject.com/KB/recipes/grammar_support_1.aspx )を読みましたが、ご覧のとおり、さらに助けが必要です ...

  • 入力: 'abcdefghijklmnop"qrstuvwxyz"abcdefg'
  • 望ましい出力:["abcdefghijklmnop", "qrstuvwxyz", "abcdefg"]
  • 実際の出力:"abcdefghijklmnop\"qrstuvwxyz\"abcdefg"
start
  = Words

Words
  = Quote
  / Text
  / EOF

Quote
  = quote: ('"' .* '"') Words {return quote.join("")}

Text
  = text: (!Quote . Words) {return text.join("")}

EOF
  = !.
4

3 に答える 3

21

PEG.js Google グループで返信を受け取り、正しい軌道に乗ることができました。私のような他の PEG 初心者のための初歩的なチュートリアルとして役立つことを期待して、3 つの問題すべてに対する回答を投稿しています。再帰は必要ないことに注意してください。

例 1:

基本的な PEG イディオムを理解すれば、これは簡単です。

start
  = Text+

Text
  = Numbers
  / Characters

Numbers
  = numbers: [0-9]+ {return numbers.join("")}

Characters
  = text: [a-z]+ {return text.join("")}

例 2:

ここでの問題は、Peek 式 (&expr および !expr) 用の PEG.js パーサー ジェネレーターの特異な設計上の選択です。どちらも文字を消費せずに入力ストリームを先読みしているため、何も返さないと誤って想定していました。ただし、どちらも空の文字列を返します。PEG.js の作成者がこの動作を変更することを願っています。なぜなら (私が知る限り)、これは出力ストリームを汚染する不必要な粗悪品だからです。私がこれについて間違っている場合は、私を修正してください!

とにかく、ここに回避策があります:

start
  = Text+

Text
  = Numbers
  / Words

Numbers
  = numbers: [0-9]+ {return numbers.join("")}

Words
  = text: Letter+ {return text.join("")}

Letter
  = !Numbers text: . {return text}

例 3:

問題は、式 like('"' .* '"')が決して成功しないことです。PEG は常に貪欲であるため.*、残りの入力ストリームを消費し、2 番目の引用符を表示することはありません。これが解決策です (例 2 と同じ Peek 回避策が必要です)。

start
  = Words+

Words
  = QuotedString
  / Text

QuotedString
  = '"' quote: NotQuote* '"' {return quote.join("")}

NotQuote
  = !'"' char: . {return char}

Text
  = text: NotQuote+ {return text.join("")}
于 2010-09-02T11:47:36.570 に答える
1

の現在のバージョンではpegjs、次のことを試してみてください。

例 1

入力:"abcdef1234567ghijklmn8901opqrs"

望ましい出力:["abcdef", "1234567", "ghijklmn", "8901", "opqrs"]

{
  /**
   * Deeply flatten an array.
   * @param  {Array} arr - array to flatten
   * @return {Array} - flattened array
   */
  const flatten = (arr) =>  Array.isArray(arr) ? arr.reduce((flat, elt) => flat.concat(Array.isArray(elt) ? flatten(elt) : elt), []) : arr
}

start = result:string {
  console.log(JSON.stringify(result))
  return result
}

string = head:chars tail:( digits chars? )* {
  return flatten([head,tail])
}

chars = [a-z]+ {
  return text()
}

digits = $[0-9]+ {
  return text()
}

例 2

上記の答えから簡単に推測できるはずです。

例 3

入力:'abcdefghijklmnop"qrstuvwxyz"abcdefg'

望ましい出力:["abcdefghijklmnop", "qrstuvwxyz", "abcdefg"]

{
  /**
   * Deeply flatten an array.
   * @param  {Array} arr - array to flatten
   * @return {Array} - flattened array
   */
  const flatten = (arr) =>  Array.isArray(arr) ? arr.reduce((flat, elt) => flat.concat(Array.isArray(elt) ? flatten(elt) : elt), []) : arr
}

start = result:string {
  console.log(JSON.stringify(result))
  return result
}

string = head:chars tail:quote_chars* {
  return flatten([head,tail])
}

quote_chars = DQUOTE chars:chars {
  return chars
}

chars = [a-z]+ {
  return text()
}

DQUOTE = '"'
于 2017-01-26T21:28:12.947 に答える