скачать рефераты
  RSS    

Меню

Быстрый поиск

скачать рефераты

скачать рефератыКурсовая работа: Интерактивный интерпретатор

·          Константы записываются в результат как объекты классов, представляющих соответствующие типы данных, переменные – как объекты VarName, операции и вызовы функций – как объекты Call.

Рассмотрим пример. Пусть имеется строка (a*c+-b{а+с})/а. Применим описанный алгоритм.

1.   Вначале стек операндов и результат пусты.

2.   Первая лексема – открывающая круглая скобка. Записываем ее в стек.

Стек: (Результат: <пусто>.

3.   Вторая лексема – идентификатор «а». За ним нет открывающей квадратной или фигурной скобки, поэтому записываем его в результат.

4.   Стек: (Результат: а

5.   Следующая лексема – операция умножения. Записываем ее в стек. На вершине стека нет операций с большим или равным приоритетом, ничего выталкивать не нужно.

6.   Стек: (*Результат: а

7.   Вторая лексема – идентификатор «с». За ним нет открывающей квадратной или фигурной скобки, поэтому записываем его в результат.

Стек: (*Результат: ас

8.   Следующая лексема – знак «+». Перед ним находится идентификатор, поэтому он является знаком операции сложения. Он выталкивает из стека операцию умножения как имеющую более высокий приоритет, затем  сам дописывается в стек.

9.   Стек: (+ Результат: ас*

10.      Следующая лексема знак «минус». Перед ним нет ни закрывающей скобки ни идентификатора, поэтому он является знаком операции унарный минус (обозначим ее как «_»), записываем ее в стек.

11.      Стек: (+_Результат: ас*

12.      Следующая лексема идентификатор b. За ним следует фигурная скобка, поэтому он рассматривается как имя массива. В фигурных скобках находится строка «а+с», которая, будучи преобразованной по рассматриваемому алгоритму, даст в результате «ас+». Допишем это в результат разбора исходного выражения. Затем допишем в результат имя массива («b») и операцию индексации (обозначим ее «{}»). И, наконец, вытолкнем находящуюся на вершине стека операцию унарный минус.

13.      Стек: (+Результат: ас*ас+b{}_

14.      Следующая (за закрывающей фигурной скобкой) лексема – закрывающая круглая скобка. Она вытолкнет из стека в результат находящуюся перед открывающей скобкой операцию сложения, затем открывающая скобка будет удалена из стека.

Стек; <пусто>Результат: ac*ac+b{}_+

15.      Следующая лексема операция деления. Она дописывается в стек (перед этим стек пуст, ничего выталкивать не нужно).

Стек: /Результат: ac*ac+b{}_+

16.      Последняя лексема идентификатор «а». После него нет никаких скобок, поэтому он сразу же добавляется к результату.

Стек: /Результат: ac*ac+b{}_+a

17.      В конце выталкиваем из стека оставшуюся в нем операцию умножения в результат. Итого получаем ac*ac+_b{}+a/, что является обратной польской записью исходного выражения.

При загрузке функции обработка ее текста осуществляется в конструкторе класса Subroutine, который принимает два параметра имя функции и текст функции (в виде массива строк). При этом отдельно рассматривается первая строка – заголовок функции. Для ее анализа используется private-метод Subroutine.AnalyseHeader(), в котором проверяется соответствие этой строки требуемому формату и извлекается список формальных параметров. Также проверяется соответствие имени функции в заголовке требуемому (первому параметру конструктора). При этом используется объект класса Parser. Затем по очереди подвергаются разбору с помощью метода LineCompiler.CompileOperator() остальные строки, результат «компиляции» каждой из которых добавляется в список операторов функции. При этом используется стек вложенности операторов (применяется объект класса System.Collections.Stack). После обработки каждой строки проверяется тип полученного оператора с помощью метода IOperator.GetType(). Если оператор открывает блок кода (if, elseif, else, while, for), то его номер заносится в стек. Если оператор закрывает блок кода, то из стека извлекается номер парного оператора и присваиваются необходимые значения свойствам NextPos, LoopPos и т. д. соответствующих объектов. Операторы elseif и else рассматриваются одновременно и как закрывающие расположенный выше блок кода, и как открывающие следующий. Нужно отметить, что в первый элемент списка операторов функции (с нулевым индексом) в объекте Subroutine помещается пустой оператор (объект EmptyCommand), благодаря чему каждой строке текста функции соответствует элемент этого списка с индексом, равным номеру этой строки. Основная часть кода конструктора класса Subroutine находится в блоке try, при возникновении исключения SyntaxErrorException в котором генерируется исключение класса LineSyntaxException, объект которого содержит информацию о месте ошибки (имя функции и номер строки).


Графический интерфейс пользователя.

Главной форме приложения, которая изображена на рис. 7, соответствует класс Form1. Основную часть формы занимает компонент ConsoleBox, созданный на основе класса UserControl. Он включает в себя один экземпляр компонента RichTextBox, «растянутый» с помощью свойства Dock на всю доступную площадь. Компонент ConsoleBox представляет собой окно консоли, в которой пользователь вводит команды, и на которую выводятся результаты работы команд. Класс ConsoleBox является единственным классом в окончательной версии проекта, реализующим рассмотренный выше интерфейс IConsole. Важнейшие члены класса ConsoleBox:

·     методы Print(string)  и PrintLn(string) – реализуют методы интерфейса IConsole, производят вывод текста в окно консоли.

·     метод Prompt() – выводит приглашение командной строки (“>>>”) и переводит консоль в режим ожидания команды.

·     событие GetCommand (object sender, ConsoleBoxGetCommandEventArgs e) – возникает, когда в режиме ожидания команды была нажата клавиша Enter. При этом в параметре e, имеющем тип класса ConsoleBoxGetCommandEventArgs, который унаследован от System.EventArgs, в свойстве Command содержится введенная пользователем команда в виде строки.

Методы Print, PrintLn и Prompt рассчитаны на безопасное использование из другого потока. В них используется вызов private-методов через объект класса System.Windows.Forms.MethodInvoker. Возможны два состояния компонента консоли – режим ожидания ввода команды и режим работы команды. Ввод текста в поле RichTextBox допускается только в режиме ожидания ввода команды и только после последнего приглашения командной строки, что обеспечивается с помощью свойства RichTextBox.SelectionProtected. Вызов метода Prompt() переводит консоль в режим ожидания команды. При нажатии Enter в режиме ожидания команды, помимо генерации события GetCommand, происходит переход из режима ожидания в режим работы команды.


        

Рис. 7.

Главная форма.

При нажатии кнопки «Функции» на главной форме выводится диалоговое окно, которому соответствует класс FunctionsForm (см. рис. 8). В этом окне в верхнем поле отображается список успешно загруженных функций, в нижнем – функций, загрузка которых прошла неудачно по причине наличия синтаксических ошибок. Кнопки позволяют редактировать, удалить (в этом случае требуется подтверждение) выбранную функцию, создать новую функцию (в этом случае будет запрошено имя функции, и, если оно не является корректным идентификатором, функция создана не будет). Для запроса имени при создании функции используется форма, описывающаяся классом InputForm (см. рис. 9). Если функция создана успешно, она открывается для редактирования. При двойном щелчке по имени функции в любом из списков в окне «Функции» также она открывается для редактирования. Окно «Функции» является модальным диалогом и должно быть закрыто для продолжения работы с интерпретатором. Оно закрывается при открытии функции для редактирования. При этом вместо него на экране появляется окно редактора кода.

Рис. 8.

Окно «Функции»

        

Рис. 9.

Окно ввода имени создаваемой функции.

Окну редактора кода соответствует класс EditorForm (см. рис. 10). Кнопка «Сохранить» в нем сохраняет функцию в файле, расположенном в подкаталоге subroutines рабочего каталога интерпретатора, с именем, совпадающим с именем функции (без расширения). Кнопка «Выход» - закрывает окно редактора (с запросом на сохранение). В метке справа от кнопок отображается номер строки текущего положения курсора (начала выделения) в тексте. В ее текст номер текущей строки заносится приблизительно 10 раз в секунду, что обеспечивается с помощью таймера (компонент System.Windows.Forms.Timer).Окно редактора кода не является модальным – в любой момент работы с интерпретатором может быть открыто сколько угодно таких окон для разных функций. Заблокированы открытие функции второй раз (в двух окнах одновременно) и выход из интерпретатора до закрытия всех окон редактора кода. Основную часть окна редактора кода составляет компонент SourceBox, который также как и ConsoleBox, унаследован от  классаUserControl. Он содержит элемент управления RichTextBox, в котором, собственно, и осуществляется редактирование текста функции, и элемент TextBox, расположенный за RichTextBox на заднем плане и невидимый для пользователя. На него переключается фокус на время выполнения синтаксического цветовыделения, так как для изменения цвета фрагмента текста в RichTextBox необходимо этот фрагмент выделить, что приводило бы к заметному мерцанию текста, если бы фокус ввода оставался у поля RichTextBox. Такой подход к решению проблемы позволяет реализовать синтаксическое цветовыделение с использованием свойств класса RichTextBox небольшим объемом кода (иначе бы пришлось производить «ручную» перерисовку с непосредственным использованием GDI+). Но к сожалению, заметно снижается быстродействие, в связи с этим были введены следующие ограничения: синтаксическое цветовыделение производится только при изменении номера строки, в которой находится курсор, например, при нажатии Enter, а также при щелчке левой кнопкой мыши в окне редактора (в RichTextBox). При этом обрабатываются только строки текста, отображаемые в данный момент времени в окне. Конечно, это несколько неудобно для пользователя, подобное можно наблюдать, например, в такой среде программирования, как MS Visual Basic 6. Для выполнения синтаксического цветовыделения используется вложенный private-класс HighlightParser, который имеет методы для разбора строки на отдельные лексемы, для определения положения в строке и типа этих лексем. Применить класс interpr.logic.Parser здесь нельзя, так как он работает с преобразованной строкой (удалены лишние пробелы и комментарии). Класс SourceBox также имеет методы для чтения текста функции из файла и сохранения текста в файле.


 

Рис. 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(). Через эти методы производятся операции во многих обработчиках событий пользовательского интерфейса.

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


Новости

Быстрый поиск

Группа вКонтакте: новости

Пока нет

Новости в Twitter и Facebook

  скачать рефераты              скачать рефераты

Новости

скачать рефераты

© 2010.