Курсовая работа по предмету Системное программное обеспечение.
Язык был сделан статически типизированным для простоты реализации.
Ниже идет описание каждой из частей компилятора.
-
Поддержка целочисленных, логических и строковых констант;
-
Поддержка комментариев (строчные и блочные(без вложенности));
-
Хранение строки и позиции в строке для каждого токена и их вывод при ошибке;
-
Поддержка следующих типов:
Список
IDENTIFIER, LET, CONST, NUMBER, BOOLEAN, STRING, VOID, ANY, DO_WHILE, WHILE, FOR, BREAK, CONTINUE, IF, ELSE, LESS, GREATER, LESS_EQUAL, GREATER_EQUAL, EQUAL, NOT_EQUAL, AND, OR, PLUS, MINUS, STAR, SLASH, INC, DEC, LBRA, RBRA, LPAR, RPAR, LSQR, RSQR, ASSIGN, ADD_ASSIGN, SUB_ASSIGN, MUL_ASSIGN, DIV_ASSIGN, NUMBER_CONST, STRING_CONST, TRUE, FALSE, FUNCTION, RETURN, SEMICOLON, COLON, COMMA, POINT, QUESTION, EXCLAMATION, LINE_COMMENT, BLOCK_COMMENT_START, BLOCK_COMMENT_END, NEW, DECLARE, IMPORT, EXPORT, FROM, -
Проверка корректности идентификаторов.
-
Поддерживаются следующие конструкции (значение в
[]
является опциональным):-
Блоки объявляются с помощью фигурных скобок
{ }
-
Для циклов и условий, если в теле одна инструкция, скобки можно не ставить
-
Объявление переменных и констант
-
let
для переменных; -
const
для констант;Объявление переменных и констант имеет следующий вид:
let|const <name>: <type>;
Однако тип можно не задавать явно, но тогда обязательно присваивание переменной некоторого значения (поддерживается только для типов
number
иboolean
,string
):let|const <name> = <expression>;
Объявление типа в виде:
: <type>
, встречается повсюду, где используется объявления типа.
-
-
Поддерживаются следующие виды присваивания:
=
+=
-=
*=
/=
-
Поддерживаются следующие логические операторы:
&&
||
!
==
!=
<
>
<=
>=
-
Поддерживаются следующие унарные операторы:
-
!
-
Поддерживаются следующие арифметические операторы
+
-
*
/
-
Поддерживаются следующие типы:
number
boolean
string
number[]
boolean[]
void
(только для возвращаемого значения функций)
-
Поддерживается обращение к элементам массива по индексу
-
Поддерживается два вида инициализации массивов:
-
С помощью списка инициализаторов:
let array: number[] = [10, 5, 2];
-
С помощью оператора
new
:let array: number[] = new Array(10)
-
-
Циклы (с поддержкой операторов
break
иreturn
)for
, в виде:for (let i = 0; i < 10; i += 1)
;while
;do while
;
-
Условная конструкция
if else
-
Функции
-
Определение функции с помощью ключевого слова
function
. Определение функции имеет следующий вид:function <name>(<arguments>)[: return_type] { <statements> }
, где аргументы имеют следующий вид:<name> : <type>, ...
.Определение типа возвращаемого значения может быть опущено, по-умолчанию. устанавливается
void
-
Объявление функции с помощью ключевого слова
declare
, объявление не может иметь реализации. Имеет следующий вид:declare function <name>(<arguments>)[: return_type];
Тип возвращаемого значения также может быть опущен.
-
Поддерживается оператор
return
.
-
-
Модули:
-
Поддерживается экспорт и импорт функций и переменных из других файлов с исходным кодом
-
Импорт имеет следующий вид:
import { <import list> } from "<relative path>";
, где список импорта — это список идентификаторов. Расширение у файла для импорта не должно иметь расширений. -
Экспорт имеет следующий вид:
export { <export list> };
, где список экспорта — это список идентификаторов
-
-
-
Проверяются следующие правила:
- Переменная должна быть объявлена до ее использования
- Присвоить переменной можно только значение того же типа
- Константа должна быть инициализирована при объявлении
- Константе не может быть присвоено значение после ее объявления
- Массивы должны быть явно инициализированы
- В списке инициализаторов для массива все элементы должны иметь тип, как у объявляемого массива
- Присваивать массивам другие массивы нельзя
- Функции должны вызываться с правильным количеством и типами переменных
- В выражении операторы должны иметь операнды одного типа
- При экспорте, все элементы списка экспорта должны быть объявлены в файле или быть импортированными из другого модуля
- При импорте, все элементы списка импорта должны быть объявлены в файле, из которого импортируются элементы.
- Файл для импорта должен существовать
Для каждой ошибки, при ее возникновении, в консоль выдается ошибка, а компиляция заканчивается.
- Все выше сказанное
- Набор стандартных функций
- Модуль
io
:print
— функция для распечатки целочисленных значенийprintln
— функция для распечатки константных строк (из специальных символов поддерживается только\n
)input
— функция для ввода целочисленных значений
- Модуль
math
:sqrt
— функция для расчета квадратного корня.
- Модуль
string
:at
— функция для получения символа по индексу;strlen
— функция для получения длины строки;concat
— функция для склейки двух строк;slice
— функция для получения подстроки;find
— функция для поиска первого вхождения подстроки в строке;toString
— функция для конвертации числа в строку;toNumber
— функция для конвертации строки в число.
- Модуль
Для того, чтобы скомпилировать файл, необходимо прописать путь к файлу с исходным кодом:
stc <input file>
По-умолчанию, выходной файл будет иметь такое же имя с расширением .asm
. Для того, чтобы явно указать выходной файл нужно добавить флаг -o
после которого прописать путь к файлу:
stc <input file> -o <output file>
В компиляторе предусмотрена возможность вывода всей информации, полученной на стадии компиляции (таблица токенов, AST, таблица функций и переменных). Для вывода используется поток вывода, а также вывод в файл. Для того, чтобы включить логирование, нужно добавить флаг -d
или --debug
. По-умолчанию, логирование идет в файл log.txt
, но при необходимости его можно поменять, для этого нужно после флага написать путь к файлу для логов.
Для того, чтобы вывести время компиляции, необходимо добавить флаг -t
или --time
.
В данном режиме, компилятор, после компиляции, ждет изменений в компилируемом файле, и в случае его изменения производит повторную компиляцию. Для включения данного режима нужно добавить флаг -u
или --update
.
Важно!
Для работы данной опции необходимо иметь установленный пакет
MASM32
. Также путь к компилятору и линкеруmasm
должны быть в переменной средыPATH
.
Данная опция включает компиляцию, полученного после компиляции, ассемлерного кода. Результат выполнения программы выводится сразу в консоль, после вывода самого компилятора.
Для включения нужно добавить флаг -r
или --run
.
В компиляторе предусмотрена справка, для ее вызова используется флаг -h
, --help
, сразу после stc
:
stc -h
Для вызова дополнительной информации о разработчике нужно добавить флаг -a
или --about
.
import {print, println, input} from "./lib/io";
print(5); // --> 5
println("Hello\nWorld"); // --> Hello
// World
let b: number = input(); // <-- 10
print(b + 4); // --> 14
// можно не указывать тип, он выведется самостоятельно
let c = 100; // тип number
let val = c == 100 && b == 10; // тип boolean
function factorial(num: number): number
{
if (num < 2)
return 1; // если у блока одна инструкция,
// то ставить скобки не обязательно
return num * factorial(num - 1);
}
print(factorial(5)); // --> 120
let array: number[] = [5, 2, 1, 4, 1];
for (let i = 0; i < 5; i += 1)
{
print(array[i]);
}
// 5 2 1 4 1
Файл io.ts
:
/**
* Function print number to console.
* @param n The number to print.
*/
declare function print(n: number): void;
/**
* Function print constant string to console.
* @param n The string to print.
*/
declare function println(n: string): void;
/**
* Function for reading a number from user input.
*/
declare function input(): number;
export { print, println, input };
Предположим у нас есть файл printArray.ts
:
import {print} from "./lib/io";
function printArray(array: number[], size: number)
{
for (let i = 0; i < 5; i += 1)
{
print(array[i]);
}
}
export { printArray };
В нем мы экспортируем функцию. Теперь в файле main.ts
импортируем эту функцию и используем:
import {printArray} from "./printArray";
let array: number[] = [5, 2, 1, 4, 1];
printArray(array, 5);
Мы экспортировали только функцию printArray
, но в ней используется функция print
, поэтому при каждом импорте и экспорте производится анализ используемых функций, которые также добавляются в список экспортируемых, поэтому теперь вполне можно использовать функцию print
, хотя она явно не импортирована и не экспортирована.