// Copyright (c) Ivan Bondarev, Stanislav Mihalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Text;
using PascalABCCompiler.SyntaxTree;
using PascalABCCompiler.TreeConverter;
//Визитор предназначен для обхода поддерева синтаксического дерева и поиска
//обращений к константам и элементам массива - их не получится найти в семантическом дереве.
//Запускать через RunVisitor()
//После работы визитора в нем содержится:
//- Список констант, обьявленных внутри функций или класса. Их нужно включать в генерируемую функцию в любом случае.
//- Список локальных переменных, параметров функций и полей класса. Их нужно включать только если генерируется обьект-функция.
//- Список локальных блочных переменных. Если они есть - генерировать объект-функцию и включать в нее все локальные переменные,
// иначе можно обойтись просто локальной функцией и переменные не включать.
namespace PascalABCCompiler.TreeConverter
{
class VarFinderSyntaxVisitor : SyntaxTree.WalkingVisitorNew
{
///
/// Содержит список определений констант, объявленных внутри функций [и класса].
///
public List Constants =
new List();
///
/// Содержит список определений переменных
///
public List Variables =
new List();
///
/// Необходимо инициализировать для правильного поиска.
///
private compilation_context context;
///
/// Признак того, что мы распараллеливаем цикл for. Установить в true перед обходом тела распараллеливаемого цикла.
/// Нужно для коррекции continue и break
///
private bool isForNode = false;
///
/// Признак обхода счетчика цикла.
/// При запрете объявления счетчиков цикла вне самого цикла - не нужно
///
private bool isLoopVariable = false;
///
/// Конструктор
///
/// Корень обходимого поддерева
/// Контекст. Необходим для поиска переменных
/// True при обходе цикла, False при обходе секций
public VarFinderSyntaxVisitor(syntax_tree_node root, compilation_context context, bool isForNode)
{
this.isForNode = isForNode;
this.context = context;
ProcessNode(root);
FixResultVar();
}
///
/// Исправление VariableDefinitionNode для переменной Result функций.
/// В семантике эта переменная называется $rv_{FuncName}, а нам нужно имя result
/// Создается новый фиктивный экземпляр local_variable с тем же типом и именем result и возвращается вместо старого.
///
private void FixResultVar()
{
for (int i=0; i переменная совсем локальная, никуда добавлять не нужно
if ((si.sym_info is SemanticTree.ICommonParameterNode) //параметр или
|| (si.sym_info is SemanticTree.ILocalVariableNode) //локальная переменная
|| (si.sym_info is SemanticTree.ICommonClassFieldNode) //поле класса
|| (si.sym_info is SemanticTree.ILocalBlockVariableNode)//локальная блочная переменная
|| isLoopVariable) //счетчик цикла
{
if (!Variables.Contains(si.sym_info as SemanticTree.IVAriableDefinitionNode))
Variables.Add(si.sym_info as SemanticTree.IVAriableDefinitionNode);
}
else if ((si.sym_info is SemanticTree.ICommonFunctionConstantDefinitionNode)//константа из функции
||(si.sym_info is SemanticTree.IClassConstantDefinitionNode)) //константа из класса
{
if (!Constants.Contains(si.sym_info as SemanticTree.IConstantDefinitionNode))
Constants.Add(si.sym_info as SemanticTree.IConstantDefinitionNode);
}
}
public override void visit(dot_node _dot_node)
{
ProcessNode(_dot_node.left);
//правую часть не обходим
}
public override void visit(for_node _for_node)
{
if (!_for_node.create_loop_variable && (_for_node.type_name == null))
throw new OpenMPException("Счетчик цикла должен быть обьявлен в заголовке цикла", _for_node.source_context);
bool _isForNode = isForNode;
isForNode = false;
isLoopVariable = true;
ProcessNode(_for_node.loop_variable);
isLoopVariable = false;
ProcessNode(_for_node.initial_value);
ProcessNode(_for_node.increment_value);
ProcessNode(_for_node.finish_value);
ProcessNode(_for_node.type_name);
ProcessNode(_for_node.statements);
isForNode = _isForNode;
}
//public override void visit(method_call _method_call)
public override void visit(procedure_call _proc_call)
{
if (isForNode)
if ((_proc_call.func_name!= null)
&& (_proc_call.func_name is ident))
{
if ((_proc_call.func_name as ident).name.ToLower() == "continue")
(_proc_call.func_name as ident).name = "exit";
if ((_proc_call.func_name as ident).name.ToLower() == "break")
throw new OpenMPException("Нельзя использовать break в распараллеливаемом цикле", _proc_call.source_context);
}
base.visit(_proc_call);
}
}
}