Простой разбор вопросов с использованием PEG.js

Я пытаюсь понять PEG, вводя простые грамматики на площадку PEG.js.

Пример 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
  = 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
  = !.

person Nick Evans    schedule 31.08.2010    source источник
comment
К вашему сведению: этот вопрос относится к очень старой версии pegjs. Более новые версии имеют совершенно другой синтаксис, поддерживающий множество полезных функций, таких как text(), который ссылается на полную текстовую строку, которая была сопоставлена, например: digits = [0-9]+ { return text() }   -  person Rob Raisch    schedule 26.01.2017


Ответы (3)


Я получил ответ в группе Google PEG.js, который помог мне встать на правильный путь. Я публикую ответы на все три задачи в надежде, что они могут послужить элементарным руководством для других новичков в PEG, таких как я. Обратите внимание, что рекурсия не требуется.

Пример 1:

Это просто, если вы понимаете основные идиомы PEG.

start
  = Text+

Text
  = Numbers
  / Characters

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

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

Пример 2:

Проблема здесь заключается в специфическом выборе дизайна генератора синтаксического анализатора PEG.js для выражений Peek (&expr и !expr). Оба заглядывают во входной поток, не потребляя никаких символов, поэтому я ошибочно предположил, что они ничего не возвращают. Однако оба они возвращают пустую строку. Я надеюсь, что автор 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:

Проблема в том, что такое выражение, как ('"' .* '"'), никогда не может быть успешным. PEG всегда жадный, поэтому .* потребляет оставшуюся часть входного потока и никогда не увидит вторую кавычку. Вот решение (которое, кстати, требует того же обходного пути Peek, что и в примере 2).

start
  = Words+

Words
  = QuotedString
  / Text

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

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

Text
  = text: NotQuote+ {return text.join("")}
person Nick Evans    schedule 02.09.2010
comment
Опечатка в тексте Примера 3: следует читать... и никогда не видеть вторую цитату. Похоже, вы не можете редактировать ответы на свои вопросы в Stackoverflow (если вы не вошли в систему, я полагаю). - person Nick Evans; 02.09.2010
comment
@NickEvans, вам просто нужно немного больше представителей, чтобы иметь возможность редактировать вещи. в любом случае я исправил это для вас. - person chakrit; 19.04.2013
comment
@NickEvans Пожалуйста, не могли бы вы принять свой ответ? Это поможет людям быстрее найти этот ответ в поисковых системах. - person Toothbrush; 17.05.2015

Для текущих версий 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 = '"'
person Rob Raisch    schedule 26.01.2017
comment
равны ли цифры = $[0-9]+ { return text() } $[0-9]+? $ равно text()? - person crapthings; 18.04.2021

Пример 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' ]
person alsotang    schedule 09.01.2014