Большая часть примеров PEG.js (или Peggy) не поддерживают ничего, кроме базовой математической формулы. Я написал эту арифметическую логику, чтобы обеспечить полную поддержку математического анализа с основой для добавления пользовательских функций.
Конечно, в рабочей среде операторы console.log() не будут работать быть там, но он используется для определения того, сколько раз выполняются функции оценки.
Похоже, что с каждым уровнем в порядке операций количество выполнений кода удваивается. .
Мои вопросы есть... как можно избежать этой проблемы с производительностью?
Я новичок в PEG, и, возможно, существует расширенная функция, которая предотвратит такую сложную атаку.
Приведенный ниже код можно поместить в https://peggyjs.org/online.html
А вот несколько примеров функций, на которые стоит обратить внимание:
5*(3+3)
Эта 7-символьная функция создает более 500 операторов console.log
Возможно, это хороший первый признак того, что синтаксический анализ неоптимален. Я не понимаю, как можно использовать процесс из 500 шагов для анализа 7 символов.
Это достаточно грубо, что PEG.js нашел 500 задач, но настоящая проблема в том, что он выполняется. часть обратного вызова (часть {}, содержащая console.log) каждый раз. Каждый раз. Обратите внимание, что это тот же языковой элемент, ему передается «9», а в прошлый раз он вернул 9. Есть ли какой-нибудь трюк, чтобы PEG.js мог просто запомнить это? Текущий код тривиален, но более сложные функции выполняют значительный код, и разработчик не ожидает, что он будет выполнен 64 раза, если грамматика присутствует в формуле только один раз.
1.1 / ( 2 * ( (9 * 9) + (9 * 9) + (9 * 9) ) ) или
1.1 / (( 2 * ( 9 * 9 + 9 * 9 + 9 * 9 ) ) )
Любой из этих вариантов работает нормально (можно оставить консоль браузера закрытой, количество журналов дикое), но учтите, что они анализируют быстро.
1.1 / (( 2 * ( (9 * 9) + (9 * 9) + (9 * 9) ) ) )
Здесь появляется скала. Всего лишь еще один уровень сложности, чем два вышеперечисленных, но он полностью заблокирует браузер на достаточное время.
{
const operations = {
"√": (power, value) => Math.pow(value, 1 / power),
"^": (base, exponent) => base ** exponent,
"*": (multiplicand, multiplier) => multiplicand * multiplier,
"/": (dividend, divisor) => dividend / divisor,
"+": (addend1, addend2) => addend1 + addend2,
"-": (minuend, subtrahend) => minuend||0 - subtrahend,
"": (a, b) => a > b,
"=": (a, b) => a >= b,
"=": (a, b) => a == b,
"!=": (a, b) => a != b
};
}
// Define our end result should be something ReadyToReturn
Start = _ value:ReadyToReturn _ { return value; }
// * means zero or more so even empty strings count as whitespace
_ "whitespace" = [ \t\n\r]*
NumericValue
= scientific:(_ "-"? [0-9]+ ("." [0-9]+)? [eE] [+-]? [0-9]+) { console.log('scientific'); return text(); }
/ double:(_ $("-"? [0-9]+ "." [0-9]* / "-"? "." [0-9]+)) { console.log('double'); return Number(text()); }
/ integer:(_ "-"? [0-9]+) { console.log('integer'); return parseInt(text(), 10); }
QuoteText = "\"" chars:([^\\"] / "\\" .)* "\"" { return chars.join(""); }
PlainText = [a-zA-Z0-9 ]+ { return text().trim(); }
SimpleValue = NumericValue / QuoteText / PlainText
// After functions and columns are handled, we are left with simple numbers and ready to do math
ReadyToCalculate
= negate:[-]? result:Function {
if (!negate) {
return result;
}
return isNumber(result) ? 0 - result : "-" + result;
}
/ SimpleValue
// Functions need to be processed in the order they appear in the formula so finding arguments works correctly
Function = firstFunction:(
Parentheses
/ RoundDown
/ RoundUp
) {
return firstFunction;
}
Parentheses = "(" _ expr:ReadyToReturn _ ")" {
console.log('( )');
return expr;
}
RoundDown = "ROUNDDOWN(" _ value:ReadyToReturn places:(_ "," _ ReadyToReturn)? _ ")" {
console.log('ROUNDDOWN');
const digitNum = (places && places[2]) || 0;
const base = 10 ** digitNum
if (value < 0) {
return Math.ceil(value * base) / base;
}
return Math.floor(value * base) / base;
}
RoundUp = "ROUNDUP(" _ value:ReadyToReturn _ places:("," _ ReadyToReturn _)? ")" {
console.log('ROUNDUP');
const digitNum = (places && places[2]) || 0;
const base = 10 ** digitNum
if (value < 0) {
return Math.floor(value * base) / base;
}
return Math.ceil(value * base) / base;
}
////////////////////////////////////////////////////////
// This is the order of operations part.
// Each ReadyTo* definition expects the previous ReadyTo* which means that higher level has completed.
////////////////////////////////////////////////////////
// When handling roots, the two definitions are processed in order.
// If it can consume a number to the left, it will
// Then it will process √ characters without needing a number to the left
// Finally, if no root work needs to be done, a ReadyToCalculate can simploy pass through
ReadyToExponent
= head:ReadyToCalculate _ tail:(_ "√" _ ReadyToCalculate)+ {
console.log('_√_');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ negate:[-]? tail:(_ "√" _ ReadyToCalculate)+ {
console.log('√_');
if (negate) {
return 0 - tail.reduce((result, element) => operations[element[1]](result, element[3]), 2);
}
return tail.reduce((result, element) => operations[element[1]](result, element[3]), 2);
}
/ ReadyToCalculate
ReadyToMultiply
= head:ReadyToExponent tail:(_ "^" _ ReadyToExponent)+ {
console.log('Exponent');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToExponent
ReadyToAdd
= head:ReadyToMultiply tail:(_ ("*" / "/") _ ReadyToMultiply)+ {
console.log('Multiply');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToMultiply
ReadyToCompare
= head:ReadyToAdd tail:(_ ("+" / "-") _ ReadyToAdd)+ {
console.log('Add');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToAdd
ReadyToReturn
= head:ReadyToCompare tail:(_ ("" / "!=" / "=") _ ReadyToCompare)+ {
console.log('Compare');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToCompare
Подробнее здесь: https://stackoverflow.com/questions/793 ... -on-peg-js
Предотвратите чрезмерное замедление работы PEG.js ⇐ Javascript
Форум по Javascript
-
Anonymous
1736903065
Anonymous
Большая часть примеров PEG.js (или Peggy) не поддерживают ничего, кроме базовой математической формулы. Я написал эту арифметическую логику, чтобы обеспечить полную поддержку математического анализа с основой для добавления пользовательских функций.
Конечно, в рабочей среде операторы console.log() не будут работать быть там, но он используется для определения того, сколько раз выполняются функции оценки.
Похоже, что с каждым уровнем в порядке операций количество выполнений кода удваивается. .
Мои вопросы есть... как можно избежать этой проблемы с производительностью?
Я новичок в PEG, и, возможно, существует расширенная функция, которая предотвратит такую сложную атаку.
Приведенный ниже код можно поместить в https://peggyjs.org/online.html
А вот несколько примеров функций, на которые стоит обратить внимание:
5*(3+3)
Эта 7-символьная функция создает более 500 операторов console.log
Возможно, это хороший первый признак того, что синтаксический анализ неоптимален. Я не понимаю, как можно использовать процесс из 500 шагов для анализа 7 символов.
Это достаточно грубо, что PEG.js нашел 500 задач, но настоящая проблема в том, что он выполняется. часть обратного вызова (часть {}, содержащая console.log) каждый раз. Каждый раз. Обратите внимание, что это тот же языковой элемент, ему передается «9», а в прошлый раз он вернул 9. Есть ли какой-нибудь трюк, чтобы PEG.js мог просто запомнить это? Текущий код тривиален, но более сложные функции выполняют значительный код, и разработчик не ожидает, что он будет выполнен 64 раза, если грамматика присутствует в формуле только один раз.
1.1 / ( 2 * ( (9 * 9) + (9 * 9) + (9 * 9) ) ) или
1.1 / (( 2 * ( 9 * 9 + 9 * 9 + 9 * 9 ) ) )
Любой из этих вариантов работает нормально (можно оставить консоль браузера закрытой, количество журналов дикое), но учтите, что они анализируют быстро.
1.1 / (( 2 * ( (9 * 9) + (9 * 9) + (9 * 9) ) ) )
Здесь появляется скала. Всего лишь еще один уровень сложности, чем два вышеперечисленных, но он полностью заблокирует браузер на достаточное время.
{
const operations = {
"√": (power, value) => Math.pow(value, 1 / power),
"^": (base, exponent) => base ** exponent,
"*": (multiplicand, multiplier) => multiplicand * multiplier,
"/": (dividend, divisor) => dividend / divisor,
"+": (addend1, addend2) => addend1 + addend2,
"-": (minuend, subtrahend) => minuend||0 - subtrahend,
"": (a, b) => a > b,
"=": (a, b) => a >= b,
"=": (a, b) => a == b,
"!=": (a, b) => a != b
};
}
// Define our end result should be something ReadyToReturn
Start = _ value:ReadyToReturn _ { return value; }
// * means zero or more so even empty strings count as whitespace
_ "whitespace" = [ \t\n\r]*
NumericValue
= scientific:(_ "-"? [0-9]+ ("." [0-9]+)? [eE] [+-]? [0-9]+) { console.log('scientific'); return text(); }
/ double:(_ $("-"? [0-9]+ "." [0-9]* / "-"? "." [0-9]+)) { console.log('double'); return Number(text()); }
/ integer:(_ "-"? [0-9]+) { console.log('integer'); return parseInt(text(), 10); }
QuoteText = "\"" chars:([^\\"] / "\\" .)* "\"" { return chars.join(""); }
PlainText = [a-zA-Z0-9 ]+ { return text().trim(); }
SimpleValue = NumericValue / QuoteText / PlainText
// After functions and columns are handled, we are left with simple numbers and ready to do math
ReadyToCalculate
= negate:[-]? result:Function {
if (!negate) {
return result;
}
return isNumber(result) ? 0 - result : "-" + result;
}
/ SimpleValue
// Functions need to be processed in the order they appear in the formula so finding arguments works correctly
Function = firstFunction:(
Parentheses
/ RoundDown
/ RoundUp
) {
return firstFunction;
}
Parentheses = "(" _ expr:ReadyToReturn _ ")" {
console.log('( )');
return expr;
}
RoundDown = "ROUNDDOWN(" _ value:ReadyToReturn places:(_ "," _ ReadyToReturn)? _ ")" {
console.log('ROUNDDOWN');
const digitNum = (places && places[2]) || 0;
const base = 10 ** digitNum
if (value < 0) {
return Math.ceil(value * base) / base;
}
return Math.floor(value * base) / base;
}
RoundUp = "ROUNDUP(" _ value:ReadyToReturn _ places:("," _ ReadyToReturn _)? ")" {
console.log('ROUNDUP');
const digitNum = (places && places[2]) || 0;
const base = 10 ** digitNum
if (value < 0) {
return Math.floor(value * base) / base;
}
return Math.ceil(value * base) / base;
}
////////////////////////////////////////////////////////
// This is the order of operations part.
// Each ReadyTo* definition expects the previous ReadyTo* which means that higher level has completed.
////////////////////////////////////////////////////////
// When handling roots, the two definitions are processed in order.
// If it can consume a number to the left, it will
// Then it will process √ characters without needing a number to the left
// Finally, if no root work needs to be done, a ReadyToCalculate can simploy pass through
ReadyToExponent
= head:ReadyToCalculate _ tail:(_ "√" _ ReadyToCalculate)+ {
console.log('_√_');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ negate:[-]? tail:(_ "√" _ ReadyToCalculate)+ {
console.log('√_');
if (negate) {
return 0 - tail.reduce((result, element) => operations[element[1]](result, element[3]), 2);
}
return tail.reduce((result, element) => operations[element[1]](result, element[3]), 2);
}
/ ReadyToCalculate
ReadyToMultiply
= head:ReadyToExponent tail:(_ "^" _ ReadyToExponent)+ {
console.log('Exponent');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToExponent
ReadyToAdd
= head:ReadyToMultiply tail:(_ ("*" / "/") _ ReadyToMultiply)+ {
console.log('Multiply');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToMultiply
ReadyToCompare
= head:ReadyToAdd tail:(_ ("+" / "-") _ ReadyToAdd)+ {
console.log('Add');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToAdd
ReadyToReturn
= head:ReadyToCompare tail:(_ ("" / "!=" / "=") _ ReadyToCompare)+ {
console.log('Compare');
return tail.reduce((result, element) => operations[element[1]](result, element[3]), head);
}
/ ReadyToCompare
Подробнее здесь: [url]https://stackoverflow.com/questions/79356798/prevent-extreme-slowdown-on-peg-js[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия