Привет, хабр!
UPD: третья часть цикла.
Это вторая статья цикла про QScintilla. Первая здесь. Для начала хочу сказать огромное спасибо всем, кто вывел меня из кармоямы! А теперь можно начать. Что мы сегодня будем делать? Мы напишем лексер для Assembler'а! «В коробке» его нету — не беда, напишем сами! Процесс довольно длительный, поэтому я буду немного меньше расписывать и комментировать. Тем более я не знаю язык ассемблера, так что лексер будет до ужаса примитивный и будет разрисовывать только команды и комментарии.
Как сказал Гагарин — «Поехали!»
На данном этапе, я полагаю, что вы уже прочитали первую статью (смотрите выше) и знаете как создать редактор и его кастомить. Продолжаем с того же проекта, который мы редактировали прошлый раз и с тем же набором инструментов (тут и поймались те, кто не читал первую статью).
Написать расцветку синтаксиса языка Assembler'а. Но лексера (схемы расцветки) в QScintilla по умолчанию нету. Ничего, напишем. Для этого есть класс QsciLexerCustom (по-секрету: у него есть виртуальные методы).
Давайте заготовимтесто наш лексер. Заготовка выглядит так:
Некоторые перечисленные тут функции нужны не нам, а QScintilla. Но если они кому-то нужны, значит реализуем их в mainwindow.h:
Я думаю, тут все понятно. Пойдем дальше. Теперь надо реализовать дефолтный цвет раскраски для всех стилей:
А вот только теперь мы будем раскрашивать наш код. Для этого нам надо знать какие есть в ассемблере ключевые слова. В википедии я нашел немного. Для этого зададим наш keywordsList в конструкторе:
Продолжим. Теперь надо быть очень аккуратным — мы пришли к месту где мы будем раскрашивать синтаксис! Я приведу листинг кода функции styleText(), а потом его коротко докомментирую:
Один момент. Две последние строчки метода. Функции paintKeyword() и paintComments() занимаются расцветкой ключевых слов и комментариев соответственно. Мы вызываем разукраску команд, а только потом комментариев. Почему? Догадайтесь.
Теперь все хорошо. Почти все методы реализованы. Осталось только реализовать paintKeyword() и paintComments():
Готово. Теперь наш лексер умеет что-то делать. Можно компилировать. Да, можно. Да, определенно можно компилировать.
С ассемблером я не работал. Поэтому дико извиняюсь. Не знаю даже базовые принципы работы с ним. Но я посчитал, что он отлично подходит как пример к этой статье.
Вот что у нас получилось:

Видно некоторые баги, например в слове «dword» подсвечивается вхождение «or» как ключевое. Как подсказал товарищ TheHorse — нужно проверять, что вхождение ключевых слов в тексте, разделено сепараторами. Но к концу написания статьи я так устал, что решил не фиксить этот баг, а оставить его фикс на читателей:)
А вот и наше творение: qscintilla-demo-2
Спасибо, и еще раз извиняюсь за выбор языка.
UPD: третья часть цикла.
Это вторая статья цикла про QScintilla. Первая здесь. Для начала хочу сказать огромное спасибо всем, кто вывел меня из кармоямы! А теперь можно начать. Что мы сегодня будем делать? Мы напишем лексер для Assembler'а! «В коробке» его нету — не беда, напишем сами! Процесс довольно длительный, поэтому я буду немного меньше расписывать и комментировать. Тем более я не знаю язык ассемблера, так что лексер будет до ужаса примитивный и будет разрисовывать только команды и комментарии.
Как сказал Гагарин — «Поехали!»
На данном этапе, я полагаю, что вы уже прочитали первую статью (смотрите выше) и знаете как создать редактор и его кастомить. Продолжаем с того же проекта, который мы редактировали прошлый раз и с тем же набором инструментов (тут и поймались те, кто не читал первую статью).
Задача и идеи
Написать расцветку синтаксиса языка Assembler'а. Но лексера (схемы расцветки) в QScintilla по умолчанию нету. Ничего, напишем. Для этого есть класс QsciLexerCustom (по-секрету: у него есть виртуальные методы).
Заготовка
Давайте заготовим
class QsciLexerASM : public QsciLexerCustom { Q_OBJECT public: explicit QsciLexerASM(QObject *parent = 0); ~QsciLexerASM(); //! Разбор текста на стили (самая важная функция) void styleText(int start, int end); //! Наши функции расцветки (вызывается из styleText()) void paintKeywords(const QString &source, int start); void paintComments(const QString &source, int start); //! Название языка (в нашем случае ASM const char * language() const; //! Цвета для стилей QColor defaultColor(int style) const; //! Описание стиля QString description(int style) const; //! Список стилей enum { Default = 0, Comment = 1, Keyword = 2 }; private: QsciLexerASM(const QsciLexerASM &); QsciLexerASM &operator=(const QsciLexerASM &); QStringList keywordsList; };
Некоторые перечисленные тут функции нужны не нам, а QScintilla. Но если они кому-то нужны, значит реализуем их в mainwindow.h:
QString QsciLexerASM::description(int style) const { switch(style) { case Default: return "Default"; case Comment: return "Comment"; case Keyword: return "Keyword"; } return QString(style); } const char * QsciLexerASM::language() { return "ASM"; }
Я думаю, тут все понятно. Пойдем дальше. Теперь надо реализовать дефолтный цвет раскраски для всех стилей:
QColor QsciLexerASM::defaultColor(int style) const { switch(style) { case Comment: return Qt::darkGreen; case Keyword: return Qt::blue; } return Qt::black; }
А вот только теперь мы будем раскрашивать наш код. Для этого нам надо знать какие есть в ассемблере ключевые слова. В википедии я нашел немного. Для этого зададим наш keywordsList в конструкторе:
QsciLexerASM::QsciLexerASM(QObject *parent) : QsciLexerCustom(parent) { keywordsList << "mov" << "add" << "sub" << "imul" << "or" << "and" << "xor" << "shr" << "jmp" << "loop" << "ret" << "int"; }
Продолжим. Теперь надо быть очень аккуратным — мы пришли к месту где мы будем раскрашивать синтаксис! Я приведу листинг кода функции styleText(), а потом его коротко докомментирую:
void QsciLexerASM::styleText(int start, int end) { if(!editor()) return; // получим кусок сорца который нам надо разукрасить char * data = new char[end - start + 1]; // обращение к Scintilla editor()->SendScintilla(QsciScintilla::SCI_GETTEXTRANGE, start, end, data); QString source(data); delete [] data; if(source.isEmpty()) return; // Начнем разрисовывать! paintKeywords(source, start); paintComments(source, start); }
Один момент. Две последние строчки метода. Функции paintKeyword() и paintComments() занимаются расцветкой ключевых слов и комментариев соответственно. Мы вызываем разукраску команд, а только потом комментариев. Почему? Догадайтесь.
Теперь все хорошо. Почти все методы реализованы. Осталось только реализовать paintKeyword() и paintComments():
void QsciLexerASM::paintKeywords(const QString &source, int start) { foreach(QString word, keywordsList) { // перебираем ключевые слова if(source.contains(word)) { int p = source.count(word); // считаем вхождения int index = 0; // начнем считать индексы c 0 while(p != 0) { int begin = source.indexOf(word, index); // считаем индекс вхождения index = begin+1; // задаем точку отсчета для следущей итерации startStyling(start + begin); // начнем стилизировать с индекса вхождения setStyling(word.length(), Keyword); // для длины word.length задаем стиль Keyword startStyling(start + begin); // заканчиваем стилизацию p--; } } } } void QsciLexerASM::paintComments(const QString &source, int start) { int p = source.count(";"); // посчитаем вхождения знака комментария if(p == 0) return; int index = 0; // начнем считать индексы ";" с 0 while(p != 0) { int begin = source.indexOf(";", index); // считаем индекс вхождения int length=0; // длина комментария index = begin+1; // задаем точку отсчета для следущей итерации for(int k = begin; source[k] != '\n'; k++) // ведь source необязательно одна строка length++; startStyling(start + begin); // начнем стилизировать с индекса вхождения setStyling(length, Comment); // для длины length задаем стиль Comment startStyling(start + begin); // заканчиваем стилизацию p--; } }
Готово. Теперь наш лексер умеет что-то делать. Можно компилировать. Да, можно. Да, определенно можно компилировать.
Результат
С ассемблером я не работал. Поэтому дико извиняюсь. Не знаю даже базовые принципы работы с ним. Но я посчитал, что он отлично подходит как пример к этой статье.
Вот что у нас получилось:

Видно некоторые баги, например в слове «dword» подсвечивается вхождение «or» как ключевое. Как подсказал товарищ TheHorse — нужно проверять, что вхождение ключевых слов в тексте, разделено сепараторами. Но к концу написания статьи я так устал, что решил не фиксить этот баг, а оставить его фикс на читателей:)
А вот и наше творение: qscintilla-demo-2
Спасибо, и еще раз извиняюсь за выбор языка.
