21

我试图通过将简单的语法输入到PEG.js playground中来围绕 PEG 。

示例 1:

  • 输入:"abcdef1234567ghijklmn8901opqrs"
  • 期望的输出:["abcdef", "1234567", "ghijklmn", "8901", "opqrs"]

  • 实际输出:["abcdef", ["1234567", ["ghijklmn", ["8901", ["opqrs", ""]]]]]

这个例子非常有效,但我可以让 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 Group 的回复,帮助我走上了正轨。我正在发布所有三个问题的答案,希望它们可以作为像我这样的其他 PEG 初学者的基本教程。请注意,不需要递归。

示例 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 中相同的 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,您可以尝试:

示例一

输入:"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 回答
-2

示例 1

start
  = alnums

alnums
  = alnums:(alphas / numbers) {
    return alnums;
  }

alphas
  = alphas:$(alpha+)

numbers
  = numbers:$(number+)

number
  = [0-9]

alpha
  = [a-zA-Z]

示例 2

忽视

示例 3

> 'abcdefghijklmnop"qrstuvwxyz"abcdefg'.split('"')
[ 'abcdefghijklmnop',
  'qrstuvwxyz',
  'abcdefg' ]
于 2014-01-09T06:41:19.387 回答