Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 07:41
Оценка:
Добрый день, помогите советом кто может.

В целом, проблема такая — есть приложение которое хостит MS HTML (ну или ActiveScripting, на данный момент это неважно). Логика в нём реализована на JScript. Хотелось бы иметь возможность добавлять туда дебажные выражения, типа логов или ассёртов, при чём чтобы была возможность в релизе их полностью отключать. ИМХО самым ровным вариантом для этого было бы парсенье файлов перед исполнением (препроцессинг) с выкидыванием "лишнего". Хотелось бы для этого подключить JavaScript парсер, и естественно писАть его ручками нет ни желания, ни времени. Мне не нужна виртуальная машина, только генератор AST (с возможностью по AST вновь сформировать код, мне ж его надо будет ActiveScripting отдать).

Вопрос знатокам — посоветуйте плиз парсер, удовлетворяющий условиям:

1. Фришный для коммерческого использования
2. Без лишних зависимостей в рантайме
3. Написанный на C или С++
4. По возможности безбажный и обкатанный на других проектах
5. Желательно — с описанием грамматики в простом виде, допускающем модификацию

Заранее спасибо.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re: Посоветуйте хороший парсер для JavaScript
От: Сергей  
Дата: 06.05.08 08:03
Оценка:
Здравствуйте, Left2, Вы писали:

L>Заранее спасибо.


А зачем для всего этого нужен парсер яваскрипта?
Re[2]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 08:13
Оценка:
С>А зачем для всего этого нужен парсер яваскрипта?

Хочу вырезать, к примеру, вызов функции Log. Отсюда хочу вырезать:
Log("Debug info bla-bla-bla");


а отсюда — нет:
"Пример использования:\
Log("Debug info bla-bla-bla");\
"


а ещё хочу отсюда вырезать:
[1, 2, 3, 4].foreach(function(x) { y += x; Log(x); });


Конечно если решения не найду то на последние два случая забью (потребую чтобы Log начинался с начала строки), но хочется всё делать честно...
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[3]: Посоветуйте хороший парсер для JavaScript
От: vitalyk  
Дата: 06.05.08 09:01
Оценка: 8 (1)
Здравствуйте, Left2, Вы писали:

С>>А зачем для всего этого нужен парсер яваскрипта?


L>Хочу вырезать, к примеру, вызов функции Log. Отсюда хочу вырезать:

L>
L>Log("Debug info bla-bla-bla");
L>


L>а отсюда — нет:

L>
L>"Пример использования:\
L>Log("Debug info bla-bla-bla");\
L>"
L>


L>а ещё хочу отсюда вырезать:

L>
L>[1, 2, 3, 4].foreach(function(x) { y += x; Log(x); });
L>


L>Конечно если решения не найду то на последние два случая забью (потребую чтобы Log начинался с начала строки), но хочется всё делать честно...


На правах идеи (т.е. сам не использовал, немного посмотрел только что — может сработать). Можно сделать небольшой тул, который будет использовать уже имеющийся у Вас парсер (а именно jscript.dll). Создаем (или используем имеющийся) экземпляр jscript'ового движка, запрашиваем у него IActiveScriptDebug, и вызываем метод GetScriptTextAttributes последнего, куда передаем имеющийся исходник, а на выходе имеем массив "аттрибутов символов". На скорую руку сбацанный исходник выглядит так:
 // Предполагается, что spScript - это IActiveScriptPtr или IActiveScriptParsePtr
CComQIPtr<IActiveScriptDebug> spScriptDebug( spScript );
ATLASSERT( spScriptDebug );
ULONG nStrLen = static_cast<ULONG>( ::_tcslen( pstrCode ) ); // pstrCode - строка с исходником 
CAutoVectorPtr<SOURCE_TEXT_ATTR> arrSourceAttr( new SOURCE_TEXT_ATTR[nStrLen] );
if( SUCCEEDED( spScriptDebug->GetScriptTextAttributes( 
    pstrCode, 
    nStrLen,
    pstrDelimiter,   // например, "</SCRIPT>"
    0x8101 /*GETATTRTYPE_DEPSCAN | GETATTRFLAG_THIS | GETATTRFLAG_HUMANTEXT*/,
    arrSourceAttr ) )
{
    // а теперь работаем, как описано ниже
}


Далее банально ищем в исходнике строку "Log" и смотрим на аттрибуты соответствующих символов — как показали простейшие опыты, в случае, если Log является вызовом функции / объектом, его символы будут отмечены флагом 0x01, если же это комментарий / строка — то 0x80 / 0x02 (тут надо бы посмотреть внимательнее).

Опять же, будет ли оно работать на самом деле — . Есть подозрение, что, покопав глубже в эту сторону, можно получить требуемый Вам функционал.
... << RSDN@Home 1.2.0 alpha 4 rev. 1052>>
Re[4]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 09:09
Оценка:
Идея интересная, большое спасибо.
Главный недостаток — не получится работать из MS HTML — из него вообще нельзя достучаться к ActiveScripting-овым интерфейсам
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[3]: Посоветуйте хороший парсер для JavaScript
От: Сергей  
Дата: 06.05.08 09:13
Оценка: 8 (1)
Здравствуйте, Left2, Вы писали:

L>Конечно если решения не найду то на последние два случая забью (потребую чтобы Log начинался с начала строки), но хочется всё делать честно...


Предлагаю рассмотреть препроцессор С.
Пишем вот такой код:
#ifndef NDEBUG
#define Log(x) alert(x)
#else
#define Log(x)
#endif

<html>
<script language="javascript">

// Пример использования:

function f() {
    Log("Debug info bla-bla-bla");
    var s = 'Пример использования:\
    Log("Debug info bla-bla-bla");\
    '
    [1, 2, 3, 4].foreach(function(x) { y += x; Log(x); });
}

</script>
<body>
<a href="#" onclick="f()">LINK</a>
</body>
</html>


Потом для дебаг-варианта поступаем вот так:
Сергей@localhost [test]> cpp -P test.htmlx
<html>
<script language="javascript">
function f() {
    alert("Debug info bla-bla-bla");
    var s = 'Пример использования:    Log("Debug info bla-bla-bla");    '
    [1, 2, 3, 4].foreach(function(x) { y += x; alert(x); });
}
</script>
<body>
<a href="#" onclick="f()">LINK</a>
</body>
</html>


Для релиза вот так:
Сергей@localhost [test]> cpp -P -DNDEBUG test.htmlx
<html>
<script language="javascript">
function f() {
    ;
    var s = 'Пример использования:    Log("Debug info bla-bla-bla");    '
    [1, 2, 3, 4].foreach(function(x) { y += x; ; });
}
</script>
<body>
<a href="#" onclick="f()">LINK</a>
</body>
</html>


Ну, и вместо CPP можно взять GPP.
Re[4]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 09:24
Оценка:
С>Ну, и вместо CPP можно взять GPP.

GPP идёт под GNU General Public Licence. Использовать его без изменений как бинарный файл (чтобы не погрешить против лицензии) — криво.

Но сама идея порыть не в сторону парсера JS, а в сторону препроцессоров интресна, спасибо.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[5]: Посоветуйте хороший парсер для JavaScript
От: Сергей  
Дата: 06.05.08 09:36
Оценка:
Здравствуйте, Left2, Вы писали:

L>GPP идёт под GNU General Public Licence. Использовать его без изменений как бинарный файл (чтобы не погрешить против лицензии) — криво.


Не понимаю проблемы, честно говоря. Или ты чего-то с ним линковать собрался?
Ну, и если будешь вносить в него модификации — неужто они настолько ценны, что тебе будет жалко отдать код со своими изменениями?
Тем более, код ты обязан выдавать только тому, кому ты отдал саму модифицированную программу.

L>Но сама идея порыть не в сторону парсера JS, а в сторону препроцессоров интресна, спасибо.
Re[5]: Посоветуйте хороший парсер для JavaScript
От: srggal Украина  
Дата: 06.05.08 10:02
Оценка:
Здравствуйте, Left2, Вы писали:

С>>Ну, и вместо CPP можно взять GPP.


L>GPP идёт под GNU General Public Licence. Использовать его без изменений как бинарный файл (чтобы не погрешить против лицензии) — криво.


L>Но сама идея порыть не в сторону парсера JS, а в сторону препроцессоров интресна, спасибо.


PERL а именно PCRE — не спасут отца русской демократии?
Re[6]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 10:25
Оценка:
S>PERL а именно PCRE — не спасут отца русской демократии?
PCRE == Perl Compatible Regular Expressions?
А чем они мне помогут?
JavaScript конечно не С++, но даже его парсить регэкспами это черезчур...
Или я неправильно понял идею?
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[6]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 10:25
Оценка:
С>Не понимаю проблемы, честно говоря. Или ты чего-то с ним линковать собрался?
Конечно линковать. У меня standalone EXE файл, который во время работы грузит десятки JS-файлов. Если на каждый файл запускать какой-то процесс, то подтормаживание будет заметно. Я может не силён в ньюансах GPL — но разве я могу взять сорцы, модифицировать их и прилинковать к программе которую я распространяю на коммерческой основе и без исходных кодов?
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[7]: Посоветуйте хороший парсер для JavaScript
От: srggal Украина  
Дата: 06.05.08 11:46
Оценка:
Здравствуйте, Left2, Вы писали:

S>>PERL а именно PCRE — не спасут отца русской демократии?

L>PCRE == Perl Compatible Regular Expressions?
L>А чем они мне помогут?
L>JavaScript конечно не С++, но даже его парсить регэкспами это черезчур...
L>Или я неправильно понял идею?
Скорей извратил...

Парсить то не нужно, нужно убрать текст определенного формата, чем не работа для регэкспа (не обязательно PCRE)?

Ниже прочитал про рантайм — ИМХО регекспы подойдут
Re[7]: Посоветуйте хороший парсер для JavaScript
От: Сергей  
Дата: 06.05.08 12:02
Оценка:
Здравствуйте, Left2, Вы писали:

С>>Не понимаю проблемы, честно говоря. Или ты чего-то с ним линковать собрался?

L>Конечно линковать. У меня standalone EXE файл, который во время работы грузит десятки JS-файлов. Если на каждый файл запускать какой-то процесс, то подтормаживание будет заметно. Я может не силён в ньюансах GPL — но разве я могу взять сорцы, модифицировать их и прилинковать к программе которую я распространяю на коммерческой основе и без исходных кодов?

Нет, GPL, конечно, такое не позволяет.
А зачем в результирующий продукт включать препроцессор?
Я предлагаю фактически добавить в Makefile (не знаю, используешь ли ты именно make, но не в этом суть) этап сборки JS-файлов.
Т.е. ты пишешь JS-файлы, которые будут обрабатываться препроцессором. Потом собираешь проект — именно на этом этапе отрабатывает препроцессор и на выходе получаются JS-файлы, которые будут идти внутри конечного продукта. В отладочную сборку попадут JS-файлы с отладочным выводом, в релизе — этого не будет. Препроцессор JS-файлов в составе конечного продукта очевидно будет не нужен.
Такой вариант устраивает или преобразование JS-файлов необходимо именно в конечном продукте? Если так, то таки да — нужно искать другой препроцессор.
Re[5]: Посоветуйте хороший парсер для JavaScript
От: lazyden  
Дата: 06.05.08 13:54
Оценка: 8 (1) +1
Здравствуйте, Left2, Вы писали:

L>GPP идёт под GNU General Public Licence. Использовать его без изменений как бинарный файл (чтобы не погрешить против лицензии) — криво.


Есть препроцессоры под лицензией BSD:
* отдельный препроцессор mcpp -- a portable C preprocessor,
* препроцессор из пакета nwcc.
Re: Посоветуйте хороший парсер для JavaScript
От: IIY Украина  
Дата: 06.05.08 13:59
Оценка:
Здравствуйте, Left2, Вы писали:

L>Добрый день, помогите советом кто может.


L>В целом, проблема такая — есть приложение которое хостит MS HTML (ну или ActiveScripting, на данный момент это неважно). Логика в нём реализована на JScript. Хотелось бы иметь возможность добавлять туда дебажные выражения, типа логов или ассёртов, при чём чтобы была возможность в релизе их полностью отключать. ИМХО самым ровным вариантом для этого было бы парсенье файлов перед исполнением (препроцессинг) с выкидыванием "лишнего". Хотелось бы для этого подключить JavaScript парсер, и естественно писАть его ручками нет ни желания, ни времени. Мне не нужна виртуальная машина, только генератор AST (с возможностью по AST вновь сформировать код, мне ж его надо будет ActiveScripting отдать).


Conditional Compilation
@set Statement

Не проверял.
Re[8]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 14:30
Оценка:
С>Такой вариант устраивает или преобразование JS-файлов необходимо именно в конечном продукте? Если так, то таки да — нужно искать другой препроцессор.

Хотелось бы иметь галочку "включить отладочный режим" не заморачиваясь на дебаг-релиз версии. То есть, ИМХО оптимальный вариант — это одна версия со встроенным (отключаемым) препроцессором JS-файлов, который выкидывает лишнее во время загрузки скриптов. Самый близкий аналог — это ассерты в джаве, которые попадают в откомпилированный код, но выкидываются (если галочка выключена) JIT-компилятором.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[2]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 14:30
Оценка:
IIY>Conditional Compilation
IIY>@set Statement

IIY>Не проверял.


Cпасибо, про это знаю. Но не нравится подход — слишком многословно.

/*@cc_on @*/
/*@if (@_log)
  Log("Something");
/*@end @*/


вместо этого хочется просто

  Log("Something");


причём если вместо "Something" функция которая производит какие-то тяжёлые вычисления — хочется чтобы её выкидывали на этапе препроцессинга и она не вызывалась вообще.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[8]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 14:30
Оценка:
S>Парсить то не нужно, нужно убрать текст определенного формата, чем не работа для регэкспа (не обязательно PCRE)?
S>Ниже прочитал про рантайм — ИМХО регекспы подойдут

Регэксп прийдётся делать с многострочным синтаксисом и довольно большой грамматикой дабы правильно обработать JS-овские комментарии и строки. ИМХО, тупиковый путь (вряд ли будет шустро работать + синтаксис регэкспов это сильно на любителя) — наверное проще будет иметь неполную грамматику (типа такой которой оперируют препроцессоры), которая будет отличать строки и комментарии, а всё остальное для неё будет просто набором токенов.
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[6]: Посоветуйте хороший парсер для JavaScript
От: Left2 Украина  
Дата: 06.05.08 14:30
Оценка:
L>Есть препроцессоры под лицензией BSD:
L>* отдельный препроцессор mcpp -- a portable C preprocessor,
L>* препроцессор из пакета nwcc.

Спасибо, посмотрю на досуге
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[9]: Посоветуйте хороший парсер для JavaScript
От: srggal Украина  
Дата: 06.05.08 19:21
Оценка:
Здравствуйте, Left2, Вы писали:

S>>Парсить то не нужно, нужно убрать текст определенного формата, чем не работа для регэкспа (не обязательно PCRE)?

S>>Ниже прочитал про рантайм — ИМХО регекспы подойдут

L>Регэксп прийдётся делать с многострочным синтаксисом и довольно большой грамматикой дабы правильно обработать JS-овские комментарии и строки. ИМХО, тупиковый путь (вряд ли будет шустро работать + синтаксис регэкспов это сильно на любителя) — наверное проще будет иметь неполную грамматику (типа такой которой оперируют препроцессоры), которая будет отличать строки и комментарии, а всё остальное для неё будет просто набором токенов.


Вот, пример скипера при парсе mail box
        struct comments_linear_white_space_lwsp_parser
                :       public  boost::spirit::parser< comments_linear_white_space_lwsp_parser >
        {
        typedef comments_linear_white_space_lwsp_parser  self_t;

        template <typename ScannerT>
                        typename boost::spirit::parser_result<self_t, ScannerT>::type
                        parse( ScannerT const& scan ) const
                        {
                                if ( scan.at_end() )
                                {
                                        return scan.no_match();
                                }

                                typename ScannerT::iterator_t save = scan.first;
                                std::size_t len = 0;

                                // comments
                                if ( *scan == '(' )
                                {
                                        std::size_t             counter = 1;
                                        ++len;
                                        ++scan.first;

                                        for     ( ;
                                                        scan.first != scan.last
                                                                && counter != 0;
                                                        ++len, ++scan.first
                                                )
                                        {
                                                // CHAR
                                                if( *scan<= 0 || *scan >127 )
                                                {
                                                        // invalid char
                                                        break;
                                                }

                                                switch( *scan )
                                                {
                                                        case '\\':
                                                                // Quoting - skip next symbol if it exists
                                                                ++len;
                                                                ++scan.first;

                                                                if( scan.first == scan.last )
                                                                {
                                                                        return scan.no_match();
                                                                }
                                                                continue;

                                                        case ')':
                                                                --counter;
                                                                break;

                                                        case '(':
                                                                ++counter;
                                                                break;
                                                }
                                        }
                                                
                                        if ( !counter )
                                        {       
                                                return scan.create_match( len, boost::spirit::nil_t(), save, scan.first);
                                        }       
                                        else
                                        {
                                                return scan.no_match();
                                        }
                                }
                                // folding
                                if( match_linear_whitespace( scan ) )
                                {
                                        len = 3;
                                        ++scan.first;
                                        return scan.create_match( len, boost::spirit::nil_t(), save, scan.first);
                                }

                                // LWSP
                                if ( scan.first != scan.last && ( *scan == 0x9 || *scan == 0x20  )   )
                                {
                                        ++scan.first;
                                        ++len;
                                        return scan.create_match( len, boost::spirit::nil_t(), save, scan.first);
                                }

                                return scan.no_match();
                        }
        };

как бы все наглядно ИМХО
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.