Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Разбор вложенных структур (парсинг CSS) (https://javascript.ru/forum/misc/25226-razbor-vlozhennykh-struktur-parsing-css.html)

monolithed 29.01.2012 15:31

Разбор вложенных структур (парсинг 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']


Если есть какие-то идеи по поводу алгоритма с удовольствием выслушаю.

Gvozd 29.01.2012 15:55

мне кажется такие вещи надо писать как автомат, контролируя логику парсера матрицей переходов, а не хардкорить ее

monolithed 29.01.2012 16:03

В идеале да, так оно и будет (пример грамматики).
Сейчас же мне просто нужно понять сам алгоритм получения родительских селекторов для каждого блока, с какой стороны подобраться.

Aetae 29.01.2012 16:36

Ну есть тупой вариант, - рекурсивный реплейс снизу.
Т.е. регой типа /([^{\s]+)\s*\{([^}]+)\}/g, рекурсивно пройтись, заменяя на идентификаторы, а потом обратно - идентификаторы на дополненные значения.

А вот как умно и красиво - навскидку не скажешь.)

monolithed 29.01.2012 16:48

Не регой такие дела не далают. Согласно правилам синтаксического анализа считывать нужно посимвольно запоминая уровни. Вот только как это грамотно сделать большой вопрос.

Aetae 29.01.2012 16:52

Ну нязнанаю, парсинг обычно регой и делают.)
Посимвольно это вообще заумь будет, а на счёт скорости не ясно.
С другой стороны если не лень уйти в далёкие дали, то наверное можно.

monolithed 29.01.2012 16:56

Цитата:

Сообщение от Aetae
Ну нязнанаю, парсинг обычно регой и делают.

Разбор email адресов и пр. да, но в формальной грамматике все посимвольно.
Может по тому что в С никогда не было регекстов, а в С++ они появились только в сентябре прошлого года (шутка)

Цитата:

Сообщение от Aetae
рекурсивно пройтись, заменяя на идентификаторы, а потом обратно - идентификаторы на дополненные значения.

Не совсем понял что ты имел ввиду, как сами идентификаторы получить, чтобы учесть дочерние уровни?

Aetae 29.01.2012 17:21

Максимально тупо. Сейчас набросаю.)

Aetae 29.01.2012 17:38

Сделано на коленке, не красиво, не оптимизировано, но суть отображает.
<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>

monolithed 29.01.2012 17:56

Хм... понял, подумаю как без регексов переписать)


Часовой пояс GMT +3, время: 15:48.