Для учебных целей разработан простой компилятор simpcomp (от англ. Simple Compiler), реализованный с помощью утилит Lex и Yacc (вернее, их свободных версий flex и bison). В исходном языке специально опущены некоторые существенные возможности (логические переменные, инициализация переменных при описании, стандартные функции, разбиение программы на функции и их вызов и т.п.) — они оставлены в качестве задач для самостоятельного решения.
Входной язык представляет собой минимальное подмножество языка C, за одним существенным исключением: в языке не используются фигурные скобки для указания границ блоков. Вместо этого каждая конструкция имеет строку-заголовок и строку-конец (аналог закрывающей фигурной скобки в C). Например, цикл while, который в C можно записать как
while (условие) { тело цикла }в учебном языке записывается как
while (условие) тело цикла endwhile(в роли закрывающей фигурной скобки используется оператор endwhile, а открывающая фигурная скобка вообще не нужна).
Кроме того, в число управляющих конструкций учебного языка
добавлен оператор выбора, отсутствующий в C:
if (условие1) ...
elseif (условие2) ...
elseif (условие3) ...
else ...
endif
Например, следующий фрагмент вычисляет функцию signum
— знак числа x:
double x; int sgn; . . . if (x > 0.) sgn = 1; elseif (x < 0.) sgn = -1; else sgn = 0; endif
Программа может завершаться концом файла или необязательным ключевым словом end. В последнем случае весь текст, следующий за словом end, игнорируется.
Типы данных ограничиваются базовыми типами и массивами. Базовых типов всего 3: целое число int, вещественное число double и текстовая строка string. При описании массива размер его может быть любым выражением (а не только константой, как в C/C++). Примеры описаний переменных:
int n; double x, y; double a[10]; n = 1000; string s[n]; double b[2*n + 1];
В выражениях можно использовать 4 обычные арифметические операции и операцию взятия остатка от деления % (как и в C). Присваивание, в отличие от C, не является операцией. Стандартные функции, такие как sin, cos, exp в языке не реализованы (реализация оставлена в качестве задачи). Числовые константы записываются так же, как и в языке C (правда, целочисленные восьмеричные и шестнадцатеричные константы не реализованы). Строковые константы записываются в двойных апострофах: "abc", "x=" и т.п.
Оператор присваивания имеет вид
переменная = выражение;где переменная в левой части может быть простой переменной или элементом массива. Оператор заканчивается символом ";" (точка с запятой). Примеры:
int x, y, a[100]; x = 5; y = 2*x + 1; a[x] = y;
При присваивании переменной выражения, тип которого не совпадает с типом переменной, происходит приведение типа. Так, например, переменной строкового типа string можно присваивать численное значение, при этом число будет переведено в текстовую форму.
string s; double x; x = 1.5; s = x/2.;После выполнения этого фрагмента строковая переменная s приобретет значение "0.75".
Численным переменным также можно присваивать строковое значение, в этом случае строка интерпретируется как текстовая запись числа, его значение присваивается переменной. Например, при выполнении фрагмента
string s; int n; double x; s = "2.25"; n = s; x = s;переменной n присваивается значение 2, переменной x — значение 2.25.
Структурных операторов всего 2 типа: оператор цикла while
и условный оператор if.
Условный оператор может иметь 3 формы:
— оператор "если" if... endif
— оператор "если-иначе"
if... else... endif
— оператор выбора
"если - иначе если - иначе если -... иначе":
if... elseif... elseif...
else... endif
Структурные операторы имеют строку-заголовок и строку-окончание. В окончании к ключевому слову, с которого начинается строка-заголовок, добавляется префикс "end": if-endif, while-endwhile. Фигурные скобки или ключевые слова begin-end для указания границ структурных операторов становятся ненужными, а программа более ясной; кроме того, это позволяет избежать неоднозначностей в грамматике языка и конфликтов при разборе, которые присутствуют, например, в языке C при разборе конструкции
if (условие1) if (условие2) оператор1; else оператор2;(непонятно, к какому if относится else — в C считается, что к внутреннему; при LR-разборе конфликт сдвиг-свертка в данном случае разрешается в пользу сдвига).
Примеры использования структурных операторов.
1. Суммирование массива:
int i; double x; double a[100]; . . . s = 0.; i = 0; while (i < 100) s = s + a[i]; i = i + 1; endwhile2. Определение окончания множественного числа в русском языке (0 мячей, 1 мяч, 2 мяча, 3 мяча, 4 мяча, 5 мячей, 6 мячей...):
int n; string numBalls; numBalls = ""; . . . numBalls = numBalls+n; // число n преобразуется // к текстовому виду и добавляется в конец строки numBalls = numBalls + " мяч"; if (n == 0 || n >= 5) numBalls = numBalls + "ей"; elseif (n >= 2) numBalls = numBalls + "а"; endif
Для выхода из цикла while можно, как и в C,
использовать оператор break;
для перехода к следующей итерации цикла — оператор
continue;
В условиях можно использовать операции сравнения и логические операции, записываемые так же, как и в языке C. Например,
double x, y; . . . if (y > 0. && x / y > 10.) x = 10. * y; endifВ отличие от C, целочисленные выражения нельзя использовать в качестве логических. Например, фрагмент программы
int x, y; . . . if (x) y = 1; endifявляется ошибочным (впрочем, и в C это дурной стиль!). Вместо этого надо писать
int x, y; . . . if (x != 0) y = 1; endif(такому стилю хорошо бы следовать и при программировании на C).
Реализация логического типа bool и логических переменных оставлена в качестве задачи для самостоятельного решения.
В программах можно использовать встроенные операторы ввода и вывода, вводящие значения переменных с клавиатуры (стандартного входа) или выводящие значения в стандартный поток вывода.
Для ввода используется оператор input, за которым следует список ввода, элементы которого разделяются запятыми; список завершается точкой с запятой. Элементами списка ввода могут быть имена простых переменных или элементы массивов. Пример:
int n; input n; double a[n]; int i; i = 0; while (i < n) input a[i]; endwhileЗдесь мы вводим с клавиатуры сначала длину n массива, затем создаем массив a длины n и в цикле вводим все его элементы.
Для вывода можно использовать операторы output и outputln, за которыми следует список вывода, разделенный запятыми и завершающийся точкой с запятой. В качестве элементов списка вывода можно использовать любые выражения. Оператор outputln вслед за последним элементом списка выводит дополнительно символ перевода строки. При этом список вывода оператора outputln, в отличие от оператора output, может быть пустым (что соответствует просто печати символа перехода на новую строку). Пример:
int n, i; input n; double a[n] ... outputln "n=", n; i = 0; while (i < n) output a[i], " "; i = i + 1; if (i % 5 == 0) outputln; endif endwhile outputln;Здесь сначала печатается значение целочисленной переменной n, предваряемое текстом "n=", а затем после перевода строки — n элементов вещественного массива a по 5 элементов в каждой строке. После окончания печати массива выводится еще один символ перевода строки.
1. Печать квадратов первых 20 натуральных чисел — файл "square20.s":
int n; n = 1; while (n <= 20) outputln n, " ", n*n; n = n+1; endwhile endВывод программы:
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 11 121 12 144 13 169 14 196 15 225 16 256 17 289 18 324 19 361 20 400
2. Печать n первых простых чисел — файл "nprimes.s":
int n, k, p; n = 50; outputln "Number of primes:"; input n; k = 0; p = 2; while (k < n) if (p == 2) output " ", p; k = k+1; p = p+1; else int d, prime; prime = 1; d = 3; while (prime == 1 && d*d <= p) if (p % d == 0) prime = 0; break; endif d = d+2; endwhile if (prime == 1) output " ", p; k = k+1; if (k % 10 == 0) outputln; endif endif p = p+2; endif endwhile outputln; endВывод программы (для n=50):
Number of primes: 50 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229
3. Приближенное вычисление квадратного корня вещественного числа — файл "sqrt.s":
double x0, x, a; double EPS; EPS = 0.0000001; while (0 == 0) outputln "Input a number:"; input a; if (a <= 0.) break; endif x = a; if (x < 1.) x = 1.; endif double xPrev; xPrev = x + 1.; while (xPrev - x > EPS) xPrev = x; x = (x + a/x) / 2.; outputln x; endwhile outputln "sqrt(", a, ") = ", x; endwhile endВывод программы для чисел 2 и 10 (для заданного числа печатаются последовательность приближений и окончательный результат):
Input a number: 2 1.5 1.41667 1.41422 1.41421 1.41421 sqrt(2) = 1.41421 Input a number: 10 5.5 3.65909 3.19601 3.16246 3.16228 3.16228 sqrt(10) = 3.16228 Input a number: 0
Исходные текста проекта
(архивы в tar-gzip формате и в zip-формате):
"SimpComp.tgz"
"SimpComp.zip"
Для компиляции проекта следует выполнить команду
makeв Unix'е или команду
nmake -f Makefile.winв операционной системе MS Windows. В последнем случае предполагается, что на компьютере установлена программа "Visual Studio 2008" (компонент C++) или ее более новая версия. Нужно в меню программ выбрать пункт
Microsoft Visual Studio 2008 -> Visual Studio Tools -> Visual Studio 2008 Command PromptДалее, в открывшемся окне консоли надо переместиться в директорию с исходными текстами проекта, например, "С:\MyProjects\SimpComp":
c: cd \MyProjects\SimpCompи выполнить команду
nmake -f Makefile.winПосле этого в случае успешной компиляции в текущей директории будет создана программа компилятора "simpcomp.exe".
Для запуска компилятора в Unix'е следует исполнить команду
./simpcompпод MS Wndows — команду
.\simpcompили просто
simpcompПри этом компилятор читает текст программы из входного потока (с клавиатуры). Для окончания ввода программы следует набрать строку
end(можно ввести также символ конца файла — Ctrl+D в Unix'е или Ctrl+Z в MS Windows). После этого программа выполняется, ее ввод и вывод происходит в консоли.
Можно также выполнить программу, предварительно записанную в файле. Например, для исполнения программы из файла "square20.s" (этот файл содержится в проекте) в Unix'е следует выполнить команду
./simpcomp square20.sили под MS Windows — команду
simpcomp square20.s
Компилятор состоит из следующих модулей и файлов: