Рефераты. Интерактивный интерпретатор

Рис. 10.

Окно редактора кода.

При нажатии на кнопку «Переменные» в главном окне интерпретатора отображается диалоговое окно со списком переменных среды консоли (см. рис. 11). Переменные отображаются вместе с их значениями (приведенными к строковому типу). Данное окно позволяет удалить выбранную или все переменные из памяти. Этому окну соответствует класс VariablesForm. При нажатии кнопки «Перезапуск» производится перезапуск интерпретатора (возможно, с прерыванием зациклившейся или долго работающей пользовательской функции). При перезапуске не восстанавливаются измененные значения переменных среды консоли, поэтому предусмотрена возможность сохранения значений переменных. Сохранение переменных происходит автоматически при выходе из интерпретатора и вручную при нажатии кнопки «Сохранить переменные». Переменные сохраняются в двоичном файл variables, который автоматически создается в рабочем каталоге интерпретатора, и считываются из него при запуске или перезапуске интерпретатора. Сохранять переменные вручную имеет смысл перед запуском пользовательской функции, которая может зациклиться или слишком долго работать, чтобы можно было прервать ее работу, не опасаясь потерять результаты предыдущих вычислений. Работа с переменными осуществляется с помощью методов класса Facade, обращающихся к соответствующим методам классов из пространства имен interpr.logic.

Классы, относящиеся к пользовательскому интерфейсу интерпретатора, показаны на диаграмме на рис. 12.

Рис. 11.

Окно «Переменные».

Рис. 12.

Классы, связанные с графическим интерфейсом пользователя.

Взаимодействие подсистем интерпретатора. Класс Facade.

Как уже было сказано выше, класс Facade является посредником между двумя основными подсистемами - графическим интерфейсом пользователя и логикой работы интерпретатора. Здесь использован паттерн Facade. Все обращения извне к классам пространства имен interpr.logic производятся через вызов методов класса Facade. Сама же подсистема логики работы интерпретатора не хранит ссылок, как это требует данный паттерн, ни на класс Facade, ни на другие классы, не входящие в нее. Таким образом, класс Facade является как бы мостом между пространством имен interpr.logic и классами, реализующими пользовательский интерфейс.

При запуске интерпретатора в обработчике события Load класса Form1 происходит начальная инициализация приложения. Вначале вызывается статический метод Facade.Create(), которому передается ссылка на элемент управления ConsoleBox, расположенный на главной форме интерпретатора. Тип этого параметра - интерфейс IConsole. Переданная ссылка но объект консоли присваивается свойству InterprEnvironment.CurrentConsole. В методе Facade.Create() создается единственный объект класса Facade, к которому в дальнейшем доступ осуществляется через статическое свойство только для чтения Facade.Instance. Здесь используется паттерн Singleton.

При первом обращении к свойству InterprEnvironment.Instance вызывается конструктор класса InterprEnvironment, В нем создается объект ConsoleNamespace для пространства имен консоли. Затем производится восстановление переменных, сохраненных в файле variables в рабочем каталоге интерпретатора. Если этот файл отсутствует, то он создается (пустой) и восстановление не производится. Данный файл является двоичным. В его начале записывается общее число переменных, затем для каждой из них сохраняется информация о типе (один символ), имя (строка) и значение. Для массива после имени записывается общее число элементов, затем каждый из элементов в виде пары «тип-значение». Восстановление переменных производится в методе ConsoleNamespace.Restore(). Если восстановление не прошло успешно по причине неправильного формата файла variables, то в методе Restore() генерируется исключение NamespaceSerialisationException. Оно перехватывается в конструкторе класса InterprEnvironment, в результате чего изменяется значение соответствующего поля, после этого свойство InterprEnvironment.NotRestored, как и обращающееся к нему свойство Facade.NotRestored, возвращает истину. В случае, если такая ошибка произошла, в обработчике Form1.Form1_Load выдается соответствующее сообщение пользователю.

На следующем шаге инициализации устанавливается обработчик для события Facade.Done (завершение выполнения команды). Затем загружаются пользовательские функции с помощью метода Facade.LoadSubs(), вызывающего метод InterprEnvironment.LoadSubs(). Если при загрузке какой-либо функции произошла ошибка, сообщение выводится на консоль. Наконец, вызывается метод Prompt() (вывести приглашение и ждать ввода команды) элемента управления ConsoleBox, расположенного на главной форме.

Класс Facade имеет целый ряд методов для работы с пользовательскими функциями и переменными среды консоли, которые вызывают соответствующие методы объекта InterprEnvironment.Instance. Среди них: LoadSub(), LoadSubs(), GetSubs(), UnloadSub(), GetVariables(), DeleteVariable(), SaveVariables(). Через эти методы производятся операции во многих обработчиках событий пользовательского интерфейса.

Но, пожалуй, наиболее важным из методов класса Facade является ExecuteCommand() - выполнить команду. Он вызывается в обработчике события GetCommand элемента ConsoleBox на главной форме. В нем в отдельном потоке запускается на выполнение частный метод ThrStart(), в котором введенная с консоли команда сначала «компилируется» методом LineCompiler.CompileCommand(), затем выполняется, по окончании чего генерируется событие Facade.Done(), в обработчике которого консоль переводится в состояние ожидания следующей команды методом ConsoleBox.Prompt(). И «компиляция» и выполнение команды производятся в блоках try, в случае возникновения исключения на консоль выдается соответствующее сообщение об ошибке.

Необходимость выполнять команды в отдельном потоке связана с тем, что только в этом случае можно прервать зациклившуюся или долго работающую пользовательскую функцию без аварийного завершения интерпретатора. Для перезапуска интерпретатора, возможно, с прерыванием работы пользовательской функции, предназначен метод Facade.Restart(). В нем в отдельном потоке запускается метод DoRestart(), в котором выполняются следующие действия. Во-первых, если в данный момент времени выполняется команда, то вызывается статический метод Subroutine.Moment.Break().В нем с помощью метода Interlocked.Exchange() (безопасное при параллельном выполнении присваивание) статическому полю Subroutine.Moment.s_break присваивается значение 1. На каждой итерации цикла в методе Subroutine.Moment.Run(), помимо выполнения очередного оператора функции, проверяется значение этого поля. Если оно равно единице, то генерируется исключение CalcException, то есть выполнение команды завершается с ошибкой времени выполнения. После вызова Subroutine.Moment.Break() в методе DoRestart() следует цикл без тела, который выполняется до тех пор, пока выполнение команды не будет завершено, чего, конечно же, не приходится долго ждать. После того, как выполнение будет прервано, производится повторная инициализация, аналогичная происходящей при запуске интерпретатора.

Для реализации многопоточности используется стандартный класс System.Threading.Thread. Его конструктору передается один параметр типа делегата System.Threading.ThreadStart (процедура без параметров). Метод, на который указывает этот делегат, начинает выполняться в отдельном потоке при вызове метода Start() объекта потока. Когда метод, запущенный в потоке, возвращается, выполнение потока завершается. Повторное использование того же объекта класса Thread невозможно, его нужно создавать заново. При использовании многопоточности следует принимать ряд мер предосторожности для обеспечения безопасного доступа к общим данным. Например, присваивание значений переменным, используемым несколькими потоками, по возможности следует производить с помощью метода Interlocked.Exchange, который гарантирует атомарность операции, то есть то, что ее выполнение не будет прервано до полного завершения для передачи управления другому потоку. Также обращаться к методам и свойствам элементов графического интерфейса пользователя напрямую можно только из того же потока, в котором они были созданы. Если необходимо воздействовать на графический интерфейс пользователя из других потоков, то это следует делать в методе (процедуре без параметров), вызываемом с помощью делегата System.Windows.Forms.MethodInvoker. В языке C# имеются и другие средства синхронизации работы потоков, которые не используются в данном интерпретаторе.

Заключение

Мною выполнен интерпретатор несложного языка программирования. Интерпретатор работает в интерактивном режиме, выполняя команды, вводимые с консоли, которые могут содержать вызовы пользовательских функций (подпрограмм). Пользовательские функции могут содержать структурные конструкции - циклы, ветвления, вызовы других функций (возможна и рекурсия). Возможна работа с числовыми и строковыми данными, а также с одномерными массивами. Имеется достаточно большое число встроенных математических и других функций. Предварительного объявления переменных не требуется, синтаксис математический выражений - традиционный для языков высокого уровня. Это делает интерпретатор удобным в использовании. Данный интерпретатор может применяться как в учебных целях, например, для обучения школьников основам программирования, так и качестве «программируемого микрокалькулятора» для практических расчетов, сложность которых не требует применения специфического программного обеспечения.

Приложение. Исходный текст (сокращенно).

Ввиду большого объема исходного кода, приведены лишь наиболее важные его фрагменты.

1. Класс VarBase.

using System;

using System.IO;

namespace interpr.logic.vartypes {

public abstract class VarBase : ICloneable , IComputable {

public bool IsArray() {

return (this is ArrayVar);

}

public bool IsNum() {

return (this is NumVar);

}

public bool IsString() {

return (this is StringVar);

}

public bool IsInt() {

return (this is IntVar);

}

public bool IsReal() {

return (this is RealVar);

}

public bool IsSingle() {

return (this is SingleVar);

}

public virtual VarBase Compute() {

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.