Разработка простого синтаксического анализатора, страница 6

rtf.Append (@"\red" + c.R + @"\green" + c.G + @"\blue" + c.B + ";");

}

}

// Повторите цикл и выберите цвета из коллекции комментариев (highlighter.Comments)

}

Имея таблицу colorTable, вы в любой момент можете окрасить ключевое слово, используя такой алгоритм:

Keyword w = FindKeyword (word); // Метод FindKeyword надо разработать

if (w.Word != null)

{

if (w.Color != Color.Black)

rtf.Append ("\\cf" + colorTable[w.Color] + ' ' + word +

"\\cf" + colorTable[ForeColor] + ' ');

}

else

rtf.Append (word);

Здесь мы включили цвет, вывели слово и вновь восстановили (выключили) цвет. Такой же алгоритм справедлив для комментариев. Предположим, что в процессе разбора текста (свойство Text элемента типа RichTextBox) вы обнаружили комментарий (точнее, его OpenTag). Для обработки этого случая создадим новый метод:

private void ProcessComment (Comment com)

{

rtf.Append ("\\cf" + colorTable[com.Color] + ' '); // Включаем цвет комментария

if (com.CloseTag == null)  // Это значит, что комментарий действует лишь до конца строки

{

// Цикл перебора и вставки символов до конца строки (пока не найден символ '\n')

}

else // Сложнее с многострочным комментарием

{

rtf.Append (com.OpenTag);

pos += com.OpenTag.Length;

while (pos < TextLength && !IsTagClosed (com))

;

}

rtf.Append ("\\cf" + colorTable[ForeColor] + ' '); // Восстанавливаем цвет текста

}

Метод IsTagClosed необходимо разработать. У меня он имеет такую схему. Заметьте, что CloseTag — это не один символ, а строка произвольной длины, в частном случае: "*/".

private bool IsTagClosed (Comment com)

{

if (!MoveToCloseTag (com))

return false;

int last = com.CloseTag.Length, i = 1;

bool valid = true;

for (Цикл перебора и вставки символов пока они совпадают с символами CloseTag)

{

c = AppendChar ();

valid &= // Условие совпадения с символами CloseTag

}

return valid;

}

Метод MoveToCloseTag также надо разработать. Подскажу примерную схему.

private bool MoveToCloseTag (Comment com)

{

bool found = false;

for (char c = '\0'; // пока не найден CloseTag[0] и еще что-то; pos++)

c = AppendChar ();

return found;

}

Учет особенностей формата RTF

Формат RTF имеет некоторые особенности. Например, он помечает абзацы специальным тегом: \\par\n. Символы фигурных скобок: { и } являются для него служебными. Для того, чтобы использовать их по прямому назначению, надо перед ними вставить символ \. Для избежания коллизии с синтаксисом языка С (и C#) нам надо использовать двойные символы \\. Приведу текст метода AppendChar, который решает все эти проблемы.

private char AppendChar()

{

char c = Text[pos];

if (c == '\n')

rtf.Append ("\\par");

else if (c == '{' || c == '}')

rtf.Append ('\\');

rtf.Append (c);

if (c == '\\')

rtf.Append (c);

return c;

}

Напомню, что Text — это свойство класса RichTextBox, а значит и SyntaxBox. Оно имеет тип string и дает доступ к массиву символов неразмеченного текста RTF-документа.

Реализация функций автодополнения

Для реализации функциональности, которая выводит и использует список подсказок, надо затратить значительно меньше усилий, чем для реализации цветовой окраски текста.

¨  Во-первых, надо обеспечить реакцию на нажатие комбинации клавиш Ctrl+Space.

¨  Затем надо проанализировать слово (или часть слова) слева от курсора.

¨  Если слева откурсора стоит разделитель, то надо показать список со всеми ключевыми словами.

¨  Если слева откурсора стоит лексема, например, st, надо в списке ключевых слов найти все слова, которые начинаются на st (например: stackalloc, static, string, struct) и показать их в специальном окне.

¨  Если найдено только одно слово, то его надо автоматически завершить (AutoComplete).

¨  Если не найдено ни одного слова, а текст слева от курсора существует и не является разделителем, то список не отображается.

¨  Роль специального окна может выполнять обычный ListBox или отдельная форма со встроенным ListBox.