pascalabcnet/_ParsePABC1/ParsePABC1/YieldVisitors/ProcessYieldsCapturedVars.cs

251 lines
11 KiB
C#
Raw Permalink Normal View History

2015-06-28 10:53:10 +03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PascalABCCompiler;
using PascalABCCompiler.SyntaxTree;
using PascalABCCompiler.ParserTools;
using PascalABCCompiler.Errors;
namespace SyntaxVisitors
2015-06-28 10:53:10 +03:00
{
public static class Consts
{
public static string Current = "<>current";
public static string State = "<>state";
}
public class ProcessYieldCapturedVarsVisitor : BaseChangeVisitor
2015-06-28 10:53:10 +03:00
{
int clnum = 0;
public string newClassName()
{
clnum++;
return "clyield#" + clnum.ToString();
}
public FindMainIdentsVisitor mids; // захваченные переменные процедуры по всем её yield
2015-06-28 10:53:10 +03:00
public int countNodesVisited;
public bool hasYields = false;
public static ProcessYieldCapturedVarsVisitor New
{
get { return new ProcessYieldCapturedVarsVisitor(); }
}
2015-06-28 10:53:10 +03:00
public ProcessYieldCapturedVarsVisitor()
{
//PrintInfo = false;
}
public override void Enter(syntax_tree_node st)
{
base.Enter(st);
countNodesVisited++;
// сокращение обходимых узлов. Как сделать фильтр по тем узлам, которые необходимо обходить? Например, все операторы (без выражений и описаний), все описания (без операторов)
if (st is assign || st is var_def_statement || st is procedure_call || st is procedure_header || st is expression)
{
visitNode = false; // фильтр - куда не заходить
2015-06-28 10:53:10 +03:00
}
}
type_declarations GenClassesForYield(procedure_definition pd, IEnumerable<var_def_statement> fields)
2015-06-28 10:53:10 +03:00
{
var fh = (pd.proc_header as function_header);
if (fh == null)
throw new SyntaxError("Only functions can contain yields", "", pd.proc_header.source_context, pd.proc_header);
var seqt = fh.return_type as sequence_type;
if (seqt == null)
throw new SyntaxError("Functions with yields must return sequences", "", fh.return_type.source_context, fh.return_type);
// Теперь на месте функции генерируем класс
// Захваченные переменные
var cm = class_members.Public;
foreach (var m in fields)
cm.Add(m);
// Параметры функции
List<ident> lid = new List<ident>();
var pars = fh.parameters;
if (pars != null)
foreach (var ps in pars.params_list)
{
if (ps.param_kind != parametr_kind.none)
throw new SyntaxError("Parameters of functions with yields must not have 'var', 'const' or 'params' modifier", "", pars.source_context, pars);
if (ps.inital_value != null)
throw new SyntaxError("Parameters of functions with yields must not have initial values", "", pars.source_context, pars);
var_def_statement vds = new var_def_statement(ps.idents, ps.vars_type);
cm.Add(vds); // все параметры функции делаем полями класса
lid.AddRange(vds.vars.idents);
}
var stels = seqt.elements_type;
// Системные поля и методы для реализации интерфейса IEnumerable
cm.Add(new var_def_statement(Consts.State, "integer"),
new var_def_statement(Consts.Current, stels),
procedure_definition.EmptyDefaultConstructor,
new procedure_definition("Reset"),
new procedure_definition("MoveNext", "boolean", pd.proc_body),
new procedure_definition("get_Current", "object", new assign("Result", Consts.Current)),
new procedure_definition("GetEnumerator", "System.Collections.IEnumerator", new assign("Result", "Self"))
);
var className = newClassName();
var classNameHelper = className + "Helper";
var interfaces = new named_type_reference_list("System.Collections.IEnumerator", "System.Collections.IEnumerable");
var td = new type_declaration(classNameHelper, SyntaxTreeBuilder.BuildClassDefinition(interfaces, cm));
// Изменение тела процедуры
var stl = new statement_list(new var_statement("res", new new_expr(className)));
stl.AddMany(lid.Select(id => new assign(new dot_node("res", id), id)));
stl.Add(new assign("Result", "res"));
pd.proc_body = new block(stl);
// Второй класс
var tpl = new template_param_list(stels);
var IEnumeratorT = new template_type_reference("System.Collections.Generic.IEnumerator", tpl);
var cm1 = class_members.Public.Add(
procedure_definition.EmptyDefaultConstructor,
new procedure_definition(new function_header("get_Current", stels), new assign("Result", Consts.Current)),
new procedure_definition(new function_header("GetEnumerator", IEnumeratorT), new assign("Result", "Self")),
new procedure_definition("Dispose")
);
var interfaces1 = new named_type_reference_list(classNameHelper);
var IEnumerableT = new template_type_reference("System.Collections.Generic.IEnumerable", tpl);
interfaces1.Add(IEnumerableT).Add(IEnumeratorT);
var td1 = new type_declaration(className, SyntaxTreeBuilder.BuildClassDefinition(interfaces1, cm1));
var cct = new type_declarations(td);
cct.Add(td1);
return cct;
}
public override void visit(procedure_definition pd)
{
hasYields = false;
if (pd.proc_header is function_header)
mids = new FindMainIdentsVisitor();
base.visit(pd);
if (!hasYields) // т.е. мы разобрали функцию и уже выходим. Это значит, что пока yield будет обрабатываться только в функциях. Так это и надо.
return;
2015-08-26 13:48:16 +03:00
var dld = new DeleteAllLocalDefs(); // mids.vars - все захваченные переменные
pd.visit(dld); // Удалить в локальных и блочных описаниях этой процедуры все переменные и вынести их в отдельный список var_def_statement
mids.vars.Except(dld.LocalDeletedDefsNames); // параметры остались. Их тоже надо исключать - они и так будут обработаны
// В результате работы в mids.vars что-то осталось. Это не локальные переменные и с ними непонятно что делать
LoweringVisitor.Accept(pd);
var cfa = new ConstructFiniteAutomata((pd.proc_body as block).program_code);
cfa.Transform();
(pd.proc_body as block).program_code = cfa.res;
// Конструируем определение класса
2015-08-26 13:48:16 +03:00
var cct = GenClassesForYield(pd, dld.LocalDeletedDefs); // все удаленные описания переменных делаем описанием класса
UpperNodeAs<declarations>().InsertBefore(pd, cct);
mids = null; // вдруг мы выйдем из процедуры, не зайдем в другую, а там - оператор! Такого конечно не может быть
2015-06-28 10:53:10 +03:00
}
2015-06-28 10:53:10 +03:00
public override void visit(yield_node yn)
{
hasYields = true;
if (mids != null) // если мы - внутри процедуры
yn.visit(mids);
else throw new SyntaxError("Yield must be in functions only", "", yn.source_context, yn);
2015-06-28 10:53:10 +03:00
// mids.vars - надо установить, какие из них - локальные, какие - из этого класса, какие - являются параметрами функции, а какие - глобальные (все остальные)
// те, которые являются параметрами, надо скопировать в локальные переменные и переименовать использование везде по ходу данной функции
// самое сложное - переменные-поля этого класса - они требуют в создаваемом классе, реализующем итератор, хранить Self текущего класса и добавлять это Self везде по ходу алгоритма
// вначале будем считать, что переменные-поля этого класса и переменные-параметры не захватываются yield
//base.visit(yn);
}
}
class ConstructFiniteAutomata
{
public statement_list res = new statement_list();
statement_list stl;
int curState = 0;
statement_list curStatList;
statement_list StatListAfterCase = new statement_list();
case_node cas; // формируемый case
public ConstructFiniteAutomata(statement_list stl)
{
this.stl = stl;
}
public void Process(statement st)
{
if (!(st is yield_node || st is labeled_statement))
{
curStatList.Add(st);
}
if (st is yield_node)
{
var yn = st as yield_node;
curState += 1;
curStatList.AddMany(
new assign(Consts.Current, yn.ex),
new assign(Consts.State, curState),
new assign("Result", true),
new procedure_call("exit")
);
curStatList = new statement_list();
case_variant cv = new case_variant(new expression_list(new int32_const(curState)), curStatList);
cas.conditions.variants.Add(cv);
}
if (st is labeled_statement)
{
var ls = st as labeled_statement;
curStatList = StatListAfterCase;
curStatList.Add(new labeled_statement(ls.label_name));
Process(ls.to_statement);
}
}
public void Transform()
{
cas = new case_node(new ident(Consts.State));
curStatList = new statement_list();
case_variant cv = new case_variant(new expression_list(new int32_const(curState)), curStatList);
cas.conditions.variants.Add(cv);
foreach (var st in stl.subnodes)
Process(st);
stl.subnodes = BaseChangeVisitor.SeqStatements(cas, StatListAfterCase).ToList();
//statement_list res = new statement_list(cas);
res = stl;
}
}
2015-06-28 10:53:10 +03:00
}