По моему хорошо сделано в библиотеке Scintilla (http://www.scintilla.org)
Там для каждого языка свой лексер, который определяет расскраску.
Доступны исходники на C++.
Эту библиотеку использует редактор SciTE.
Постоянно использую его в работе, и еще не разу не замечал глюков расскраски (в отличии от UltraEdit`а).
Здравствуйте, ukman, Вы писали:
U>Кто знает, каким образом работают всякие там редакторы с подсветкой синтаксиса? Понятно, что просто разобраться, что есть что в тексте- не проблема, но как это сделать во время редактирования текста, ведь юзер может обрабатывать огромный файл и анализировать его после каждого нажатия на кнопку не оправдано загружать проц.
Тут были отзывы по поводу разбора только одной строки, по моему, это не верно. Можно так: первый раз Вы полностью разпознаете текст. и у Вас есть информация, с какого по какой символ текста какой токен. Например:
int main
^ ^^
| |keyword
| white space
keyword
Потом, что такое процесс, редактирования? Минимальная операция: замена одного куска текста на другой (не строки и ничего такого подобного, и именно тескта, ведь я могу просто вставить текст, или пометив, удалить его).
Обычно лексические анализаторы распознают токены в порядке следования их в тексте. После одной минимальной операции редактирования можно запустить парсер от первого токена который был изменен, и до, тех пор пока парсер либо не выйдем на токен который не менялся либо не достигнем конца файла (если кому то хочеться привязываться к экрану, то можно до конца экрана, а при прокрутке вниз, считать добавлением текста). Понятно, что дальше текст распознавать не имеет смысла. Легко видеть, что при элементарных операциях редактирования, распознаваться будут обычно 1-2 токена. При многострочном комментировании/раскомментировании парсер будет работать чуть дольше (тут никуда не денешься).
Кто знает, каким образом работают всякие там редакторы с подсветкой синтаксиса? Понятно, что просто разобраться, что есть что в тексте- не проблема, но как это сделать во время редактирования текста, ведь юзер может обрабатывать огромный файл и анализировать его после каждого нажатия на кнопку не оправдано загружать проц.
Здравствуйте, ukman, Вы писали:
U>Кто знает, каким образом работают всякие там редакторы с подсветкой синтаксиса? Понятно, что просто разобраться, что есть что в тексте- не проблема, но как это сделать во время редактирования текста, ведь юзер может обрабатывать огромный файл и анализировать его после каждого нажатия на кнопку не оправдано загружать проц.
Можно хранить какие строки уже раскрашены, а какие еще нет. И тогда пересчет только одной строки(кстати редактор VB, похоже, так и сделан, поскольку раскраска и проверка там происходит только после перехода на новую строку)
Здравствуйте, ukman
U>Кто знает, каким образом работают всякие там редакторы с подсветкой синтаксиса? Понятно, что просто разобраться, что есть что в тексте- не проблема, но как это сделать во время редактирования текста, ведь юзер может обрабатывать огромный файл и анализировать его после каждого нажатия на кнопку не оправдано загружать проц.
Полный парсинг текста идет только при загрузке текста.
В процессе работы разбирается только текущая строка, остальные остаются неизменными. Да это и не логично, разбирать заново весь текст при нажатии одной клавиши .
Насколько я помню, такая проблема была с Borland Pascal 6.0 — на старых машинах расцветка страшно тормозила.
А насчет редакторов — в интернете я встречал несколько, рабочих и бесплатных. Попробуй поиск
Здравствуйте, ukman, Вы писали:
U>Кто знает, каким образом работают всякие там редакторы с подсветкой синтаксиса? Понятно, что просто разобраться, что есть что в тексте- не проблема, но как это сделать во время редактирования текста, ведь юзер может обрабатывать огромный файл и анализировать его после каждого нажатия на кнопку не оправдано загружать проц.
Дык не бог весть что этот парсинг.
Как правило делается в момент отрисовки строки.
Здравствуйте, c-smile, Вы писали:
CS>Дык не бог весть что этот парсинг. CS>Как правило делается в момент отрисовки строки.
CS>Вот здесь например: http://www.codeguru.com/editctrl/crysedit.shtml
Ну хорошо, если каждую строку можно отдельно просканерить, а например комменты в C или многострочные строковые литералы. Или все-таки там настолько все быстро происходит, что можно не заморачиваться?
Здравствуйте, ukman, Вы писали:
U>Ну хорошо, если каждую строку можно отдельно просканерить, а например комменты в C
Запомнили что открыт коммент, и не забудим об этом пока его не закроют. Обрабатываем по-прежнему по одной строке.
U>или многострочные строковые литералы. Или все-таки там настолько все быстро происходит, что можно не заморачиваться?
Аналогично. Помним, что идет литерал.
P.S. Многие оболочки не позволябт дробить константные строки.
P.P.S. Собственно текущую подсветку мы можем помнить и в одной строке, обрабатывая ее по мере ввода(как в VS).
К прмеру: нажали " Прога:
if stringType<>stLiteral then//"Ага! Литерал попер". begin
CurrentFontColor:= stringColor;
stringType:=stLiteral;
end
else
begin//кончился литерал
stringType:=stBasic;
CurrentFontColor:=basicColor;
Здравствуйте, ukman, Вы писали:
U>Здравствуйте, c-smile, Вы писали:
CS>>Дык не бог весть что этот парсинг. CS>>Как правило делается в момент отрисовки строки.
CS>>Вот здесь например: http://www.codeguru.com/editctrl/crysedit.shtml
U>Ну хорошо, если каждую строку можно отдельно просканерить, а например комменты в C или многострочные строковые литералы. Или все-таки там настолько все быстро происходит, что можно не заморачиваться?
ИМХО, алгоритм может быть следующий.
Каждая строка имеет статус Неизвестно, ОбычныйТекст, ВКомментарии, ВСтроке.
(В предположении, что строки переносятся)
При загрузке текста, все строки принимают значения Неизвестно, кроме первой — ОбычныйТекст.
При отрисовке проверяется статус строки:
— Неизвестно — смотрится статус предыдущей строки и присваивается текущей — (делается рекурсивно).
— ОбычныйТекст — начинает выводиться как обычная, пока не встретится начало комментария или начало строки.
— ВКомментарии — поиск конца комментария.
— ВСтроке — поиск конца строки.
В конце строки, если найдено начало комментария или строки, то следующая строка принимает соответствующий статус.
При редактировании текущей строки, она перерисовывается (это не нагружает) и автоматом пересчитываются статусы следующих строк.
Что-то вроде этого.
Евгений, с приветом (но без остроумной подписи, к сожалению )
Здравствуйте, ukman, Вы запостили весьма интересный вопрос.
Ясен хобот, что почти вся подсветка синтаксиса выразима при помощи regExp'ов.
Но вот теперь у нас ситуация: двухмегабайтный(для примеру) исходник С, который закомментирован /**/. Мы стоим в середине файла и набираем */. Перерегекспливание всего текста на каждый keypress — это убийство. Интуитивно понятно, что спасение должно лежать в двух фактах:
1. Нас интересует только тот фрагмент, который сейчас на экране. В плохом случае, который я привел, в любом случае придется прокатиться до начала текста для нахождения открывающего коммента
2. У нас уже есть результат разбора текста, который отличается от нового ровно на один символ.
Пока мне неясно, как использовать эту информацию.
... << RSDN@Home 1.0 beta 3 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, menify, Вы писали:
M>Здравствуйте, ukman.
M>По моему хорошо сделано в библиотеке Scintilla (http://www.scintilla.org) M>Там для каждого языка свой лексер, который определяет расскраску. M>Доступны исходники на C++. M>Эту библиотеку использует редактор SciTE. M>Постоянно использую его в работе, и еще не разу не замечал глюков расскраски (в отличии от UltraEdit`а).
И я того же мнения. Пожалуй, это один из наиболее интересных и полнофункциональных вариантов кроссплатформенного редактора с открытым исходным кодом. Хотя, Scintilla изначально разрабатывалась для языка Python, реализация добавления поддержки нового языка сделана на высоком уровне. Я сам добавил несколько лексеров к Scintillе.
Вот тут http://www.uic.nnov.ru/~ruiv/ еще есть довольно неплохая библиотека (colorer) для раскраски текстов. Она используется в одноименном плагине к FAR manager. В своё время я "увлекался" текстовыми редакторами и использовал её в своём проекте. Весьма быстро и удобно. С иходниками. Платформонезависимая. Уже поддерживает более 90 языков + она полностью настраивается через xml-файлики для раскраски чего угодно и как угодно =)
Здравствуйте, ukman, Вы писали:
CS>>Вот здесь например: http://www.codeguru.com/editctrl/crysedit.shtml
U>Ну хорошо, если каждую строку можно отдельно просканерить, а например комменты в C или многострочные строковые литералы. Или все-таки там настолько все быстро происходит, что можно не заморачиваться?
В CrystalEdit каждая строка парсится не отдельно(!). Если внимательно присмотреться, там первым параметром
в ParseLine() идет dwCookie. Смысл его следующий — это есть состояние вашего парсера от предыдущей
строки. Если писать парсер на базе лексического анализатора, то это есть состояние автомата в тот момент,
когда он покинул предыдущую строку. Теперь осталось только проинициализировать автомат в это состояние
и активизировать парсинг для текущей строки. Таким образом разрешается проблема с многострочными литералами
и комментариями.
К CrystalEdit хорошо "прикручиваются" Лексические Анализаторы, построенные на основе автоматов.
Сам "прикручивал" Заботится об инвалидации строк тоже не надо, там все на уровне "движка"
самого редактора реализовано. Он сам этот dwCookie пересчитывает когда надо. Твоя задача только
состоит в парсинге строки основываясь на этом самом значении.
Здравствуйте, comer, Вы писали:
C>Тут были отзывы по поводу разбора только одной строки, по моему, это не верно. Можно так: первый раз Вы полностью разпознаете текст. и у Вас есть информация, с какого по какой символ текста какой токен. Например:
C>int main C>^ ^^ C>| |keyword C>| white space C>keyword
C>Потом, что такое процесс, редактирования? Минимальная операция: замена одного куска текста на другой (не строки и ничего такого подобного, и именно тескта, ведь я могу просто вставить текст, или пометив, удалить его).
C>Обычно лексические анализаторы распознают токены в порядке следования их в тексте. После одной минимальной операции редактирования можно запустить парсер от первого токена который был изменен, и до, тех пор пока парсер либо не выйдем на токен который не менялся либо не достигнем конца файла (если кому то хочеться привязываться к экрану, то можно до конца экрана, а при прокрутке вниз, считать добавлением текста). Понятно, что дальше текст распознавать не имеет смысла. Легко видеть, что при элементарных операциях редактирования, распознаваться будут обычно 1-2 токена. При многострочном комментировании/раскомментировании парсер будет работать чуть дольше (тут никуда не денешься).
В теории это красиво, но немного о возможной реализации:
Как рисовать разноцветный текст — либо как в CrystalEdit с помощью ExtTextOut — вот уж действительно головняк, либо используя стандартный rich edit. Я выбираю второй вариант.
Тогда при первом разборе блокам текста устанавливается определенный цвет и в дальнейшем rich edit им и рисует. При редактировании наша задача определить границы операции изменения и разобрать/раскрасить текст в этих границах (иногда и вне их).
Не могу решить как определить эти самые границы хотя бы с точностью до строки при стандарнтных операциях rich edit вставить/вырезать и особенно анду/реду. Есть ли варианты как узнать какие строки изменились после этих операций?
Я понимаю что это оффтопик для "Алгоритмов", но хотелось бы решить вопрос не отходя от кассы
Здравствуйте, Диагностик, Вы писали:
Д>Как рисовать разноцветный текст — либо как в CrystalEdit с помощью ExtTextOut — вот уж действительно головняк, либо используя стандартный rich edit. Я выбираю второй вариант.
У меня когда-то стоял выбор — rich или Crystal. ИХМО Я выбираю первый. Аргумент — скорость работы.
А почему ExtTextOut — головняк? Редактор достаточно толково построен. Все сводится к ParseLine. И "представление" отделено от самого процесса редактирования. CrystalBuffer занимается редактированием. А CrystalView при рисовке строк запрашивает как их рисовать. Можно усомнится в скорости парсинга. Ведь на первый взгляд кажется, что отрисовка достаточно частая операция и постоянно тратить время на разбор слишком расточительно. Но это если текст разбирать "руками" — массивы ключевых слов, поиск в них, сравнение в выделеным токеном и.т.д. Проходил я это. Потом когда сделал на основе автоматных грамматик (регулярных выражений- кому как нравится). Все стало летать. Причем при росте сложности и объема ключевых слов и лексических конструкций скорость совершенно не падает. Я те парсеры, что идут вместе с CrystalEdit — это не более чем примеры. В реальности делается это по другому. Потому как у многих такие конструкции как #define выглядят совершенно по разному:
#define
# define
— А ведь должен в обеих ситуациях покрасить в синий
Как ни крути, а "руками" все ситуации не покроешь. Руки устанут, да и голова заболит
Юзать надо однозначно автоматные грамматики. Вначале все это конечно "завести" надо. С этим естественно попарится придется. Но окупится с лихвой. Поверьте.
Да, и чтобы не казаться голословным — www.compare-file.com. Там как раз Crystal'овский движок используется.
Просьба не считать это рекламой .
Здравствуйте, Диагностик, Вы писали:
Д>Как рисовать разноцветный текст — либо как в CrystalEdit с помощью ExtTextOut — вот уж действительно головняк, либо используя стандартный rich edit. Я выбираю второй вариант.
Движок CrystalEdit'a весьма быстро всё это дело отрисовывает.
В своё время (когда был мааленьким) писал редактор HTML Force 2000 (http://vke.kaliningrad.ru/down.php?page=hf2k), но так и не довёл его до ума =)
Писал его (и потом им же редактировал HTML) на Pentium-100/24 — никаких тормозов не наблюдалось =)
Основан он был на CrystalEdit'e. Насколько я сейчас помню, я писал свой парсер для HTML-файлов, функция получилась объёмом примерно в 10 килобайт =) Зато работало весьма быстро и раскрашивало весьма детально и красиво.
Здравствуйте, Nikeware, Вы писали:
N>Здравствуйте, Диагностик, Вы писали:
Потом когда сделал на основе автоматных грамматик (регулярных выражений- кому как нравится). Все стало летать. Причем при росте сложности и объема ключевых слов и лексических конструкций скорость совершенно не падает.
А можно узнать, как автоматы строились? Ну не руками же вы писали? Наверное lex или что-то подобное?
Здравствуйте, ukman, Вы писали:
U>А можно узнать, как автоматы строились? Ну не руками же вы писали? Наверное lex или что-то подобное?
Да. При определенных усилиях можно "прикрутить" подобную бублиотеку к своему коду.
Потом остается только описывать регулярные выражения и некоторую логику конкретного парсера,
а это гараздо приятнее, чем конструкции if-else десятого уровня вложенности .
Да и мыслишь потом уже понятиями предметной области.
Здравствуйте, Nikeware, Вы писали:
N>У меня когда-то стоял выбор — rich или Crystal. ИХМО Я выбираю первый. Аргумент — скорость работы. N>А почему ExtTextOut — головняк? Редактор достаточно толково построен. Все сводится к ParseLine. И "представление" отделено от самого процесса редактирования. CrystalBuffer занимается редактированием. А CrystalView при рисовке строк запрашивает как их рисовать. Можно усомнится в скорости парсинга. Ведь на первый взгляд кажется, что отрисовка достаточно частая операция и постоянно тратить время на разбор слишком расточительно. Но это если текст разбирать "руками" — массивы ключевых слов, поиск в них, сравнение в выделеным токеном и.т.д. Проходил я это. Потом когда сделал на основе автоматных грамматик (регулярных выражений- кому как нравится). Все стало летать. Причем при росте сложности и объема ключевых слов и лексических конструкций скорость совершенно не падает. Я те парсеры, что идут вместе с CrystalEdit — это не более чем примеры. В реальности делается это по другому. Потому как у многих такие конструкции как #define выглядят совершенно по разному:
CrystalEdit я не хочу, чтобы не связываться с MFC. Вопрос стоял — делать раскраску РУКАМИ с помощью ExTextOut или rich edit. Вроде с rich edit все оказывается совсем не сложно, даже область для меток слева где устанавливаются брейкпойнты и т.д. легко создать с помощью EM_SETMARGINS. Мой вопрос про "как определить на какие строки влияют операции..." снимается.
А насчет лексического/синтаксического анализа текста — так их я уже сделал руками, благо разбираемая грамматика совсем простая:
Текст состоит из строк, каждая строка независима.
Строка -> :Пауза | Текст_на_отправку_устройству | Пустая_строка
Пауза -> Целое_число_десятые_доли_секунды
Текст_на_отправку_устройству -> Команда | Команда Операнд | Непонятно_что_наверно_неизвестная_команда
Команда, Операнд, Непонятно_что -> любой текст пока не встретим ';' и '\n'
+ однострочные комментарии ';'
Нужно подсвечивать распознанные команды просто для удобства, т.к. их порядка 100 и могут быть добавлены новые. А так строка отправляется на исполнение целиком как есть и нам по барабану где там команда, а где операнд.
N>
N>#define
N>
N>
N># define
N>
N> — А ведь должен в обеих ситуациях покрасить в синий
N>Как ни крути, а "руками" все ситуации не покроешь. Руки устанут, да и голова заболит N>Юзать надо однозначно автоматные грамматики. Вначале все это конечно "завести" надо. С этим естественно попарится придется. Но окупится с лихвой. Поверьте.
Ну кто же спорит, естественно нужно юзать автоматные грамматики (блин тоже не знаю как их правильно называть). Другое дело что в моем случае мало смысла привлекать генераторы анализаторов типа yacc/lexx если вы об этом.
Здравствуйте, ukman, Вы писали:
U> Потом когда сделал на основе автоматных грамматик (регулярных выражений- кому как нравится). Все стало летать. Причем при росте сложности и объема ключевых слов и лексических конструкций скорость совершенно не падает.
U>А можно узнать, как автоматы строились? Ну не руками же вы писали? Наверное lex или что-то подобное?
Практики у меня в этом деле мало, в основном теория.
Вроде почти везде используется т.н. синтаксически управляемая трансляция. Это значит что первой фазой трансляции управляет конечный автомат — синтаксический анализатор, для сложных грамматик он может быть создан генератором анализаторов, рулит здесь YACC. Этот анализатор не имеет дело с сырым текстом, а запрашивает токены по одному у лексического анализатора.
Лексический анализатор (тоже КА) по запросу читает посимвольно текст пока не распознает допустимую лексему и возвращает токен (т.е. тип лексемы, например число, оператор, идентификатор) и его атрибуты (значение числа или текст идентификатора). Всякие пробелы/комментарии отбрасываются. Лекс. ан. тоже может быть создан генератором анализаторов, под YACC вроде удобнее использовать LEXX.
На входе генераторов анализаторов — регулярные выражения описывающие грамматику/лексику и описание действий, соответствующих некоторой распознаной конструкции, на выходе — С код анализатора.
Для YACC наверно можно найти готовое описание многих языков, насчет C++ не уверен, а просто C вроде есть. Но где не знаю
Если надо только раскрасить, то на фазе синтаксического анализа все и заканчивается.
Хоть че то полезное я сказал ??
Перечитал свой постинг и понял что меня не туда понесло.
Чтобы раскрашивать как в IDE VC нужно просто различать:
1. ключевые слова языка и директивы препроцессора
2. комментарии
3. все остальное
Поскольку ключевые слова зарезервированы и их ни с чем не спутаешь, то можно обойтись только лексическим анализом, а синтаксис языка совершенно не интересует.
Д>А насчет лексического/синтаксического анализа текста — так их я уже сделал руками, благо разбираемая грамматика совсем простая: Д>Нужно подсвечивать распознанные команды просто для удобства, т.к. их порядка 100 и могут быть добавлены новые. А так строка отправляется на исполнение целиком как есть и нам по барабану где там команда, а где операнд.
Д>Ну кто же спорит, естественно нужно юзать автоматные грамматики (блин тоже не знаю как их правильно называть). Другое дело что в моем случае мало смысла привлекать генераторы анализаторов типа yacc/lexx если вы об этом.
Ну да, в данной ситуации мудрить с yacc/lexx не стоит. Это именно тот случай когда "руками" можно .
Просто у меня лично стояла задача более общая — С++, Perl, JScript, C#. И в дальнейшем еще много чего придется реализовывать. Поэтому когда я "руками" сделал пару первых парсеров, понял, что так дело дальше не пойдет. В ошибках "вязнешь"- что в болоте. Пришлось искать более общее решение. Потратил время, но теперь оно окупается.
При достаточном опыте у меня на очередной язык уходит не больше одного рабочего дня (чтобы изучить саму структуру языка и написать лексический анализатор).
Здравствуйте, Диагностик, Вы писали:
Д>Перечитал свой постинг и понял что меня не туда понесло. Д>Поскольку ключевые слова зарезервированы и их ни с чем не спутаешь, то можно обойтись только лексическим анализом, а синтаксис языка совершенно не интересует.
Да. Лексического анализатора хватает на 95% случаев.
Здравствуйте, Nikeware, Вы писали:
N>При достаточном опыте у меня на очередной язык уходит не больше одного рабочего дня (чтобы изучить саму структуру языка и написать лексический анализатор).
Эх, только это хорошо для себя, а как другой пользователь захочет пользоваться твоим редактором, и еще захочет какой нибудь новый язык в анализатор добавить. IMHO, лучше всего когда это описываеться в каких-то конфигурационных файлах, на каком нибудь языке, что нибудь ввиде регулярных выражений (как например LEX файлы) проанализировав который можно легко получить КА (думаю КА для подсветки синтаксиса хватит).
Здравствуйте, comer, Вы писали:
C>Здравствуйте, Nikeware, Вы писали:
N>>При достаточном опыте у меня на очередной язык уходит не больше одного рабочего дня (чтобы изучить саму структуру языка и написать лексический анализатор). C>Эх, только это хорошо для себя, а как другой пользователь захочет пользоваться твоим редактором, и еще захочет какой нибудь новый язык в анализатор добавить. IMHO, лучше всего когда это описываеться в каких-то конфигурационных файлах, на каком нибудь языке, что нибудь ввиде регулярных выражений (как например LEX файлы) проанализировав который можно легко получить КА (думаю КА для подсветки синтаксиса хватит).
Так вы батенька чужие мысли на расстоянии читать умеете! Нехорошо
Естественно. Это следующий логический шаг в подобной ситуации.
Здравствуйте, Nikeware, Вы писали:
N>Просто у меня лично стояла задача более общая — С++, Perl, JScript, C#.
В C# для тебя есть замечательные грабли
Там вроде ключевые слова не зарезервированы и для ДЕЙСТВИТЕЛЬНО правильной раскраски надо решать что есть else в данном контексте — ключевое слово или переменная.
Спорю на что угодно что ты игнорировал это извращение
Интересно, как родное IDE раскрасит переменную else?
Здравствуйте, Диагностик, Вы писали:
Д>Здравствуйте, Nikeware, Вы писали:
N>>Просто у меня лично стояла задача более общая — С++, Perl, JScript, C#.
Д>В C# для тебя есть замечательные грабли Д>Там вроде ключевые слова не зарезервированы и для ДЕЙСТВИТЕЛЬНО правильной раскраски надо решать что есть else в данном контексте — ключевое слово или переменная. Д>Спорю на что угодно что ты игнорировал это извращение Д>Интересно, как родное IDE раскрасит переменную else?
Цитирую MSDN:
Keywords are predefined reserved identifiers that have special meanings to the compiler. They cannot be used as identifiers in your program unless they include @ as a prefix. For example, @if is a legal identifier but if is not because it is a keyword.
Ниже идет таблица с ключевыми словами (не привожу из-за размеров), но там else есно есть .
Вся фишка в @. А это я отрабатываю
N>Keywords are predefined reserved identifiers that have special meanings to the compiler. They cannot be used as identifiers in your program unless they include @ as a prefix. For example, @if is a legal identifier but if is not because it is a keyword.
Значит я не прав. Мне казалось что @ используется только при объявлении, а в дальнейшем — без нее.