Чтобы попытаться реализовать PEG в JavaScript, который не приводит к сбою старых браузеров из, я хотел бы сделать грамматику выражения синтаксического анализа, которая анализирует строку нерекурсивным образом. Как ты делаешь это? Он чувствует изгиб ума.
Скажем, у вас есть такая структура:
- A
grammar
имеет много выражений - An
expression
имеет многоmatchers
- A
matcher
имеет многоtokens
(или что-то другое для него лучше) - A
token
может либо указывать на другойexpression
, либо быть примитивной строкой или регулярным выражением. Поэтому, если это указывает на другое выражение, здесь начинается рекурсия.
Итак, скажите, что вы определяете иерархию следующим образом:
var grammar = new Grammar('math');
var expression = grammar.expression;
expression('math')
.match(':number', ':operator', ':number', function(left, operator, right){
switch (operator) {
case '+': return left + right;
case '-': return left - right;
case '*': return left * right;
case '/': return left / right;
}
});
expression('number')
.match(/\d+/, parseInt);
expression('operator')
.match('+')
.match('-')
.match('*')
.match('/');
var val = grammar.parse('6*8'); // 42
Когда вы вызываете grammar.parse
, он начинается с корневого выражения (которое имеет то же имя, что и "математика" ). Затем он выполняет итерацию через каждый соединитель, затем каждый токен, и если токен является выражением, он рекурсирует. В основном это (парсер будет отслеживать смещение/позицию строки, которая соответствует шаблонам, это просто псевдокод):
function parse(str, expression, position) {
var result = [];
expression.matchers.forEach(function(matcher){
matcher.tokens.forEach(function(token){
var val;
if (token.expression) {
val = parse(str, token.expression, position);
} else {
val = token.parse(str, position);
}
if (val) result.push(val);
});
});
return result;
}
parse('6*8', grammar.root, 0);
Итак, для простого выражения типа 6*8
очень мало рекурсии, но вы можете быстро перейти к сложному выражению со множеством уровней вложенности. Плюс умножьте вложенность на все те вложенные для циклов, и стек становится большим (я фактически не использую forEach
, я использую для циклов, но в цикле for он вызывает функцию большую часть времени, поэтому он заканчивается в то же время).
Вопрос в том, что как вы "сглаживаете это"? Вместо того, чтобы делать рекурсию, как вы это делаете, так это примерно так:
while (token = stack.pop()) {
val = token.parse(val);
if (val) result.push(val);
}
Я не ищу подробностей о том, как реализовать решение этой конкретной проблемы PEG, я больше всего ищу общий способ отслеживания рекурсивного состояния нерекурсивным способом.