URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 753
[ Назад ]

Исходное сообщение
"Парзеры"

Отправлено Dvorkin , 21-Май-02 09:56 
Hi, all!

Кто-нибудь из вас занимался написанием парзеров, например, для разбора HTML
тагов в документе? Я занимался. По-хорошему необходимо написать конечный
автомат, который в общем случае получается громоздким и нелинейным. Когда я
делал такой автомат, я потратил 3 дня головной боли, чтобы только построить
таблицу. А вы видели документы RFC, а именно спецификации протоколов? Там
везде используется для описания формата Бэкуса-Наура формулы. И это
правильно. Они интуитивно понятны и могут описывать любой строго заданный
синтаксис. Я тут подумал... И написал некоторую фишку, которая, возможно,
будет интересна обществености... Итак, представляю вам на суд универсальный
парзер (звучит почти как Вечный Двигатель :) ).

Описание: синтаксический анализатор
Назначение: разбор текстовых данных по набору правил, заданных в BNF
Область применения: обработчики протоколов, разбор текстовых данных,
проверка синтаксиса данных, языковые интерпретаторы. И другие.
Принцип работы: обход бинарного дерева правил BNF
Средства разработки: голый GNU C++ v 2.96.
Использованные технологии:
перегрузка операторов, сборка мусора, бинарное дерево, конечный автомат.
Оттестирован на платформах: Intel(Linux), SPARC(Solaris). Не содержит ничего
специфичного для конкретной реализации CPP, операционки или процессора,
поэтому должен компилироваться и под Виндами. Я не пробовал, но уверен. :)

Пример:
#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>
#include "Formula.h"

#ifdef CCXX_NAMESPACES
using namespace DVLIB;
#endif

void cr( void) {
DVLIB_TOKEN tok = { NULL, 0};
String strtr;
bool ret = false;
try {
------------------------- определяем синтаксис записи URL -------------
Formula OCTET;  OCTET.R( 1, 255);   --------- область с первого по 255
символ
Formula UPALPHA;  UPALPHA.R( 'A', 'Z');  -------- большие буквы
Formula LOALPHA;  LOALPHA.R( 'a', 'z');  --------- маленькие
Formula ALPHA = 1*( LOALPHA | UPALPHA);   -------- просто буквы
Formula DIGIT;  DIGIT.R( "0123456789");   --------- цифры
Formula spec;  spec.R( "~._-");   -------------- спецзнаки
Formula spec1;  spec1.R( "=+-_)(*&%$#@!~'<>?,./:\";[]{}|\\");  ------------
спецзнаки
Formula sheme = ( 1*ALPHA).B(1,10);  ------------ от одной до 10 букв
sheme.setName( "sheme");  ------------- присвоили имя - чтобы сохранялся
контент
Formula fseg = 1*( ALPHA | DIGIT);  ----------- одна буква или цифра
Formula seg = ++( ALPHA | DIGIT);  ------------- от нуля до много букв или
цифр
Formula fsegc = 1*( ALPHA | DIGIT | spec);
Formula segc = ++( ALPHA | DIGIT | spec);
Formula port = (1*DIGIT).B(1,5);  --------- от одного до 5 цифровых
символов
port.setName( "port");
Formula pp = ":" + port;  ------------ после двоеточия следует правило port
Formula rel_path = fsegc + ( ++( "/" + segc));
Formula path = "/" + ( !rel_path);  ------------ после слеша необязательное
правило rel_path
Formula host = fseg + ( 1*( "." + fseg));
host.setName( "host");
Formula abs_URI = !( sheme + "://") + host + ( !pp) + ( !path);
Formula rel_URI = path;
Formula fragment = 1*( ALPHA | DIGIT);
fragment.setName( "fragments");
Formula params = 1*( ALPHA | DIGIT | spec1);
params.setName( "parameters");
Formula url = ( abs_URI | rel_URI) + ( !( "#" + fragment)) + ( !( "?" +
params));
url.setName( "URL");
------------------------- синтаксис записи УРЛа определен, можно
оперировать... :)
ret = url.Parse( "xxx.9090023./ru");
---------- ret = false ==> не подчиняется правилам
ret = url.Parse( "http://xxx.ru:80/index.html");
--------- ret = true - можно работать. :)
String port = url.getElement( "port");
----------- port = "80", заменим его на 8080:
ret = url.setElement( "port", "8080");
----------- ret = true ==> поле изменилось ==> можно собрать новый URL
ret = url.Construct();
----------- ret = true == > формат правилен, новый УРЛ собран
String tmp = url.getElement( "URL");
---------- tmp = "http://xxx.ru:8080/index.html"

---------- и так далее.....

} catch ( Exception &_e) {  _e.print( stdout);  }
}

int main( int argc, char *argv[]) {

printf( "Compiled %s %s to %s\n", __DATE__, __TIME__, argv[ 0]);
cr();
return( 0);
}

Этот код сейчас (без серьезной оптимизации) выполняется на моем 266
Celeron'е под Линуксом 10.000 раз в секунду. Используемая память - 1.2 Kb,
что эквивалентно конечному автомату с матрицей размером 10x10.

Есть, конечно, и недостатки, над которыми я сейчас работаю...
1) если в процессе парзинга обнаружилось, что входной текст не соответствует
формату, парзер тихо завершается со словами "не по правилам!" и нельзя
узнать, что именно "не по правилам". Этот недостаток устраним. :)
2) он на порядок медленнее конечного автомата, что, конечно, не есть хорошо.
КА - самый быстрый способ парзинга и я занимаюсь оптимизацией кода Формулы.
А вот здесь... Сложно приблизить к скорости КА, но можно. Если извернуться
маленько.

Если кого-то заинтересовала эта штучка - пишите.
Я думаю ее приковырять к какому-нибуть стоящему проекту. Есть идеи?
А можно ли найти ей коммерческое применение?
Какие мнения?
---------------------------------------------------------------------
WBR, Dvorkin

Можно, конечно, использовать lexx или bison... Но там все не так просто и
могут быть неоднозначности. :)


Содержание

Сообщения в этом обсуждении
"RE: Парзеры"
Отправлено pth , 23-Май-02 01:25 
Flex&Bison уже освоены,а новую фигню ещё надо разбирать.
Да и на с виду - синтаксис ещё ужаснее.