Uploaded by Yulya Li

Курсовая

advertisement
Федеральное агентство связи
ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
“Поволжский университет телекоммуникаций и информатики”
Факультет информационных систем и технологий
Кафедра программного обеспечения вычислительной техники
и управления в технических системах
КУРСОВАЯ РАБОТА
по теории языков программирования и методов трансляции
Разработка компилятора модельного языка
Пояснительная записка
Руководитель работы
_______________Бахарева Н.Ф.
"____"______________2014г.
Исполнитель
студентка гр. ПО-12у
__________________Закирова А.Ф.
"____"______________2014г.
Самара 2014
Содержание
Введение .................................................................................................................. 3
1. Постановка задачи ........................................................................................... 5
2. Формальная модель задачи ............................................................................. 6
2.1 Формальные грамматики ............................................................................. 6
2.2 Формы Бэкуса-Наура (БНФ) ....................................................................... 6
2.3 Расширенные формы Бэкуса-Наура (РБНФ) ............................................. 7
3. Спецификация основных процедур и функций .............................................. 9
3.1 Лексический анализатор .............................................................................. 9
3.2 Синтаксический анализатор ...................................................................... 14
3.3 Семантический анализатор ....................................................................... 14
4. Разработка алгоритма решения задачи ........................................................16
5. Структурная организация данных ................................................................17
5.1Спецификация входной информации........................................................ 17
5.2Спецификация выходной информации ..................................................... 17
6. Установка и эксплуатация ПО ......................................................................18
ЗАКЛЮЧЕНИЕ ....................................................................................................19
СПИСОК ЛИТЕРАТУРЫ ....................................................................................20
Приложение 1. Проверка работоспособности программы ..............................21
Приложение 2. Листинг программы .................................................................212
Введение
Компилятор - это программа, которая осуществляет перевод исходной
программы в эквивалентную ей объектную программу на языке машинных
команд или языке ассемблере.
Несмотря на более чем полувековую историю вычислительной техники,
формально годом рождения теории компиляторов можно считать 1957, когда
появился первый компилятор языка Фортран, созданный Бэкусом и дающий
достаточно
эффективный
объектный
код.
До
этого
времени
создание
компиляторов было весьма творческим процессом. Лишь появление теории
формальных языков и строгих математических моделей позволило перейти от
творчества к науке. Именно благодаря этому, стало возможным появление сотен
новых языков программирования.
Несмотря на то, что к настоящему времени разработаны тысячи различных
языков и их компиляторов, процесс создания новых приложений в этой области
не прекращается. Это связно как с развитием технологии производства
вычислительных систем, так и с необходимостью решения все более сложных
прикладных задач. Такая разработка может быть обусловлена различными
причинами,
в
частности,
функциональными
ограничениями,
отсутствием
локализации, низкой эффективностью существующих компиляторов. Поэтому,
основы теории языков и формальных грамматик, а также практические методы
разработки компиляторов лежат в фундаменте инженерного образования по
информатике и вычислительной технике.
Цель курсовой работы:
-закрепление теоретических знаний в области теории формальных языков,
грамматик, автоматов и методов трансляции;
-формирование практических умений и навыков разработки собственного
компилятора модельного языка программирования;
закрепление практических навыков самостоятельного решения инженерных
задач, развитие творческих способностей студентов и умений пользоваться
технической, нормативной и справочной литературой.
В
настоящее
время
в
мире
появляются
более
новые
языки
программирования и не каждый из ныне существующих трансляторов могут
прочитать программы, написанный на новом языке, и перевести его в другой
язык. Поэтому сейчас разрабатываются новые трансляторы, в этом и заключается
актуальность данной курсовой работы.
1. Постановка задачи
Разработать компилятор модельного языка, выполнив следующие действия:
1. Написать пример программы, раскрывающих особенности конструкций
учебного языка программирования, отразив его функциональные возможности.
2. Составить таблицу лексем для тестового примера.
3. Разработать программное средство, реализующее лексический анализ
текста программы на входном языке.
4. Вывести примеры таблиц идентификаторов и чисел.
5. Реализовать синтаксический анализатор текста программы на модельном
языке методом рекурсивного спуска.
6.
Дополнить
синтаксический
анализатор
процедурами
проверки
семантической правильности программы на модельном языке в соответствии с
контекстными условиями варианта.
2. Формальная модель задачи
Существуют
три
основных
метода
описания
синтаксиса
языков
программирования: формальные грамматики, формы Бэкуса-Наура и диаграммы
Вирта.
2.1 Формальные грамматики
Определение. Формальной грамматикой называется четверка вида:
G  (VT , VN , P, S ) ,
где VN - конечное множество нетерминальных символов грамматики
(обычно прописные латинские буквы);
VT
-
множество терминальных символов грамматики (обычно
строчные латинские буквы, цифры, и т.п.), VT VN =;
Р –
множество правил вывода грамматики, являющееся конечным
подмножеством множества (VT  VN)+  (VT  VN)*; элемент
( ,
) множества Р называется правилом вывода и записывается в
виде  (читается: «из цепочки  выводится цепочка »);
S - начальный символ грамматики, S VN.
Для записи правил вывода с одинаковыми левыми частями вида
  1,    2 ,,   n
используется
сокращенная
форма
записи
  1 |  2 |  |  n .
2.2 Формы Бэкуса-Наура (БНФ)
Метаязык, предложенный Бэкусом и Науром, использует следующие
обозначения:
- символ «::=» отделяет левую часть правила от правой (читается:
«определяется как»);
- нетерминалы
обозначаются
произвольной
символьной
заключенной в угловые скобки «<» и «>»;
- терминалы - это символы, используемые в описываемом языке;
строкой,
- правило может определять порождение нескольких альтернативных
цепочек, отделяемых друг от друга символом вертикальной черты «|» (читается:
«или»).
2.3 Расширенные формы Бэкуса-Наура (РБНФ)
Для повышения удобства и компактности описаний, в РБНФ вводятся
следующие дополнительные конструкции (метасимволы):
- квадратные скобки «[» и «]» означают, что заключенная в них
синтаксическая конструкция может отсутствовать;
-
фигурные скобки «{» и «}» означают повторение заключенной в них
синтаксической конструкции ноль или более раз;
сочетание фигурных скобок и косой черты «{/» и «/}» используется
для обозначения повторения один и более раз;
круглые
скобки
«(»
и
«)»
используются
для
ограничения
альтернативных конструкций.
В соответствии с данными правилами синтаксис модельного языка будет
выглядеть следующим образом:
<буква>:: =A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U
| V | W |X |Y | Z |a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y
|z
<цифра>::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<целое>::= <двоичное> | <восьмеричное> | <десятичное> |
<шестнадцатеричное>
<двоичное>::= {/ 0 | 1 /} (B | b)
<восьмеричное>::= {/ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 /} (O | o)
<десятичное>::= {/ <цифра> /} [D | d]
<шестнадцатеричное>::= <цифра> {<цифра> | A | B | C | D | E | F | a | b |
c | d | e | f} (H | h)
<идентификатор>::= <буква> { <буква> | <цифра> }
<число> ::= {/< цифра> /}
<ключевое_слово>::= dim | ass | % | ! | $ | if | then | else | for | to | do | while |
end | read |write | true | false
<разделитель>::= <> | = | < | < = | > | >= | ( | ) | { | } | / | * | :
<программа>= {/(<описание> | <оператор>) (: | переход строки) /} end
<описание>::= dim <идентификатор> {, <идентификатор> } <тип>
<составной>::= <оператор> { ( : | перевод строки) <оператор> }
<присваивания>::= <идентификатор> ass <выражение>
<условный>::= if <выражение> then <оператор> [ else <оператор>]
<фиксированного_цикла>::= for <присваивания>
to <выражение> do
<оператор>
<условного_цикла>::= while <выражение> do <оператор>
<ввода>::= read (<идентификатор> {, <идентификатор> })
<вывода>::= write (<выражение> {, <выражение> })
3. Спецификация основных процедур и функций
3.1 Лексический анализатор
Лексический анализатор (ЛА) – это первый этап процесса компиляции, на
котором символы, составляющие исходную программу, группируются в
отдельные минимальные единицы текста, несущие смысловую нагрузку –
лексемы.
Задача лексического анализа - выделить лексемы и преобразовать их к виду,
удобному для последующей обработки. ЛА использует регулярные грамматики.
ЛА необязательный этап компиляции, но желательный по следующим
причинам:
1) замена идентификаторов, констант, ограничителей и служебных слов
лексемами делает программу более удобной для дальнейшей обработки;
2)
ЛА
уменьшает
длину
программы,
устраняя
из
ее
исходного
представления несущественные пробелы и комментарии;
3) если будет изменена кодировка в исходном представлении программы, то
это отразится только на ЛА.
В процедурных языках лексемы обычно делятся на классы:
1) операторы;
2) разделители;
3) числа;
4) идентификаторы.
Каждая лексема представляет собой пару чисел вида (n, k), где n – номер
таблицы лексем, k - номер лексемы в таблице.
Входные данные ЛА - текст транслируемой программы на входном языке.
Выходные данные ЛА - списковая структура, содержащая лексемы в
числовом представлении.
Для модельного языка таблица операторов (1):
0. dim
1. end
2. %
3. !
4. $
5. read
6. write
7. for
8. to 9. do
10. while 11. if
12. then 13. else 14. true
Таблица разделителей(2):
0. <>
1. =
2. <
3. <=
15. false
4. >
5. >=
6. +
7. 8. or
9. /
10. not
11. \n
12. *
13. :
14. ass
15. and
16. (
17. )
18. .
19. {
20. }
Таблицы идентификаторов (4) и таблица чисел(3) формируются в ходе
лексического анализа.
Опишем результаты работы лексического анализатора для модельного языка
М.
Входные данные ЛА:
dim A,I %:
dim Min %:
Min ass 32767:
for I ass 1 to 10 do
read (A)
if A< Min then Min ass A: {poisk minimalnogo}
write(Min):
end
Выходные данные ЛА:
(0,0) (3,0) (1,18) (3,1) (0,2) (1,13) (1,11) (0,0) (3,2) (0,2) (1,13) (1,11) (3,2)
(1,14) (2,0) (1,13) (1,11) (0,7) (3,1) (1,14) (2,1) (0,8) (2,2) (0,9) (1,11) (0,5) (1,16) (3,0)
(1,17) (1,11) (0,11) (3,0) (1,2) (3,2) (0,12) (3,2) (1,14) (3,0) (1,13) (1,11) (0,6) (1,16)
(3,2) (1,17) (1,13) (1,11) (0,1)
Анализ текста проводится путем разбора по регулярным грамматикам и
опирается
на
способ
разбора
по
диаграмме
состояний,
снабженной
дополнительными пометками-действиями. Для удобства разбора вводится
дополнительное состояние диаграммы ERR, попадание в которое соответствует
появлению ошибки в алгоритме разбора. Переход по дуге, не помеченной ни
одним символом, осуществляется по любому другому символу, кроме тех,
которыми помечены все другие дуги, выходящие из данного состояния.
Составим ЛА для модельного языка . Предварительно введем следующие
обозначения для переменных, процедур и функций.
Переменные:
1) СН – очередной входной символ;
2) S – буфер для накапливания символов лексемы;
3) B – переменная для формирования числового значения константы;
4)
CS – текущее состояние буфера накопления лексем с возможными
значениями: P – начало, I – идентификатор, N – число, С – комментарий, M –
конец программы («.»), D – дробное число, D1 – знак после «е», D2 – считывание
степени, D3 – запись дробного числа, NB – запись 2-го числа, NV – запись 8-го
числа, NS – 16-го числа, ND – запись десятичного числа, NH – проверка 16-е
число или ошибка, L1 – проверка знака «>=» и «<>», L2 –проверка знака «<=», V
– выход, ER –ошибка;
6)
t - таблица лексем анализируемой программы с возможными
значениями: TW – таблица служебных слов М-языка, TL – таблица ограничителей
М-языка, TI – таблица идентификаторов программы, TC – таблица целых чисел,
TD – таблица вещественных чисел;
7) z - номер лексемы в таблице t (если лексемы в таблице нет, то z=0).
LEX – переменная, содержащая текущую лексему, считанную из файла
лексем;
8) gl – процедура считывания очередной лексемы из файла лексем в
переменную LEX;
9) EQ(S) – логическая функция, проверяющая, является ли текущая лексема
LEX лексемой для S;
10) NUM - логическая функция, проверяющая, является ли LEX числом.
Процедуры и функции:
1) gc – процедура считывания очередного символа текста в переменную СН;
2) let – логическая функция, проверяющая, является ли переменная СН
буквой;
3) digit - логическая функция, проверяющая, является ли переменная СН
цифрой;
4) nill – процедура очистки буфера S;
5) add – процедура добавления очередного символа в конец буфера S;
6) look(n,k) – процедура поиска лексемы из буфера S в таблице с номером n
с возвращением номера лексемы в таблице, k – количество элементов в таблице;
7) put(t) – процедура записи лексемы из буфера S в таблицу t, если там не
было этой лексемы, возвращает номер данной лексемы в таблице;
8) out(n, k) – процедура записи пары чисел (n, k) в файл лексем;
10) nill – очистке бефера S;
11) let – проверка является ли символ буквой;
Рис. 1 – Диаграмма состояний с действиями для модельного языка М
3.2 Синтаксический анализатор
Задача синтаксического анализатора: провести разбор текста программы,
сопоставив его с эталоном, данным в описании языка. Для синтаксического
разбора используются контекстно-свободные грамматики (КС-грамматика).
Один из эффективных методов синтаксического анализа
– метод
рекурсивного спуска.
Метод рекурсивного спуска или нисходящий разбор – это один из методов
определения принадлежности входной строки к некоторому формальному языку,
описанному контекстно-свободной грамматикой (КС-грамматика).
Входные данные – файл лексем в числовом представлении.
Выходные
данные
–
заключение
о
синтаксической
правильности
программы или сообщение об имеющихся ошибках.
3.3 Семантический анализатор
В ходе семантического анализа проверяются отдельные правила записи
исходных программ, которые не описываются КС-грамматикой. Эти правила
носят
контекстно-зависимый
характер,
их
называют
семантическими
соглашениями или контекстными условиями.
В программе синтаксический и семантический анализаторы совмещены и
осуществляются параллельно.
Соблюдение контекстных условий для языка М предполагает три типа
проверок:
1) обработка описаний;
2) анализ выражений;
3) проверка правильности операторов.
В
оптимизированном
варианте
синтаксический
и
семантический
анализаторы совмещены и осуществляются параллельно. Поэтому процедуры
СеА будем внедрять в ранее разработанные процедуры СиА.
Вход: файл лексем в числовом представлении.
Выход: заключение о семантической правильности программы или о типе
обнаруженной семантической ошибки.
Задача обработки описаний – проверить, все ли переменные программы
описаны правильно и только один раз.
Задача анализа выражений – проверить описаны ли переменные,
встречающиеся в выражениях, и соответствуют ли типы операндов друг другу и
типу операции.
Задачи проверки правильности операторов:
1) выяснить, все ли переменные, встречающиеся в операторах, описаны;
2) установить соответствие типов в операторе присваивания слева и справа
от символа «=»;
3) определить, является ли выражение E в операторах условия и цикла
булевым.
Задача решается проверкой типов в соответствующих местах программы.
На основе лексем с помощью рекурсивного спуска анализируется цепочка
идущих лексем и на выходе получается программа в виде дерева, в узлах (Node)
которого проводится одна маленькая операция.
Если при анализе цепочек лексем возникает ошибка или встретилась
неизвестная лексема или лексема не принадлежащая анализируемому оператору,
то вызывается исключение с текстом ошибки.
4. Разработка алгоритма решения задачи
Анализ
Лексический
анализ
Синтаксический
разбор
Анализ
и локализация
Семантический
анализ
обнаруженных
ошибок
Внутреннее
представление
программы
Сообщение об
ошибке
Синтез
Подготовка к
Таблицы идентификаторов
Исходная
программа
генерации кода
Объектная
программа
Генерация кода
Рис.2 Общая схема работы компилятора
5.
Структурная организация данных
5.1
Спецификация входной информации
В программе используется три типа класса:
class LexicalAnalysis для лексического анализа;
class Parsing для синтаксического и семантического анализа
class Error для вывода ошибок
Входными данными данного программного средства является текст
программы на модельном языке. В главное окно программы вводится текст,
который в последующем анализируется и интерпретируется в машинный
исполняемый код.
Таблица 1 – Входная информация
Имя
Тип
Text
string
Назначение
Текст
программы
модельном языке
на
5.2 Спецификация выходной информации
Выходными данными является заключение о лексической, синтаксической
и семантической правильности введенной программы, или сообщение об ошибке
с указанием номера строки и характером ошибки. А также запускается
консольное приложение, непосредственно реализующее описанный в тексте
программы алгоритм.
Таблица 2 – Выходная информация
Имя
Тип
numbers
List<string>
identif
List<string>
lexList
List<Lex>
Назначение
Список чисел, результат работы
лексического анализатора
Список
идентификаторов,
результат работы лексического
анализатора
Список
лексем,
результат
работы
лексического
анализатора
6. Установка и эксплуатация ПО
Для того чтобы установить данное программное средство на ПК
необходимо подключить носитель (CD, Flash Device), на котором это
программное средство находится. Далее необходимо открыть проводник, найти
данный носитель информации, найти на нем данное программное средство и
скопировать его на локальный диск (винчестер).
Данное программное средство позволяет быстро получить результат, не
требуя
больших
затрат
ресурсов
компьютера.
Оно
предназначено
для
использования в семействе операционных систем MS Windows. Поставляется оно
в виде одного файла размером 28 Кбайт. Для работы необходимо запустить
исполняемый exe-файл.
ЗАКЛЮЧЕНИЕ
Разработали на языке программирования C# в среде Microsoft Visual Studio
2010 на базе Microsoft NET Framework 4.0 программное средство, реализующее
компилятор модельного языка программирования.
Программное средство способно выполнять следующие функции:
- ввод и редактирование текста программ, написанных на определенном
модельном языке;
- производить лексический анализ программ;
- выполнять синтаксическую и семантическую проверку программ;
В случае возникновения ошибок на любом из этапов работы программы
информирует о них пользователя.
Программное
средство
написанных на модельном языке.
протестировано
на
различных
программах,
СПИСОК ЛИТЕРАТУРЫ
1.
Ахо А., Сети Р., Ульман Д. Компиляторы: принципы, технологии и
инструменты.: Пер. с англ. – М.: Изд. дом «Вильямс», 2001. – 768с.
2.
Опалева Э. А., Самойленко В. П. Языки программирования и методы
трансляции. – СПб.: БХВ-Петербург, 2005. – 480 с.: ил.
3.
Ишакова Е. Н. Разработка компиляторов: Методические указания к курсовой
работе. – Оренбург: ГОУ ОГУ, 2005. – 50 с.
4.
Афанасьев А.Н. Формальные языки и грамматики: Учебное пособие. –
Ульяновск: УлГТУ, 1997. – 84с.
5.
Волкова И.А., Руденко Т.В. Формальные языки и грамматики. Элементы
теории трансляции. – М.: Диалог-МГУ, 1999. – 62 с.
6.
Пратт Т., Зелковиц М. Языки программирования: разработка и реализация /
Под ред. А. Матросова. – СПб: Питер, 2002. – 688с.
7.
Рейуорд-Смит В. Теория формальных языков. Вводный курс: Пер. с
англ. – М.: Радио и связь, 1988. – 128с.
8.
Соколов
А.П.
Системы
программирования:
теория,
методы,
алгоритмы: Учеб.пособие. – М.: Финансы и статистика, 2004. – 320с.
9.
Федоров В.В. Основы построения трансляторов: Учебное пособие. –
Обнинск: ИАТЭ, 1995. – 105с.
Приложение 1. Проверка работоспособности программы
Рис.3- Лексический анализ
Рис.3- Синтаксический и семантический анализ
Приложение 2. Листинг программы
namespace Analis
{
public class Error : Exception
{
string text;
public Error(string text)
{
this.text = text;
}
public string Text
{
get { return text; }
}
}
public class LexAnalis
{
// лексический анализ
public enum TypeLex //типы лексем
{
Operator = 0,
Separator = 1,
Constant = 2,
Ident = 3,
Nothing,
Err
};
public static string[] separators = new string[]
{
"<>", "=", "<", "<=", ">", ">=", "+", "-","or", "/", "not", "\n", "*",
":", "ass", "and", "(", ")", ",", "{", "}"
};
public static string[] operators = new string[]
{
"dim", "end", "%", "!", "$", "read", "write", "for", "to", "do", "while",
"if", "then", "else" , "true", "false"
};
string prog;
string value;
TypeLex typeLex;
int index;
char ch;
int pos;
List<string> identif;
List<string> constants;
public LexAnalis(string program)
{
this.prog = program;
identif = new List<string>();
constants = new List<string>();
Comment();
}
bool Comment() // проверка комментария
{
if (pos < prog.Length) // если меньше длины программы
{
ch = prog[pos]; // считываем символ
if (ch == '{') // если начало комментария
{
while (pos < prog.Length && prog[pos] != '}') pos++; // ищем конец
комментария
pos++;
if (pos < prog.Length)
ch = prog[pos];
else
{
ch = (char)0;
return false; //если не закрыт то false
}
}
pos++;
return true; // если закрыт true
}
else
{
ch = (char)0;
return false;
}
}
public TypeLex Next() // определение типа лексем
{
TypeLex ret = TypeLex.Nothing;
value = "";
typeLex = TypeLex.Nothing;
while (ch != 0) // пока не 0
{
if (ch == ' ')
Comment();
else if (char.IsDigit(ch)) // проверка на цифру
{
value = string.Empty;
while (char.IsDigit(ch) || ch == '.') // проверка
вещественного
{
value += ch;
Comment();
}
index = constants.IndexOf(value); // ищем вхождения
if (index < 0) // если нет, то добавляем в массив констант
{
index = constants.Count;
constants.Add(value);
}
ret = TypeLex.Constant;
break;
}
else if (char.IsLetterOrDigit(ch)) // проверка типа числа
{
value = string.Empty;
while (char.IsLetterOrDigit(ch))
{
value += ch;
Comment();
}
index = IsSepar(value); // проверка на разделитель
if (index >= 0)
ret = TypeLex.Separator;
else
{
index = IsOper(value);
if (index >= 0)
{
ret = TypeLex.Operator; // проверка на оператор
}
else
{
index = identif.IndexOf(value);
if (index < 0)
{
index = identif.Count;
identif.Add(value);
}
ret = TypeLex.Ident;
}
}
break;
}
else
{
value = String.Empty;
value = value + ch;
index = IsSepar(value);
if (index >= 0)
ret = TypeLex.Separator;
else
{
index = IsOper(value);
if (index >= 0)
{
ret = TypeLex.Operator;
}
else
{
ret = TypeLex.Err;
value = "неизвестный символ '" + ch + "'";
throw new Error(value);
}
}
Comment();
break;
}
}
typeLex = ret;
return ret;
}
int IsSepar(string v)
{
for (int i = 0; i < separators.Length; i++)
if (separators[i] == v)
return i;
return -1;
}
int IsOper(string v)
{
for (int i = 0; i < operators.Length; i++)
if (operators[i] == value)
return i;
return -1;
}
public string Value
{
get { return value; }
set { this.value = value; }
}
public int Index
{
get { return index; }
}
public List<string> Identif
{
get { return identif; }
}
public List<string> Numbers
{
get { return constants; }
}
public TypeLex Type
{
get { return typeLex; }
}
}
}
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
System.Globalization;
System.Windows.Forms;
namespace Analis
{
public class Parsing
{
LexAnalis lexer;
string textError;
public List<string> dimvars;
public Parsing(LexAnalis lexer)
{
this.lexer = lexer;
textError = string.Empty;
dimvars = new List<string>();
}
LexAnalis.TypeLex Next(bool IgnEndStr = false) // получаем тип следующего символа
{
while (true)
{
LexAnalis.TypeLex type = lexer.Next();
if (!IgnEndStr || lexer.Value != "\n")
return type;
}
}
public Node ParseProgram() // разбор программы
{
LexAnalis.TypeLex lexem = Next(true);
{
Block block = new Block();
if (lexem == LexAnalis.TypeLex.Operator && lexer.Value == "dim") // если dim
или оператор
{
bool flag = false;
while (true)
{
Next(true);
if (lexer.Value == "dim")
{
flag = false;
continue;
}
if (lexer.Type == LexAnalis.TypeLex.Ident && !flag) // если
идентификтор
{
block.Append(ParseTypeVar()); // определяем тип идентификаторп
}
else
{
block.Append(ParseInstruct());
return new Prg(block, string.Empty);
}
flag = true;
}
}
else
throw new Analis.Error("Отсутствует dim или оператор");
}
}
public Node ParseInstructs() // разбор операторов
{
Block block = new Block();
while (true)
{
if (lexer.Type == LexAnalis.TypeLex.Ident)
block.Append(ParseVarEqually());
else if (lexer.Value == "for")
block.Append(ParseFor());
else if (lexer.Value == "while")
block.Append(ParseWhile());
else if (lexer.Value == "if")
block.Append(ParseIf());
else if (lexer.Value == "read")
block.Append(ParseRead());
else if (lexer.Value == "write")
block.Append(ParseWrite());
else if (lexer.Value == "end")
{
return block;
}
else if (lexer.Value == ":")
{
Next();
}
else if (lexer.Value == "\n")
{
Next(true);
return block;
}
else if (lexer.Type == LexAnalis.TypeLex.Operator)
return block;
else
break;
}
throw new Analis.Error("Нет конца программы");
}
public Node ParseInstruct() // разбор end
{
Block block = new Block();
while (true)
{
Node node = ParseInstructs();
block.Append(node);
if (lexer.Value == "end")
{
Next(true);
break;
}
}
return block;
}
public Node ParseBlock() // разбор блоков операторов
{
return ParseInstructs();
}
public Node ParseVarEqually() // проверка объявлена ли переменная раннее
{
string name = lexer.Value;
if (dimvars.IndexOf(name) < 0)
throw new Analis.Error("Переменная " + name + " не объявлена");
LexAnalis.TypeLex lexem = Next();
if (lexer.Value == "ass")
{
Next();
Node expr = ParseExpr();
return new Var(name, expr);
}
else
throw new Analis.Error("Ожидался ass, встретился " + lexer.Value);
}
public Node ParseTypeVar() // разбор переменной по типам
{
List<string> vars = new List<string>();
if (dimvars.IndexOf(lexer.Value) > 0) throw new Analis.Error("Переменная " +
lexer.Value + " уже объявлена ");
vars.Add(lexer.Value);
LexAnalis.TypeLex lexem = Next(true);
while (lexer.Value == ",")
{
lexem = Next(true);
if (lexem == LexAnalis.TypeLex.Ident)
{
if (dimvars.IndexOf(lexer.Value) > 0)
throw new Analis.Error("Переменная " + lexer.Value + " уже объявлена
");
vars.Add(lexer.Value);
}
else
throw new Analis.Error("Ожидался идентификатор, встретился " +
lexer.Value);
lexem = Next();
}
if (lexem == LexAnalis.TypeLex.Operator && (lexer.Value == "%" || lexer.Value
== "!" || lexer.Value == "$"))
{
string typeName = lexer.Value;
Next(true);
dimvars.AddRange(vars);
return new TypeVar(typeName, vars);
}
else if (lexer.Value == "ass") return ParseVarEqually();
else
throw new Analis.Error("Тип переменной " + lexer.Value + "неизвестен");
}
public Node ParseFor() // разбор оператора for
{
Next();
Node from = ParseVarEqually();
if (lexer.Value == "to")
{
Next(true);
Node to = ParseExpr();
if (lexer.Value == "do")
{
Next(true);
Node block = ParseBlock();
return new For(from, to, block);
}
else
throw new Analis.Error("Отсутствует do в цикле for ");
}
else
throw new Analis.Error("Отсутствует to в цикле for ");
}
public Node ParseWhile() // разбор оператора while
{
Next();
Node expr = ParseExpr();
if (lexer.Value == "do")
{
Next(true);
Node block = ParseBlock();
return new OpWhile(expr, block);
}
else
throw new Analis.Error("Отсутствует do в цикле while ");
}
public Node ParseIf() // разбор оператора if
{
Next();
Node expr = ParseExpr();
if (lexer.Value == "then")
{
Next(true);
Node blockThen = ParseBlock();
Node blockElse = null;
if (lexer.Value == "else")
{
Next(true);
blockElse = ParseBlock();
}
return new OpIf(expr, blockThen, blockElse);
}
else
throw new Analis.Error("Отсутствует then в цикле if ");
}
public Node ParseRead() // разбор оператора read
{
Next();
List<string> vars = new List<string>();
if (lexer.Value == "(")
{
while (Next() == LexAnalis.TypeLex.Ident)
{
vars.Add(lexer.Value);
if (Next() == LexAnalis.TypeLex.Separator)
{
if( lexer.Value == ")" ) break;
if (lexer.Value != ",")
throw new Analis.Error("Отсутствует ',' в операторе read");
}
}
Next();
return new OpRead(vars);
}
else
throw new Analis.Error("Отсутствует ',' в операторе read");
}
public Node ParseWrite() // разбор оператора write
{
Next();
List<Node> vars = new List<Node>();
if (lexer.Value == "(")
{
while (true)
{
Next();
vars.Add(ParseExpr());
if (lexer.Type == LexAnalis.TypeLex.Separator)
{
if (lexer.Value == ")")
{
break;
}
if (lexer.Value == ",")
{
}
else
throw new Analis.Error("Отсутствует ')' в операторе write");
}
}
Next();
return new OpWrite(vars);
}
else
throw new Analis.Error("Отсутствует '(' в операторе write");
}
int OpOrder(string op) // опеределение порядка
{
switch (op)
{
case "<>": case "=": case "<": case "<=": case ">": case ">=":
return 0;
case "+": case "-": case "or":
return 1;
case "*": case "/": case "and":
return 2;
}
throw new Analis.Error("Неизвестная операция " + op);
}
public Node ParseExpr() // разбор операций с переменными
{
Expr expr = new Expr();
Stack<int> opOrder = new Stack<int>();
Stack<string> opName = new Stack<string>();
while (true)
{
if (lexer.Type == LexAnalis.TypeLex.Constant)
{
string type = "!";
if (lexer.Value.IndexOf('.') >= 0)
type = "%";
expr.Append(1, lexer.Value, double.Parse(lexer.Value,
CultureInfo.InvariantCulture.NumberFormat), type);
}
else if (lexer.Type == LexAnalis.TypeLex.Ident)
{
int i = dimvars.IndexOf(lexer.Value);
if (i < 0)
throw new Analis.Error("Переменная " + lexer.Value + " не объявлена");
expr.Append(2, lexer.Value, 0, string.Empty);
}
else if (lexer.Value == "not")
{
string op = lexer.Value;
Next();
if (lexer.Type == LexAnalis.TypeLex.Constant)
expr.Append(1, lexer.Value, double.Parse(lexer.Value),
"%");
else if (lexer.Type == LexAnalis.TypeLex.Ident)
{
if (dimvars.IndexOf(lexer.Value) < 0)
throw new Analis.Error("Переменная " + lexer.Value + " не
объявлена");
expr.Append(2, lexer.Value, 0, string.Empty);
}
else
throw new Analis.Error("Ожидалось число или идентификатор, вместо " +
lexer.Value);
expr.Append(3, op, 0, string.Empty);
}
else if (lexer.Value == "(")
{
opOrder.Push(-1);
opName.Push(lexer.Value);
Next();
continue;
}
else
throw new Analis.Error("Ожидалось число или идентификатор, вместо " +
lexer.Value);
Next();
if (lexer.Type == LexAnalis.TypeLex.Separator)
{
if ( lexer.Value == ":" || lexer.Value == "\n" || lexer.Value
== ",")
{
break;
}
else if (lexer.Value == ")")
{
bool LP = false;
while (opOrder.Count > 0)
{
int o = opOrder.Pop();
string name = opName.Pop();
if (o == -1)
{
LP = true;
break;
}
expr.Append(0, name, 0, string.Empty);
}
if (!LP) break;
Next();
}
if (lexer.Type == LexAnalis.TypeLex.Separator)
{
int o = OpOrder(lexer.Value);
if (opOrder.Count > 0 && opOrder.Peek() >= o)
{
opOrder.Pop();
expr.Append(0, opName.Pop(), 0, string.Empty);
}
opOrder.Push(o);
opName.Push(lexer.Value);
}
else
if (lexer.Type == LexAnalis.TypeLex.Operator)
break;
else
throw new Analis.Error("Ожидалась операция , вместо " +
lexer.Value);
}
else
if (lexer.Type == LexAnalis.TypeLex.Operator )
break;
else
throw new Analis.Error("Ожидалась операция или разделитель, вместо " +
lexer.Value);
Next();
}
while (opName.Count > 0)
expr.Append(0, opName.Pop(), 0, string.Empty);
return expr;
}
}
abstract public class Node
{
}
public class Prg : Node
{
Node block;
string name;
public Prg(Node block, string name)
{
this.block = block;
this.name = name;
}
}
public class Block : Node
{
List<Node> nodes;
public Block()
{
nodes = new List<Node>();
}
public void Append(Node node)
{
nodes.Add(node);
}
}
public class TypeVar : Node
{
List<string> vars;
string type;
public TypeVar(string type, List<string> vars)
{
this.type = type;
this.vars = vars;
}
}
public class Var : Node
{
string name;
Node expr;
public Var(string name, Node expr)
{
this.name = name;
this.expr = expr;
}
}
public class For : Node
{
Node from;
Node to;
Node block;
public For(Node from, Node to, Node block)
{
this.from = from;
this.to = to;
this.block = block;
}
}
public class OpWhile : Node
{
Node expr;
Node block;
public OpWhile(Node expr, Node block)
{
this.expr = expr;
this.block = block;
}
}
public class OpIf : Node
{
Node expr;
Node blockThen;
Node blockElse;
public OpIf(Node expr, Node blockThen, Node blockElse)
{
this.expr = expr;
this.blockThen = blockThen;
this.blockElse = blockElse;
}
}
public class OpRead : Node
{
List<string> vars;
public OpRead(List<string> vars)
{
this.vars = vars;
}
}
public class OpWrite : Node
{
List<Node> nodes;
public OpWrite(List<Node> vars)
{
this.nodes = vars;
}
}
public class Expr : Node
{
class Item
{
public int type;
public string name;
public double num;
public string typeVar;
}
List<Item> items;
public Expr()
{
items = new List<Item>();
}
public void Append(int type, string name, double num, string typeVar)
{
Item item = new Item();
item.type = type;
item.name = name;
item.num = num;
item.typeVar = typeVar;
items.Add(item);
}
}
}
Download