В данном разделе рассмотрено изменение дерева разбора, представленное с помощью библиотеки Sage++. Добавление нового оператора или выражения состоит из следующих шагов:
- Необходимо определить вариант (
variant()) добавляемого оператора или выражения - Необходимо определить класс добавляемого оператора или выражения. Можно использовать как базовые классы (SgStatement / SgExpression), либо производные, например, SgForStmt.
- Выделить память для нового выражения или оператора. Все операторы и выражения (да и вообще все сущности Sage++) могут использовать только по указателю.
- Вставить полученный оператор или выражения в дерево разбора.
- После вставки оператора в дерево его также можно наполнить выражениями в соответствии с правилами формирования конкретного оператора. С помощью функции
SgStatement::setExpression(int i, SgExpression &e)- можно вставить соответствующее выражение в позицию i = 0, 1, 2.
Вставка оператора делается с помощью следующих основных функций:
InsertStmtBefore- вставить до текущего оператора, иInsertStmtAfter- вставить после текущего оператора. Данные функции принимают первым параметром новый оператор и вторым - оператор родитель. Второй параметр рекомендуется указывать явно для исключения ошибки в структуре полученного кода.
Вставка выражения делается с помощью следующих основных функций:
setLsh()- установить (добавить ссылку) в левое поддерево, иsetRsh()- установить (добавить ссылку) в правое поддерево.
Также есть возможность вставить в дерево разбора копию оператора или выражения. Для этого необходимо использовать функции copy(), copyPtr() для того, чтобы создать соответствующую копию для вставки в дерево разбора. Необходимо следить за тем, чтобы в дереве разбора не было вставлено, например, одно выражение в нескольких местах (одна и та же ссылка в разных местах дерева). Для этих целей необходимо создавать копию.
Например, для того, чтобы создать следующий код на языке Фортран:
DO i_=1,N
DO j_=1,N
A = A + B
ENDDO
ENDDO
необходимо в проходе SAPFOR использовать следующий код на C++:
SgSymbol* N_symb = ...;
SgSymbol* i_ = findSymbolOrCreate(file, "i_" + to_string(i), SgTypeInt(), scope);
SgSymbol* j_ = findSymbolOrCreate(file, "j_" + to_string(i), SgTypeInt(), scope);
SgStatement* body=new SgAssignStmt(*new SgVarRef(A), &(*new SgVarRef(A)+*new SgVarRef(B)));
SgForStmt* newLoop = new SgForStmt (*j_, *new SgValueExp(1), *new SgVarRef(N_symb), *body);
newLoop = new SgForStmt (*i_, *new SgValueExp(1), *new SgVarRef(N_symb), *newLoop);
Далее необходимо выполнить вставку оператора newLoop в код перед или после необходимого оператора с помощью описанных выше функций insertAfter/insertBefore. Также нужно объявить новые переменные i_, j_, если они не были ранее объявлены. Данное действие выполняется с помощью функции makeDeclaration(currStat, vector<SgSymbol*> { i_, j_});, в которую первым параметром передается текущий оператор, по которому будет вычислена позиция вставки нового объявления, вторым - набор новых только что созданных символов. Если символы уже созданы и используются, то объявлять их не нужно.
Создание других операторов происходит примерно по таким же принципам. Существует набор производных классов, которые позволяют легко создавать новые операторы и наполнять их соответствующими выражениями.