Курсовая работа: Интерактивный интерпретатор
cos_alpha:=(a*a+b*b-c*c)/(2*a*b)
if (cos_alpha>=1)|(cos_alpha<=-1)
println "Not a triangle"
error
endif
alpha:=arccos[cos_alpha]
result :=alpha*180/pi[]
Проектирование и реализация программы-интерпретатора
Для реализации интерпретатора было решено использовать платформу Microsoft .NETv.1.1 и язык программирования C#. Это связано с тем, что платформа .NET обеспечивает достаточно высокую производительность (быстродействие) приложений при значительном увеличении скорости разработки. Последнее обеспечивается за счет наличия удобных визуальных средств разработки, обширной и мощной стандартной библиотеки классов, использования автоматической сборки мусора, когда память из-под более неиспользуемых объектов освобождается автоматически. Язык C# же является основным языком платформы .NET, позволяющим полностью использовать все преимущества технологии Microsoft .NET, он имеет весьма гибкий синтаксис, позволяющий реализовывать достаточно сложные алгоритмы сравнительно небольшими, но легко читаемыми фрагментами кода.
В программе можно выделить две основные группы классов, две подсистемы, ответственные за логику работы интерпретатора и графический интерфейс пользователя соответственно. Поскольку первая подсистема содержит значительно большее число классов, чем вторая, было решено расположить ее в отдельном пространстве имен logic, вложенном в корневое пространство имен проекта. Классы, ответственные за графический интерфейс пользователя, расположены непосредственно в корневом пространстве имен проекта. Кроме того, в пространстве имен logic имеется два вложенных пространства имен – operators и vartypes, соответствующие двум основным иерархиям наследования в проекте – операторам программы и типам данных. Корневое пространство имен имеет имя interpr. Диаграмма пакетов проекта изображена на рис. 1.
Роль посредника между пользовательским интерфейсом и подсистемой, реализующей логику работы интерпретатора, выполняет класс Facade (фасад). Он также ответственен за создание отдельного потока для выполнения команд пользователя (вводимых с консоли). Выполнять их в том же потоке, что и обрабатывать сообщения пользовательского интерфейса нельзя так как в этом случае зациклившуюся пользовательскую функцию будет невозможно прервать. Многие методы класса Facade сводятся к простому вызову методов других классов из пространства имен logic. Этот класс в дальнейшем будет рассмотрен более подробно.
Для обработки ошибок применяется механизм структурной обработки исключений. При этом используются следующие классы пользовательских исключений (для ошибок в классах пространства имен interpr.logic):
· CalcException – ошибка по вине пользователя (синтаксическая или в вычислениях);
· SyntaxErrorException – синтаксическая ошибка, обнаруживаемая во время «компиляции», т. е. при загрузки функции или преобразования введенной команды во внутренний формат. Унаследован от CalcException;
· LineSyntaxException – синтаксическая ошибка в конкретном операторе функции. Содержит информацию об месте обнаружения (имя функции, строка).
· OtherException – ошибки, связанные с некорректной работой интерпретатора не по вине пользователя. Класс используется для отладочных целей. При нормальной работе такое исключение никогда не должно генерироваться.
· LinkedListException – ошибкавметодахкласса LinkedList. Унаследован от класса OtherException.
· NamespaceSerializationException – унаследован непосредственно от System.Exception. Такое исключение – генерируется если пространство имен консоли не может быть успешно восстановлено.
Соответствующая диаграмма классов изображена на рис. 2.
Можно выделить несколько групп классов в пространстве имен interpr.logic – классы, ответственные за вычисление выражений, за выполнение пользовательских функций, за преобразование текста команд и пользовательских функций во внутренний формат («компиляцию» текста программы), классы, участвующие в организации интерактивной работы интерпретатора. Эти группы классов, равно как и подсистема графического интерфейса пользователя, будут рассмотрены ниже. В пространстве имен interpr.logic также имеется один класс вспомогательного назначения – LinkedList. Он представляет двухсвязный список. В нем имеются методы и свойства добавления и чтения элементов в начале и конце списка, определения числа элементов списка. При этом, при попытке чтения из пустого списка, генерируется исключениеLinkedListException. Метод GetIterator(), существующий в двух перегруженных версиях (для первого элемента списка и для заданного индекса), возвращает объект вложенного класса LinkedList.Iterator, который представляет собой итератор, позволяющий читать элементы списка, перемещаясь по нему от начала к концу, а также двигаться в обратном направлении. Элемент списка представляется объектом частного вложенного класса Link, содержащего три поля с видимостью internal – одно для хранения значения элемента списка и два для ссылок на предыдущий и следующий элементы.
Следует также отметить интерфейс interpr.logic.IConsole, представляющий нечто, что может быть использовано для вывода текста. Он имеет два метода - void Print(string str) и void PrintLn(string str), назначение которых понятно из названия.
Основные классы пространства имен interpr.logic показаны на диаграмме на рис. 3.
Рис. 3.
Классы пространства имен interpr . logic .
Внутреннее представление и выполнение программы.
Большинство операторов реализованного языка программирования содержат выражения. Выражение представляет собой совокупность операндов и операций над ними, которая может быть вычислена, то есть на основании которой можно получить некоторое значение-результат. В языке программирования выражения представляются построенными по определенным требованиям строками. При обработке текста программы (этот процесс будет рассмотрен в следующем параграфе) строковое представление выражений переводится в представление внутреннее. В данном интерпретаторе внутреннее представление выражений использует так называемую обратную польскую запись (ОПЗ). Рассмотрим ОПЗ подробнее.
Обычная математическая запись арифметических выражений представляет собой так называемую инфиксную запись, в которой знаки операций располагаются между операндами. При этом для уточнения порядка вычисления операций используются приоритеты операций и круглые скобки. Такая форма записи удобна для человека, но неудобна для ЭВМ. Поэтому часто используют так называемую постфиксную или обратную польскую запись. В этом случае знак операции записываются после всех ее операндов, а вычисление производится по довольно простому алгоритму: выражение в ОПЗ последовательно просматриваем слева направо. Если встречаем операнд, то заносим его в стек, если же встречаем операцию, то выбираем ее операнды из стека, выполняем операцию и заносим результат в стек. В начале вычисления выражения стек пуст. Если выражение записано корректно, то при выполнении каждой операции число элементов стека будет не меньше числа ее операндов, и в конце процесса в стеке останется ровно одно значение – результат вычисления выражения. Особенностью ОПЗ является отсутствие необходимости в использовании скобок.
Например, выражение a+(b*c-d)/e в ОПЗ имеет вид abc*d-e/+. Применим к нему описанный выше алгоритм вычисления.
1. Заносим в стек a.