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 ;
2015-08-15 00:31:26 +03:00
using PascalABCCompiler.ParserTools ;
using PascalABCCompiler.Errors ;
2015-08-17 21:15:22 +03:00
namespace SyntaxVisitors
2015-06-28 10:53:10 +03:00
{
2015-08-20 14:36:29 +03:00
public static class Consts
{
public static string Current = "<>current" ;
public static string State = "<>state" ;
}
2015-08-17 21:15:22 +03:00
public class ProcessYieldCapturedVarsVisitor : BaseChangeVisitor
2015-06-28 10:53:10 +03:00
{
int clnum = 0 ;
public string newClassName ( )
{
clnum + + ;
return "clyield#" + clnum . ToString ( ) ;
}
2015-08-10 11:31:02 +03:00
public FindMainIdentsVisitor mids ; // захваченные переменные процедуры по всем её yield
2015-06-28 10:53:10 +03:00
public int countNodesVisited ;
public bool hasYields = false ;
2015-08-17 21:15:22 +03:00
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 )
{
2015-08-10 11:31:02 +03:00
visitNode = false ; // фильтр - куда не заходить
2015-06-28 10:53:10 +03:00
}
}
2015-08-10 11:31:02 +03:00
2015-08-19 23:28:54 +03:00
type_declarations GenClassesForYield ( procedure_definition pd , IEnumerable < var_def_statement > fields )
2015-06-28 10:53:10 +03:00
{
2015-08-15 00:31:26 +03:00
var fh = ( pd . proc_header as function_header ) ;
2015-08-19 23:28:54 +03:00
if ( fh = = null )
throw new SyntaxError ( "Only functions can contain yields" , "" , pd . proc_header . source_context , pd . proc_header ) ;
2015-08-15 00:31:26 +03:00
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 ) ;
2015-08-20 14:36:29 +03:00
// Теперь на месте функции генерируем класс
// Захваченные переменные
var cm = class_members . Public ;
foreach ( var m in fields )
cm . Add ( m ) ;
// Параметры функции
2015-08-15 21:54:06 +03:00
List < ident > lid = new List < ident > ( ) ;
2015-08-15 00:31:26 +03:00
var pars = fh . parameters ;
2015-08-19 23:28:54 +03:00
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 ) ;
2015-08-20 14:36:29 +03:00
cm . Add ( vds ) ; // все параметры функции делаем полями класса
2015-08-19 23:28:54 +03:00
lid . AddRange ( vds . vars . idents ) ;
}
2015-08-15 00:31:26 +03:00
var stels = seqt . elements_type ;
2015-08-20 14:36:29 +03:00
// Системные поля и методы для реализации интерфейса 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" ) )
) ;
2015-08-15 00:31:26 +03:00
2015-08-19 23:28:54 +03:00
var className = newClassName ( ) ;
2015-08-20 14:36:29 +03:00
var classNameHelper = className + "Helper" ;
2015-08-19 23:28:54 +03:00
2015-08-20 14:36:29 +03:00
var interfaces = new named_type_reference_list ( "System.Collections.IEnumerator" , "System.Collections.IEnumerable" ) ;
var td = new type_declaration ( classNameHelper , SyntaxTreeBuilder . BuildClassDefinition ( interfaces , cm ) ) ;
2015-08-15 00:31:26 +03:00
2015-08-20 14:36:29 +03:00
// Изменение тела процедуры
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 ) ) ) ;
2015-08-19 23:28:54 +03:00
stl . Add ( new assign ( "Result" , "res" ) ) ;
2015-08-15 21:54:06 +03:00
pd . proc_body = new block ( stl ) ;
2015-08-19 23:28:54 +03:00
// Второй класс
2015-08-17 21:15:22 +03:00
2015-08-19 23:28:54 +03:00
var tpl = new template_param_list ( stels ) ;
2015-08-17 21:15:22 +03:00
2015-08-20 14:36:29 +03:00
var IEnumeratorT = new template_type_reference ( "System.Collections.Generic.IEnumerator" , tpl ) ;
2015-08-17 21:15:22 +03:00
2015-08-20 14:36:29 +03:00
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" )
) ;
2015-08-17 21:15:22 +03:00
2015-08-20 14:36:29 +03:00
var interfaces1 = new named_type_reference_list ( classNameHelper ) ;
var IEnumerableT = new template_type_reference ( "System.Collections.Generic.IEnumerable" , tpl ) ;
2015-08-19 23:28:54 +03:00
2015-08-20 14:36:29 +03:00
interfaces1 . Add ( IEnumerableT ) . Add ( IEnumeratorT ) ;
2015-08-19 23:28:54 +03:00
var td1 = new type_declaration ( className , SyntaxTreeBuilder . BuildClassDefinition ( interfaces1 , cm1 ) ) ;
var cct = new type_declarations ( td ) ;
cct . Add ( td1 ) ;
return cct ;
2015-08-17 21:15:22 +03:00
}
2015-08-10 11:31:02 +03:00
public override void visit ( procedure_definition pd )
{
hasYields = false ;
2015-08-15 00:31:26 +03:00
if ( pd . proc_header is function_header )
mids = new FindMainIdentsVisitor ( ) ;
2015-08-10 11:31:02 +03:00
base . visit ( pd ) ;
2015-08-15 00:31:26 +03:00
if ( ! hasYields ) // т.е . мы разобрали функцию и уже выходим. Это значит, что пока yield будет обрабатываться только в функциях. Так это и надо.
2015-08-10 11:31:02 +03:00
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 ) ; // параметры остались. Их тоже надо исключать - они и так будут обработаны
2015-08-10 11:31:02 +03:00
// В результате работы в mids.vars что-то осталось. Это не локальные переменные и с ними непонятно что делать
2015-08-17 21:15:22 +03:00
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-15 21:54:06 +03:00
2015-08-10 11:31:02 +03:00
// Конструируем определение класса
2015-08-26 13:48:16 +03:00
var cct = GenClassesForYield ( pd , dld . LocalDeletedDefs ) ; // все удаленные описания переменных делаем описанием класса
2015-08-10 11:31:02 +03:00
2015-08-19 23:28:54 +03:00
UpperNodeAs < declarations > ( ) . InsertBefore ( pd , cct ) ;
2015-08-10 11:31:02 +03:00
mids = null ; // вдруг мы выйдем из процедуры, не зайдем в другую, а там - оператор! Такого конечно не может быть
2015-06-28 10:53:10 +03:00
}
2015-08-10 11:31:02 +03:00
2015-06-28 10:53:10 +03:00
public override void visit ( yield_node yn )
{
hasYields = true ;
2015-08-10 11:31:02 +03:00
if ( mids ! = null ) // если мы - внутри процедуры
yn . visit ( mids ) ;
2015-08-15 00:31:26 +03:00
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);
}
}
2015-08-19 23:28:54 +03:00
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 ;
2015-08-20 14:36:29 +03:00
curStatList . AddMany (
new assign ( Consts . Current , yn . ex ) ,
new assign ( Consts . State , curState ) ,
new assign ( "Result" , true ) ,
new procedure_call ( "exit" )
) ;
2015-08-19 23:28:54 +03:00
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 ;
2015-08-20 14:36:29 +03:00
curStatList . Add ( new labeled_statement ( ls . label_name ) ) ;
2015-08-19 23:28:54 +03:00
Process ( ls . to_statement ) ;
}
}
public void Transform ( )
{
2015-08-20 14:36:29 +03:00
cas = new case_node ( new ident ( Consts . State ) ) ;
2015-08-19 23:28:54 +03:00
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
}