Транслятор как программа или техническое средство, выполняющее трансляцию программы. Рассмотрение основных особенностей постройки лексического анализатора. Знакомство с этапами разработки транслятора с ограниченного подмножества языка высокого уровня.
Аннотация к работе
В информатике лексический анализ - процесс аналитического разбора входной последовательности символов (например, такой как исходный код на одном из языков программирования) с целью получения на выходе последовательности символов, называемых «токенами «(подобно группировке букв в слова). Особенностью архитектуры является то, что все действия выполняются только над элементами в вершине стека, результаты операций также помещаются в вершину стека . Если операция имеет 2 операнда, то ее выполнения подразумевает перенос элемента из вершины стека в регистр-аккумулятор и «понижение» на один элемент вниз указателя стека. Грамматика определяется, как следующая четверка Vt - алфавит, символы которого называются терминалами из них строятся цепочки порождаемые грамматикой; Vn-алфавит, символы которого называется нетерминальными (не терминалами), используются при построении цепочек. Как правило, транслятор состоит из основных функциональных блоков, к которым относятся парсер файлов исходного языка, таблица идентификаторов, лексический анализатор, синтаксический анализатор и генератор кода.В данной курсовой работе была рассмотрена разработка транслятора, в среде VISUALSTUDIO 2008, на языке C#. Поставленная цель была достигнута путем решения следующих задач: · Построен лексический анализатор с отлавливанием ошибок на данном этапе трансляции.
Введение
Как известно, целью трансляции является преобразование исходного текста программы в текст, который будет понятен адресату. В качестве адресата может выступать как программное, так и техническое средство. Следовательно, с развитием вычислительных систем разработка качественного транслятора остается актуальной темой. Известно, что транслятор имеет ряд характеристик: · Корректная обработка исходного(входного) текста.
· Наличие на выходе корректного результата обработки исходного текста. Выше перечисленные пункты значимы при разработке, потому что транслятор, который будет некорректно обрабатывать входные данные, или иметь на выходе ложный результат, никому не нужен. Так же очень важна скорость обработки входных данных, поэтому оптимизация играет не малую роль. Целью данной курсовой работы является разработка транслятора. Для достижения поставленной цели необходимо решить следующие задачи: · Представить синтаксис языка в БНФ. Определить терминалы, нетерминалы, начальный символ и набор правил для данного языка.
· Создать каркас транслятора.
· Построить лексический анализатор. Результатом работы анализатора должна быть таблица лексем.
· Построить синтаксический анализатор. Приведение выражений к обратной польской записи.
· Построить генератор кода.
1.Теоретическая часть
1.1 Транслятор
Транслятор - программа или техническое средство, выполняющее трансляцию программы. Цель трансляции - преобразовать текст с одного языка на другой, который понятен адресату текста. В случае программ-трансляторов, адресатом является техническое устройство (процессор) или программа-интерпретатор.
Язык процессоров (машинный код) обычно является низкоуровневым. Существуют платформы, использующие в качестве машинного язык высокого уровня , но они являются исключением из правила в силу сложности и дороговизны. Транслятор, который преобразует программы в машинный язык, принимаемый и исполняемый непосредственно процессором, называется компилятором.
Процесс компиляции как правило состоит из нескольких этапов: лексического, синтаксического и семантического анализов, генерации промежуточного кода, оптимизации и генерации результирующего машинного кода. Помимо этого, программа как правило зависит от сервисов, предоставляемых операционной системой и сторонними библиотеками (например, файловый ввод-вывод или графический интерфейс), и машинный код программы необходимо связать с этими сервисами. Связывание со статическими библиотеками выполняется редактором связей или компоновщиком (который может представлять собой отдельную программу или быть частью компилятора), а с операционной системой и динамическими библиотеками связывание выполняется при начале исполнения программы загрузчиком.
Достоинство компилятора: программа компилируется один раз и при каждом выполнении не требуется дополнительных преобразований. Соответственно, не требуется наличие компилятора на целевой машине, для которой компилируется программа. Недостаток: отдельный этап компиляции замедляет написание и отладку и затрудняет исполнение небольших, несложных или разовых программ.
1.2 Лексический анализатор
В информатике лексический анализ - процесс аналитического разбора входной последовательности символов (например, такой как исходный код на одном из языков программирования) с целью получения на выходе последовательности символов, называемых «токенами « (подобно группировке букв в слова). Группа символов входной последовательности, идентифицируемая на выходе процесса как токен, называется лексемой. В процессе лексического анализа производится распознавание и выделение лексем из входной последовательности символов.
Как правило, лексический анализ производится с точки зрения определенного формального языка или набора языков. Язык, а точнее его грамматика, задает определенный набор лексем, которые могут встретиться на входе процесса.
Традиционно принято организовывать процесс лексического анализа, рассматривая входную последовательность символов как поток символов. При такой организации процесс самостоятельно управляет выборкой отдельных символов из входного потока. Распознавание лексем в контексте грамматики обычно производится путем их идентификации (или классификации) согласно идентификаторам (или классам) токенов, определяемых грамматикой языка. При этом любая последовательность символов входного потока (лексема), которая согласно грамматике не может быть идентифицирована как токен языка, обычно рассматривается как специальныйтокен-ошибка.
Каждый токен можно представить в виде структуры, содержащей идентификатор токена (или идентификатор класса токена) и, если нужно, последовательность символов лексемы, выделенной из входного потока.
Цель такой конвертации обычно состоит в том, чтобы подготовить входную последовательность для другой программы, например для грамматического анализатора, и избавить его от определения лексических подробностей в контекстно-свободной грамматике (что привело бы к усложнению грамматики).
1.3 Синтаксический анализатор
В информатике, синтаксический анализ - это процесс сопоставления линейной последовательности лексем (слов, токенов) языка с его формальной грамматикой. Результатом обычно является дерево разбора (синтаксическое дерево). Обычно применяется совместно с лексическим анализом. Синтаксический анализатор (парсер) - это программа или часть программы, выполняющая синтаксический анализ.
При парсинге исходный текст преобразуется в структуру данных, обычно - в дерево, которое отражает синтаксическую структуру входной последовательности и хорошо подходит для дальнейшей обработки.
Как правило, результатом синтаксического анализа является синтаксическая структура предложения, представленная либо в виде дерева зависимостей, либо в виде дерева составляющих, либо в виде некоторой комбинации первого и второго способов представления.
1.4 Генератор кода
Последней фазой компиляции является генерация кода. Результатом выполнения этой фазы обычно является программа в выполняемых кодах той ЭВМ, на которой она должна выполняться. Однако в ряде случаев в качестве выходного языка транслятора используют ассемблер. В данной работе мы будем генерировать программу на языке ассемблера. Чтобы облегчить написание генератора кода и освободить его от посторонних соображений, связанных с конкретными особенностями какой-либо ЭВМ, будем использовать гипотетический процессор (виртуальную машину). Этот процессор не существует на самом деле (в аппаратном виде). При выборе его архитектуры требовалась максимальная простота и, в то же время, возможность легко выполнять на нем программы на языках, реализуемых в процессе выполнения лабораторных работ и курсового проектирования. Особенностью архитектуры является то, что все действия выполняются только над элементами в вершине стека, результаты операций также помещаются в вершину стека . Поэтому в арифметических и логических операциях нет необходимости в указании адреса операндов. Если операция имеет 2 операнда, то ее выполнения подразумевает перенос элемента из вершины стека в регистр-аккумулятор и «понижение» на один элемент вниз указателя стека. Второй операнд, оказавшийся в вершине стека, подается непосредственно в АЛУ. Результат операции помещается в вершину стека вместо него.
2.Практическая часть
2.1 Синтаксис языка в БНФ. Терминалы, нетерминалы, начальный символ и правила
Вариант задания: ::= ::= BEGINEND ::= Int |
Int
::= ; | ,::= |
::= := ;
::= | :: = ( ) | |
::= "-"
::= "-" | " " | "*" | "/"
::= |
::= |
::= |
Форма Бекуса - Наура - набор правил, последовательным применением которых можно построить любое предложение.
Грамматика определяется, как следующая четверка Vt - алфавит, символы которого называются терминалами из них строятся цепочки порождаемые грамматикой; Vn- алфавит, символы которого называется нетерминальными (не терминалами), используются при построении цепочек. P - Набор правил, по которым строится грамматика; S - начальное правило.
Как правило, транслятор состоит из основных функциональных блоков, к которым относятся парсер файлов исходного языка, таблица идентификаторов, лексический анализатор, синтаксический анализатор и генератор кода.
Создадим классы и перечисления необходимые в дальнейшем для реализации всех частей транслятора: ///
Дальше создаем необходимый для удобной работы интерфейс (рис.1), с верхним меню:
Рис.1 Интерфейс приложения
2.3 Лексический анализатор
Для задания типа лексемы обычно используется перечисление, включающее в себя все возможные в исходном языке типы: ключевые слова, операторы, идентификаторы и числа.
Далее создаем отдельную структуру, описывающую данный тип лексемы: ///
///Ключевоеслово
///
INTERNALSTRUCTKEYWORD
{ publicstring word;
PUBLICLEXEMSLEX;
} publicstaticvoid Initialize()
{ keywords = NEWKEYWORD[10];
KEYWORDPOINTER = 0;
CURRENTLEXEM = 0;
CURRENTNAME = null;
ADDKEYWORD("Begin", Lexems.Begin);
ADDKEYWORD("End", Lexems.End);
ADDKEYWORD("Integer", Lexems.Int);
ADDKEYWORD("Long Integer", Lexems.LONGINT);
ADDKEYWORD("Print", Lexems.Print);
ADDKEYWORD("UNTIL", Lexems.Until);
ADDKEYWORD("DO", Lexems.Do);
ADDKEYWORD("ENDUNTIL", Lexems.ENDUNTIL);
}
В рамках класса LEXICALANALYZERБУДЕМ реализовывать методы лексического анализа: хранение ключевых слов языка и их распознавание, разбор текущей лексемы в зависимости от ее типа.
Далее создаем различные методы, для синтаксического анализа отдельных частей исходного кода: Блок объявления переменных, блок непосредственно выполнения каких либо операция, блок вывода на печать, блок цикла UNTIL.
LOAD n - поместить переменную, размещенную по адресу n в вершину стека.
STO n - запись значения из вершины стека по адресу n (присваивание).
JMP k - безусловный переход к команде, расположенной по адресу k.
JEQ k - переход к команде, расположенной по адресу k в случае равенства двух верхних элементов стека.
JLT k - переход к команде, расположенной по адресу k, если число в вершине стека меньше следующего за ним числа стека.
JLE k - переход к команде, расположенной по адресу k, если число в вершине стека меньше или равно следующему за ним числу стека.
JGT k - переход к команде, расположенной по адресу k, если число в вершине стека больше следующего за ним числа стека.
JGE k - переход к команде, расположенной по адресу k, если число в вершине стека больше или равно следующему за ним числу стека.
JNE k - переход к команде, расположенной по адресу k в случае неравенства двух верхних элементов стека.
ADR - содержимое регистра адреса данных помещается в вершину стека.
STAD - содержимое вершины стека помещается в регистр адреса данных.
ADD - сложение двух верхних элементов стека, результат помещается в вершину стека.
MUL - умножение двух верхних элементов стека, результат помещается в вершину стека.
SUB - вычитание элемента в вершине стека из следующего за ним элемента стека, результат помещается в вершину стека.
DIV - деление на элемент в вершине стека следующего за ним элемента стека, результат помещается в вершину стека.
AND - логическое "И" (логическое умножение) двух верхних элементов стека, результат помещается в вершину стека.
OR - логическое "ИЛИ" (логическое сложение) двух верхних элементов стека, результат помещается в вершину стека.
DIV - деление на элемент в вершине стека следующего за ним
XOR - сложение по модулю 2 двух верхних элементов стека, результат помещается в вершину стека. NOT - знаковая инверсия элемента в вершине стека
NOL - поразрядная логическая инверсия элемента в вершине стека. NOP - пустая операция .
Так как наш транслятор является однопроходным, методы генератора кода будут вызываться из методов синтаксического анализатора. По мере выполнения синтаксического разбора будет генерироваться и ассемблерный код. Для удобства будем хранить его в массиве строк.
Для реализации арифметических (и не только) операций в языке ассемблер используется работа с регистровой памятью. Так, чтобы поместить значение (числовую константу или переменную) в регистр, используется инструкция mov. Команды для арифметических операций приведены в таблице ниже.
Таблица
Операция Инструкция ассемблера
add
- sub
* mul
/ div
Таким образом, код для выражения a b будет выглядеть следующим образом: mov ax, a movbx b addax, bx
Результат этой последовательности команд будет сохранен в регистре ax. Но в ситуации со сложными выражениями необходимо где-то сохранять промежуточные результаты. Для этих целей мы будем использовать ассемблерный стек: инструкция push - помещение значения регистра на верхушку стека, pop - извлечение из стека в регистр. После каждой атомарной операции мы будем сохранять результат в стек, чтобы не потерять его.
3.Тестирование приложения
Так как наше приложение должно отлавливать ошибки на любом из трех этапов выполнения, для проверки правильности работы, необходимо протестировать программу. Будем подавать ей на вход несколько примеров исходного кода, как правильного, так и заведомо некорректного, для того, чтобы проверить этапы выполнения.
Пример 1: Inta,s,r;
Begin a=c b;
Print a;
End
Результат выполнения программы: Таблица
Пример 2: Integer a,s,r;
Begin s=5;
r=9;
u=0;
a=s r;
s=((a*4)/2)ggg*r 10;
ENDРЕЗУЛЬТАТ: Таблица
Вывод
В данной курсовой работе была рассмотрена разработка транслятора, в среде VISUALSTUDIO 2008, на языке C#.
Поставленная цель была достигнута путем решения следующих задач: · Построен лексический анализатор с отлавливанием ошибок на данном этапе трансляции.
· Построен синтаксический анализатор с отлавливанием ошибок на данном этапе трансляции.
· Построен генератор кода основных блоков исходной программы, соответствующей заданному языку, а также дополнительных блоков SWITCH и WHILE
· Проведено тестирование приложения, для проверки правильности работы.