Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Автоматические сгенерированные парсеры весьма объёмны.
char — парсит одну букву в строке. это универсально. но когда парсинг каждой буквы приводит к созданию объектов и вызову пачки методов, я сомневаюсь, что такая архитектура позволит достичь производительности сколь-нибудь выше плинтуса. именно поэтому «академические» реализации встречаются только в учебниках, а на практике грамматика компиляется в хитросплетенную, развесистую и только машине понятную «лапшу». это не прихоть. x4 в объеме кода приятнее, нежели x4 по потребляемой памяти или процессору.local digit = peg.R"09"
--- калькулятор
local function accum(u,op,v)
if op=="+" then return u+v
elseif op=="-" then return u-v
elseif op=="*" then return u*v
elseif op=="/" then return u/v
end
end
local space = peg.S" \n\t":star()
local digit = peg.R"09"
local sign = peg.S"+-"
local unsigned = digit:plus()
local number = (sign^{0,1}*unsigned*("."*unsigned)^{0,1}):C()/tonumber*space
local Open = "(" * space
local Close = ")" * space
local mulop = peg.S"/*":C() * space
local sumop = peg.S"-+":C() * space
-- необходимо сделать Fake(), т.к. тут рекурсия value -> sum -> mul -> value
-- реальная формула для value доопределяется тремя строками ниже
local value = peg.Fake()
local mul = (value * (mulop*value):Cg():star()):Cf(accum)
local sum = (mul * (sumop*mul):Cg():star()):Cf(accum)
value:define(number + Open*sum*Close)
local expression = sum * peg.eof
--print(expression"3 + 5*9 / (1+1) - 12 + 0.012")
print(expression(io.read()))
То что у вас получилось, кстати, похоже на красивый синтаксический сахар для метода рекурсивного спуска.
Регулярки здесь не помогутНу, это смотря какие регулярки! Не знаю что умеет RegExp на Javascript, а на Perl это делается например так:
use Data::Dumper;
"(1, 2, (4, 5, (6), 7, (8), 9), 0)" =~
/(?{@s=();$r=[]})(\((?{local @s=(@s,$r);local $r=[];push @{$s[-1]},$r})(?:(\d), (?{push $r,$^N})|(?1), )*(\d)(?{push $r,$^N;$r=pop @s})\))/ms;
print Dumper @{$r};
результат:
$VAR1 = [
'1',
'2',
[
'4',
'5',
[
'6'
],
'7',
[
'8'
],
'9'
],
'0'
];
При желании это регулярное выражение можно записать более читабельно:"(1, 2, (4, 5, (6), 7, (8), 9), 0)" =~ /
(?{ @s=(); $r=[]; })
(
\( (?{
local @s = (@s, $r);
local $r = [];
push $s[-1], $r;
})
(?:
(\d),\s (?{ push $r, $^N })
| (?1),\s
)*
(\d) (?{ push $r, $^N })
\) (?{ $r = pop @s })
)
/xms;
И эта проблема нерешаема в RegExp. В таких случаях нужен парсер.
Если бы в JS были конструкторы-по-умолчанию (как в C++), то запись txt(«abc») можно было бы сократить до «abc» в контексте конструирования более сложного паттерна.
String.prototype.parse = function (str, pos) {
if (str.substr(pos, this.length) == this)
return { res: this, end: pos + this.length };
};
RegExp.prototype.parse = function(str, pos) {
var m = this.exec(str.substr(pos));
if (m && m.index === pos)
return { res: m[0], end: pos + m[0].length };
};
assert.deepEqual("abc".parse("abc", 0), { res: "abc", end: 3 });
assert.deepEqual(/\d+/.parse("123", 0), { res: "123", end: 3 });
var createParse = function(fn){
return function(str, pos){
if (arguments.length == 1)
pos = 0;
var res = fn.call(this, str, pos);
if (res && arguments.length == 1)
res = res.res.length == res.end ? res.res : undefined;
return res;
};
};
String.prototype.parse = createParse(function (str, pos) {
if (str.substr(pos, this.length) == this)
return { res: this, end: pos + this.length };
});
regexp.exec(str.slice(pos))
var char = rgx(/[^"&]/i);
rep(char).then(r => r.join(''))
rgx(/^[^"&]*/i)
В этом случае можно парсить в 2 прохода: на первом генерировать массив токенов, которыми будут текст и теги (открывающий, закрывающий, самозакрывающийся), а на втором строить дерево.<book attr1="..." attr2="..." очень много атрибутов ???
То в том месте где стоит ??? может оказаться как /> так и просто >. Теперь если LL парсер пытался всё это распарсить как <book ...> а на месте ??? оказался />, то парсер отбросит всё то что он так долго парсил и начнёт заново в предположении, что это <book .../>
Как писать парсеры на JavaScript