diff --git a/3.-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-Sage%2B%2B-%D0%B8-%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B..md b/3.-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-Sage%2B%2B-%D0%B8-%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B..md index b6443a1..d2f0c67 100644 --- a/3.-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-Sage%2B%2B-%D0%B8-%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B..md +++ b/3.-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0-Sage%2B%2B-%D0%B8-%D0%B2%D0%BD%D1%83%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%B5%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B..md @@ -1 +1,86 @@ -в \ No newline at end of file +Внутреннее представление программы с языка Фортран представляет собой AST (абстрактное синтаксическое дерево). Библиотека Sage++ представляет собой реализацию данного дерева разбора. Все высокоуровневые функции, структуры и классы в Sage++ начинаются с префикса **Sg**, за исключением некоторых функций, которые проверяют некоторые свойства, например, `isSgForStmt(SgStatement*)` - функция, проверяющая является ли выбранный узел циклом или нет. + +Внутреннее представление создается с помощью парсера. Парсер использует грамматику для синтаксического анализа. Данная грамматика расширена для поддержки директив DVMH и SAPFOR. Для того, чтобы начать работать с внутренним представлением, необходимо загрузить в память все файлы проекта, созданные парсером. Это делается с помощью класса **SgProject**, в конструктор, которому передается имя текстового файла, в котором перечислен список *.dep файлов. После создания проекта можно использовать следующий уровень абстракции - файл. Файл является единицей трансляции, поэтому для того, чтобы переключиться на конкретный файл, необходимо использовать следующий вызов:` SgFile *file = &(project.file(0));` - данная функция получает файл с индексом 0. Также, класс SgProject содержит в себе весь необходимый функционал для работы с проектом, например, количество файлов в проекте ( `project.numberOfFiles()` ), или имя каждого файла в проекте ( `project.fileName(0)` ). Таким образом, стандартная фаза компиляции представляет из себя проход по всем файлам для анализа кода программы пользователя. + +В текущей реализации SAPFOR уже определен механизм проходов, открытия проекта и выбора файлов. Каждый проход (фаза анализа) может обрабатывать каждый файл отдельно, а также, если необходима агрегация результатов анализа, может иметь функцию, которая работает после обработки всех файлов проекта. + +Каждый файл проекта представляет из себя набор связанных между собой операторов или **SgStatement**. Данный класс является абстрактным представлением всех операторов (базовым классом), от которого наследуются остальные классы для реализации специальных возможностей, присущие отдельно взятому оператору. Например, **SgForStmt**, является производным классом от **SgStatement** и содержит весь необходимый функционал для работы с оператором цикла. Согласно правилам языка С++, любой производный класс может быть приведен к базовому классу. + +В дереве разбора пользователь "видит" только **SgStatement**. К примеру, заголовок функции, оператор присваивания или оператор цикла - все это содержит в себе **SgStatement**. У каждого такого оператора есть вариант ( `SgStatement->variant()` ), который и задает вид конкретного оператора. Узнав вариант рассматриваемого оператора, можно использовать производное представление данного класса, преобразовав базовый класс к производному. Гарантируется, что данный оператор с правильным вариантом содержит всю необходимую информацию для производного класса. Например, чтобы узнать, является ли данный оператор оператором цикла, можно использовать следующий код: + +``` +SgStatement *st = currSt; +if (st->variant() == FOR_NODE) + SgForStmt *forSt = (SgForStmt*) st; +``` +либо можно использовать такой код: +``` +SgStatement *st = currSt; +SgForStmt *forSt = isSgForStmt(st); +if (forSt) + DoSomth(); +``` + +Все варианты ( `*->variant()` ) описаны в файле **tag** и **dvm_tag.h**. Каждый узел обязан иметь какой-либо вариант. Помимо класса **SgStatement**, есть класс **SgExpression**, представляющий собой выражения. Данный класс реализует выражения, которые есть у операторов. Например, следующий оператор присваивания: + +`A[i] = B[i] + C[i]` + +содержит в себе два выражения - то, что находится слева от оператора присваивания, и то, что находится справа. Для того, чтобы получить доступ к этим выражениям, необходимо использовать соответствующие функции у класса **SgStatement**: например, **expr(N)** позволяет получить выражение N. Если выражения с номером N не существует, то вернется пустой указатель (NULL). Всего оператор может содержать не более трех выражений (то есть N = 0, 1, 2). Данные выражения представляю собой **SgExpression**, которыми наполняется оператор. + +Класс **SgExpression** также является базовым классом для представления выражений. У данного класса есть такая же функция для взятия варианта ( `SgExpression->variant()` ). Используя данную функцию, можно узнать, с каким именно выражением необходимо работать и выполнить соответствующее преобразование к производному. Способ преобразования и проверки для выражений такой же, как и для операторов (_см. пример выше_). + +Все операторы файла (**SgStatement**) связаны между собой, есть понятие следующего оператора за данным в лексическом порядке, и предыдущего оператора перед данным в лексическом порядке. Также у каждого оператора есть родительский оператор, который задает уровни вложенности операторов. Таким образом, следующий оператор, который идет за данным, не обязательно должен принадлежать текущей области вложенности (иметь одного и того же родителя). Например, +``` +if (condition) then + op1 + op2 +endif +op3 +``` +за первым оператором, лексически следует оператор 2, за вторым - конец IF блока, а за концом IF блока - оператор 3. Узнать, какой оператор является родителем для данного оператора, можно с помощью функции `controlParent()`. + +Стоит отметить оператор с вариантом CONTROL_END. Данный оператор определяет конец блока операторов языка фортран, например, END IF, END DO, END FUNCTION и т.д. Для определения родителя для данного оператора нужно использовать функцию `controlParent()`. У каждого оператора есть функция определения последнего оператора для данного - `lastNodeOfStatement()`. Если оператор является составным, например, IF – END IF, то последним оператором будет ENDIF с вариантом CONTROL_END. + +В отличие от операторов, выражения связаны в правое рекурсивное двоичное дерево. У каждого узла есть левый потомок ( `SgExpression->lhs()` ) и/или правый потомок ( `SgExpression->rhs()` ). Каждый потомок также является **SgExpression**. У какого узла может быть только левый потомок, либо только правый, либо вообще может не быть потомков (_в данном случае соответствующие функции вернут пустой указатель_). Для работы с такими деревьями требуется понимание рекурсии и двоичного дерева. Рекурсивно обойти такое дерево из выражений можно, например, следующим образом: +``` +static void recExpression(SgExpression *exp, const int lvl) { + if (exp) { + SgExpression *lhs = exp->lhs(); + SgExpression *rhs = exp->rhs(); + doSmth() + recExpression(lhs, lvl + 1); + recExpression(rhs, lvl + 1); + } +} +``` +У каждого оператора и выражения есть возможность получения его исходного кода на языке Фортран, то есть можно выполнить генерацию кода отдельного взятого оператора и выражения. Соответствующая функция называется `unparsestdout()`. Данная функция позволяет выполнить генерацию кода в консоль. Она служит в основном для отладки. Стоит заметить, что если вызвать данную функцию для оператора "Функция" или "IF блок", то вместе с этим оператором будут сгенерированы все вложенные операторы в данный (или все те операторы, у которых родитель - данный оператор). Аналогично и для выражений - будет сгенерирован код для всего бинарного дерева, начиная от текущего узла и ниже. + +Для удобства отладки, в SAPFOR есть функция `recExpressionPrint (SgExpression *exp);`, которая позволяет получить наглядное представление бинарного дерева разбора для выражений в формате GraphViz, именуются узлы графа по такому правилу: **NODENUM_LVL_LR_TAGNAME_VALUE**: + +- **NODENUM** - номер узла, +- **LVL** - глубина узла в дереве, +- **LR** - левое или правое это поддерево, +- **TAGNAME** - имя варианта, соответствующее предопределенным макросам в файлах tag и dvm_tag.h, +- **VALUE** - значение, которое было в исходном коде, если оно доступно (например, имя символа, функции или операции). + +Данная функция на начальном этапе может **существенно упростить процесс отладки** и понимания того, как устроено внутреннее представление. + +Рассмотрим такой оператор: `B(I, J, K) = A(I, J, K-1) + A(I, J-1, K) + A(I-1, J, K)`. Для него с помощью функции `recExpressionPrint()` можно получить представление для левого выражения (_стоящего слева от присваивания_) и для правого. Данная функция (`recExpressionPrint`) выводит код для GraphViz в стандартный поток вывода (_консоль_). Код для визуализации правого выражения будет выглядеть так: +``` +digraph G{ +"0_0_L_ADD_OP_(+)" -> "1_1_L_ADD_OP_(+)"; +"0_0_L_ADD_OP_(+)" -> "2_1_R_ARRAY_REF_(a)"; +"1_1_L_ADD_OP_(+)" -> "3_2_L_ARRAY_REF_(a)"; +"1_1_L_ADD_OP_(+)" -> "4_2_R_ARRAY_REF_(a)"; + и т.д. +}; +``` +Визуализировать данный код можно с помощью Web GraphViz по этой [ссылке ](https://dreampuf.github.io/GraphvizOnline/) или по этой [ссылке](http://webgraphviz.com/) , либо можно скачать с сайта по последней ссылке программу для визуализации графов. По построенному графу легко сопоставить исходное выражение с внутренним представлением. + +Помимо операторов и выражений есть таблицы символов (**SgSymbol**) и типов (**SgType**), которые свои для каждого файла и общие для всех операторов и выражений в данном файле. Символы представляют собой наполнение выражений и операторов. Например, в приведенном выше выражении, есть символы A, B - которые являются символами следующего типа: трехмерный массив из double (базовый тип double, производный - массив из трех измерений), а символы I, J, K являются символами с типом Integer. В данном выражении для всех обращений к I, J, K будет ссылка на таблицу символов к единственным экземплярам I, J, K. Таблицы символов и типов доступны по соответствующей функции класса **SgFile**. На базе встроенных типов можно строить производные типы. Таблица типов доступна на уровне файла. + +На текущий момент существует две версии библиотеки Sage++. Компилятором FDVMH и SAPFOR используется первая версия. На сайте Sage++ также доступна вторая версия. Описание классов, приведенных на сайте, может отличаться в зависимости от версии. [По этой ссылке](https://extreme.indiana.edu/) доступна некоторая документация и иерархия классов первой версии, а также доступно более наглядное представление всех интерфейсов второй версии. + +Некоторые примеры можно найти на сайте этой библиотеки. Также можно рассмотреть некоторые простые проходы, реализованные в SAPFOR, которые описаны ниже. + +Так как данная библиотека разрабатывалась в зарубежных университетах в том числе студентами, можно встретить какие-то некорректности или ошибки. Периодически мы стараемся вносить изменения и исправлять некоторые ошибки и некорректности как на высоком уровне, так и на низком. Также в интерфейс Sage++ можно добавлять некоторые возможности, которые могут упростить его использование. Изменение уже существующих классов крайне не рекомендуется и в большинстве случаев не практикуется, а расширение функциональности - наоборот приветствуется.