Разбор вложенных структур (парсинг CSS)
Задача не совсем типичная, хочу написать небольшой анализатор кода для разбора вложенных структур, которые должны транслироваться в валидный CSS.
Однако не получается захватить идентификаторы родительских селекторов. Пример вложенной структуры:
#foo {
border: 1px;
a {
border: 2px;
}
b {
border: 3px;
c {
border: 4px; /* comment */
}
}
}
То, что я хочу получить в итоге:
#foo {
border: 1px;
}
#foo a {
border: 2px;
}
#foo b {
border: 3px;
}
#foo b c {
border: 4px; /* comment */
}
Пока что получилось добиться такого результата:
c {
border: 4px; /* comment */
}
b {
border: 3px;
}
a {
border: 2px;
}
#foo {
border: 1px;
}
Код, который выполняет разбор (на С++ правда, лень было переписывать на JS, но надеюсь будет понятно, т.к. там нужно только итератор на charAt() заменить ) :)
#include <iostream>
#include <iterator>
#include <string>
using namespace std;
int main() {
string str = "\
#foo {\
border: 1px;\
a {\
border: 2px;\
}\
b {\
border: 3px;\
c {\
border: 4px; /* comment */\
}\
}\
}";
string::const_iterator i = str.end(), begin = str.begin(), end;
while (i != begin) {
if (*i == ';' || (*i == '/' && *(i-1) == '*')) {
end = i++;
while (*i-- != '{');
while (true) {
if (*i == ';' || *i == '}' || *i == '{' || i == begin)
break;
i--;
}
string item(++i, ++end);
cout << item << "}" << endl;
}
i--;
}
return 0;
}
Возможно я не так начал разбор, но идея была такая: сперва получить идентификаторы, которых мне не хватает, а затем объеденить с тем что есть. Осталось собрать имена селекторов. Иными словами мне нужно получить массив с такими селекторами: ['#foo', '#foo a', '#foo b', '#foo b c'] Если есть какие-то идеи по поводу алгоритма с удовольствием выслушаю. |
мне кажется такие вещи надо писать как автомат, контролируя логику парсера матрицей переходов, а не хардкорить ее
|
В идеале да, так оно и будет (пример грамматики).
Сейчас же мне просто нужно понять сам алгоритм получения родительских селекторов для каждого блока, с какой стороны подобраться. |
Ну есть тупой вариант, - рекурсивный реплейс снизу.
Т.е. регой типа /([^{\s]+)\s*\{([^}]+)\}/g, рекурсивно пройтись, заменяя на идентификаторы, а потом обратно - идентификаторы на дополненные значения. А вот как умно и красиво - навскидку не скажешь.) |
Не регой такие дела не далают. Согласно правилам синтаксического анализа считывать нужно посимвольно запоминая уровни. Вот только как это грамотно сделать большой вопрос.
|
Ну нязнанаю, парсинг обычно регой и делают.)
Посимвольно это вообще заумь будет, а на счёт скорости не ясно. С другой стороны если не лень уйти в далёкие дали, то наверное можно. |
Цитата:
Может по тому что в С никогда не было регекстов, а в С++ они появились только в сентябре прошлого года (шутка) Цитата:
|
Максимально тупо. Сейчас набросаю.)
|
Сделано на коленке, не красиво, не оптимизировано, но суть отображает.
<div>
#foo {
border: 1px;
a {
border: 2px;
}
b {
border: 3px;
c {
border: 4px; /* comment */
}
}
}
</div>
<script>
d=document.getElementsByTagName('div')[0].innerHTML;
function css(str){
var result=[],
re = /([^{\s]+)\s*\{[^}{]+\}/g,
re2 = /(\d+)_array/g,
placer = function( all , selector){
var lower='';
all = all.replace( re2 ,function(x,y){
result[y] = selector + ' ' + result[y];
lower += x;
return ''
})
return result.push( all + '\n') - 1 + '_array' + lower;
}
while(~str.indexOf('{'))alert( str=str.replace( re , placer ) );
return str.replace( re2 , function(ar,ar){ return result[ar] } )
}
alert(css(d))
</script>
|
Хм... понял, подумаю как без регексов переписать)
|
| Часовой пояс GMT +3, время: 23:20. |