Теория языков программирования и методы трансляции:...

36
Министерство образования Российской Федерации Государственное образовательное учреждение высшего профессионального образования СЕВЕРО-ЗАПАДНЫЙ ГОСУДАРСТВЕННЫЙ ЗАОЧНЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ Кафедра компьютерных технологий и программного обеспечения ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ Методические указания к выполнению практических занятий Факультет информатики и систем управления Направление и специальность подготовки дипломированного специалиста: 654600 - информатика и вычислительная техника 220100 - вычислительные машины, комплексы, системы и сети Санкт-Петербург 2003

Upload: -

Post on 08-Dec-2016

227 views

Category:

Documents


10 download

TRANSCRIPT

Page 1: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Министерство образования Российской Федерации Государственное образовательное учреждение высшего профессионального

образования СЕВЕРО-ЗАПАДНЫЙ ГОСУДАРСТВЕННЫЙ ЗАОЧНЫЙ

ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ Кафедра компьютерных технологий и программного обеспечения

ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ

Методические указания к выполнению практических занятий

Факультет информатики и систем управления Направление и специальность подготовки дипломированного специалиста: 654600 - информатика и вычислительная техника 220100 - вычислительные машины, комплексы, системы и сети

Санкт-Петербург 2003

Page 2: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Утверждено редакционно-издательским советом университета УДК 681.3.06

Теория языков программирования и методы трансляции:

Методические указания к выполнению практических занятий. – СПб.: СЗТУ, 2003 - 36 с.

Методические указания составлены в соответствии с требованиями государственного образовательного стандарта высшего профессионального образования по направлению подготовки дипломированного специалиста 654600 (специальность 220100 – «Вычислительные машины, комплексы, системы и сети») для дисциплины специализации (ДС) «Теория языков программирования и методы трансляции».

Практические занятия предназначены для углубленного изучения приемов программирования на языке С++ и приобретения практических навыков в разработке интерпретаторов. В задании на проектирование интерпретатора предложен простейший язык Small BASIC. В процессе выполнения практических заданий студенты должны познакомиться с алгоритмизацией построения анализатора выражений и интерпретатора команд.

Рассмотрено на заседании кафедры компьютерных технологий и

программного обеспечения (КТ и ПО) 9.06.2003 г., одобрено методической комиссией факультета информатики и систем управления (ФИСУ) 17.11.2003.

Рецензенты: кафедра прикладной и компьютерной оптики СПбГИТМО (О.В.Багдасарова, канд.техн.наук., доц.); кафедра компьютерных технологий и программного обеспечения (Г.И.Анкудинов, д-р.техн.наук, проф.)

Составители: М.В. Копейкин, канд.техн.наук, доц.; Н.В. Рачева, доц.,; Е.О. Шумова, доц.

© Северо-Западный государственный заочный технический университет, 2003

Page 3: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

ВВЕДЕНИЕ

Цель практических занятий - углубление знаний в алгоритмизации

и приемах программирования на языке С++, получение практических навыков в проектировании сложнейших программных продуктов.

Основные требования данной специальной дисциплины – ознакомиться на практике со следующими вопросами: Профессиональное программирование на языке С++. Создание файла проекта в интегрированной среде Borland С++. Изучить правила описания внешних переменных и использования рекурсивных функций.

Синтаксический анализ выражений. Изучить принципы построения рекурсивно-нисходящего алгоритма, ознакомиться с продукционными правилами разбора выражений и получить навыки в программировании процедур для простых алгебраических выражений.

Интерпретатор языковых команд. Изучить возможности и принципы построения интерпретирующей системы и получить навыки в программировании простейших команд интерпретатора.

1. Задания на выполнение практических занятий

1.1. Общие указания к выполнению заданий на практических занятиях

Методические указания предназначены для студентов заочной и

очно-заочной форм обучения как в дисплейном классе, так и в домашних условиях.

Результаты выполненных заданий должны быть продемонстрированы преподавателю в дисплейном классе во время, выделенное на практические занятия. В случае самостоятельного выполнения обязательно представить отчеты в виде печатанного текста, где должны быть задание, выбранное по последней цифре студенческого шифра, исходные тексты программных модулей на языке С++ с соответствующими комментариями и приведены протоколы тестирования.

Работа № 1

Создание файла проекта в интегрированной среде Borland С++.

I. Цель работы. Изучить принципы многофайловой компиляции. Создать файлы с расширением *.prj и *.exe. II. Порядок выполнения работы.

Page 4: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

В главном верхнем меню интегрированной среды BC++ найти надпись project. Выбрать имя проекта, установить режим add и занести для компиляции имена двух файлов (исходные тексты анализатора выражений и интерпретатора команд взять с сайта http://www.de.nwpi.ru/). Запустить на трансляцию. Полученный файл с расширением *.exe проверить на выполнение. Для проверки составить исходный текст на Small BASIC в 7-10 строк. Вариант задания может быть составлен индивидуально, либо выбрать по последней цифре шифра из таблиц 1 и 2. Убедиться в правильности исполнения программы на Small BASIC и зафиксировать протокол.

Работа № 2 Расширение списка команд Small BASIC.

I. Цель работы. Добавить два простейших оператора: очистка экрана и пауза. II. Порядок выполнения работы. Разобраться с заданием №2. Исходный текст интерпретатора изучить по исполняемым функциям. Предложить самостоятельно имена (аббревиатуры) операторов. Задать синтаксис конструкций по примеру уже имеющихся семи операторов. Написать для реализации предложенных команд функции на языке BC++. Для общих занятий в дисплейном классе вариант задания выбирается наиболее простой. Рекомендуется реализовать команды очистки экрана и установки паузы. Для самостоятельной работы можно взять и более сложные команды, например, реализовать оператор цикла с неизвестным количеством повторов. После внесения изменений в текст интерпретатора команд повторить процесс отладки и тестирования.

Работа № 3 Построение рекурсивных функций на языке С++.

I. Цель работы. Изучить принципы построения рекурсивных функций на примерах, работающих программ. Написать самостоятельно функцию для построения двоичного дерева или быстрой сортировки одномерного массива. II. Порядок выполнения работы. Изучить раздел «алгоритмы построения рекурсивных функций». Ознакомиться с примером программы, реализующей работу калькулятора. Написать предложенные алгоритмы на языке С++. Провести тестирование

Page 5: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

на двух-трех примерах. Протокол зафиксировать и предъявить преподавателю по окончанию занятий.

1.2. Задание на практические занятия

Задание 1: составить примеры исходных текстов для языка Small BASIC для решения простой задачи. Задача: вычислить А для всех Х от 1 до 5, ⎧ <Выражение 1> , Х < 3 А = ⎨ ⎩ <Выражение 2> , Х ≥ 3

Таблица 1 Последняя цифра Выражение 1 шифра 0 Х+5 1 Х-1 2 Х*2 3 10-Х 4 Х/2+5 5 20-Х*2 6 Х+7 7 Х/2+10 8 Х*2-8 9 11-Х*2

Таблица 2 Последняя цифра Выражение 2 шифра 0 Х*2 1 Х*3 2 Х-2 3 10-Х 4 15-Х 5 20-Х/2 6 Х*2+5 7 9-Х*2 8 8+Х 9 11-Х

Page 6: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Задание 2: в исходном тексте интерпретатора в функцию под именем tokens добавить к перечисленным ранее командам аббревиатуры (идентификаторы) команд очистки экрана и паузы, а также указать эти команды в таблице commands и в операторе switch главного цикла программы. Не забыть, главное, написать соответствующие функции для обработки данных команд. Задание 3: изучить исходные тексты (размещены ниже) программных модулей, написанных на языке Паскаль, понять алгоритмизацию предложенных задач и реализовать заданные задачи на С++ для закрепления знаний по рекурсивным функциям. { Метод быстpой сортировки } procedure qwick(var b:vec); procedure sortqwick(kl,kr:integer; var u:vec); { sortqwick - рекурсивная процедура } Var { раздел описания переменных} r,v:real; ir,il:integer; { kl,kr - первый и последний элемент текущей группы } Begin ir:=kr; il:=kl; r:=u[(kr+kl) div 2]; repeat while u[il] < r do il:=il+1; while r < u[ir] do ir:=ir-1; if il<=ir then begin v:=u[il]; u[il]:=u[ir]; u[ir]:=v; il:=il+1; ir:=ir-1; end; until il>ir; { repeat – until подобен оператору цикла на С++ do-while } if kl<ir then sortqwick(kl,ir,u); if kl<kr then sortqwick(il,kr,u); end; { обращение к сортировке от 1-го до n-го элемента; b - одномерный массив размерности n} begin sortqwick(1,n,b); end; { Двоичное дерево: один вход и два выхода } program buildtree; TYPE

Page 7: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

f=^nd; {определение адреса } nd=RECORD k:integer; { значение, размещаемое в узле } lf,rf:f; { адреса порождаемых узлов слева и справа} END; VAR n:integer; rt:f; { построение сбалансированного двоичного дерева с n узлами } function tree(n:integer):f; VAR newnd:f; x,nl,nr:integer; { NIL – означает адрес пуст, т.е. адреса нет } BEGIN if n=0 then tree:=NIL else begin nl:=n div 2; nr:=n-nl-1; write(' Введите значение X='); readln(x); new(newnd); { запрос на текущий адрес} with newnd^ do begin k:=x; lf:=tree(nl); rf:=tree(nr); end; tree:=newnd; end; END; { результат выполнения - адрес последнего узла } { вывод дерева в горизонтальном положении } procedure printtree(T:f; H:integer); VAR i:integer; begin { печать дерева со сдвигом } if T<>NIL then with T^ do begin printtree(lf,h+1); for i:=1 to h do write(' '); writeln(k:3); { вывод значения узла } printtree(rf,h+1); end; end; { основная программа } Begin write(' Введите число узлов='); readln(n); rt:=tree(n); writeln(' Вывод дерева '); printtree(rt,0); readln; {пауза для фиксирования состояния экрана} end.

Page 8: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

2. Принципы проектирования интерпретатора

2.1. Синтаксический разбор выражений

2.1.1. Выражения

Выражения могут быть составлены из любых типов данных, но для учебных целей (доходчивого понимания) предлагается ограничиться разбором только числовых выражений[1]. В качестве элементов выражения предлагается рассмотреть следующие: числа 0 |1 | 2| 3| 4 | 5 | 6 | 7| 8| 9 операции + - * / ^ % = () <> ; . латинские буквы от А до Z. Операция ^ введена для возведения в степень. Операция % предусмотрена как деление нацело. Операции выполняются в соответствии с приведенным приоритетом: Высший ()

^ / * % + - Низший = Операции равного приоритета выполняются слева направо. Для переменных желательно ввести правило распознавания одного символа разных регистров равнозначное, т.е. "а" и "А" воспринимались одинаково.

2.1.2. Лексемы Выражения разбиваются на составные части (лексемы). Функция, выполняющая разбор выражений, должна решать следующие действия: - игнорировать пробелы и символы табуляции; - извлекать каждую лексему из исходного текста; - преобразовывать лексему во внутренний формат; - определять тип лексемы. Например, выражение А*В-( Х+10) содержит элементы "А", "*", "В", "-", "(", "Х", "+", "10", ")". Каждая лексема имеет внутренний и внешний формат (см. табл. 3). Внешний формат отражает содержательно действие, которое выполняет данная лексема. Внутренний формат необходим для реализации эффективного транслятора. Преимущество внутреннего формата заключается в том, что выполнение любых операций над целыми числами

Page 9: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

происходит более быстро, чем над символьными строками. Каждому элементу выражения в результате разбора присваивается тип лексемы.

Таблица 3 Тип лексем Внешний формат Внутренний

формат Разделитель DELIMITER 1 Переменная VARIABABLE 2 Число NUMBER 3 Команда COMMAND 4 Строка STRING 5 Кавычки QUITE 6 Пример разбора выражения: PRINT A+ 10- (B*C)/2 PRINT COMMAND A VARIABABLE + DELIMITER 10 NUMBER - DELIMITER ( DELIMITER B VARIABABLE * DELIMITER C VARIABABLE ) DELIMITER / DELIMITER 2 NUMBER Программная реализация функции разбора выражений выполнена на языке Borland C++. #define DELIMITER 1 /* разделитель */ #define VARIABLE 2 /* переменная */ #define NUMBER 3 /* число */ #define COMMAND 4 /* команда */ #define STRING 5 /* строка */ #define QUOTE 6 /* кавычки */ #define EOL 9 #define FINISHED 10 char *prog_W; /* буфер для анализируемого выражения */

Page 10: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

extern char *prog; /* *prog указывает на входной поток лексем */ char *tok_a; get_token() /* получить */ { int ch; register char *temp; token_type=0; tok=0; prog_W=prog; temp=token; if (*prog=='\0') /* проверка на конец */ { *token=0; tok=FINISHED; tok_a=tok; return(token_type=DELIMITER); } while(iswhite(*prog))++prog; /* iswhite - */ if (*prog=='\r') /* Ctrl */ { ++prog; ++prog; tok=EOL; *token='\r'; token[1]='\n'; token[2]=0; return(token_type=DELIMITER); } if (strchr("+-*^/%=';().><",*prog)) /* тип - разделитель */ { *temp=*prog; ++prog; temp++; *temp=0; return(token_type=DELIMITER); } if (*prog=='"') /* тип - кавычки */ { prog++; while(*prog!='"' && *prog!='\r') *temp++=*prog++; if(*prog=='\r') serror(1); prog++; *temp=0; return(token_type=QUOTE); } if (isdigit(*prog)) /* тип - цифра */

Page 11: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

{ while(!isdelim(*prog) && !iswhite(*prog)) *temp++=*prog++; *temp='\0'; return(token_type=NUMBER); } if (isalpha(*prog)) /* тип: переменная или команда */ { while(!isdelim(*prog) && !iswhite(*prog)) {*temp++=*prog++; }; token_type=STRING; } *temp='\0'; if (token_type==STRING) { tok=look_up(token); if(!tok) token_type=VARIABLE; else token_type=COMMAND; } return token_type; } Предложенная функция распознает шесть типов лексем (см. табл. 3). Глобальная ( extern ) переменная token_type хранит результат работы функции - тип лексемы. Внутренне представление лексемы помещается в переменную tok, имеющую глобальный статус extern. Конструкция extern (глобальная) открывает доступ к содержимому переменных для других функций. Для повторного просмотра лексемы, если это необходимо предлагается функция возврата putback(). /* Возврат лексемы во входной поток */ void putback() { char *t; t=token; for(; *t; t++)prog--; }

Page 12: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

2.1.3. Построение выражений Для синтаксического анализа выражений был выбран рекурсивно-нисходящий алгоритм. Для разбора выражений это означает ссылку на определения в терминах самого себя. Все выражения строятся по следующим правилам: Выражение ⇒ Терм[+ Терм][- Терм] Терм ⇒ Фактор[* Фактор][/ Фактор] Фактор ⇒ Переменная, Число или [Выражение] Квадратные обозначают возможное отсутствие элементов выражения. Символ ⇒ введен вместо слова "продуцирует". Перечисленные правила можно рассматривать как правила вывода выражения. Следует читать, например, вторую строку: "Терм является произведением или отношением факторов". Правила диктуют безусловный приоритет выполнения операций. Приоритет устанавливается по вложенности изложенных правил, начиная от фактора к выражению. Пример. Выражение 15/5 - (10 + 5). Анализ выполнится по следующей схеме: 1. Первый терм: 15/5. 2. Факторы 15 и 5, выполняется деление, результат 3. 3. Второй терм: (10 + 5). Стартует рекурсивный анализ второго выражения.

4. Факторы выбираются и суммируются, результат 15. 5. Число, возвращенное из рекурсии, вычитаем из первого результата. Итоговый результат: -12.

Предложенный рекурсивный разбор выражений устанавливает следующее: - приоритет операций определяется в продукционных правилах и является безусловным; - вычисления выражений соответствуют правилам обычной математики.

2.1.4. Анализатор выражений Простейший анализатор выражений поддерживает шесть арифметических операций и унарный минус. Все операции могут использоваться в сочетании с круглыми скобками, приоритет выполнение вычислений, в которых является наивысшим. Функция SERROR (error) предназначена для выдачи сообщений при обнаружении ошибки.

Page 13: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Выполнение программных модулей анализатора предусмотрено только при объединении с интерпретатором через файл проекта. Для выбора имен переменных от "А" до "Z" предусмотрен variables массив из 26 элементов, заполненный изначально нулевыми значениями. Обращение к элементам массива построено на числовой значимости кодов АSCII латыни. int variables[26]= { /*26 возможных имен переменных, A-Z */ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0 }; Функция допускает более длинные имена, но определение имени выполняется только по первой букве. /* Поиск значения переменной */ int find_var(s) char *s; { if(!isalpha(*s)) { serror(4); return 0; } return variables[toupper(*token)-'A']; }

2.2. Интерпретатор SMALL BASIC В работе интерпретатора предусмотрены семь операторов[1]. Внешний формат (выбор служебных слов) отражает содержательную сущность языковой конструкции, для преобразования во внутренний формат предусмотрена структура table: struct commands { /* Таблица служебных слов */ char command[20]; char tok; } table[]= { "print",PRINT,

Page 14: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

"input",INPUT, "if",IF, "then",THEN, "goto",GOTO, "for",FOR, "next",NEXT, "to",TO, "gosub",GOSUB, "return",RETURN, "end",END, "",END, }; Признак конца файла (нулевая строка) помещена в конец таблицы. Внутренний формат лексемы функция заносит в таблицу переменных look_up(s) /* Поиск внутреннего формата для текущей лексемы */ look_up(s) char *s; { register int i; char *p; /* преобразование к нижнему регистру */ p=s; while(*p) { *p=tolower(*p); p++; } for(i=0; *table[i].command; i++) if(!strcmp(table[i].command,s)) return table[i].tok; return 0; /* неопознанная команда */ } Исходный текст на языке SMALL BASIC готовится в любом текстовом редакторе и вводится функцией load_program(p, fname), где fname содержит внешнее имя файла. load_program(p, fname) char *p; char *fname; { FILE *fp; int i=0;

Page 15: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

printf("filename: %s\n",fname); getch(); if (!(fp=fopen(fname, "rb"))) { printf("not: %s\n",fname); return 0; } i=0; do { *p=getc(fp); p++; i++; } while(!feof(fp) && i<PROG_SIZE); *(p-2)='\0'; fclose(fp); return 1; } Синтаксис операторов(команд) SMALL BASIC Оператор присваивания: <имя переменной>=<выражение> Оператор вывода: PRINT <список переменных> Оператор ввода: INPUT <имя переменной> Оператор перехода: GOTO <номер строки> Условный оператор: IF <условие> THEN оператор Оператор цикла: FOR <имя переменной> = <начальное значение> TO <конечное значение> последовательность операторов NEXT Оператор перехода к подпрограмме и возврат: GOSUB < номер строки >

Page 16: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

тело подпрограммы RETURN

Таблица 4

Ключевое слово (внешний формат)

Внутренний формат

Содержание служебных

слов PRINT 1 Вывод INPUT 2 Ввод

IF 3 Условие "если" THEN 4 Условие "то" FOR 5 Заголовок цикла "для"

NEXT 6 Ограничитель тела цикла

TO 7 Заголовок цикла "до" GOTO 8 Безусловный переход EOL 9 Конец строки

FINISHED 10 Конец исходного текста

GOSUB 11 Переход к подпрограмме

RETURN 12 Возврат из п/программы

END 13 Конец программы

2.2.1. Основной цикл работы анализатора Вводится текст, каждая лексема анализируется на принадлежность к типу переменных. В этом случае за переменной должен следовать оператор присваивания, а если нет, то выполняется проверка на распознавание команды. do { token_type = get_token(); if ( token_type==VARIABLE) { putback(); assignment(); } else switch(tok) { case PRINT:

Page 17: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

print(); break; case IF: exec_if(); break; case GOTO: exec_goto(); break; case FOR: exec_for(); break; case NEXT: next(); break; case INPUT: input(); break; case GOSUB: gosub(); break; case RETURN: greturn(); break; case END: exit(0); } } while (tok!=FINISHED); } Из программного текста видно, что реализация анализа командных лексем сделана через case, а последовательное чтение входного потока через оператор с неизвестным количеством повтора do - while.

2.2.2. Оператор присваивания

Для реализации оператора присваивания предлагается функция assignment(), связанная с результатом ввода по get_token(). <имя переменной>=<выражение> assignment() { int var, value; get_token();

Page 18: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

if (!isalpha(*token)) { serror(4); return 0; } var = toupper(*token)-'A'; get_token(); if (*token!='=') { serror(3); return 0; } get_exp(&value); variables[var]=value; } Функция get_exp(&value) вычисляет значение опознанной переменной, в противном случае выдается сообщение об ошибке. Простота рассмотренной функции объясняется тем, что основная обработка лексем по распознаванию выполняется в анализаторе выражений.

2.2.3. Оператор вывода Команда PRINT аналог стандартного оператора вывода языка Бейсик. Список переменных - перечисление выводимых переменных через запятую, либо точку с запятой; символьные константы заключаются в кавычки. PRINT <список переменных> /* Простейшая операция вывода */ void print() { int answer; int len=0, spaces; char last_delim, str[80]; do { get_token(); if ((tok==EOL) || (tok==FINISHED)) break; if (token_type==QUOTE) { printf(token); len += strlen(token); get_token();

Page 19: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

} else { putback(); get_exp(&answer); get_token(); itoa(answer, str, 10); printf("%d", answer); len += strlen(str); } last_delim=*token; if (*token==',') { spaces=8-(len % 8); len += spaces; while(spaces) { printf(" "); spaces--; } } else if (*token==';'); else if (tok!=EOL && tok!=FINISHED) serror(0); } while ((*token==';') || (*token==',')); if (tok==EOL || tok==FINISHED) { if (last_delim !=';' && last_delim !=',') printf("\n"); } else serror(0); } Функция putback() предусмотрена для возврата лексемы во входной поток. Это необходимо в том случае, когда на вывод попадает выражение. В print() нет вывода выражений.

2.2.4. Оператор ввода Для ввода информации с клавиатуры и размещения ее в переменных предлагается оператор ввода: INPUT <имя переменной> Возможен и другой вариант ввода данных с подсказкой в виде записи <символьная строка>: INPUT "<символьная строка>" <имя переменной>

Page 20: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

/* Реализация операции ввода */ void input() { char str[80],var; int i; get_token(); if (token_type==QUOTE) { get_token(); if(*token!=',')serror(1); get_token(); } else printf("?"); var = toupper(*token) -'A'; scanf("%d",&i); variables[var]=i; } Готовность ввода обозначена выводом знака "?" , и выполнение программы переходит в ожидание.

2.2.5. Оператор перехода Оператор перехода прост в использовании и сложен в реализации. В предложенном языке GOTO необходим для программного управления. Нумерация всех строк не обязательна кроме той строки, на которую выполняется ссылка. GOTO <номер строки> Основная сложность в реализации GOTO заключается в организации переходов не только вниз, но и вверх по тексту программы. Каждая метка с указателем места помещается в таблицу: struct label { char name[LAB_LEN]; char *p; }; struct label label_table[NUM_LAB];

Page 21: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Функция scan_labels() просматривает текст и размещает метки в таблице. void scan_labels() { int addr,i; char *temp; label_init(); temp=prog; get_token(); if (token_type==NUMBER) { strcpy(label_table[0].name,token); label_table[0].p=prog; } find_eol(); do { get_token(); if (token_type==NUMBER) { addr=get_next_label(token); if (addr==-1 || addr==-2) { (addr==-1) ?serror(5):serror(6); } strcpy(label_table[addr].name,token); label_table[addr].p=prog; } if (tok!=EOL) find_eol(); } while(tok!=FINISHED); prog=temp; } /* инициализация массива меток */ void label_init() { register int i; for (t=0; t<NUM_LAB; ++t) lable_table[t].name[0]='\0'; } /* Поиск начала следующей строки*/ void find_eol() { while(*prog!='\n' && *prog!='\0') ++prog; if (*prog) prog++; }

Page 22: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Возврат индекса свободного места в массиве меток для размещения очередной ссылки-метки. При этом выполняется проверка на дублирование меток код: -2 и переполнение массива меток код: -1. get_next_label(s) char *s; { register int t; for (t=0; t<NUM_LAB; ++t) { if (label_table[t].name[0]==0) return t; if (!strcmp(label_table[t].name,s)) return -2; } return -1; } Ограничение на размер массива определяется параметром NUM_LAB, размер при необходимости можно изменить. void exec_goto() { char *loc; get_token(); loc=find_label(token); if (loc=='\0') serror(7); else prog=loc; } Поиск неопознанной метки выполняется find_label(s). Если метка не найдена, возвращается ноль, при обнаружении - указатель на место на место хранения метки . char *find_label(s) char *s; { register int t; for (t=0; t<NUM_LAB; ++t) if (!strcmp(label_table[t].name,s)) return label_table[t].p; return '\0'; }

2.2.6. Условный оператор Условный оператор реализован в соответствии с требованиями стандарта языка Бейсик.

Page 23: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

IF <условие> THEN оператор void exec_if() { int x,y,cond; char op; get_exp(&x); get_token(); if (!strchr("=<>",*token)) { serror(0); return; } op=*token; get_exp(&y); cond=0; switch(op) { case '=': if (x==y) cond=1; break; case '<': if (x<y) cond=1; break; case '>': if (x>y) cond=1; break; } if (cond) { get_token(); if (tok!=THEN) { serror(8); return; } } else find_eol(); } Функция exec_if() выполняется следующим образом: - вычислить значение левого выражения; - считать операцию сравнения; - вычислить значение правого выражения; - выполнить операцию сравнения;

Page 24: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

- если условие истинно, то выполнить поиск THEN, в противном случае find_eol() выполнить переход на начало следующей строки.

2.2.7. Оператор цикла

Предложенная версия оператора FOR реализует цикл только с приращением, равным 1 на каждом шаге цикла. Допускается вложенность цикла. Для этого используется стековая структура. FOR <имя переменной> = <начальное значение> TO <конечное значение> последовательность операторов NEXT Для реализации цикла FOR стек должен иметь следующую структуру: struct for_stack { int var; int target; char *loc; } fstack [FOR_NEST ]; struct for_stack fpop(); int ftos; Значение FOR_NEST ограничитель на вложенность цикла. Для обработки стека предлагаются функции fpush(i) и fpop(). /* перемещение результата в стек FOR */ void fpush(i) struct for_stack i; { if(ftos>FOR_NEST) serror(10); fstack[ftos]=i; ftos++; } struct for_stack fpop() { ftos--; if(ftos<0) serror(11); return(fstack[ftos]); }

Page 25: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Далее предлагается текст функций exec_for() и next() для реализации языковых конструкций FOR и NEХT. /* Цикл for */ void exec_for() { struct for_stack i; int value; get_token(); if (!isalpha(*token)) { serror(4); return; } i.var=toupper(*token)-'A'; get_token(); if(tok!='=') { serror(3); return; } get_token(); if(tok!=TO) serror(9); get_exp(&i.target); if(value>=variables[i.var]) { i.loc=prog; fpush(i); } else while(tok!=NEXT) get_token(); } /* Цикл - параметр конструкции - NEXT */ void next() { struct for_stack i; i=fpop(); variables[i.var]++; if(variables[i.var]>i.target) return; fpush(i); prog=i.loc; }

Page 26: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Следует отметить, использование оператора GOTO не предусмотрено в теле цикла.

2.2.8. Оператор построения подпрограмм Язык Small BASIC не поддерживает отдельные подпрограммы, но с помощью оператора GOSUB и RETURN реализована возможность вызова отдельных частей программы.

GOSUB < номер строки > . . тело подпрограммы . . RETURN Обращение GOSUB требует использования стека. Каждому RETURN соответствует единственный GOSUB. char *gstack[SUB_NEST]; int gtos; /* Операция обращения к заданной строке и возврат назад */ void gosub() { char *loc; get_token(); /* */ loc=find_label(token); if (loc=='\0') serror(7); else { gpush(prog); prog=loc; } } /* Операция возврата к заданной строке */ void greturn() {

Page 27: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

prog=gpop(); } /* Результат в стек gosub*/ void gpush(s) char *s; { gtos++; if (gtos==SUB_NEST) { serror(12); return; } gstack[gtos]=s; } /* Проверка уровня вложенности gosub*/ char *gpop() { if (gtos==0) { serror(13); return; } (gstack[gtos--]); } Значение prog помещается в стек GOSUB, адрес строки, с которой начинается подпрограмма, помещается в prog. Когда встречается RETURN, из стека GOSUB извлекается очередное значение и присваивается prog. Оператор допускает вложенность GOSUB. Полный текст интерпретатора размещен на сайте www.de.nwpi.ru.

2.3. Тестирование В качестве тестов предлагаются простейшие программы, использующие языковые конструкции, заложенные для Small BASIC. Далее приведены примеры исходных текстов на языке Small BASIC с использованием GOSUB и протоколы выполнения программ. Протоколы демонстрируют процесс выполнения программы и правильность реализации простых вычислений.

Page 28: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Тест 1 PRINT "Команды ввода-вывода" PRINT " O'K " INPUT "H=",H IF H<22 THEN GOTO 200 PRINT "H>=22 (H-10)*2 Результат=",(H-10)*2 PRINT “ H/2=”;H/2 200 A=H+15 IF A>10 THEN PRINT "O'K A>10" PRINT “A=”;A PRINT “A+5=”;A+5 INPUT " H=",H PRINT “Ввели значение H=”;H INPUT "Это тест Y=";Y PRINT “ H+Y=”;H+Y IF A<15 THEN GOSUB 300 PRINT “ Тест выполнен” END 300 PRINT "Это подпрограмма А<15" RETURN Протокол 1 Команды ввода-вывода О'К Н=12 О'К A>10 A=27 A+5=32 H=5 Ввели значение H=5 Это тест Y=9 H+Y=14 Тест выполнен Протокол 2 Команды ввода-вывода О'К Н=7 О'К A>10 A=22 A+5=27

Page 29: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

H=12 Это тест Y=1 H+Y=13 Тест выполнен Протокол 3Команды ввода-вывода О'К Н=25 H>=22 (H-10)*2 Результат=24 H/2=12 О'К A>10 A=40 A+5=45 H=1 Это тест Y=1 H+Y=2 Это подпрограмма А<15 Тест 2 PRINT "Команды Small BASIC" FOR X=1 TO 5 PRINT X,X/2; X,X*X NEXT GOSUB 300 PRINT " Hello !!! " INPUT H IF H<11 THEN GOTO 200 PRINT 12-4/2 PRINT 100 200 A=100/2 IF A>10 THEN PRINT "O'K" PRINT A PRINT A+34 INPUT H PRINT H INPUT "Это тест y=",Y PRINT H+Y END 300 PRINT "ЭТО ПОДПРОГРАММА" RETURN

Page 30: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Протокол 1 Команды Small BASIC 1 0 1 1 2 1 2 4 3 1 3 9 4 2 4 16 5 2 5 25 ЭТО ПОДПРОГРАММА Hello!!! ?25 O'K 50 84 ?10 10 Это тест y=5 30 Протокол 2 Команды Small BASIC 1 0 1 1 2 1 2 4 3 1 3 9 4 2 4 16 5 2 5 25 ЭТО ПОДПРОГРАММА Hello!!! ?5 10 100 O'K 50 84 ?10 10 Это тест y=5 10

Page 31: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

3. Алгоритмы построения рекурсивных функций Рекурсивность - это прием в программировании, когда при решении некоторой задачи предполагается обращение к алгоритму решения этой же задачи, т.е. в подпрограмме предусматриваются вызовы ее самое, но с другими параметрами. Рекурсия позволяет построить процедуру или функцию оптимального размера при хорошей читабельности алгоритма, а значит повысить надежность программы[2]. /* Калькулятор выражений */ #include <stdio.h> #include <math.h> #include <stdlib.h> #define false 0 #define true 1 // NextChar - для ввода значений int NextChar; int cont=true; // Прототипы float Factor(); float Term(); float Expression(); // void GetNextChar() { while ((NextChar = getchar()) == ' '); } // рекурсивная функция float Factor() // { float v=0; int count=0; int i; int d_p=false; // if ((NextChar>='0') && (NextChar<='9')) { while ((NextChar>='0') && (NextChar<='9')) { v = v * 10 + NextChar - '0';

Page 32: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

NextChar=getchar(); if (d_p) count++; if (NextChar == '.') { NextChar=getchar(); d_p=true; } } for (i=0; i<count; i++) v=v/10; return v; } else { switch(NextChar) { case '-' : { GetNextChar(); return - Factor(); } case '(' : { GetNextChar(); v=Expression(); if (NextChar !=')') { printf(" Пропущена закрывающая скобка \n"); return 0; } else NextChar =getchar(); } return v; case '.' : d_p=true; default: cont=false; } } return 0; } // Определение терма float Term() { float v,d; v=Factor(); for(;;) { switch(NextChar)

Page 33: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

{ case ' ' :GetNextChar(); break; case '*' :{GetNextChar(); v=v * Factor();} continue; case '^' :{GetNextChar(); v=pow(v,Factor());} continue; case '/' :{GetNextChar(); if ((d = Factor()) != 0) { v=v/d; continue;} else { printf("Ошибка!!! Деление на 0\n"); return 0; } } default: return v; } } } // формирование процесса вычисления введенного выражения // float Expression() { float v; v=Term(); for(;;) { switch(NextChar) { case ' ' :GetNextChar(); break; case '+' : {GetNextChar(); v=v+Term();} continue; case '-' :{GetNextChar(); v=v-Term();} continue; default: return v; } // } //

Page 34: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

} // // главный модуль void main(void) { float r; while(1) { printf("\n ВВЕДИТЕ ВЫРАЖЕНИЕ:"); GetNextChar(); r=Expression(); if ((NextChar == '\n') && (cont)) { printf("Результат: %4.2f\n",r); continue; } else { if (NextChar == '\n') exit(0); else printf("Ошибка при вводе выражения \n"); continue; } } }

Протокол выполнения ВВЕДИТЕ ВЫРАЖЕНИЕ: 48+52 Результат: 100.00 ВВЕДИТЕ ВЫРАЖЕНИЕ: 48/6+5 Результат: 13.00 ВВЕДИТЕ ВЫРАЖЕНИЕ: 48*2-6*(10-5) Результат: 66.00 ВВЕДИТЕ ВЫРАЖЕНИЕ: 48+д52 Ошибка при вводе выражения ВВЕДИТЕ ВЫРАЖЕНИЕ: (12+52 Пропущена закрывающая скобка ВВЕДИТЕ ВЫРАЖЕНИЕ: 35-7/(9-9) Ошибка!!! Деление на 0

Page 35: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

4.Библиографический список 1. Шилдт Г. Теория и практика С++. /Пер. с англ. - СПб.: ВНV - Санкт-

Петербург, 1996. 2. Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. М.:

МЦНМО, 2000.

Page 36: Теория языков программирования и методы трансляции: Методические указания к выполнению практических

Содержание Введение……………………………………………….………………….……3 1. Задания на выполнение практических занятий….………………….……3

1.1 Общие указания к выполнению заданий на практических занятиях ..3 1.2 Варианты заданий ...……………………………… ……………………4 2. Принципы проектирования интерпретатора ………………..………….. 7

2.1. Синтаксический разбор выражений ………..…………………….. 7 2.1.1. Выражения ……………………………………………………… 7 2.1.2. Лексемы …………………………..……………………….……. 8 2.1.3. Построение выражений ……………………………………….. 10 2.1.4. Анализатор выражений …………………………….…………. 11

2.2. Интерпретатор Small BASIC …………………………..………… 11 2.2.1. Основной цикл работы анализатора………………………….. 14 2.2.2. Оператор присваивания…………………………..…………… 15 2.2.3. Оператор ввода …………………………..……………………. 16 2.2.4. Оператор вывода…………………………..…………………... 17 2.2.5. Оператор перехода…………………………..………………… 18 2.2.6. Условный оператор…………………………..……………….. 20 2.2.7. Оператор цикла…………………………..……………………. 21 2.2.8. Оператор построения подпрограмм………………………….. 23

2.3. Тестирование ……………………………………………………. 25 3. Алгоритмы построения рекурсивных функций………………………. 30 4. Библиографический список ……………………………………………. 35

Редактор М.Ю.Комарова

Сводный темплан 2003 г. Лицензия ЛР N 020308 от 14.02.97

____________________________________________________________

Подписано в печать Формат 60х84 1/16

Б.кн.-журн. П.л. 2.25 Б.л. 1.125 РПТ РИО СЗТУ

Тираж 100 Заказ ____________________________________________________________

Северо-Западный государственный заочный технический университет РИО СЗТУ, член Издательско-полиграфической ассоциации

вузов Санкт-Петербурга 191186, Санкт-Петербург, ул. Миллионная, 5