Главная   Добавить в избранное Интерактивный интерпретатор | курсовая работа


Бесплатные Рефераты, дипломные работы, курсовые работы, доклады - скачать бесплатно Бесплатные Рефераты, дипломные работы, курсовые работы, доклады и т.п - скачать бесплатно.
 Поиск: 


Категории работ:
Рефераты
Дипломные работы
Курсовые работы
Контрольные работы
Доклады
Практические работы
Шпаргалки
Аттестационные работы
Отчеты по практике
Научные работы
Авторефераты
Учебные пособия
Статьи
Книги
Тесты
Лекции
Творческие работы
Презентации
Биографии
Монографии
Методички
Курсы лекций
Лабораторные работы
Задачи
Бизнес Планы
Диссертации
Разработки уроков
Конспекты уроков
Магистерские работы
Конспекты произведений
Анализы учебных пособий
Краткие изложения
Материалы конференций
Сочинения
Эссе
Анализы книг
Топики
Тезисы
Истории болезней


 





Интерактивный интерпретатор - курсовая работа


Категория: Курсовые работы
Рубрика: Программирование, компьютеры и кибернетика, ИТ технологии
Размер файла: 301 Kb
Количество загрузок:
56
Количество просмотров:
2133
Описание работы: курсовая работа на тему Интерактивный интерпретатор
Подробнее о работе: Читать или Скачать
Смотреть
Скачать



Содержание.

  • Содержание. 2
  • Введение 3
  • Постановка задачи. 4
  • Описание реализованного в интерпретаторе языка программирования. 5
  • Примеры пользовательских функций 12
    • 1. Сортировка массива. 12
    • 2. Вычисление НОД по алгоритму Евклида 12
    • 3. Рекурсивное вычисление факториала. 13
    • 4. Проверка, является ли строка корректным идентификатором. 13
    • 5. Вычисление угла треугольника по трем сторонам. 14
  • Проектирование и реализация программы-интерпретатора 15
    • Внутреннее представление и выполнение программы. 18
    • Обработка текста программы. 24
    • Графический интерфейс пользователя. 27
    • Взаимодействие подсистем интерпретатора. Класс Facade. 31
  • Заключение 33
  • Приложение. Исходный текст (сокращенно). 34
    • 1. Класс VarBase. 34
    • 2. Класс ArrayVar. 34
    • 3. Класс InterprEnvironment. 36
    • 4. Класс Namespace. 40
    • 5. Интерфейс IСomputable. 42
    • 6. Класс Call. 42
    • 7. Класс ArgList 42
    • 8. Класс Expression. 43
    • 9. Класс Operation (сокращенно). 49
    • 10. Класс Parser. 50
    • 11. Класс LineCompiler. 56
    • 12. Интерфейс IOperator. 60
    • 13. Класс Command. 60
    • 14. Класс ForOperator. 61
    • 15. Класс NextOperator 62
    • 16. Класс Subroutine. 62
    • 17. Класс Facade. 67
    • 18. Класс SourceBox. 69
    • 19. Класс Form1. 75
  • Использованная литература и документация. 78

Введение

Стандартный «Калькулятор» Windows является, пожалуй, единственной имеющей широкое распространение программой, предназначенной для мелких вычислений. Его не могут заменить из-за своей громоздкости, ни электронные таблицы, ни профессиональные математические пакеты. Но в то же время эта программа имеет существенные недостатки, причина которых проста - пользовательский интерфейс сделан «по образу и подобию» карманного калькулятора, поэтому заимствованы все неудобства последнего. Например, при работе пользователь видит только одно число и после получения результата не может проверить, правильно ли были введены операнды. Второй проблемой является невозможность добавления пользовательских функций - если приходится производить вычисления по одной и той же формуле сто раз, сто раз приходится нажимать соответствующую кнопку для каждой арифметической операции в выражении. На мое мнение, наиболее удобны для повседневного использования в качестве замены «Калькулятору» интерактивные интерпретаторы, к числу которых относятся MatLab и Python. Но основное назначение этих программных пакетов совсем другое, нет смысла устанавливать их на компьютер лишь для того, чтобы выполнять несколько сложений и умножений пару раз в день. Поэтому я решил написать несложный интерактивный интерпретатор, не громоздкий и удобный для мелких вычислений.

Постановка задачи.

Требуется реализовать интерпретатор относительно несложного языка программирования, работающий в интерактивном режиме, то есть выполняющий вводимые пользователем команды с клавиатуры. При этом должна присутствовать возможность создания пользовательских функций, в которых могут присутствовать операторы управления течением программы - ветвления, циклы и др., а также операторы вывода (на консоль). Должны быть также предусмотрены возможность сохранения промежуточных результатов вычисления (в том числе между сеансами работы с интерпретатором) и возможность прерывания выполнения зациклившейся пользовательской функции без завершения работы интерпретатора. Данный интерпретатор должен быть удобен в использовании как замена стандартному «Калькулятору» Windows, по крайней мере, для человека, владеющего минимальными навыками программирования.

Описание реализованного в интерпретаторе языка программирования.

Интерпретатор работает в режиме консоли и выполняет команды, вводимые с клавиатуры. Эти команды могут содержать вызовы пользовательских функций. Код пользовательских функций создается с помощью окна редактора кода интерпретатора. Интерпретатор предоставляет возможность создания, редактирования и удаления пользовательских функций. Функции сохраняются в файлах, имя которых не имеет расширения и совпадает с именем функции. Файлы используемых функций должны находиться в подкаталоге subroutines рабочего каталога интерпретатора. Сохраненные пользовательские функции загружаются автоматически при запуске интерпретатора. Все операторы управления течением программы могут использоваться только в тексте пользовательской функции. Все остальные операторы (команды) могут быть вызваны непосредственно из консоли. Кроме того, в консоли можно ввести выражение без дополнительных ключевых слов, и оно будет вычислено, его значение будет выведено на экран. Для вывода же данных из функции необходимо использовать операторы вывода (см. ниже).

Идентификатором (именем) переменной служит последовательность символов произвольной длины, состоящая из букв (латинского алфавита), цифр и знаков подчеркивания, не начинающаяся с цифры. Те же ограничения распространяются и на имена функций. Регистр букв имеет значение. Возможно наличие только одной переменной с каждым именем в каждой функции и в среде консоли. Но допускается совпадение имени переменной с именем какой-либо функции. Также имена переменных и функций не должны совпадать с ключевыми словами языка, к которым относятся:

· call

· clear

· else

· elseif

· endif

· error

· for

· if

· loop

· next

· print

· println

· result

· return

· while

Предварительного объявления переменных не требуется. Переменная присутствует в памяти с момента присвоения ей значения, при этом тип переменной определяется по типу присваиваемого ей значения. Массив также создается при присваивании значения какому-либо его элементу. Попытка получить значение еще не инициализированной переменной или попытка рассмотреть как массив переменную, не являющуюся таковой, приводит к ошибке. Каждая функция и среда консоли имеют собственные, не зависящие друг от друга наборы переменных. Обратиться из функции или среды консоли к “чужой” переменной невозможно.

Имеются следующие типы данных: целый, вещественный, строковый, массив. Целый, вещественный и строковый типы называются простыми в противоположность массиву; вещественный и целый типы называются числовыми. Тип переменной не описывается, но может быть определен с помощью функций issingle, isarray, isstring, isnum, isint, isreal. Кроме того, выполнение операции над аргументами недопустимых типов может привести к ошибке. Массив может хранить элементы любых простых типов, причем типы разных элементов одного и того же массива могут не совпадать. По мере заполнения массива возможно появление в нем «пустых мест», например, после команд a{0}:=1; a{2}:=4; a{4}:=5 (пропущены элементы с индексами 1 и 3). Попытка получить значение еще не инициализированного элемента массива приводит к ошибке. Проверить, инициализирован ли элемент массива с заданным индексом, можно с помощью функции defined.

Любая последовательность пробелов и табуляций, идущих подряд, считается за один пробел, если она находится между символами, которые могут входить в идентификаторы (буквы, цифры, подчеркивание), или игнорируется в противном случае. Также игнорируются интерпретатором комментарии, которыми являются строки или окончания строк, начинающиеся с символа `#. Эти правила не распространяются на строковые константы, заключаемые в двойные кавычки. Строковой константой не является последовательность символов в кавычках, если открывающая кавычка находится после символа начала комментария #, но этот символ может присутствовать в строковой константе между кавычками. Так например, команда a : = b + ”#”+c#comment эквивалентна команде a:=b+”#” + c, но не равносильна команде a:=b+” #”+c, или a+b+” (последняя команда синтаксически неверна).

Каждая строка, вводимая с консоли, содержит одну команду или ни одной (пустая строка или комментарий) команды. То же касается и строк файла функции, кроме первой, которая должна содержать описание функции в виде: <имя_функции>[<список_параметров>], где список параметров, заключаемый в квадратные скобки, состоит из имен параметров, разделенных запятой (эта строка также может содержать и комментарий после описания функции). Квадратные скобки пишутся даже при пустом списке параметров. Имена параметров (формальных) подчиняются тем же ограничениям, что и имена переменных, мало того, они рассматриваются как переменные, определенные в данной функции, но в начале выполнения функции они принимают значения соответствующих фактических параметров. Нужно отметить, что попытка передачи в качестве фактического параметра функции переменной с неопределенным значением всегда приводит к ошибке, даже если в функции к этому параметру нет ни одного обращения.. Также приводит к ошибке вызов функции с числом фактических параметров, не соответствующим числу формальных параметров. Кроме того, в каждой функции имеется переменная с предопределенным именем result. Ее значение на момент выхода из функции и является возвращаемым значением функции. В момент начала выполнения функции ее значение равно 0 (целое число). Если переменная result была удалена командой clear и осталась неопределенной на момент выхода из функции, возникает ошибка.

Значения целого, вещественного и строкового типа могут быть представлены в программе в виде констант (литералов). Целый литерал представляет собой последовательность цифр. Он представляет число, обычной записью которого является. Вещественный литерал представляет собой десятичную или экспоненциальную запись вещественного числа, при этом, в случае экспоненциальной записи, буква “е” может быть как строчной, так и прописной. Например:12.0, 1.6e87, 2Е-7, 89.0. В числовых литералах не может содержаться начальный символ «+» или «-», они могут представлять только положительное число. Отрицательные значения можно получить применением операции унарный минус. Целая часть вещественного числа не может быть опущена. Дробная часть (точка и хотя бы одна цифра после нее) должна присутствовать, если не указан порядок, например, 11е-6 - допустимая запись, а 11.е-4 и 61. - нет. Строковый литерал заключается в двойные кавычки, если в него нужно включить двойную кавычку, то она пишется дважды.

Специальный логический тип данных отсутствует, логические значения представляются переменными целого, вещественного либо строкового типа - истине соответствует положительное число либо непустая строка, лжи - неположительное число либо пустая строка. Результат всех стандартных логических операций - целые числа 1 (истина) или -1 (ложь). При попытке рассмотреть массив как логическое значение возникает ошибка.

Выражением является:

· идентификатор переменной;

· константа целого, вещественного или строкового типа;

· обращение к элементу массива с заданным индексом, имеющее синтаксис <идентификатор массива>{<индекс>} (индекс заключен в фигурные скобки). Индекс должен быть выражением. Перед открывающей фигурной скобкой должно стоять имя переменной, являющейся массивом (но не другое выражение, имеющее тип массива). Значение индекса должно быть неотрицательным целым, иначе возникает ошибка;

· результат применения унарной операции к выражению;

· результат применения бинарной операции к двум выражениям;

· вызов функции (без ключевого слова call). В этом случае функция обязана возвращать значение, иначе возникает ошибка. Фактическими параметрами функции должны быть выражения;

· выражение, заключенное в круглые скобки.

Операции, используемые в выражениях, и их приоритеты (операнды обозначены как a и b; для суммы чисел, разности и произведения результат - целое число, если оба операнда - целые, иначе - вещественное число) перечислены в таблице.

Уровень приоритета

Синтаксис

Типы операндов

Смысл

Тип результата

1

~a

простой

логическое отрицание

целый (-1 или 1)

-a

число

унарный минус

тот же, что и a

+a

число

унарный плюс

2

a*b

числа

произведение

число

a/b

числа

вещественное деление

вещественное

3

a+b

строки либо a - строка, b - число

конкатенация строк (число преобразуется в строку)

строка

числа

сумма

число

a-b

числа

разность

число

4

a=b

простые (оба - числа либо строки одновременно)

равно

целый (-1 или 1)

a<>b

не равно

a>b

больше

a<b

меньше

a<=b

меньше либо равно

a>=b

больше либо равно

5

a&b

простые

“И”

6

a^b

простые

исключающее “ИЛИ”

a~=b

логическая эквивалентность

a|b

“ИЛИ”

Выражения интерпретируются в соответствии с приоритетом операций и имеющимися в них круглыми скобками. При этом все унарные операции выполняются справа налево, бинарные операции одинакового приоритета - слева направо. Если в выражении хотя бы один из операндов операции не имеет требуемый тип, или операция не может быть произведена корректно по другой причине, например, в случае деления на ноль, то возникает ошибка.

Вызов функции имеет следующий синтаксис: <имя функции>[<фактический параметр 1>,<фактический параметр 2>,...,<фактический параметр 3>]. Даже если список параметров пуст, квадратные скобки все равно пишутся. Фактическими параметрами функции должны быть выражения.

Например, function1[a,b+c,function2[a,function3[]],56.12e-1]. Существует ряд предопределенных функций, с именами которых не должны совпадать имена пользовательских функций. Их список приведен в таблице.

Функция

Возвращаемое

значение

Описание

abs[число]

того же типа, что и параметр

абсолютная величина

cos[число]

вещественное

косинус

sin[число]

синус

tg[число]

тангенс

arctg[число]

арктангенс

arcsin[число]

арксинус

arccos[число]

арккосинус

exp[число]

степень основания натуральных логарифмов (экспонента)

pow[число, число]

первый параметр в степени второй параметр (первый параметр должен быть неотрицательным)

ln[число]

натуральный логарифм

lg[число]

десятичный логарифм

log[число, число]

логарифм первого аргумента по основанию, заданному вторым аргументом

sqrt[число]

квадратный корень

pi[]

константа pi (отношение длины окружности к диаметру)

idiv[целое число, целое число]

целое

частное целочисленного деления

imod[целое число, целое число]

целое

остаток целочисленного деления

substr[строка, целое число, целое число]

строка

подстрока (первый параметр - исходная строка, второй параметр - индекс первого символа, третий - длина подстроки; если происходит выход за пределы исходной строки, то ошибки нет, но длина результата - меньше указанной в третьем параметре)

strlen[строка]

целое

длина строки

strpos[строка, строка]

целое

позиция первого символа первого вхождения второй строки в первую, либо -1, если совпадений нет (нумерация символов с нуля)

toint[простой]

целое

преобразование к целому (если невозможно - возникает ошибка)

toreal[простой]

вещественное

преобразование к вещественному (если невозможно - возникает ошибка)

tostring[любой]

строка

преобразование к строке

issingle[любой]

целое (-1 или 1)

является ли значение выражения не массивом

isarray[любой]

является ли значение выражения массивом

isstring[любой]

является ли значение выражения строкой

isnum[любой]

является значение выражения числом

isint[любой]

является ли значение выражения целым числом

isreal[любой]

является ли значение выражения вещественным числом

size[массив]

число элементов массива

defined[массив, целое]

определен ли в массиве элемент с заданным индексом

iff[простой, любой, любой]

любой

если первый параметр - истина, то возвращает значение второго параметра, иначе - третьего

Если при вызове стандартной функции тип хотя бы одного из параметров не соответствует требуемому, возникает ошибка.

Оператор вызова call позволяет вычислить любое выражение, проигнорировав его значение, например, вызвать функцию как процедуру. Он имеет синтаксис:

call <выражение>

Например, call procedure1[param1, param2].

Оператор присваивания имеет синтаксис <переменная>:=<выражение> или <массив>{<выражение-индекс>}:=<выражение>.

В результате переменная или элемент массива принимают значение, равное значению выражения в правой части оператора присваивания, если оно было вычислено корректно.

Условный оператор имеет вид:

if <выражение>
[операторы]
[elseif <выражение>]
[операторы]
[elseif <выражение>]
...
[else]
[операторы]
endif

Последовательно проверяются выражения-условия в строках с ключевыми словами if и elseif. Как только получено истинное значение условия (положительное число или непустая строка), то выполняются операторы, следующие за строкой с данным условием, затем выполнение переходит на строку, следующую за endif. Если ни одно из условий не оказалось истинным, то выполняются операторы, расположенные после else, если строка с else имеется в данном условном операторе, иначе управление переходит ниже endif. Условный оператор может быть использован только в функции. Примеры:

1.)

if a<0

a := abs[a]

flag := 1

endif

2.)

if (ch=”a”)|(ch=”A”)

call proc_a[]

elseif (ch=”b”)|(ch=”B”)

call proc_b[]

elseif (ch=”c”)|(ch=”C”)

call proc_c[]

else

error

endif

Оператор цикла while имеет вид:

while <выражение>

[операторы]

loop

Выполнение блока операторов повторяется, пока истинно значение выражения-условия, затем управление передается на строку, следующую за loop. При этом, если значение выражения изначально ложно, то операторы не будут выполнены ни разу. Оператор цикла while может быть использован только в функции. Пример:

i := 1

s:=0

while i<=n

s := s+i

i := i+1

loop

Здесь переменная s получает значение суммы чисел от 1 до n.

Оператор цикла for имеет вид:

for <переменная-счетчик> := <выражение1> : <выражение2>

[операторы]

next

В начале выполнения цикла вычисляются выражение1 и выражение2 (их значения должны быть целыми, иначе возникает ошибка), затем переменной-счетчику присваивается значение выражение1 и, если оно меньше или равно значению выражение2, выполнение переходит внутрь цикла, иначе - за строку с ключевым словом next. После каждой итерации цикла значение счетчика увеличивается на единицу и сравнивается со значением выражение2 (оно вычисляется только один раз в начале), если оно оказывается меньшим или равным значению выражение2, то выполняется следующая итерация цикла, иначе - цикл завершается. Значение счетчика в цикле, в принципе, можно менять, не если оно окажется не целым на момент окончания очередной итерации, возникает ошибка. Оператор цикла for может быть использован только в функции. Пример:

for i :=0 : size[a]

a{i} := a{i}*2

next

Оператор возврата return незамедлительно прерывает выполнение функции (может быть использован только в функции). Например,

if a<b

result := 1

return

endif

Если при выполнении функции не встретился оператор return, выход из функции происходит как только управление переходит ниже последней строки функции.

Оператор error прерывает выполнение программы - искусственно генерируется ошибка времени выполнения. Он может быть использован только в функции.

Пример:
a:=toint[str]

if a<0

error

endif

Для вывода данных используются операторы print и println. Оператор print имеет синтаксис print <выражение>. Значение выражения автоматически приводится к строке (т. е.команды println[a] и println[tostring[a]] - равносильны). Эта строка выводится на консоль. Оператор println имеет аналогичный синтаксис и назначение. Отличие заключается в том, что println производит перевод на новую строку после вывода, print - нет. Кроме того, если при работе в консоли введено выражение без ключевых слов и оператора присваивания, то результат его вычисления выводится на консоль в отдельной строке - это сокращенная форма оператора println.

Оператор clear позволяет удалить переменную из памяти, например, команда “clear n” удаляет из памяти переменную n, после чего она считается неопределенной. Удалить отдельные элементы массива нельзя. Выполнение оператора clear над неопределенной переменной не имеет никакого эффекта и не приводит к ошибке. С помощью оператора clear можно также удалить фактические параметры функции и даже переменную result, что необходимо перед работой с ней как с массивом. Но если переменная result не определена на момент выхода из функции, то возникает ошибка времени выполнения. Синтаксис оператора clear имеет вид:

clear <имя_переменной1>

Примеры пользовательских функций

1. Сортировка массива.

sort [a]

#сортирует массив а по возрастанию.

#методом прямого выбора

if ~isarray[a]

println “Invalid argument”

error

endif

n:=size[a]

for i:=0:n-2

k:=i

for j:=i+1:n-1

k:=iff[a{j}<a{k}, j, k]

next

if i<>k

t:=a{i}

a{i}:=a{k}

a{k}:=t

endif

next

result:=a

2. Вычисление НОД по алгоритму Евклида

nod [n,m]

#вычисляет наименьший общий делитель

#натуральных чисел n и m

#по алгоритму Евклида

if ~isint[n]|~isint[m]

println "Invalid arguments"

error

endif

if (n<0)|(m<0)

println "Invalid arguments"

error

endif

if n=0

result:=m

return

endif

if m=0

result:=n

return

endif

while m>0

t:=n

n:=m

m:=imod[t,m]

loop

result:=n

3. Рекурсивное вычисление факториала.

factor [n]

#рекурсивное вычисление факториала числа n

if ~isint[n]

println "Invalid argument"

error

elseif n<0

println "Invalid argument"

error

elseif (n=0)|(n=1)

result:=1

else

result:=n*factor[n-1]

endif

4. Проверка, является ли строка корректным идентификатором.

test_d [str]

#возвращает 1, если строка является корректным

#идентификатором, то есть состоит только из

#букв, цифр, знаков подчеркивания и начинается

#c цифры, при этом имеет ненулевую длину,

#и -1 в противном случае

if ~isstring[str]

println "Invalid argument"

error

endif

n:=strlen[str]

if n=0

result:=-1

return

endif

ch:=substr[str,0,1]

if (ch>="0")&(ch<="9")

result:=-1

return

endif

for i:=0:n-1

ch:=substr[str,i,1]

if ~(((ch>="0")&(ch<="9"))|((ch>="A")&(ch<="Z"))|((ch>="a")&(ch<="z"))|(ch="_"))

result:=-1

return

endif

next

result:=1

5. Вычисление угла треугольника по трем сторонам.

angle [a,b,c]

#вычисляет угол треугольника со сторонами

#a, b и c между сторонами a и b (в градусах)

if ~isnum[a]|~isnum[b]|~isnum[c]

println "Invalid arguments"

error

endif

if (a<=0)|(b<=0)|(c<=0)

println "Not a triangle"

error

endif

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 .NET v.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.

2. Заносим в стек b.

3. Заносим в стек c.

Состояние стека на этот момент: a, b, c - вершина.

4. Извлекаем из стека операнды операции умножения - b и c и заносим в стек результат.

Стек: a, b*c.

5. Заносим в стек d.

Стек: a, b*c, d.

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

Стек: a, b*c-d.

7. Заносим в стек e.

Стек: a, b*c-d, e.

8. Извлекаем из стека операнды, производим деление, заносим в стек результат.

Стек: a, (b*c-d)/e.

9. Извлекаем из стека операнды, производим сложение, заносим в стек результат.

Итого получаем в стеке a+(b*c-d)/e, что и требовалось.

Для представления выражений в интерпретаторе используется класс Expression. Он содержит обратную польскую запись выражения в виде связанного списка (однонаправленного). Звено этого списка, равно как и стека, используемого при вычислении выражения, представляется объектом вложенного класса Expression.Element, содержащим ссылки на следующее звено и на объект, реализующий интерфейс IComputable, который содержит один метод logic.vartypes.VarBase Compute() - получить значение. Вычисление значения выражения по рассмотренном выше алгоритму производится в методе VarBase Expression.Calculate(). Строка, содержащая запись выражения, обрабатывается в конструкторе этого класса. Интерфейс IComputable реализован тремя классами:

· VarBase - абстрактный класс, представляющий значение любого типа данных;

· VarName - представляет переменную по ее имени;

· Call - представляет вызов операции либо функции.

Вначале рассмотрим классы, представляющие значения различных типов. Все они являются потомками только что названного класса VarBase. Как было сказано выше, в языке существует четыре типа данных - целое число, вещественное число, строка и массив. При этом числовые и строковый типы, в противоположность массиву, называются простыми типами. Для простых значений базовым является абстрактный класс SingleVar. Целый и вещественный типы также особо выделяются как числовые, и для них существует свой базовый абстрактный класс NumVar. Наконец, каждому из четырех типов данных соответствует свой конкретный класс - IntVar, RealVar, StringVar и ArrayVar. Эта иерархия классов находится в пространстве имен interpr.logic.vartypes. Она изображена на диаграмме на рис. 4.

Рис. 4.

Классы пространства имен interpr.logic.vartypes.

Метод Compute() класса VarBase просто возвращает ссылку this. Методы IsArray(), IsSingle(), IsString(), IsNum(), IsInt(), IsReal() позволяют определить тип значения. Они используют оператор RTTI is языка C#. В классе VarBase объявлены абстрактными унаследованные от System.Object методы Clone() и ToString(), что требует обязательного их переопределения у неабстрактных потомков. Абстрактный метод Serialise() сохраняет объект (значение и его тип) в файле. Класс ArrayVar имеет методы для присвоения и получения значений отдельных элементов массива, получения размера массива, выяснения вопроса, определено ли значение элемента массива с заданным индексом. Класс SingleVar определяет абстрактный метод ToBool(), возвращающий логическое значение объекта. В классе NumVar также имеется абстрактный метод ToDouble(), возвращающий значение объекта как вещественное число. Эти классы и их потомки содержат также методы для выполнения над значениями арифметических и логических операций.

В виде объектов классов, производных от VarBase, в выражениях (экземплярах класса Expression), хранятся только константные значения. Переменные же представляются здесь объектами класса VarName, содержащими имя (идентификатор) переменной. Сами же значения переменных хранятся в объектах класса Namespace или производного от него ConsoleNamespace.

Класс Namespace представляет пространство имен (область видимости) пользовательской функции, класс ConsoleNamespace - среды консоли. При работе интерпретатора создается стек пространств имен (областей видимости), на вершине которого находится пространство имен выполняемой в данный момент функции, на дне - среды консоли. Каждый раз при вызове функции создается и добавляется на вершину стека новый объект Namespace, при выходе из функции он уничтожается. Класс Namespace имеет поле, содержащее ссылку на предыдущий элемент стека, у находящегося на дне стека объекта ConsoleNamespace оно всегда содержит нулевой указатель.

Ссылки на вершину и на дно стека пространств имен хранятся в полях класса InterprEnvironment. Доступ к текущему пространству имен осуществляется через его свойство CurrentNamespace. Для этого класса при запуске интерпретатора создается единственный объект, хранящийся в его статическом поле и возвращаемый статическим свойством только для чтения Instance. Таким образом, здесь использован паттерн Singleton. Класс InterprEnvironment выполняет несколько различных функций. Среди них, во-первых, хранение ссылки на объект IConsole, с помощью которого производится вывод. Во-вторых - работа с переменными среды консоли - их сохранение в файле, восстановление из файла (производится во время инициализации объекта при запуске или перезапуске интерпретатора), получение их списка. В-третьих - загрузка и хранение пользовательских функций. Последняя функция будет рассмотрена подробнее ниже.

Последний из классов, реализующих интерфейс IComputable, - класс Call представляет вызов операции, встроенной или пользовательской функции в выражении. Он имеет два поля. Первое из них хранит ссылку на объект класса ArgList, который содержит список операндов. Оно инициализируется методом SetArgList() при каждом выполнении операции или функции. Второе поле содержит ссылку на абстрактный класс Operation, который и представляет операцию или функцию. Этот класс содержит абстрактное свойство только для чтения ReqCount, возвращающее необходимое число операндов (аргументов ...........



Страницы: [1] | 2 | 3 | 4 | 5 |








 
 
Показывать только:




Портфель:
Выбранных работ  


Рубрики по алфавиту:
А Б В Г Д Е Ж З
И Й К Л М Н О П
Р С Т У Ф Х Ц Ч
Ш Щ Ъ Ы Ь Э Ю Я

 

 

Ключевые слова страницы: Интерактивный интерпретатор | курсовая работа

СтудентБанк.ру © 2014 - Банк рефератов, база студенческих работ, курсовых и дипломных работ, шпаргалок и докладов по различным дисциплинам, а также отчеты по практике и многое другое - бесплатно.
Лучшие лицензионные казино с выводом денег