Uploaded by Panda Panda

пз

advertisement
Министерство науки и высшего образования Российской Федерации
Муромский институт (филиал)
федерального государственного бюджетного образовательного учреждения высшего образования
«Владимирский государственный университет
имени Александра Григорьевича и Николая Григорьевича Столетовых»
(МИВлГУ)
Факультет
ИТР
Кафедра
ПИн
КУРСОВАЯ
РАБОТА
по
дисциплине
Теория автоматов и формальных языков
(наименование дисциплины)
Тема
Транслятор с подмножества языка C
Руководитель
(оценка)
(фамилия, инициалы)
Члены комиссии
(подпись)
Студент
(подпись)
(дата)
ПИнз-121
(группа)
(Ф.И.О.)
Гусев С.Д.
(подпись)
(Ф.И.О.)
(фамилия, инициалы)
(подпись)
Муром 2024
(дата)
Министерство науки и высшего образования Российской Федерации
Муромский институт (филиал)
федерального государственного бюджетного образовательного учреждения высшего образования
«Владимирский государственный университет
имени Александра Григорьевича и Николая Григорьевича Столетовых»
(МИ ВлГУ)
Факультет
ФИТР
«УТВЕРЖДАЮ»
Зав. кафедрой
(подпись)
«
»
20
г.
ЗАДАНИЕ
Теория автоматов и формальных языков
Студенту Гусеву Сергею Денисовичу
гр. ПИНз-121
Транслятор с подмножества языка C
На курсовую работу по курсу
1. Тема проекта
2. Сроки сдачи студентом законченного проекта 18 мая 2024
3. Исходные данные к проекту
3.1 Язык для трансляции – C
3.2 Обеспечить развернутую диагностику ошибок (лексических, синтаксических, семантических)
3.3 Разработать формальную грамматику заданного подмножества языка
3.4 Синтаксический разбор – на основе L_ -грамматик
3.5 Подготовить несколько тестовых программ, содержащих все заданные элементы языка
3.6 В языке поддерживаются
– case чувствительные идентификаторы длинной до 7 символов;
– 2,4 байтные шестнадцатеричные константы;
– директива описания переменных типа int, long;
– простой арифметический оператор; оператор инкремента;
– оператор цикла for (;;) с предусловием;
3.7 Пример программы на заданном языке
main ()
{ int a,b;
long c;
c :=0;
for (b=1; b<5; b++)
c:=c+b;
}
4. Содержание расчетно-пояснительной записки (перечень подлежащих разработке вопросов)
Аннотация (на двух языках)
4.1 Введение
4.2 Анализ технического задания
4.3 Описание грамматики языка
4.4 Разработка архитектуры системы и алгоритмов
4.5 Тестирование
4.6 Руководство программиста
4.7 Руководство пользователя
Заключение
5. Перечень графического материала (с точным указанием обязательных чертежей и графиков)
5.1 структурная или функциональная схема программы
5.2 текст программы с комментариями
5.3 скриншоты окон программы
6. Рекомендуемая литература
6.1 Холкина, Н.Е. Введение в формальные грамматики и методы трансляции: учебное пособие.
– Муром: ИПЦ МИ ВлГУ, 2012. – 145 с.
6.2 Малявко, А. А. Формальные языки и компиляторы : учебник. — Новосибирск : НГТУ, 2014.
– 431 c. — Текст : электронный [сайт]. — URL: https://www.iprbookshop.ru/
6.3 Пентус, А. Е. Математическая теория формальных языков : учебное пособие.
– М.: ИНТУИТ, Ай Пи Ар Медиа, 2020. — 218 c. — URL: https://www.iprbookshop.ru/97548
7. Дата выдачи задания 17.02.2024
8. Календарный график работы над проектом (на весь период проектирования)
(с указанием сроков выполнения и трудоемкости отдельных этапов)
8.1 Введение, анализ ТЗ, описание грамматики языка
10%, 6 нед.
8.2 Разработка архитектуры системы и алгоритмов
20%, 8 нед.
8.3 Реализация алгоритмов
60%, 12 нед
8.4 Тестирование
80%, 16 нед
8.5 Оформление пояснительной записки
100%, 17 нед.
Руководитель
Задание принял к исполнению (дата)
Подпись студента
(подпись)
АННОТАЦИЯ
В данной курсовой работе был разработан транслятор подмножества
языка программирования «С». Транслятор предназначен для трансляции
программ на языке «C» в код ассемблера. Данное подмножество включает в
себя целочисленные переменные типа «int» и «long», арифметические
выражения,
операторы
присваивания,
инкремента
и
цикла
«for».
Синтаксический анализ проводился методом рекурсивного спуска на основе
LL(1)-грамматики.
In this course project, the translator of “C” the programming language subset
was developed. The translator is designed for “C” language program translation to
assembler code. The subset includes integer variables of “int” and “long” type,
arithmetical expressions, assign, increment and “for” cycle operators. Syntax
analysis was made using recursive descent method driven by LL(1)-grammar.
СОДЕРЖАНИЕ
Введение ................................................................................................................... 3
1. Анализ технического задания ............................................................................ 4
2. Описание грамматики языка .............................................................................. 6
3. Разработка архитектуры системы и алгоритмов .............................................. 9
4. Методика испытаний ........................................................................................ 14
5. Руководство пользователя ................................................................................ 24
6. Руководство программиста .............................................................................. 26
Заключение ............................................................................................................ 32
Список литературы ............................................................................................... 33
Приложение А. Исходные коды классов транслятора ...................................... 35
Изм. Лист
№ докум.
Разраб.
Гусев С.Д.
Провер.
Реценз.
Н. Контр.
Утверд.
Подпись Дата
МИВУ.09.03.04-05.000
Лит.
Транслятор с
подмножества языка С
У
КР
Лист
Листов
2
68
ПИнз-121
Введение
Несмотря на то, что к настоящему времени разработаны тысячи
различных языков и их компиляторов, процесс создания новых приложений в
этой области не прекращается. Это связно как с развитием технологии
производства вычислительных систем, так и с необходимостью решения все
более сложных прикладных задач. Такая разработка может быть обусловлена
различными причинами, в частности, функциональными ограничениями,
отсутствием
локализации,
низкой
эффективностью
существующих
компиляторов. Поэтому основы теории языков и формальных грамматик, а
также практические методы разработки компиляторов лежат в фундаменте
инженерного образования по информатике и вычислительной технике[1].
Цель курсовой работы:
– закрепление теоретических знаний в области теории формальных
языков, грамматик, автоматов и методов трансляции;
–
формирование
практических
умений
и
навыков
разработки
собственного транслятора подмножества языка программирования;
–
закрепление
практических
навыков
самостоятельного
решения
инженерных задач, развитие творческих способностей студентов и умений
пользоваться технической, нормативной и справочной литературой.
Задачи курсовой работы:
– разработать транслятор подмножества языка С в код ассемблера.
Подмножество языка должно содержать:
– case чувствительные идентификаторы длинной до 7 символов;
– 2,4 байтные шестнадцатеричные константы;
– директива описания переменных типа int, long;
– простой арифметический оператор; оператор инкремента;
– оператор цикла for (;;) с предусловием.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
3
1. Анализ технического задания
В данной курсовой работе требуется разработать транслятор с
подмножества языка C.
Транслятор должен обеспечивать развернутую диагностику ошибок
(лексических, синтаксических, семантических).
Для
реализации
требуется
разработать
формальную
грамматику
заданного подмножества языка, синтаксический разбор – на основе LLграмматик, подготовить несколько тестовых программ, содержащих все
заданные элементы языка.
В языке поддерживаются:
– case чувствительные идентификаторы длинной до 7 символов;
– 2,4 байтные шестнадцатеричные константы;
– директива описания переменных типа int, long;
– простой арифметический оператор; оператор инкремента;
– оператор цикла for (;;) с предусловием.
Пример программы на заданном языке:
main ()
{
int a,b;
long c;
c =0;
for (b=1; b<5; b++)
c=c+b;
}
Для выполнения лексического разбора необходимо выделить типы
лексем: идентификаторы, константы, разделители; построить лексический
анализатор на основе конечного автомата, выдающий на выходе таблицу
стандартных символов.
Синтаксический разбор должен выполняться методом рекурсивного
спуска (т.к. грамматика – LL, то анализатор – нисходящий).
Семантические проверки длины идентификаторов, констант могут быть
включены в лексический анализатор, остальные проверки (использования
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
4
необъявленного идентификатора, повторное объявление идентификатора,
соответствие
типов
синтаксический
операндов
анализатор.
операции)
Вложенные
должны
области
быть
встроены
видимости
в
не
поддерживаются.
Язык реализации транслятора – C# в среде Microsoft Visual Studio.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
5
2. Описание грамматики языка
В данной работе входным языком является подмножество языка
программирования высокого уровня C.
Поддерживаются
инструкции
объявления
переменных
двух
целочисленных типов int, long. Синтаксис:
<vardef> ::= 'int' <idlist> | 'long' <idlist>,
где <idlist> – список идентификаторов.
Оператор присваивания. Синтаксис:
<assign> ::= id '=' <expr>,
где id – идентификатор, <expr> – арифметическое выражение.
Оператор инкремента (префиксный и постфиксный). Синтаксис:
<prefincr> ::= '++' id
<postincr> ::= id '++',
где id – идентификатор.
Оператор цикла for. Синтаксис:
<for> ::= 'for' '(' id '=' <expr> ';' <relop> ';' <assign_incr> ')' <op>,
где <expr> – арифметическое выражение, <relop> – операция сравнения,
<assign_incr> – оператор присваивания или инкремента, <op> – оператор языка.
Составной оператор (блок). Синтаксис:
<block> ::= '{' <ops> '}
<ops> ::= <op> ';' <ops> | ε,
где <op> – оператор.
Программа
структурно
представляет
собой
последовательность
операторов, оканчивающихся символом «;», записанных последовательно
внутри функции main:
<program> ::= 'main' '(' ')' <block>,
где <block> – составной оператор.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
6
Язык поддерживает многострочные комментарии /*…*/. Вложенные
комментарии не поддерживаются.
Грамматика входного языка в БНФ (нетерминальные символы в <>
скобках, терминальные символы в ‘’ кавычках, начальный символ <program>):
<program> ::= 'main' '(' ')' <block>
<block> ::= '{' <ops> '}'
<ops> ::= <op> ';' <ops> | ε
<op> ::= <vardef> | <assign_incr> | <block> | <for>
<vardef> ::= 'int' <idlist> | 'long' <idlist>
<idlist> ::= id <idlist_tail>
<idlist_tail> ::= ',' id <idlist_tail> | ε
<asspostincr> ::= '++' | '=' <expr>
<prefincr> ::= '++' id
<expr> ::= <term> <expr_tail>
<expr_tail> ::= '+' <term> <expr_tail> | '-' <term> <expr_tail> | ε
<term> ::= <factor> <term_tail>
<term_tail> ::= '*' <factor> <term_tail> | '/' <factor> <term_tail> | ε
<factor> ::= '(' id '=' <expr> ')' | id | const
<for> ::= 'for' '(' <assign> ';' <relop> ';' <assign_incr> ')' <op>
<relop> ::= <expr> <rop> <expr>
<rop> ::= '<' | '>' | '<=' | '>=' | '==' | '!='
<assign_incr> ::= id <asspostincr> | <prefincr>
Решающая таблица показана в таблице 1.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
7
Таблица 1
Правила
<program> ::= 'main' '(' ')' <block>
<block> ::= '{' <ops> '}'
<ops> ::= <op> ';' <ops>
<ops> ::= ε
<op> ::= <vardef>
<op> ::= id <asspostincr>
<op> ::= <prefincr>
<op> ::= <block>
<op> ::= <for>
<vardef> ::= 'int' <idlist>
<vardef> ::= 'long' <idlist>
<idlist> ::= id <idlist_tail>
<idlist_tail> ::= ',' id <idlist_tail>
<idlist_tail> ::= ε
<asspostincr> ::= '++'
<asspostincr> ::= '=' <expr>
<prefincr> ::= '++' id
<expr> ::= <term> <expr_tail>
<expr_tail>
::=
'+'
<term>
<expr_tail>
<expr_tail> ::= '-' <term> <expr_tail>
<expr_tail> ::= ε
<term> ::= <factor> <term_tail>
<term_tail>
::=
'*'
<factor>
<term_tail>
<term_tail>
::=
'/'
<factor>
<term_tail>
<term_tail> ::= ε
<factor> ::= '(' <expr> ')'
<factor> ::= id
<factor> ::= const
<for> ::= 'for' '(' id '=' <expr> ';'
<relop> ';' <expr> ')' <op>
<relop> ::= <expr> <rop> <expr>
<rop> ::= '<'
<rop> ::= '>'
<rop> ::= '<='
<rop> ::= '>='
<rop> ::= '=='
<rop> ::= '!='
Грамматика
FIRST1
FOLLOW1
'main'
'{'
'int', 'long', id, '++', '{', 'for'
ε
'int', 'long'
id
'++'
'{'
'for'
'int'
'long'
id
','
ε
'++'
'='
'++'
'(', id, const
'+'
'$'
';', '$'
'}'
'}'
';'
';'
';'
';'
';'
';'
';'
';'
';'
';'
';'
';'
';'
')', ';', '<', '>', '<=', '>=', '==', '!='
')', ';', '<', '>', '<=', '>=', '==', '!='
'-'
ε
'(', id, const
'*'
')', ';', '<', '>', '<=', '>=', '==', '!='
')', ';', '<', '>', '<=', '>=', '==', '!='
'+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
'+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
'/'
'+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
ε
'('
id
const
'for'
'+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
'*', '/', '+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
'*', '/', '+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
'*', '/', '+', '-', ')', ';', '<', '>', '<=', '>=', '==', '!='
';'
'(', id, const
'<'
'>'
'<='
'>='
'=='
'!='
';'
'(', id, const
'(', id, const
'(', id, const
'(', id, const
'(', id, const
'(', id, const
принадлежит
классу
LL(1)-грамматик
(т.к.
для
альтернативных правил для одного нетерминала множества FIRST1 попарно не
пересекаются, а для нетерминалов с пустыми правилами не пересекаются с
соответствующими множествами FOLLOW1) и пригодна для реализации
синтаксического анализатора методом рекурсивного спуска.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
8
3. Разработка архитектуры системы и алгоритмов
Укрупненная схема алгоритма программного средства представлена на
рисунке 1.
начало
А
меню
1 – Файл
2 – Трансляция
3 – Справка
4 – Выход
выбор
пункта
1
1 – Создать
2 – Открыть
3 – Сохранить
4 – Выход
выбор
пункта
подпункт
1
New
2
Open
3
А
Save
4
Б
2
Scanner
+
Parser.p
.scan
+
CodeGenerator.generateTriades;
CodeGenerator.GenerateAsmCode
arse
-
+
-
33
Б
Help
А
ошибка
А
4
конец
Рисунок 1 – Укрупненная схема алгоритма программного средства
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
9
Лексический анализатор.
Лексемы хранятся в таблицах лексем:
– TW – таблица ключевых слов.
– TN – таблица констант.
– TI – таблица идентификаторов.
– TL – таблица разделителей.
Лексемы таблицы ключевых слов: 1) main, 2) int, 3) long, 4) for.
Лексемы таблицы разделителей: 1) (, 2) ), 3) {, 4) }, 5) ;, 6) ,, 7) ++, 8) =, 9)
+, 10) -, 11) *, 12) /, 13) <, 14) <=, 15) >, 16) >=, 17) ==, 18) !=.
На выходе ЛА формируется таблица стандартных символов (таблица
лексем) – входная программа в форме последовательности лексем вида (№
таблицы, № в таблице), далее эта таблица используется синтаксическим
анализатором.
Лексический анализатор (ЛА) реализован на основе конечного автомата,
граф автомата показан на рисунке 2.
Используются процедуры:
– gc – переместиться к следующему элементу входного потока.
– add – добавить очередной символ в конец буфера.
– nill – очистить буфер.
– look(номер таблицы) – поиск содержимого буфера (лексемы) в таблице.
– put(номер таблицы) – запись лексемы в таблицу (используется для
таблиц идентификаторов и констант).
– out(номер таблицы лексем, номер лексемы в таблице) – выдать
информацию о накопленной лексеме в выходной поток. Номер таблицы и
номер лексемы в таблице задаётся веткой диаграммы состояний по которой
была собрана лексема.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
10
‘‘
gc
S
letter || digit
add, gc
letter
nill, add, gc
I
+
out(1,z)
z≠0
look(TW)
put(TI), out(3,z)
digit
nill, add, gc
D1
put(TN), out(4,z)
digit || A || a || B || b || C || c || D || d || E || e || F || f
add, gc
digit || A || a || B ||
b || C || c || D || d
|| E || e || F || f
add, gc
D2
put(TN), out(4,z)
gc
gc
‘/’
gc
‘*’
gc
C1
‘*’
gc
C
‘\0’
‘/’
gc
C2
ER
out(2,12)
‘+’
gc
‘+’
gc, out(2,7)
PLUS
out(2,9)
‘=’
gc
‘=’
gc, out(2,17)
ASS
out(2,8)
‘<’
gc
‘=’
gc, out(2,14)
LE
out(2,13)
‘>’
gc
‘=’
gc, out(2,16)
GE
out(2,15)
‘!’
gc
‘=’
gc, out(2,18)
NEQ
ER
nill, add
‘\0’
O
V
z≠0
look(TL)
+
gc, out(2,z)
-
ER
V
Рисунок 2 – Граф лексического анализатора
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
11
Синтаксический анализатор.
Синтаксический анализатор реализован методом рекурсивного спуска в
виде
набора
грамматики
и
функций,
соответствующих
реализующие
правила
нетерминальным
грамматики
языка.
символам
Обоснование
применимости метода рекурсивного спуска приведено в разделе 2. Результатом
работы анализатора является синтаксическое дерево программы.
Семантический анализатор.
В ходе семантического анализа проверяются отдельные правила записи
исходных программ, которые не описываются КС-грамматикой. Эти правила
носят
контекстно-зависимый
характер,
их
называют
семантическими
соглашениями или контекстными условиями.
Соблюдение контекстных условий для языка предполагает три типа
проверок:
1) обработка описаний;
2) анализ выражений;
3) проверка правильности операторов.
В оптимизированном варианте СиА и СеА совмещены и осуществляются
параллельно.
Семантический
семантических
анализатор
проверок
в
реализован
процедурах
посредством
рекурсивного
выполнения
спуска
согласно
грамматике:
<program> ::= 'main' '(' ')' <block>
<block> ::= '{' <ops> '}'
<ops> ::= <op> ';' <ops> | ε
<vardef> ::= {inst(0)} 'int' <idlist> {dec(T.I)} | {inst(0)} 'long' <idlist> {dec(T.L)}
<idlist> ::= id {instl} <idlist_tail>
<idlist_tail> ::= ',' id {instl} <idlist_tail> | ε
<op> ::= <vardef> | <assign_incr> | <block> | <for> | ε
<asspostincr> ::= '++' {outst} | '=' <expr> {eqtype}
<prefincr> ::= '++' id {checkid; outst}
<expr> ::= <term> <expr_tail>
<expr_tail> ::= '+' {instl} <term> {checkop} <expr_tail> | '-' {instl} <term> {checkop} <expr_tail> | ε
<term> ::= <factor> <term_tail>
<term_tail> ::= '*' {instl} <factor> {checkop} <term_tail> | '/' {instl} <factor> {checkop} <term_tail> | ε
<factor> ::= '(' <expr> ')' | id {checkid} | const {inst(number_type)}
<for> ::= 'for' '(' id {checkid} '=' <expr> {eqtype} ';' <relop> ';' <assign_incr> ')' <op>
<relop> ::= <expr> <rop> <expr> {checkop}
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
12
<rop> ::= '<' {instl} | '>' {instl} | '<=' {instl} | '>=' {instl} | '==' {instl} | '!=' {instl}
<assign_incr> ::= id {checkid} <asspostincr> | <prefincr>
Спецификация семантических процедур приведена в разделе 6.
При проверке бинарных операций используется стек, в который заносятся
операнды и операция при синтаксическом разборе, а также таблица двуместных
операций, ниже приведено по одному примеру операции каждого типа –
отношения, арифметические – для всех возможных типов (таблица 2).
Таблица 2
Операция
Тип 1
Тип 2
Тип результата
==
int
int
int
==
long
long
int
==
int
long
int
==
long
int
int
+
int
int
int
+
long
long
long
+
int
long
long
+
long
int
long
Таким образом, проводится неявное приведение типа в сторону
расширения.
Кодогенератор.
От синтаксического анализатора на вход кодогенератора поступает
синтаксическое дерево, путем обхода дерева строится программа в форме
триад, далее по триадам генерируется код ассемблера.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
13
4. Методика испытаний
Тестирование проводится вручную, соответственно, инструментарий для
автоматизации тестирования не требуется. Данные тестовые примеры были
выбраны по причине наличия цикла for, операторов присваивания, переменных
разных типов, оператора инкремента и арифметических выражений, что делает
эти тестовые примеры репрезентативными.
Шаги:
1. Заполнить поле ввода «Исходный текст» тестируемым кодом;
2. Записать вручную переведенную в asm код программу;
3. Сгенерировать код ассемблера и проверить правильность результата.
Для примеров ошибочного исходного текста вместо пунктов 2 и 3
проверить сообщение транслятора – оно должно указывать на ошибку.
Тестовый пример №1
Исходный текст:
main () /* пример из задания */
{
int a,b;
long c;
c =0;
for (b=1; b<5; b++)
c=c+b;
}
Результаты тестирования показаны в таблице 3, снимок экрана
программы показан на рисунке 3.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
14
Таблица 3
Вводимое значение
Ожидаемый результат
main () /* пример из задания .386
*/
.model flat, stdcall
{
int a,b;
long c;
.data
c =0;
a
WORD 0
for (b=1; b<5; b++)
b
WORD 0
c=c+b;
c
DWORD 0
}
&t5 DWORD 0
&t7 WORD 0
Изм. Лист
№ докум.
Реальный результат
.386
.model flat, stdcall
.data
a
b
c
&t5
&t7
WORD 0
WORD 0
DWORD 0
DWORD 0
WORD 0
.code
.code
start:
start:
mov ax, 0
mov c, eax
mov ax, 1
mov b, ax
&L3: mov ax, b
cmp ax, 5
setl al
cmp al, 0
jz &L10
mov eax, c
add ax, b
mov &t5, eax
mov eax, &t5
mov c, eax
mov ax, b
add ax, 1
mov &t7, ax
mov ax, &t7
mov b, ax
jmp &L3
&L10:
END start
mov ax, 0
mov c, eax
mov ax, 1
mov b, ax
&L3: mov ax, b
cmp ax, 5
setl al
cmp al, 0
jz &L10
mov eax, c
add ax, b
mov &t5, eax
mov eax, &t5
mov c, eax
mov ax, b
add ax, 1
mov &t7, ax
mov ax, &t7
mov b, ax
jmp &L3
&L10:
END start
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
15
Рисунок 3 – Результат трансляции тестового примера
Тестовый пример №2
Исходный текст:
main ()
{
int a;
long b, c;
c = 0;
for (a=1;a<5;a++)
{
for (c = 1; c <= 0a; c = c + 2)
c = c + b * a;
}
}
Результаты тестирования показаны в таблице 4, снимок экрана
программы показан на рисунке 4.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
16
Таблица 4
Вводимое значение
main ()
{
int a;
long b, c;
c = 0;
for (a=1;a<5;a++)
{
for (c = 1; c <= 0a; c = c + 2)
c = c + b * a;
};
}
Изм. Лист
№ докум.
Ожидаемый результат
.386
.model flat, stdcall
.386
.model flat, stdcall
.data
.data
a
b
c
&t8
&t9
&t11
&t14
Реальный результат
WORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
WORD 0
a
b
c
&t8
&t9
&t11
&t14
WORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
DWORD 0
WORD 0
.code
.code
start:
start:
mov ax, 0
mov c, eax
mov ax, 1
mov a, ax
&L3: mov ax, a
cmp ax, 5
setl al
cmp al, 0
jz &L17
mov ax, 1
mov c, eax
&L6: mov eax, c
cmp ax, 10
setle al
cmp al, 0
jz &L14
mov eax, b
imul ax, a
mov &t8, eax
mov eax, c
add eax, &t8
mov &t9, eax
mov eax, &t9
mov c, eax
mov eax, c
add ax, 2
mov &t11, eax
mov eax, &t11
mov c, eax
jmp &L6
&L14: mov ax, a
add ax, 1
mov &t14, ax
mov ax, &t14
mov a, ax
jmp &L3
&L17:
END start
mov ax, 0
mov c, eax
mov ax, 1
mov a, ax
&L3: mov ax, a
cmp ax, 5
setl al
cmp al, 0
jz &L17
mov ax, 1
mov c, eax
&L6: mov eax, c
cmp ax, 10
setle al
cmp al, 0
jz &L14
mov eax, b
imul ax, a
mov &t8, eax
mov eax, c
add eax, &t8
mov &t9, eax
mov eax, &t9
mov c, eax
mov eax, c
add ax, 2
mov &t11, eax
mov eax, &t11
mov c, eax
jmp &L6
&L14: mov ax, a
add ax, 1
mov &t14, ax
mov ax, &t14
mov a, ax
jmp &L3
&L17:
END start
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
17
Рисунок 4 – Результат трансляции тестового примера
Тестовый пример №3
Исходный текст:
main ()
{
long t;
t:=0;
}
Результаты тестирования показаны в таблице 5, снимок экрана
программы показан на рисунке 5.
Таблица 5
Вводимое значение
main ()
{
long t;
t:=0;
}
Изм. Лист
№ докум.
Ожидаемый результат
Лексическая ошибка
Подпись Дата
Реальный результат
[4:3] Лексическая ошибка.
МИВУ.09.03.04-05.000
КР
Лист
18
Рисунок 5 – Результат трансляции тестового примера
Тестовый пример №4
Исходный текст:
main ()
{
long t;
t:=0;
}
Результаты тестирования показаны в таблице 6, снимок экрана
программы показан на рисунке 6.
Таблица 6
Вводимое значение
main ()
{
long t1;
int t2;
t1=0;
for(t2=1;t2==5;t2++)
t1=t2+1
}
Изм. Лист
№ докум.
Ожидаемый результат
Синтаксическая ошибка –
ожидается
один
из
символов
множеств
FIRST1
нетерминала
<expr_tail> или FOLLOW1
правила
<expr_tail>::=ε,
обозревается символ «}».
Подпись Дата
Реальный результат
[8:1]
Синтаксическая
ошибка
<expr_tail>: ожидались лексемы *, /,
+, -, ), ;, <, >, <=, >=, ==, !=,
поступила лексема }
МИВУ.09.03.04-05.000
КР
Лист
19
Рисунок 6 – Результат трансляции тестового примера
Тестовый пример №5
Исходный текст:
main ()
{
long t;
t = a + 1;
}
Результаты тестирования показаны в таблице 7, снимок экрана
программы показан на рисунке 7.
Таблица 7
Вводимое значение
main ()
{
long t;
t = a + 1;
}
Изм. Лист
№ докум.
Ожидаемый результат
Реальный результат
Семантическая ошибка – [4:6]
Семантическая
ошибка:
использование
переменная a не определена
необъявленной
переменной «a».
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
20
Рисунок 7 – Результат трансляции тестового примера
Тестовый пример №6
Исходный текст:
main ()
{
long t,a;
int a;
t=0;
}
Результаты тестирования показаны в таблице 8, снимок экрана
программы показан на рисунке 8.
Таблица 8
Вводимое значение
main ()
{
long t,a;
int a;
t=0;
}
Изм. Лист
№ докум.
Ожидаемый результат
Реальный результат
Семантическая ошибка – Семантическая ошибка: переменная a
повторное
объявление уже определена
переменной «a».
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
21
Рисунок 8 – Результат трансляции тестового примера
Тестовый пример №7
Исходный текст:
main ()
{
long a;
int b;
a=1;
b=a+1;
}
Результаты тестирования показаны в таблице 9, снимок экрана
программы показан на рисунке 9.
Таблица 9
Вводимое значение
main ()
{
long a;
int b;
a=1;
b=a+1;
}
Изм. Лист
№ докум.
Ожидаемый результат
Реальный результат
Семантическая ошибка – [6:7]
Семантическая
ошибка:
несоответствие типов.
несоответствие типов
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
22
Рисунок 9 – Результат трансляции тестового примера
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
23
5. Руководство пользователя
Программное
средство
снабжено
графическим
интерфейсом
пользователя, снимок экранной формы приведен на рисунке 10.
Рисунок 10 – Экранная форма программного средства
Посредством меню «Файл» возможно создание нового исходного текста,
загрузка и сохранение текста в файл.
Пункт меню «Трансляция» запускает процесс трансляции введенного
исходного текста.
Поле ввода «Исходный текст» предназначено для ввода исходного текста.
На вкладках «Лексический анализ», «Синт./Сем. анализ», «Генерация
пром. представления», «Генерация кода» отображается информация о
результатах работы соответствующих фаз трансляции (таблицы символов,
исходный
текст,
результирующий
артефакт
соответствующего
этапа
трансляции – список лексем, синтаксическое дерево, триады, код ассемблера).
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
24
В поле «Вывод транслятора» выводится информация об успешном
завершении процесса либо сообщения об ошибках в исходном тексте.
Порядок работы с программой:
– ввести исходный текст в поле «Исходный текст»;
– выбрать пункт меню «Трансляция».
В случае успешной трансляции на вкладках программного средства
отобразится информация о результатах этапов трансляции. Если в исходном
тексте найдена ошибка, соответствующее сообщение будет выведено в поле
«Вывод транслятора».
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
25
6. Руководство программиста
Программный
продукт
предназначен
для
трансляции
программ
подмножества команд языка «C» в код ассемблера.
Транслятор визуализирует артефакты этапов трансляции – таблицы
символов, список лексем, синтаксическое дерево, триады, код ассемблера.
Структурная организация данных.
В процессе работы программного средства используются структуры
данных: T – перечисление типов входного языка; ST – перечисление состояний
автомата лексического разбора; TIrecord - структура записи в таблице
идентификаторов; TNrecord - структура записи в таблице чисел; Lexem структура лексемы; TOPrecord - структура записи в таблице двуместных
операций; класс лексического анализатора Scanner, класс совмещенного
синтаксического
/
семантического
анализатора
Parser,
класс
узла
синтаксического дерева Node, класс триады Triade, класс кодогенератора
CodeGenerator. Спецификации указанных структур данных отображены в
таблице 10.
Таблица 10
Поле
Тип
Назначение
Структура TIrecord
Изм. Лист
id
defined
type
string
int
T
n
type
string
T
op
type1
type2
restype
int
T
T
T
n
int
k
row
col
int
int
int
№ докум.
Подпись Дата
лексема
признак определения идентификатора: 1 = описан, 0 = не описан
тип (% integer, ! real, $ boolean)
Структура TNrecord
лексема
тип (% integer, ! real, $ boolean)
Структура TOPrecord
операция (номер в таблице ограничителей)
тип первого операнда
тип второго операнда
тип результата
Структура Lexem
номер таблицы (1 - таблица служебных слов, 2 - таблица
ограничителей, 3 - таблица идентификаторов, 4 - таблица чисел)
номер лексемы в таблице
позиция в исходном тексте - строка
позиция в исходном тексте - столбец
МИВУ.09.03.04-05.000
КР
Лист
26
Таблица 10 – окончание
Поле
Тип
CH
S
CS
z
pos
row
col
rowlex
collex
char
string
ST
int
string
int
int
int
int
cur
LEX
int
Lexem
IS
TOP
Stack<int>
List<TOPrecord>
type
langType
token
childs
Node.Types
T
Lexem
List<Node>
type
langType
Triade.Types
T
token
op1
op2
lop1
lop2
op1Link
op2Link
hasLabel
Lexem
Lexem
Lexem
Triade
Triade
bool
bool
bool
type
langType
Triade.Types
T
token
op1
op2
lop1
lop2
op1Link
op2Link
hasLabel
Lexem
Lexem
Lexem
Triade
Triade
bool
bool
bool
prog
List<Triade>
Назначение
Класс Scanner
очередной входной символ
буфер для накапливания символов лексемы
текущее состояние автомата лексического разбора
номер лексемы в таблице t (если лексемы в таблице нет, то z=0)
текущая позиция в исходном тексте
текущая позиция в исходном тексте - строка
текущая позиция в исходном тексте - столбец
позиция начала текущей лексемы в исходном тексте - строка
позиция начала текущей лексемы в исходном тексте - столбец
Класс Parser
текущая позиция во входном списке лексем
переменная, содержащая текущую лексему, считанную из
списка лексем
стек семантического анализатора
таблица двуместных операций
Класс Node
тип узла
языковой тип узла (для двуместных операций и присваивания)
идентификатор узла
дочерние узлы дерева
Класс Triade
тип триады
языковой тип триады (для двуместных операций и
присваивания)
идентификатор триады
операнд 1
операнд 2
ссылка на триаду-операнд 1
ссылка на триаду-операнд 2
является ли операнд 1 ссылкой
является ли операнд 2 ссылкой
имеет ли метку (в которую ведет goto или FalseIf)
Класс Triade
тип триады
языковой тип триады (для двуместных операций и
присваивания)
идентификатор триады
операнд 1
операнд 2
ссылка на триаду-операнд 1
ссылка на триаду-операнд 2
является ли операнд 1 ссылкой
является ли операнд 2 ссылкой
имеет ли метку (в которую ведет goto или FalseIf)
Класс CodeGenerator
программа в виде списка триад
Спецификация входной информации.
Входные данные для модулей программного средства в классах Scanner,
Parser и CodeGenerator представлены в таблице 11.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
27
Таблица 11
Поле
Тип
intext
string
Назначение
Класс Scanner
исходный текст программы
Класс Parser
TW
TL
TI
TN
inlex
List<string>
List<string>
List<TIrecord>
List<TNrecord>
List<Lexem>
Класс CodeGenerator
Parser
parser
таблица служебных слов
таблица ограничителей
таблица идентификаторов
таблица чисел
входной список лексем
синтаксический анализатор
Спецификация выходной информации.
Выходные данные для модулей программного средства представлены
классами
ошибок:
LexicalError
–
лексическая
ошибка,
SyntaxError
–
синтаксическая ошибка, SemanticError – семантическая ошибка. Выходные
данные в классах Scanner, Parser и CodeGenerator, представлены в таблице 12.
Таблица 12
Поле
Тип
Назначение
Класс Scanner
List<string>
List<string>
List<TIrecord>
List<TNrecord>
List<Lexem>
Класс Parser
List<string>
List<string>
List<TIrecord>
List<TNrecord>
Node
Класс CodeGenerator
string
TW
TL
TI
TN
outlex
TW
TL
TI
TN
tree
program
таблица служебных слов
таблица ограничителей
таблица идентификаторов
таблица чисел
выходной список лексем
таблица служебных слов
таблица ограничителей
таблица идентификаторов
таблица чисел
синтаксическое дерево
программа в коде ассемблера
Спецификация основных функций.
Программное
средство
имеет
совмещенные
в
классе
Parser
синтаксический, семантический анализаторы; генератор промежуточного
представления программы, генератор кода ассемблера в классе CodeGenerator
поэтому процедуры алгоритма рекурсивного спуска в таблицах не отображены.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
28
Функции лексического анализатора представлены в классе Scanner
(таблица 13).
Таблица 13
Входные
параметры
Название
Выходные параметры
Назначение
Методы класса Scanner
void gc()
Нет
Нет
void nill()
Нет
Нет
void add()
Нет
Нет
void look(LexTables t)
Номер таблицы
Номер лексемы в
таблице в
переменную класса z
void put(LexTables t)
Номер таблицы
Нет
public List<Lexem>
getlex()
Нет
Список лексем
void output(int n, int k)
числа n, k
Нет
public bool scan()
Нет
Признак успешности
анализа
public string
progToString()
Нет
Строка
процедура считывания очередного
символа текста в переменную СН
процедура очистки буфера S
процедура добавления очередного
символа в конец буфера S
процедура поиска лексемы из буфера S в
таблице t
записывает лексемы из буфера S в
таблицу t, если там не было этой лексемы
возвращает программу в виде списка
лексем
записывает пару чисел (n, k) в выходной
поток лексем
лексический анализ входного текста,
возвращает признак успешности анализа
строковое представление выходного
списка лексем
Функции синтаксического анализатора представлены в классе Parser
(таблица 14).
Таблица 14
Входные
параметры
Название
Выходные параметры
Назначение
Методы класса Parser
void gl()
Нет
Нет
bool EQ (string S)
Строка
Булево значение
bool ID ()
Нет
Булево значение
bool NUM()
Нет
Булево значение
public string
PrintSyntaxTree(int spaces =
0)
Начальный
отступ
Строковое
представление дерева
Отступ,
string printSyntaxTree(int
spaces, Node root)
узел дерева
public bool parse()
Нет
Изм. Лист
№ докум.
Подпись Дата
Строка
Признак успешности
разбора
процедура считывания очередной лексемы
из файла лексем в переменную LEX
логическая функция, проверяющая,
является ли текущая лексема LEX
лексемой для S
логическая функция, проверяющая,
является ли LEX идентификатором
логическая функция, проверяющая,
является ли LEX числом
вывод синтаксического дерева публичный метод
вывод синтаксического дерева
функция разбора
МИВУ.09.03.04-05.000
КР
Лист
29
Функции семантического анализатора представлены в классе Parser
(таблица 15).
Таблица 15
Название
Входные
параметры
void inst(int l)
int outst()
Число
Нет
void instl()
Нет
void dec(T t)
Тип
void decid(int l, T t)
Номер
лексемы,
тип
void checkid()
Нет
int checkop()
Нет
void eqtype()
Нет
Выходные
Назначение
параметры
Методы класса Parser
Нет
процедура записи в стек числа l
Число
функция вывод из стека числа l
процедура записи в стек номера, под которым
лексема хранится в таблице, т.е.inst(LEX.k),
Нет
примечание: записывается номер предыдущей
лексемы, т.к. при разборе к данному моменту
текущей становится следующая лексема
процедура вывода всех чисел из стека и вызова
Нет
процедуры decid(l, t)
процедура проверки и заполнения поля «описан» и
Нет
«тип» таблицы идентификаторов для лексемы с
номером l и типа t
процедура, которая для лексемы, предшествующей
LEX, являющейся идентификатором, проверяет по
Нет
таблице идентификаторов TI, описан ли он, и, если
описан, то помещает его тип в стек
Тип операции
функция, выводящая из стека типы операндов и знак
операции, вызывающая процедуру gettype(op, t1, t2,
t), проверяющая соответствие типов и
записывающая в стек тип результата операции
Нет
сравненивает два типа в стеке
Функции кодогенератора представлены в классе CodeGenerator (таблица
16).
Таблица 16
Название
Входные
параметры
bool EQ(Lexem lex,
string S)
Лексема,
значение
лексемы
Признак совпадения
T TYPE(Lexem lex)
Лексема
Тип
void put_id(string id, T
langType)
Идентификатор,
тип
Нет
string REG(T
langType)
Тип
Имя регистра
void addNop()
Нет
Нет
public void
GenerateTriades()
Нет
Нет
Выходные параметры
Назначение
Методы класса CodeGenerator
Изм. Лист
№ докум.
Подпись Дата
логическая функция, проверяющая,
является ли лексема lex лексемой для S
функция, возвращающая языковой тип
лексемы lex
процедура, сохраняющая идентификатор
id типа langType в таблице
идентификаторов
// функция, возвращающая имя регистра,
соответствующего языковому типу
langType
добавление "no operator" триады в
программу
генерация кода программы в триадах публичный метод
МИВУ.09.03.04-05.000
КР
Лист
30
Таблица 16 – окончание
Название
void
generateTriades(Node
root, ref int
triadeIndex)
public string
PrintTriades()
public string
GenerateAsmCode()
Входные
параметры
Корень
поддерева,
индекс вставки
новой триады
Нет
Нет
Выходные параметры
Нет
Строковое
представление
программы в триадах
Строка с программой
в коде ассемблера
Назначение
генерация кода программы в триадах
обходом синтаксического дерева
вывод программы в триадах
генерация и вывод программы в коде
ассемблера
Перечень отслеживаемых лексическим, синтаксическим и семантическим
анализаторами ошибок:
- лексическая ошибка;
- синтаксическая ошибка;
- семантическая ошибка, переменная не определена;
- семантическая ошибка, переменная уже определена;
- семантическая ошибка, несоответствие типов.
Также при выводе сообщений об ошибках указывается номер строки и
столбца в исходном тексте, где обнаружена эта ошибка.
Исходные коды классов транслятора представлены в приложении А.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
31
Заключение
В ходе выполнения курсовой работы были изучены методы построения
трансляторов. Проектирование проходило в несколько этапов: выделение
подмножества языка, определение алфавита, описание синтаксиса и разработка
алгоритмов трансляции языка. Была выполнена реализации транслятора.
Были получены практические навыки по реализации фаз транслятора
входного языка, улучшены навыки написания исходного кода, получены
практические навыки проектирования модулей транслятора.
В результате выполнения курсовой работы был разработан транслятор
подмножества команд языка программирования «C». Перспективой развития
проекта является расширение поддержки функциональности языка, но
дальнейшая работа над проектом не планируется.
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
32
Список литературы
1. Ахо А.В. Компиляторы: принципы, технологии и инструментарий. —
М.: ООО «ИД Вильямс», 2008 г. — 1184 с.
2. Гордеев А.В., Молчанов А.Ю. Системное программное обеспечение.
3. Шульга, Т. Э. Теория автоматов и формальных языков : учебное
пособие / Т. Э. Шульга. — Саратов : Саратовский государственный
технический университет имени Ю.А. Гагарина, ЭБС АСВ, 2015. — 104 c. —
ISBN 987-5-7433-2968-7. — Текст : электронный // Электронно-библиотечная
система IPR BOOKS : [сайт]. — URL: https://www.iprbookshop.ru/76519.html
(дата обращения: 15.04.2021). — Режим доступа: для авторизир. пользователей.
- DOI: https://doi.org/10.23682/76519 - https://www.iprbookshop.ru/76519.html
4. Алымова, Е. В. Конечные автоматы и формальные языки : учебник / Е.
В. Алымова, В. М. Деундяк, А. М. Пеленицын. — Ростов-на-Дону, Таганрог :
Издательство Южного федерального университета, 2018. — 292 c. — ISBN 9785-9275-2397-9. — Текст : электронный // Электронно-библиотечная система IPR
BOOKS
:
[сайт].
—
URL:
https://www.iprbookshop.ru/87427.html
(дата
обращения: 15.04.2021). — Режим доступа: для авторизир. пользователей https://www.iprbookshop.ru/87427.html
5. Пентус, А. Е. Математическая теория формальных языков : учебное
пособие / А. Е. Пентус, М. Р. Пентус. — 3-е изд. — Москва : ИнтернетУниверситет Информационных Технологий (ИНТУИТ), Ай Пи Ар Медиа,
2020. — 218 c. — ISBN 978-5-4497-0662-1. — Текст : электронный //
Электронно-библиотечная
система
IPR
BOOKS
:
[сайт].
—
URL:
https://www.iprbookshop.ru/97548.html (дата обращения: 15.04.2021). — Режим
доступа: для авторизир. пользователей - https://www.iprbookshop.ru/97548.html
6. Миронов, С. В. Формальные языки и грамматики : учебное пособие для
студентов факультета компьютерных наук и информационных технологий / С.
В. Миронов. — Саратов : Издательство Саратовского университета, 2019. — 80
c. — ISBN 978-5-292-04613-4. — Текст : электронный // ЭлектронноИзм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
33
библиотечная
система
IPR
BOOKS
:
[сайт].
—
URL:
https://www.iprbookshop.ru/99047.html (дата обращения: 15.04.2021). — Режим
доступа: для авторизир. пользователей - https://www.iprbookshop.ru/99047.html
7. Малявко, А. А. Формальные языки и компиляторы : учебник / А. А.
Малявко. — Новосибирск : Новосибирский государственный технический
университет, 2014. — 431 c. — ISBN 978-5-7782-2318-9. — Текст : электронный
// Электронно-библиотечная система IPR BOOKS : [сайт]. — URL:
https://www.iprbookshop.ru/47725.html (дата обращения: 15.04.2021). — Режим
доступа: для авторизир. пользователей - https://www.iprbookshop.ru/47725.html
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
34
Приложение А. Исходные коды классов транслятора
Класс Scanner.
using System;
using System.Collections.Generic;
namespace CCompiler
{
public class LexicalError : Exception // класс лексической ошибки
{
public LexicalError(string msg) : base(msg) { }
}
public class SemanticError : Exception // класс семантической ошибки
{
public SemanticError(string msg) : base(msg) { }
public SemanticError(int row, int col, string msg) : base(String.Format("[{0}:{1}]
Семантическая ошибка {2}", row, col, msg)) { }
}
public enum T // перечисление типов языка
{
I, // integer
L, // long
}
public struct TIrecord // структура записи в таблице идентификаторов
{
public string id; // лексема
public int defined; // признак определения идентификатора: 1 = описан, 0 = не
описан
public T type; // тип (int, long)
public string getType() // строковое представление типа
{
switch (type)
{
case T.I:
return "int";
case T.L:
return "long";
default:
return "int";
}
}
}
public struct TNrecord // структура записи в таблице чисел
{
public string n; // лексема
public T type; // тип (int, long)
public TNrecord(string n, T type) { this.n = n; this.type = type; }
public string getType() // строковое представление типа
{
switch (type)
{
case T.I:
return "int";
case T.L:
return "long";
default:
return "int";
}
}
}
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
35
public enum ST // перечисление состояний автомата лексического разбора
{
S, // начало
I, // идентификатор
D1, // целое шестнадцатеричное число, первая цифра
D2, // целое шестнадцатеричное число, остальные цифры/буквы
C1, // первый символ входа в комментарий
C, // комментарий
C2, // первый символ выхода из комментария
ASS, // присваивание
PLUS, // ++
LE, // <=
GE, // >=
NEQ, // !=
O, // разделитель
V, // выход
ER // ошибка
}
public enum LexTables // перечисление таблиц лексем
{
TW = 1, TL, TI, TN
}
public struct Lexem // структура лексемы
{
public int n; // номер таблицы (1 - таблица ключевых слов, 2 - таблица
разделителей, 3 - таблица идентификаторов, 4 - таблица чисел)
public int k; // номер лексемы в таблице
public int row; // позиция в исходном тексте - строка
public int col; // позиция в исходном тексте - столбец
public Lexem(int n, int k) { this.n = n; this.k = k; row = 1; col = 1; }
public Lexem(int n, int k, int row, int col) { this.n = n; this.k = k; this.row =
row; this.col = col; }
public Lexem(Lexem other)
{
n = other.n;
k = other.k;
row = other.row;
col = other.col;
}
}
public class Scanner // класс лексического анализатора
{
// поле с индексом 0 в таблицах зарезервировано
public List<string> TW; // таблица ключевых слов
public List<string> TL; // таблица разделителей
public List<TIrecord> TI; // таблица идентификаторов
public List<TNrecord> TN; // таблица чисел
char CH; // очередной входной символ
string S; // буфер для накапливания символов лексемы
ST CS; // текущее состояние автомата лексического разбора
int z; // номер лексемы в таблице t (если лексемы в таблице нет, то z=0)
string intext; // исходный текст программы
List<Lexem> outlex; // выходной список лексем
int pos; // текущая позиция в исходном тексте
int row; // текущая позиция в исходном тексте - строка
int col; // текущая позиция в исходном тексте - столбец
int rowlex; // позиция начала текущей лексемы в исходном тексте - строка
int collex; // позиция начала текущей лексемы в исходном тексте - столбец
public Scanner(string input) // конструктор
{
TW = new List<string> { "", "main", "int", "long", "for" };
TL = new List<string> { "", "(", ")", "{", "}", ";", ",", "++", "=", "+", "-",
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
36
"*", "/", "<", "<=", ">", ">=", "==", "!=" };
TI = new List<TIrecord>();
TI.Add(new TIrecord());
TN = new List<TNrecord>();
TN.Add(new TNrecord());
CH = '\0';
S = "";
CS = ST.S;
z = 0;
outlex = new List<Lexem>();
pos = -1;
row = 1;
col = 1;
intext = input;
}
void gc() // процедура считывания очередного символа текста в переменную СН
{
if (pos < intext.Length - 1)
{
pos++;
if (CH == '\n')
{
row++;
col = 0;
}
CH = intext[pos];
col++;
}
else
CH = '\0';
}
void nill() // процедура очистки буфера S
{
S = "";
}
void add() // процедура добавления очередного символа в конец буфера S
{
if (S.Equals(""))
{
rowlex = row;
collex = col;
}
S += CH.ToString();
}
void look(LexTables t) // процедура поиска лексемы из буфера S в таблице t с
возвращением номера лексемы в таблице
{
switch (t)
{
case LexTables.TW:
for (int i = 1; i < TW.Count; i++)
if (TW[i].Equals(S))
{
z = i;
return;
}
z = 0;
break;
case LexTables.TL:
for (int i = 1; i < TL.Count; i++)
if (TL[i].Equals(S))
{
z = i;
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
37
return;
}
z = 0;
break;
case LexTables.TI:
for (int i = 1; i < TI.Count; i++)
if (TI[i].id.Equals(S))
{
z = i;
return;
}
z = 0;
break;
case LexTables.TN:
for (int i = 1; i < TN.Count; i++)
if (TN[i].Equals(S))
{
z = i;
return;
}
z = 0;
break;
default:
break;
}
}
void put(LexTables t) // процедура записи лексемы из буфера S в таблицу t, если
там не было этой лексемы, возвращает номер данной лексемы в таблице
{
switch (t)
{
case LexTables.TW:
for (int i = 1; i < TW.Count; i++)
if (TW[i].Equals(S))
{
z = i;
return;
}
TW.Add(S);
z = TW.Count - 1;
break;
case LexTables.TL:
for (int i = 1; i < TL.Count; i++)
if (TL[i].Equals(S))
{
z = i;
return;
}
TL.Add(S);
z = TL.Count - 1;
break;
case LexTables.TI:
for (int i = 1; i < TI.Count; i++)
if (TI[i].id.Equals(S))
{
z = i;
return;
}
if (S.Length >= 7)
throw new SemanticError(String.Format("[{0}:{1}] Семантическая
ошибка: превышение длины идентификатора (допустимо до 7 символов)", rowlex, collex));
TIrecord irec = new TIrecord();
irec.id = S;
TI.Add(irec);
z = TI.Count - 1;
break;
case LexTables.TN:
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
38
TNrecord nrec = new TNrecord();
int leadingZeroes = 0;
string value;
while (leadingZeroes < S.Length && S[leadingZeroes] == '0')
leadingZeroes++;
if (leadingZeroes == S.Length)
value = "0";
else
value = S.Substring(leadingZeroes);
if (value.Length <= 4)
nrec.type = T.I;
else if (value.Length <= 8)
nrec.type = T.L;
else
throw new SemanticError(String.Format("[{0}:{1}] Семантическая
ошибка: превышение длины константы типа long (допустимо до 8 значащих разрядов включительно)",
rowlex, collex));
int b = Convert.ToInt32(value, 16);
S = b.ToString();
for (int i = 1; i < TN.Count; i++)
if (TN[i].n.Equals(S))
{
z = i;
return;
}
nrec.n = S;
TN.Add(nrec);
z = TN.Count - 1;
break;
default:
break;
}
}
void output(int n, int k) // процедура записи пары чисел (n, k) в выходной поток
лексем
{
outlex.Add(new Lexem(n, k, rowlex, collex));
}
public List<Lexem> getlex() // возвращает программу в виде списка лексем
{
return outlex;
}
public bool scan() // лексический анализ входного текста, возвращает признак
успешности анализа
{
gc();
CS = ST.S;
do
{
switch (CS)
{
case ST.S:
{
if (Char.IsWhiteSpace(CH))
gc();
else
{
if (Char.IsLetter(CH))
{
nill(); add(); gc();
CS = ST.I;
}
else if (CH == '+')
{
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
39
gc();
CS = ST.PLUS;
}
else if (CH == '=')
{
gc();
CS = ST.ASS;
}
else if (CH == '<')
{
gc();
CS = ST.LE;
}
else if (CH == '>')
{
gc();
CS = ST.GE;
}
else if (CH == '!')
{
gc();
CS = ST.NEQ;
}
else if (Char.IsDigit(CH))
{
CS = ST.D1;
nill(); add(); gc();
}
else if (CH == '/')
{
gc();
CS = ST.C1;
}
else if (CH == '\0')
CS = ST.V;
else
{
nill();
add();
CS = ST.O;
}
}
}
break;
case ST.I:
{
if (Char.IsLetterOrDigit(CH))
{
add(); gc();
}
else
{
look(LexTables.TW);
if (z != 0)
output(1, z);
else
{
put(LexTables.TI);
output(3, z);
}
CS = ST.S;
}
}
break;
case ST.D1:
{
if (CH == 'A' || CH == 'a' || CH == 'B' || CH == 'b' ||
CH == 'C' || CH == 'c' || CH == 'D' || CH == 'd' ||
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
40
CH == 'E' || CH == 'e' || CH == 'F' || CH == 'f' ||
Char.IsDigit(CH))
{
CS = ST.D2;
add(); gc();
}
else
{
put(LexTables.TN);
output(4, z);
CS = ST.S;
}
}
break;
case ST.D2:
{
if (CH == 'A' || CH == 'a' || CH == 'B' || CH == 'b' ||
CH == 'C' || CH == 'c' || CH == 'D' || CH == 'd' ||
CH == 'E' || CH == 'e' || CH == 'F' || CH == 'f' ||
Char.IsDigit(CH))
{
add(); gc();
}
else
{
put(LexTables.TN);
output(4, z);
CS = ST.S;
}
}
break;
case ST.C1:
{
if (CH == '*')
{
gc();
CS = ST.C;
}
else
{
output(2, 12);
CS = ST.S;
}
}
break;
case ST.C:
{
if (CH == '*')
{
gc();
CS = ST.C2;
}
else if (CH == '\0')
CS = ST.ER;
else
gc();
}
break;
case ST.C2:
{
if (CH == '/')
{
gc();
CS = ST.S;
}
else
{
gc();
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
41
CS = ST.C;
}
}
break;
case ST.PLUS:
{
if (CH == '+')
{
gc();
output(2, 7);
}
else
output(2, 9);
CS = ST.S;
}
break;
case ST.ASS:
{
if (CH == '=')
{
gc();
output(2, 17);
}
else
output(2, 8);
CS = ST.S;
}
break;
case ST.LE:
{
if (CH == '=')
{
gc();
output(2, 14);
}
else
output(2, 13);
CS = ST.S;
}
break;
case ST.GE:
{
if (CH == '=')
{
gc();
output(2, 16);
}
else
output(2, 15);
CS = ST.S;
}
break;
case ST.NEQ:
{
if (CH == '=')
{
gc();
output(2, 18);
CS = ST.S;
}
else
CS = ST.ER;
}
break;
case ST.O:
{
look(LexTables.TL);
if (z != 0)
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
42
{
gc();
output(2, z);
CS = ST.S;
}
else
CS = ST.ER;
}
break;
case ST.V:
break;
case ST.ER:
break;
}
} while (CS != ST.V && CS != ST.ER);
if (CS == ST.ER)
throw new LexicalError(String.Format("[{0}:{1}] Лексическая ошибка.", row,
col));
return CS == ST.V;
}
public string progToString() // строковое представление выходного списка лексем
{
string res = "";
for (int i = 0; i < outlex.Count; i++)
{
Lexem lex = outlex[i];
string lexname = "";
if (lex.n == (int)LexTables.TW)
lexname = TW[lex.k];
if (lex.n == (int)LexTables.TL)
lexname = TL[lex.k];
if (lex.n == (int)LexTables.TI)
lexname = TI[lex.k].id;
if (lex.n == (int)LexTables.TN)
lexname = TN[lex.k].n;
lexname = String.Format("({0}, {1});", i.ToString(), lexname);
res += String.Format("{0} ", lexname);
}
return res + "\r\n";
}
}
}
Класс Parser.
using System;
using System.Collections.Generic;
namespace CCompiler
{
public class SyntaxError : Exception // класс синтаксической ошибки
{
public SyntaxError(int row, int col, string msg) : base(String.Format("[{0}:{1}]
Синтаксическая ошибка {2}", row, col, msg)) { }
}
public struct TOPrecord // структура записи в таблице двуместных операций
{
public int op; // операция (номер в таблице ограничителей)
public T type1; // тип первого операнда
public T type2; // тип второго операнда
public T restype; // тип результата
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
43
public TOPrecord(int op, T type1, T type2, T restype) { this.op = op; this.type1 =
type1; this.type2 = type2; this.restype = restype; }
}
public class Parser // класс совмещенных синтаксического и семантического анализаторов
и генератора промежуточного представления программы (синтаксическое дерево)
{
// поле с индексом 0 в таблицах зарезервировано
public List<string> TW; // таблица служебных слов
public List<string> TL; // таблица ограничителей
public List<TIrecord> TI; // таблица идентификаторов
public List<TNrecord> TN; // таблица чисел
List<Lexem> inlex; // входной список лексем
int cur; // текущая позиция во входном списке лексем
Lexem LEX; // переменная, содержащая текущую лексему, считанную из списка лексем
// переменные семантического анализатора
Stack<int> IS; // стек семантического анализатора
List<TOPrecord> TOP; // таблица двуместных операций
// переменные генератора промежуточного представления программы
static int sp_inc = 2; // приращение отступа для отображения уровней дерева
public Node tree = null; // синтаксическое дерево
public Parser(List<Lexem> inlex, List<string> TW, List<string> TL, List<TIrecord>
TI, List<TNrecord> TN) // конструктор
{
this.inlex = inlex;
this.TW = TW;
this.TL = TL;
this.TI = TI;
this.TN = TN;
cur = -1;
IS = new Stack<int>();
// инициализация таблицы двуместных операций
TOP = new List<TOPrecord>();
for (int i = 13; i <= 18; i++) // <, <=, >, >=, ==, !=
{
TOP.Add(new TOPrecord(i, CCompiler.T.I, CCompiler.T.I, CCompiler.T.I));
TOP.Add(new TOPrecord(i, CCompiler.T.L, CCompiler.T.L, CCompiler.T.I));
TOP.Add(new TOPrecord(i, CCompiler.T.I, CCompiler.T.L, CCompiler.T.I));
TOP.Add(new TOPrecord(i, CCompiler.T.L, CCompiler.T.I, CCompiler.T.I));
}
for (int i = 9; i <= 12; i++) // +, -, *, /
{
TOP.Add(new TOPrecord(i, CCompiler.T.I, CCompiler.T.I, CCompiler.T.I));
TOP.Add(new TOPrecord(i, CCompiler.T.L, CCompiler.T.L, CCompiler.T.L));
TOP.Add(new TOPrecord(i, CCompiler.T.I, CCompiler.T.L, CCompiler.T.L));
TOP.Add(new TOPrecord(i, CCompiler.T.L, CCompiler.T.I, CCompiler.T.L));
}
// дополнение таблицы TL для генерации синтаксического дерева
TL.Add("program");
// дополнение таблицы TN для генерации оператора инкремента
TN.Add(new TNrecord("1", CCompiler.T.I));
TN.Add(new TNrecord("1", CCompiler.T.L));
}
// функции синтаксического анализа
void gl() // процедура считывания очередной лексемы из файла лексем в переменную
LEX
{
if (cur < inlex.Count - 1)
{
cur++;
LEX = inlex[cur];
}
}
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
44
string lexstr() // функция получения значения текущей лексемы
{
string lex = "";
switch (LEX.n)
{
case 1:
lex = TW[LEX.k];
break;
case 2:
lex = TL[LEX.k];
break;
case 3:
lex = TI[LEX.k].id;
break;
case 4:
lex = TN[LEX.k].n;
break;
}
return lex;
}
public string lexstr(Lexem l) // функция получения значения заданной лексемы
{
string lex = "";
switch (l.n)
{
case 1:
lex = TW[l.k];
break;
case 2:
lex = TL[l.k];
break;
case 3:
lex = TI[l.k].id;
break;
case 4:
lex = TN[l.k].n;
break;
}
return lex;
}
public int getLexIdx(LexTables t, string lex, T numType = T.I)
получения индекса заданной лексемы в таблице
{
switch (t)
{
case LexTables.TW:
for (int i = 1; i < TW.Count; i++)
if (TW[i].Equals(lex))
return i;
break;
case LexTables.TL:
for (int i = 1; i < TL.Count; i++)
if (TL[i].Equals(lex))
return i;
break;
case LexTables.TI:
for (int i = 1; i < TI.Count; i++)
if (TI[i].id.Equals(lex))
return i;
break;
case LexTables.TN:
for (int i = 1; i < TN.Count; i++)
if (TN[i].n.Equals(lex) && TN[i].type == numType)
return i;
break;
default:
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
//
КР
функция
Лист
45
break;
}
return -1;
}
bool EQ(string S) // логическая функция, проверяющая, является ли текущая лексема
LEX лексемой для S
{
switch (LEX.n)
{
case 1:
if (TW[LEX.k].Equals(S))
return true;
break;
case 2:
if (TL[LEX.k].Equals(S))
return true;
break;
case 3:
if (TI[LEX.k].Equals(S))
return true;
break;
case 4:
if (TN[LEX.k].Equals(S))
return true;
break;
}
return false;
}
bool ID() // логическая функция, проверяющая, является ли LEX идентификатором
{
return LEX.n == (int)LexTables.TI;
}
bool NUM() // логическая функция, проверяющая, является ли LEX числом
{
return LEX.n == (int)LexTables.TN;
}
// функции семантического анализатора
void inst(int l) // процедура записи в стек числа l
{
IS.Push(l);
}
int outst() // функция вывод из стека числа l
{
return IS.Count == 0 ? 0 : IS.Pop();
}
void instl() // процедура записи в стек номера, под которым лексема хранится в
таблице, т.е.inst(LEX.k)
// примечание: записывается номер предыдущей лексемы,
// т.к. при разборе к данному моменту текущей становится следующая
лексема
{
IS.Push(inlex[cur - 1].k);
}
void dec(T t) // процедура вывода всех чисел из стека и вызова процедуры decid(l,
t)
{
int l;
l = outst();
while (l != 0)
{
decid(l, t);
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
46
l = outst();
}
}
void decid(int l, T t) // процедура проверки и заполнения поля «описан» и «тип»
таблицы идентификаторов для лексемы с номером l и типа t
{
if (TI[l].defined == 1)
throw new SemanticError(String.Format("Семантическая ошибка: переменная
{0} уже определена", TI[l].id));
else
{
TIrecord ti = TI[l];
ti.defined = 1;
ti.type = t;
TI[l] = ti;
}
}
void checkid() // процедура, которая для лексемы, предшествующей LEX, являющейся
идентификатором,
// проверяет по таблице идентификаторов TI, описан ли он, и, если
описан,
// то помещает его тип в стек
{
int k = inlex[cur - 1].k;
if (TI[k].defined == 0)
throw new SemanticError(String.Format("[{0}:{1}] Семантическая ошибка:
переменная {2} не определена", LEX.row, LEX.col, TI[k].id));
inst((int)TI[k].type);
}
int checkop() // функция, выводящая из стека типы операндов и знак операции,
// вызывающая процедуру gettype(op, t1, t2, t), проверяющая
соответствие типов
// и записывающая в стек тип результата операции
// (также возвращает тип операции)
{
int op, top1, top2, t;
top2 = outst(); op = outst(); top1 = outst();
gettype(op, top1, top2, out t);
inst(t);
return t;
}
void gettype(int op, int t1, int t2, out int t) // процедура, которая по таблице
операций TOP для
// операции ор и типов t1 и t2
операндов выдает тип t результата
{
for (int i = 0; i < TOP.Count; i++)
{
if (TOP[i].op == op && TOP[i].type1 == (T)t1 && TOP[i].type2 == (T)t2)
{
t = (int)TOP[i].restype;
return;
}
}
throw
new
SemanticError(String.Format("[{0}:{1}]
Семантическая
ошибка:
несоответствие типов", LEX.row, LEX.col));
}
void eqtype() // процедура сравнения двух типов
{
int t1, t2;
t2 = outst(); t1 = outst();
if (t1 == (int)T.I && t1 != t2)
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
47
throw new SemanticError(String.Format("[{0}:{1}]
несоответствие типов", LEX.row, LEX.col));
}
Семантическая
ошибка:
// функции генератора промежуточного представления программы
// вывод count пробелов
string printSpaces(int count)
{
return new string(' ', count);
}
// вывод синтаксического дерева - публичный метод
public string PrintSyntaxTree(int spaces = 0)
{
return printSyntaxTree(spaces, tree);
}
// вывод синтаксического дерева
// spaces - отступ
// root - корень поддерева
string printSyntaxTree(int spaces, Node root)
{
string s = "";
s += printSpaces(spaces);
s += string.Format("{0}\r\n", lexstr(root.Token));
spaces += sp_inc;
for (int i = 0; i < root.Childs.Count; i++)
s += printSyntaxTree(spaces, root.Childs[i]);
return s;
}
// процедуры рекурсивного спуска
// <program> ::= 'main' '(' ')' <block>
void Program()
{
if (EQ("main"))
gl();
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<program>:
ожидалась лексема {0}, поступила лексема {1}", "main", lexstr()));
if (EQ("("))
gl();
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<program>:
ожидалась лексема {0}, поступила лексема {1}", "(", lexstr()));
if (EQ(")"))
gl();
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<program>:
ожидалась лексема {0}, поступила лексема {1}", ")", lexstr()));
Node block = Block();
tree = new Node(Node.Types.N_PROGRAM, new Lexem(2, getLexIdx(LexTables.TL,
"program")));
tree.Childs.Add(block);
}
// <block> ::= '{' <ops> '}'
Node Block()
{
if (EQ("{"))
gl();
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<block>: ожидалась
лексема {0}, поступила лексема {1}", "{", lexstr()));
Node ops = Ops();
if (EQ("}"))
gl();
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
48
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<block>: ожидалась
лексема {0}, поступила лексема {1}", "}", lexstr()));
return ops;
}
// <ops> ::= <op> ';' <ops> | ε
Node Ops()
{
if (ID() || EQ("int") || EQ("long") || EQ("++") || EQ("{") || EQ("for"))
{
Node op = Op();
if (EQ(";"))
gl();
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<ops>:
ожидалась лексема {0}, поступила лексема {1}", ";", lexstr()));
Node ops = Ops();
if (op != null)
ops.Childs.Insert(0, op);
return ops;
}
else if (EQ("}")) // ε
return new Node(Node.Types.N_BLOCK, new Lexem(2, getLexIdx(LexTables.TL,
"{")));
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<ops>: ожидались
лексемы id, int, long, ++, {{, for, }}, поступила лексема {0}", lexstr()));
}
// <vardef> ::= {inst(0)} 'int' <idlist> {dec(T.I)} | {inst(0)} 'long' <idlist>
{dec(T.L)}
void Vardef()
{
inst(0); // инициализация стека для анализа описаний переменных
if (EQ("int"))
{
gl();
Idlist();
dec(CCompiler.T.I); // установка типа переменным
}
else if (EQ("long"))
{
gl();
Idlist();
dec(CCompiler.T.L); // установка типа переменным
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<vardef>: ожидались
лексемы int, long, поступила лексема {0}", lexstr()));
}
// <idlist> ::= id {instl} <idlist_tail>
void Idlist()
{
if (!ID())
throw new SyntaxError(LEX.row, LEX.col, string.Format("<idlist>: ожидалась
лексема {0}, поступила лексема {1}", "id", lexstr()));
gl();
instl(); // помещение номера переменной в стек
Idlist_tail();
}
// <idlist_tail> ::= ',' id {instl} <idlist_tail> | ε
void Idlist_tail()
{
if (EQ(","))
{
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
49
gl();
if (!ID())
throw new SyntaxError(LEX.row, LEX.col, string.Format("<idlist_tail>:
ожидалась лексема {0}, поступила лексема {1}", "id", lexstr()));
gl();
instl(); // помещение номера переменной в стек
}
else if (EQ(";")) // ε
return;
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<idlist_tail>:
ожидались лексемы ',', ;, поступила лексема {0}", lexstr()));
}
// <op> ::= <vardef> | <assign_incr> | <block> | <for> | ε
Node Op()
{
if (EQ("int") || EQ("long"))
{
Vardef();
return null;
}
else if (ID() || EQ("++"))
{
return Assign_incr();
}
else if (EQ("{"))
{
return Block();
}
else if (EQ("for"))
{
return For();
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<op>:
лексемы int, long, id, ++, {{, for, поступила лексема {0}", lexstr()));
}
ожидались
// <asspostincr> ::= '++' {outst} | '=' <expr> {eqtype}
Node Asspostincr()
{
if (EQ("++"))
{
gl();
outst();
Node
expr
=
new
Node(Node.Types.N_BINOP,
new
Lexem(2,
getLexIdx(LexTables.TL, "+")));
expr.Childs.Add(null); // резерв для узла id
expr.Childs.Add(new
Node(Node.Types.N_OPERAND,
new
Lexem(4,
getLexIdx(LexTables.TN, "1"))));
return expr;
}
else if (EQ("="))
{
gl();
Node expr = Expr();
eqtype(); // проверка соответствия типов левой и правой частей операции
присваивания
return expr;
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<asspostincr>:
ожидались лексемы ++, =, поступила лексема {0}", lexstr()));
}
// <prefincr> ::= '++' id {checkid; outst}
Node Prefincr()
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
50
{
if (EQ("++"))
{
Node
assign
=
new
Node(Node.Types.N_BINOP,
new
Lexem(2,
getLexIdx(LexTables.TL, "=")));
gl();
if (!ID())
throw new SyntaxError(LEX.row, LEX.col, string.Format("<prefincr>:
ожидалась лексема {0}, поступила лексема {1}", "id", lexstr()));
Node id = new Node(Node.Types.N_OPERAND, new Lexem(LEX));
T idType = TI[LEX.k].type;
gl();
checkid(); // проверка описания переменной
outst();
Node
expr
=
new
Node(Node.Types.N_BINOP,
new
Lexem(2,
getLexIdx(LexTables.TL, "+")));
expr.Childs.Add(id);
expr.Childs.Add(new
Node(Node.Types.N_OPERAND,
new
Lexem(4,
getLexIdx(LexTables.TN, "1", idType))));
assign.LangType = idType; // тип инкремента = тип переменной
assign.Childs.Add(id); // генерация левой части присваивания
assign.Childs.Add(expr); // генерация правой части присваивания
return assign;
}
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<prefincr>:
ожидалась лексема {0}, поступила лексема {1}", "++", lexstr()));
}
// <expr> ::= <term> <expr_tail>
Node Expr()
{
if (EQ("(") || ID() || NUM())
{
Node term = Term();
Node expr_tail = Expr_tail();
if (expr_tail == null)
return term;
else
{
Node place = expr_tail;
while (place.Childs.Count > 1) // поиск самого левого узла поддерева
expr_tail с пустым левым поддеревом
place = place.Childs[0];
place.Childs.Insert(0, term);
return expr_tail;
}
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<expr>: ожидались
лексемы (, id, const, поступила лексема {0}", lexstr()));
}
// <expr_tail> ::= '+' {instl} <term> {checkop} <expr_tail> | '-' {instl} <term>
{checkop} <expr_tail> | ε
Node Expr_tail()
{
if (EQ("+") || EQ("-"))
{
Lexem opLex = new Lexem(LEX); // знак операции
gl();
instl();
Node term = Term();
T langType = (T)checkop(); // проверка соответствия типов двуместной
операции
Node expr_tail = Expr_tail();
Node curnode = new Node(Node.Types.N_BINOP, opLex); // текущий узел дерева
curnode.Childs.Add(term);
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
51
curnode.LangType = langType; // языковой тип узла = тип результата
if (expr_tail == null)
return curnode;
else
{
Node place = expr_tail;
while (place.Childs.Count > 1) // поиск самого левого узла поддерева
expr_tail с пустым левым поддеревом
place = place.Childs[0];
place.Childs.Insert(0, curnode);
return expr_tail;
}
}
else if (EQ(")") || EQ(";") || EQ("<") || EQ(">") || EQ("<=") || EQ(">=") ||
EQ("==") || EQ("!=")) // ε
return null;
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<expr_tail>:
ожидались лексемы +, -, ), ;, <, >, <=, >=, ==, !=, поступила лексема {0}", lexstr()));
}
// <term> ::= <factor> <term_tail>
Node Term()
{
if (EQ("(") || ID() || NUM())
{
Node factor = Factor();
Node term_tail = Term_tail();
if (term_tail == null)
return factor;
else
{
Node place = term_tail;
while (place.Childs.Count > 1) // поиск самого левого узла поддерева
term_tail с пустым левым поддеревом
place = place.Childs[0];
place.Childs.Insert(0, factor);
return term_tail;
}
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<term>: ожидались
лексемы (, id, const, поступила лексема {0}", lexstr()));
}
// <term_tail> ::= '*' {instl} <factor> {checkop} <term_tail> | '/' {instl}
<factor> {checkop} <term_tail> | ε
Node Term_tail()
{
if (EQ("*") || EQ("/"))
{
Lexem opLex = new Lexem(LEX); // знак операции
gl();
instl();
Node factor = Factor();
T langType = (T)checkop(); // проверка соответствия типов двуместной
операции
Node term_tail = Term_tail();
Node curnode = new Node(Node.Types.N_BINOP, opLex); // текущий узел дерева
curnode.Childs.Add(factor);
curnode.LangType = langType; // языковой тип узла = тип результата
if (term_tail == null)
return curnode;
else
{
Node place = term_tail;
while (place.Childs.Count > 1) // поиск самого левого узла поддерева
term_tail с пустым левым поддеревом
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
52
place = place.Childs[0];
place.Childs.Insert(0, curnode);
return term_tail;
}
}
else if (EQ("+") || EQ("-") || EQ(")") || EQ(";") || EQ("<") || EQ(">") ||
EQ("<=") || EQ(">=") || EQ("==") || EQ("!=")) // ε
return null;
else
throw
new
SyntaxError(LEX.row,
LEX.col,
string.Format("<expr_tail>:
ожидались лексемы *, /, +, -, ), ;, <, >, <=, >=, ==, !=, поступила лексема {0}", lexstr()));
}
// <factor> ::= '(' <expr> ')' | id {checkid} | const {inst(number_type)}
// примечание: number_type определяется на этапе лексического анализа и уже
присутствует в таблице чисел
Node Factor()
{
Node curnode = new Node(); // текущий узел дерева
if (EQ("("))
{
gl();
curnode = Expr();
if (!EQ(")"))
throw new SyntaxError(LEX.row, LEX.col, string.Format("<factor>:
ожидалась лексема {0}, поступила лексема {1}", ")", lexstr()));
else
gl();
}
else if (ID())
{
curnode.Type = Node.Types.N_OPERAND;
curnode.Token = new Lexem(LEX);
gl();
checkid();
}
else if (NUM())
{
curnode.Type = Node.Types.N_OPERAND;
curnode.Token = new Lexem(LEX);
inst((int)TN[LEX.k].type);
gl();
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<expr>: ожидались
лексемы (, id, const, поступила лексема {0}", lexstr()));
return curnode;
}
// <for> ::= 'for' '(' id {checkid} '=' <expr> {eqtype} ';'
//
<relop> ';' <assign_incr> ')' <op>
Node For()
{
Node curnode = new Node(Node.Types.N_FOR, new Lexem(LEX)); // текущий узел
дерева
gl();
if (!EQ("("))
throw new SyntaxError(LEX.row, LEX.col, string.Format("<assign>: ожидалась
лексема {0}, поступила лексема {1}", "(", lexstr()));
gl();
// блок начального присваивания цикла for
if (!ID())
throw new SyntaxError(LEX.row, LEX.col, string.Format("<assign>: ожидалась
лексема {0}, поступила лексема {1}", "id", lexstr()));
Node id = new Node(Node.Types.N_OPERAND, new Lexem(LEX));
gl();
checkid(); // проверка описания переменной
if (!EQ("="))
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
53
throw new SyntaxError(LEX.row, LEX.col, string.Format("<assign>: ожидалась
лексема {0}, поступила лексема {1}", "id", lexstr()));
Node assign = new Node(Node.Types.N_BINOP, new Lexem(LEX));
gl();
Node expr = Expr();
eqtype(); // проверка соответствия типов левой и правой частей операции
присваивания
assign.Childs.Add(id); // генерация левой части присваивания
assign.Childs.Add(expr); // генерация правой части присваивания
curnode.Childs.Add(assign);
if (!EQ(";"))
throw new SyntaxError(LEX.row, LEX.col, string.Format("<for>: ожидалась
лексема {0}, поступила лексема {1}", ";", lexstr()));
gl();
// блок условия цикла for
Node relop = Relop();
curnode.Childs.Add(relop);
if (!EQ(";"))
throw new SyntaxError(LEX.row, LEX.col, string.Format("<for>: ожидалась
лексема {0}, поступила лексема {1}", ";", lexstr()));
gl();
// блок инкремента цикла for
if (ID() || EQ("++"))
{
Node incr = Assign_incr();
curnode.Childs.Add(incr);
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<for>: ожидались
лексемы id, ++, поступила лексема {0}", lexstr()));
if (!EQ(")"))
throw new SyntaxError(LEX.row, LEX.col, string.Format("<for>: ожидалась
лексема {0}, поступила лексема {1}", ")", lexstr()));
gl();
// тело цикла for
Node op = Op();
curnode.Childs.Add(op);
return curnode;
}
// <relop> ::= <expr> <rop> <expr> {checkop}
Node Relop()
{
int op; // знак операции
Node e1 = Expr();
Rop();
op = IS.Peek();
Node e2 = Expr();
T langType = (T)checkop(); // проверка соответствия типов двуместной операции
Node curnode = new Node(Node.Types.N_BINOP, new Lexem(2, op)); // текущий узел
дерева
curnode.LangType = langType; // языковой тип узла = тип результата
curnode.Childs.Add(e1);
curnode.Childs.Add(e2);
return curnode;
}
// <rop> ::= '<' {instl} | '>' {instl} | '<=' {instl} | '>=' {instl} | '=='
{instl} | '!=' {instl}
void Rop()
{
if (EQ("<") || EQ(">") || EQ("<=") || EQ(">=") || EQ("==") || EQ("!="))
{
gl();
instl(); // помещение знака операции в стек
}
else
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
54
throw new SyntaxError(LEX.row, LEX.col, string.Format("<rop>: ожидались
лексемы <, >, <=, >=, ==, !=, поступила лексема {0}", lexstr()));
}
// <assign_incr> ::= id {checkid} <asspostincr> | <prefincr>
Node Assign_incr()
{
Node
assign
=
new
Node(Node.Types.N_BINOP,
new
Lexem(2,
getLexIdx(LexTables.TL, "=")));
if (ID())
{
Node id = new Node(Node.Types.N_OPERAND, new Lexem(LEX));
T idType = TI[LEX.k].type;
gl();
checkid(); // проверка описания переменной
Node expr = Asspostincr();
if (expr.Childs.Count == 2 && expr.Childs[0] == null) // инкремент
{
expr.Childs[0] = id;
// коррекция типа единицы инкремента
Lexem token = expr.Childs[1].Token;
token.k = getLexIdx(LexTables.TN, "1", idType);
expr.Childs[1].Token = token;
}
assign.LangType = idType; // тип присваивания/инкремента = тип переменной
assign.Childs.Add(id); // генерация левой части присваивания
assign.Childs.Add(expr); // генерация правой части присваивания
return assign;
}
else if (EQ("++"))
{
return Prefincr();
}
else
throw new SyntaxError(LEX.row, LEX.col, string.Format("<assign_incr>:
ожидались лексемы id, ++, поступила лексема {0}", lexstr()));
}
// функция разбора
public bool parse()
{
gl();
Program();
return true;
}
}
public class Node // узел дерева
{
// типы узлов дерева
public enum Types : int { N_PROGRAM = 1, N_OPERAND, N_BLOCK, N_FOR, N_BINOP };
Types type; // тип узла
T langType; // языковой тип узла (для двуместных операций и присваивания)
Lexem token; // идентификатор узла
List<Node> childs; // дочерние узлы дерева
public Types Type { get => type; set => type = value; }
public T LangType { get => langType; set => langType = value; }
public Lexem Token { get => token; set => token = value; }
public List<Node> Childs { get => childs; set => childs = value; }
public Node()
{
type = Types.N_OPERAND;
langType = T.I;
token = new Lexem();
childs = new List<Node>();
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
55
}
public Node(Node.Types newType, Lexem newToken)
{
type = newType;
langType = T.I;
token = newToken;
childs = new List<Node>();
}
public bool IsLeaf() // является ли узел листом дерева
{
return Childs.Count == 0 ? true : false;
}
}
}
Класс CodeGenerator.
using System.Collections.Generic;
namespace CCompiler
{
public class Triade // триада
{
// типы триад
public enum Types : int { T_ASSIGN = 1, T_LESS, T_GREATER, T_LESS_EQUAL,
T_GREATER_EQUAL, T_EQUAL, T_NEQUAL, T_PLUS, T_MINUS, T_MULT, T_DIV, T_FALSEIF, T_GOTO, T_NOP };
Types type; // тип триады
T langType; // языковой тип триады (для двуместных операций и присваивания)
Lexem token; // идентификатор триады
Lexem op1; // операнд 1
Lexem op2; // операнд 2
Triade lop1; // ссылка на триаду-операнд 1
Triade lop2; // ссылка на триаду-операнд 2
bool op1Link; // является ли операнд 1 ссылкой
bool op2Link; // является ли операнд 2 ссылкой
bool hasLabel = false; // имеет ли метку (в которую ведет goto или FalseIf)
public Types Type { get => type; set => type = value; }
public T LangType { get => langType; set => langType = value; }
public Lexem Token { get => token; set => token = value; }
public Lexem Op1 { get => op1; set => op1 = value; }
public Lexem Op2 { get => op2; set => op2 = value; }
public Triade Lop1 { get => lop1; set => lop1 = value; }
public Triade Lop2 { get => lop2; set => lop2 = value; }
public bool Op1Link { get => op1Link; set => op1Link = value; }
public bool Op2Link { get => op2Link; set => op2Link = value; }
public bool HasLabel { get => hasLabel; set => hasLabel = value; }
public Triade(Triade.Types newType, Lexem newToken, T newLangType = T.I)
{
Type = newType;
LangType = newLangType;
Token = newToken;
}
}
class CodeGenerator // генератор кода (триад и ассемблера)
{
Parser parser; // синтаксический анализатор
List<Triade> prog; // программа в виде списка триад
public CodeGenerator(Parser newParser)
{
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
56
parser = newParser;
prog = new List<Triade>();
// дополнение таблицы TL для генерации триад
parser.TL.Add("NOP");
parser.TL.Add("FalseIf");
parser.TL.Add("goto");
}
bool EQ(Lexem lex, string S) // логическая функция, проверяющая, является
ли лексема lex лексемой для S
{
switch (lex.n)
{
case 1:
if (parser.TW[lex.k].Equals(S))
return true;
break;
case 2:
if (parser.TL[lex.k].Equals(S))
return true;
break;
case 3:
if (parser.TI[lex.k].Equals(S))
return true;
break;
case 4:
if (parser.TN[lex.k].Equals(S))
return true;
break;
}
return false;
}
T TYPE(Lexem lex) // функция, возвращающая языковой тип лексемы lex
{
switch (lex.n)
{
case 3:
return parser.TI[lex.k].type;
case 4:
return parser.TN[lex.k].type;
}
return T.I;
}
void put_id(string id, T langType) // процедура, сохраняющая идентификатор
id типа langType в таблице идентификаторов
{
TIrecord TIrec = new TIrecord();
TIrec.id = id;
TIrec.type = langType;
TIrec.defined = 1;
parser.TI.Add(TIrec);
}
string
REG(T
langType)
соответствующего языковому типу langType
{
if (langType == T.I)
return "ax";
else
return "eax";
}
//
функция,
возвращающая
имя
регистра,
// добавление "no operator" триады в программу
void addNop()
{
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
57
Triade
nop
=
parser.getLexIdx(LexTables.TL, "NOP")));
prog.Add(nop);
}
new
Triade(Triade.Types.T_NOP,
// генерация кода программы в триадах - публичный метод
public void GenerateTriades()
{
addNop(); // NOP триада в начало списка триад
int triadeIndex = 0; // индекс вставки новой триады
generateTriades(parser.tree,
ref
triadeIndex);
синтаксического дерева
}
new
Lexem(2,
//
// генерация кода программы в триадах обходом синтаксического дерева
// root - корень поддерева
// triadeIndex - индекс вставки новой триады
void generateTriades(Node root, ref int triadeIndex)
{
switch (root.Type)
{
case Node.Types.N_PROGRAM:
{
generateTriades(root.Childs[0],
обход
ref
triadeIndex);
break;
}
case Node.Types.N_BLOCK:
{
for (int i = 0; i < root.Childs.Count; i++)
generateTriades(root.Childs[i],
ref
triadeIndex);
break;
}
case Node.Types.N_FOR:
{
// начальное присваивание цикла
if (root.Childs[0] != null)
generateTriades(root.Childs[0],
ref
triadeIndex);
// условие цикла
Triade cond = prog[triadeIndex];
generateTriades(root.Childs[1],
ref
triadeIndex);
// переход по ложному условию
Triade falseif = prog[prog.Count - 1];
falseif.Type = Triade.Types.T_FALSEIF;
falseif.Token
=
new
Lexem(2,
parser.getLexIdx(LexTables.TL, "FalseIf"));
falseif.Op1Link = true;
falseif.Lop1 = prog[triadeIndex - 1];
falseif.Op2Link = true;
addNop();
triadeIndex++;
// тело цикла
generateTriades(root.Childs[3],
ref
// инкремент цикла
if (root.Childs[2] != null)
generateTriades(root.Childs[2],
ref
triadeIndex);
triadeIndex);
// безусловный переход к условию цикла
Triade goto_triade = prog[prog.Count - 1];
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
58
goto_triade.Type = Triade.Types.T_GOTO;
goto_triade.Token
=
new
Lexem(2,
parser.getLexIdx(LexTables.TL, "goto"));
goto_triade.Op1Link = true;
goto_triade.Lop1 = cond;
goto_triade.Lop1.HasLabel = true;
addNop();
triadeIndex++;
// адрес перехода по ложному условию цикла
falseif.Lop2 = prog[prog.Count - 1];
falseif.Lop2.HasLabel = true;
break;
case
}
Node.Types.N_BINOP:
//
операции
=,<,>,<=,>=,==,!=,+,-
,/,*
{
if (EQ(root.Token, "="))
{
if (root.Childs[1].IsLeaf())
{
//
выражение
присваивания
-
операнд
Triade act = prog[prog.Count 1];
act.Type = Triade.Types.T_ASSIGN;
act.LangType = root.LangType;
act.Token = root.Token;
act.Op1Link = false;
act.Op1 = root.Childs[0].Token;
act.Op2Link = false;
act.Op2 = root.Childs[1].Token;
}
else
{
// выражение присваивания - не
операнд
generateTriades(root.Childs[1],
ref triadeIndex);
Triade act = prog[prog.Count 1];
act.Type = Triade.Types.T_ASSIGN;
act.LangType = root.LangType;
act.Token = root.Token;
act.Op1Link = false;
act.Op1 = root.Childs[0].Token;
act.Op2Link = true;
act.Lop2 = prog[prog.Count - 2];
}
}
else if (EQ(root.Token, "<") || EQ(root.Token,
">") ||
EQ(root.Token,
"<=")
||
EQ(root.Token,
"==")
||
EQ(root.Token,
"+")
||
EQ(root.Token,
"*")
||
EQ(root.Token, ">=") ||
EQ(root.Token, "!=") ||
EQ(root.Token, "-") ||
EQ(root.Token, "/"))
{
// левый операнд операции - операнд
if (root.Childs[0].IsLeaf())
{
// правый операнд операции - не
операнд
if (!root.Childs[1].IsLeaf())
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
59
generateTriades(root.Childs[1], ref triadeIndex);
Triade act = prog[prog.Count 1];
if (EQ(root.Token, "<"))
act.Type
=
else if (EQ(root.Token, ">"))
act.Type
=
else if (EQ(root.Token, "<="))
act.Type
=
else if (EQ(root.Token, ">="))
act.Type
=
else if (EQ(root.Token, "=="))
act.Type
=
else if (EQ(root.Token, "!="))
act.Type
=
else if (EQ(root.Token, "+"))
act.Type
=
else if (EQ(root.Token, "-"))
act.Type
=
else if (EQ(root.Token, "*"))
act.Type
=
else if (EQ(root.Token, "/"))
act.Type
=
Triade.Types.T_LESS;
Triade.Types.T_GREATER;
Triade.Types.T_LESS_EQUAL;
Triade.Types.T_GREATER_EQUAL;
Triade.Types.T_EQUAL;
Triade.Types.T_NEQUAL;
Triade.Types.T_PLUS;
Triade.Types.T_MINUS;
Triade.Types.T_MULT;
Triade.Types.T_DIV;
act.LangType = root.LangType;
act.Token = root.Token;
act.Op1Link = false;
act.Op1 = root.Childs[0].Token;
// правый операнд операции - не
операнд
if (!root.Childs[1].IsLeaf())
{
act.Op2Link = true;
act.Lop2
=
}
else
// правый
-
prog[prog.Count - 2];
операнд
операции
операнд
{
act.Op2Link = false;
act.Op2
=
root.Childs[1].Token;
}
}
// левый операнд операции - не операнд
else
{
generateTriades(root.Childs[0],
ref triadeIndex);
Triade
code_oper1
=
prog[prog.Count - 2];
// правый операнд операции - не
операнд
if (!root.Childs[1].IsLeaf())
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
60
generateTriades(root.Childs[1], ref triadeIndex);
Triade act = prog[prog.Count 1];
if (EQ(root.Token, "<"))
act.Type
=
else if (EQ(root.Token, ">"))
act.Type
=
else if (EQ(root.Token, "<="))
act.Type
=
else if (EQ(root.Token, ">="))
act.Type
=
else if (EQ(root.Token, "=="))
act.Type
=
else if (EQ(root.Token, "!="))
act.Type
=
else if (EQ(root.Token, "+"))
act.Type
=
else if (EQ(root.Token, "-"))
act.Type
=
else if (EQ(root.Token, "*"))
act.Type
=
else if (EQ(root.Token, "/"))
act.Type
=
Triade.Types.T_LESS;
Triade.Types.T_GREATER;
Triade.Types.T_LESS_EQUAL;
Triade.Types.T_GREATER_EQUAL;
Triade.Types.T_EQUAL;
Triade.Types.T_NEQUAL;
Triade.Types.T_PLUS;
Triade.Types.T_MINUS;
Triade.Types.T_MULT;
Triade.Types.T_DIV;
act.LangType = root.LangType;
act.Token = root.Token;
act.Op1Link = true;
act.Lop1 = code_oper1;
// правый операнд операции - не
операнд
if (!root.Childs[1].IsLeaf())
{
act.Op2Link = true;
act.Lop2
=
}
else
// правый
-
prog[prog.Count - 2];
операнд
операции
операнд
{
act.Op2Link = false;
act.Op2
=
root.Childs[1].Token;
}
}
}
addNop();
triadeIndex++;
break;
}
default:
break;
}
}
// вывод программы в триадах
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
61
public string PrintTriades()
{
string s = "";
for (int i = 0; i < prog.Count; i++)
{
s += string.Format("{0})\t",
i
+
1);
//
индекс
триады
(индексация с 1)
s += string.Format("{0}(", parser.lexstr(prog[i].Token)); //
тип инструкции
if (prog[i].Type == Triade.Types.T_NOP)
{
s += string.Format(")\r\n");
continue;
}
if (prog[i].Op1Link) // операнд1 - ссылка или операнд
s += string.Format("^{0}", prog.IndexOf(prog[i].Lop1)
+ 1);
else
s += string.Format("{0}", parser.lexstr(prog[i].Op1));
if (prog[i].Type == Triade.Types.T_GOTO) // goto не имеет
второго операнда
{
s += string.Format(")\r\n");
continue;
}
s += string.Format(", ");
if (prog[i].Op2Link) // операнд2 - ссылка или операнд
s
+=
string.Format("^{0})\r\n",
prog.IndexOf(prog[i].Lop2) + 1);
else
s
+=
string.Format("{0})\r\n",
parser.lexstr(prog[i].Op2));
}
return s;
}
// генерация и вывод программы в коде ассемблера
public string GenerateAsmCode()
{
string s = "";
// обработка программы в триадах
for (int i = 0; i < prog.Count; i++)
{
// метка для инструкции, в которую есть переход
if (prog[i].HasLabel)
s += string.Format("&L{0}:", prog.IndexOf(prog[i]) +
1);
s += string.Format("\t");
// обработка триады
if (prog[i].Type == Triade.Types.T_ASSIGN)
{
if (prog[i].Op2Link)
s
+=
string.Format("mov
{0},
&t{1}\r\n",
REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
else
s
+=
string.Format("mov
{0},
{1}\r\n",
REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
s
+=
string.Format("\tmov
{0},
{1}",
parser.lexstr(prog[i].Op1), REG(TYPE(prog[i].Op1)));
}
else if (prog[i].Type == Triade.Types.T_PLUS ||
prog[i].Type == Triade.Types.T_MINUS ||
prog[i].Type == Triade.Types.T_MULT ||
prog[i].Type == Triade.Types.T_DIV)
{
if (prog[i].Op1Link)
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
62
s
+=
string.Format("mov
{0},
&t{1}\r\n",
REG(prog[i].Lop1.LangType), prog.IndexOf(prog[i].Lop1) + 1);
else
s
+=
string.Format("mov
{0},
{1}\r\n",
REG(TYPE(prog[i].Op1)), parser.lexstr(prog[i].Op1));
if (prog[i].Op2Link)
{
if (prog[i].Type == Triade.Types.T_PLUS)
s
+=
string.Format("\tadd
{0},
&t{1}\r\n", REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
else if (prog[i].Type == Triade.Types.T_MINUS)
s
+=
string.Format("\tsub
{0},
&t{1}\r\n", REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
else if (prog[i].Type == Triade.Types.T_MULT)
s
+=
string.Format("\timul
{0},
&t{1}\r\n", REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
else if (prog[i].Type == Triade.Types.T_DIV)
s
+=
string.Format("\timul
{0},
&t{1}\r\n", REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
}
else
{
if (prog[i].Type == Triade.Types.T_PLUS)
s += string.Format("\tadd {0}, {1}\r\n",
REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
else if (prog[i].Type == Triade.Types.T_MINUS)
s += string.Format("\tsub {0}, {1}\r\n",
REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
else if (prog[i].Type == Triade.Types.T_MULT)
s
+=
string.Format("\timul
{0},
{1}\r\n", REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
else if (prog[i].Type == Triade.Types.T_DIV)
s
+=
string.Format("\tidiv
{0},
{1}\r\n", REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
}
s
+=
string.Format("\tmov
&t{0},
{1}",
prog.IndexOf(prog[i]) + 1, REG(prog[i].LangType));
put_id(string.Format("&t{0}", prog.IndexOf(prog[i]) +
1), prog[i].LangType);
}
else if (prog[i].Type == Triade.Types.T_LESS ||
prog[i].Type == Triade.Types.T_GREATER ||
prog[i].Type == Triade.Types.T_LESS_EQUAL ||
prog[i].Type == Triade.Types.T_GREATER_EQUAL ||
prog[i].Type == Triade.Types.T_EQUAL ||
prog[i].Type == Triade.Types.T_NEQUAL)
{
if (prog[i].Op1Link)
s
+=
string.Format("mov
{0},
&t{1}\r\n",
REG(prog[i].Lop1.LangType), prog.IndexOf(prog[i].Lop1) + 1);
else
s
+=
string.Format("mov
{0},
{1}\r\n",
REG(TYPE(prog[i].Op1)), parser.lexstr(prog[i].Op1));
if (prog[i].Op2Link)
s += string.Format("\tcmp {0}, &t{1}\r\n",
REG(prog[i].Lop2.LangType), prog.IndexOf(prog[i].Lop2) + 1);
else
s
+=
string.Format("\tcmp
{0},
{1}\r\n",
REG(TYPE(prog[i].Op2)), parser.lexstr(prog[i].Op2));
if (prog[i].Type == Triade.Types.T_LESS)
s += string.Format("\tsetl al");
else if (prog[i].Type == Triade.Types.T_GREATER)
s += string.Format("\tsetg al");
else if (prog[i].Type == Triade.Types.T_LESS_EQUAL)
s += string.Format("\tsetle al");
else if (prog[i].Type == Triade.Types.T_GREATER_EQUAL)
s += string.Format("\tsetge al");
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
63
else if (prog[i].Type == Triade.Types.T_EQUAL)
s += string.Format("\tsete al");
else if (prog[i].Type == Triade.Types.T_NEQUAL)
s += string.Format("\tsetne al");
}
else if (prog[i].Type == Triade.Types.T_FALSEIF)
{
s += string.Format("cmp al, 0\r\n" +
"\tjz &L{0}\t", prog.IndexOf(prog[i].Lop2)
+
1);
}
else if (prog[i].Type == Triade.Types.T_GOTO)
{
s
+=
string.Format("jmp
&L{0}\t",
prog.IndexOf(prog[i].Lop1) + 1);
}
else if (prog[i].Type == Triade.Types.T_NOP) // "no operator"
не генерирует код ассемблера
;
s += "\r\n";
}
string prologue = ".386\r\n.model flat, stdcall\r\n\r\n.data\r\n";
// пролог программы
for (int i = 1; i < parser.TI.Count; i++)
{
prologue += string.Format("\t{0}", parser.TI[i].id);
if (parser.TI[i].type == T.I)
prologue += "\tWORD 0\r\n";
else
prologue += "\tDWORD 0\r\n";
}
prologue += "\r\n.code\r\n\r\nstart:\r\n";
string epilogue = "END start\r\n"; // эпилог программы
return prologue + s + epilogue;
}
}
}
Класс графического интерфейса пользователя Form1.
using System;
using System.IO;
using System.Windows.Forms;
namespace CCompiler
{
public partial class Form1 : Form
{
Scanner scanner; // лексический анализатор
Parser parser; // совмещенный синтаксический/семантический анализатор
CodeGenerator generator; // кодогенератор
public Form1()
{
InitializeComponent();
}
private void трансляцияToolStripMenuItem_Click(object sender, EventArgs e)
{
// лекс. анализ
scanner = new Scanner(intext.Text);
try
{
scanner.scan();
}
catch (LexicalError err)
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
64
{
log.Text = err.Message;
return;
}
catch (SemanticError err)
{
log.Text = err.Message;
return;
}
// вывод результатов лекс. анализа
intextLex.Text = intext.Text;
outLex.Text = scanner.progToString();
TWlex.RowCount = scanner.TW.Count - 1;
for (int i = 1; i < scanner.TW.Count; i++)
{
TWlex[0, i - 1].Value = i.ToString();
TWlex[1, i - 1].Value = scanner.TW[i];
}
TLlex.RowCount = scanner.TL.Count - 1;
for (int i = 1; i < scanner.TL.Count; i++)
{
TLlex[0, i - 1].Value = i.ToString();
TLlex[1, i - 1].Value = scanner.TL[i];
}
TIlex.RowCount = scanner.TI.Count - 1;
for (int i = 1; i < scanner.TI.Count; i++)
{
TIlex[0, i - 1].Value = i.ToString();
TIlex[1, i - 1].Value = scanner.TI[i].id;
TIlex[2, i - 1].Value = scanner.TI[i].defined.ToString();
TIlex[3, i - 1].Value = scanner.TI[i].getType();
}
TNlex.RowCount = scanner.TN.Count - 1;
for (int i = 1; i < scanner.TN.Count; i++)
{
TNlex[0, i - 1].Value = i.ToString();
TNlex[1, i - 1].Value = scanner.TN[i].n;
TNlex[2, i - 1].Value = scanner.TN[i].getType();
}
// синт./сем. анализ / генерация синтаксического дерева
parser = new Parser(scanner.getlex(), scanner.TW, scanner.TL,
scanner.TI,
scanner.TN);
try
{
parser.parse();
}
catch (SyntaxError err)
{
log.Text = err.Message;
return;
}
catch (SemanticError err)
{
log.Text = err.Message;
return;
}
// вывод результатов
intextTree.Text = intext.Text;
outTree.Text = parser.PrintSyntaxTree();
TWtree.RowCount = parser.TW.Count - 1;
for (int i = 1; i < parser.TW.Count; i++)
{
TWtree[0, i - 1].Value = i.ToString();
TWtree[1, i - 1].Value = parser.TW[i];
}
TLtree.RowCount = parser.TL.Count - 1;
for (int i = 1; i < parser.TL.Count; i++)
{
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
65
TLtree[0, i - 1].Value = i.ToString();
TLtree[1, i - 1].Value = parser.TL[i];
}
TItree.RowCount = parser.TI.Count - 1;
for (int i = 1; i < parser.TI.Count; i++)
{
TItree[0, i - 1].Value = i.ToString();
TItree[1, i - 1].Value = parser.TI[i].id;
TItree[2, i - 1].Value = parser.TI[i].defined.ToString();
TItree[3, i - 1].Value = parser.TI[i].getType();
}
TNtree.RowCount = parser.TN.Count - 1;
for (int i = 1; i < parser.TN.Count; i++)
{
TNtree[0, i - 1].Value = i.ToString();
TNtree[1, i - 1].Value = parser.TN[i].n;
TNtree[2, i - 1].Value = parser.TN[i].getType();
}
// генерация триад
generator = new CodeGenerator(parser);
generator.GenerateTriades();
// вывод результатов
intextTriade.Text = intext.Text;
outTriade.Text = generator.PrintTriades();
TWtriade.RowCount = parser.TW.Count - 1;
for (int i = 1; i < parser.TW.Count; i++)
{
TWtriade[0, i - 1].Value = i.ToString();
TWtriade[1, i - 1].Value = parser.TW[i];
}
TLtriade.RowCount = parser.TL.Count - 1;
for (int i = 1; i < parser.TL.Count; i++)
{
TLtriade[0, i - 1].Value = i.ToString();
TLtriade[1, i - 1].Value = parser.TL[i];
}
TItriade.RowCount = parser.TI.Count - 1;
for (int i = 1; i < parser.TI.Count; i++)
{
TItriade[0, i - 1].Value = i.ToString();
TItriade[1, i - 1].Value = parser.TI[i].id;
TItriade[2, i - 1].Value = parser.TI[i].defined.ToString();
TItriade[3, i - 1].Value = parser.TI[i].getType();
}
TNtriade.RowCount = parser.TN.Count - 1;
for (int i = 1; i < parser.TN.Count; i++)
{
TNtriade[0, i - 1].Value = i.ToString();
TNtriade[1, i - 1].Value = parser.TN[i].n;
TNtriade[2, i - 1].Value = parser.TN[i].getType();
}
// генерация выходного кода
// вывод результатов
intextCode.Text = intext.Text;
outCode.Text = generator.GenerateAsmCode();
TWcode.RowCount = parser.TW.Count - 1;
for (int i = 1; i < parser.TW.Count; i++)
{
TWcode[0, i - 1].Value = i.ToString();
TWcode[1, i - 1].Value = parser.TW[i];
}
TLcode.RowCount = parser.TL.Count - 1;
for (int i = 1; i < parser.TL.Count; i++)
{
TLcode[0, i - 1].Value = i.ToString();
TLcode[1, i - 1].Value = parser.TL[i];
}
TIcode.RowCount = parser.TI.Count - 1;
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
66
for (int i = 1; i < parser.TI.Count; i++)
{
TIcode[0, i - 1].Value = i.ToString();
TIcode[1, i - 1].Value = parser.TI[i].id;
TIcode[2, i - 1].Value = parser.TI[i].defined.ToString();
TIcode[3, i - 1].Value = parser.TI[i].getType();
}
TNcode.RowCount = parser.TN.Count - 1;
for (int i = 1; i < parser.TN.Count; i++)
{
TNcode[0, i - 1].Value = i.ToString();
TNcode[1, i - 1].Value = parser.TN[i].n;
TNcode[2, i - 1].Value = parser.TN[i].getType();
}
log.Text = "Трансляция завершена успешно.";
}
private void formreset()
{
TWlex.RowCount = 0;
TLlex.RowCount = 0;
TIlex.RowCount = 0;
TNlex.RowCount = 0;
intextLex.Clear();
outLex.Clear();
TWtree.RowCount = 0;
TLtree.RowCount = 0;
TItree.RowCount = 0;
TNtree.RowCount = 0;
intextTree.Clear();
outTree.Clear();
TWtriade.RowCount = 0;
TLtriade.RowCount = 0;
TItriade.RowCount = 0;
TNtriade.RowCount = 0;
intextTriade.Clear();
outTriade.Clear();
TWcode.RowCount = 0;
TLcode.RowCount = 0;
TIcode.RowCount = 0;
TNcode.RowCount = 0;
intextCode.Clear();
outCode.Clear();
log.Clear();
tabControl1.SelectTab(0);
}
private void intext_TextChanged(object sender, EventArgs e)
{
formreset();
}
private void создатьToolStripMenuItem_Click(object sender, EventArgs e)
{
intext.Clear();
formreset();
}
private void выходToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void выходToolStripMenuItem1_Click(object sender, EventArgs e)
{
Close();
}
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
67
private void открытьToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
od.InitialDirectory = Application.StartupPath;
if (od.ShowDialog() != DialogResult.OK)
return;
string f = od.FileName;
StreamReader r = new StreamReader(f);
intext.Text = r.ReadToEnd();
r.Close();
od.Dispose();
tabControl1.SelectTab(0);
}
private void сохранитьToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog sd = new SaveFileDialog();
sd.InitialDirectory = Application.StartupPath;
sd.Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*";
if (sd.ShowDialog() != DialogResult.OK)
return;
string f = sd.FileName;
StreamWriter w = new StreamWriter(f);
w.Write(intext.Text);
w.Close();
sd.Dispose();
}
private void справкаToolStripMenuItem_Click(object sender, EventArgs e)
{
string help =
"Компилятор модельного языка." + "\r\n" +
"Введите исходный текст программы в поле 'Исходный текст'" + "\r\n" +
"
и выберите пункт меню 'Трансляция' для трансляции." + "\r\n" +
"";
MessageBox.Show(this, help, "Справка", MessageBoxButtons.OK);
}
}
}
Изм. Лист
№ докум.
Подпись Дата
МИВУ.09.03.04-05.000
КР
Лист
68
Download