Простые вопросы парсерации с использованием PEG.js
Я пытаюсь обернуть голову вокруг PEG, введя простые грамматики на
Желаемый вывод: ["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. Я читал (), но, как вы можете видеть, мне нужна дополнительная помощь...
Входной сигнал:
'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
= !.
Ответы
Ответ 1
Я получил ответ в группе PEG.js Google, которая помогла мне на правильном пути. Я отправляю ответы на все три проблемы в надежде, что они могут служить рудиментарным учебником для других начинающих 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:
Проблема заключается в том, что выражение типа ('"' .* '"')
никогда не будет успешным. ПЭГ всегда жадный, поэтому .*
будет потреблять остальную часть входного потока и никогда не увидеть вторую цитату. Вот решение (которое, кстати, нуждается в обходном пути Peek, как в примере 2).
start
= Words+
Words
= QuotedString
/ Text
QuotedString
= '"' quote: NotQuote* '"' {return quote.join("")}
NotQuote
= !'"' char: . {return char}
Text
= text: NotQuote+ {return text.join("")}
Ответ 2
Для текущих версий 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 = '"'
Ответ 3
Пример 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' ]