/// <summary>
/// WithStatement is translated to the DLR AST equivalent to
/// the following Python code snippet (from with statement spec):
///
/// mgr = (EXPR)
/// exit = mgr.__exit__ # Not calling it yet
/// value = mgr.__enter__()
/// exc = True
/// try:
/// VAR = value # Only if "as VAR" is present
/// BLOCK
/// except:
/// # The exceptional case is handled here
/// exc = False
/// if not exit(*sys.exc_info()):
/// raise
/// # The exception is swallowed if exit() returns true
/// finally:
/// # The normal and non-local-goto cases are handled here
/// if exc:
/// exit(None, None, None)
///
/// </summary>
public override MSAst.Expression Reduce()
{
// Five statements in the result...
ReadOnlyCollectionBuilder <MSAst.Expression> statements = new ReadOnlyCollectionBuilder <MSAst.Expression>(6);
ReadOnlyCollectionBuilder <MSAst.ParameterExpression> variables = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression>(6);
MSAst.ParameterExpression lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_with");
variables.Add(lineUpdated);
//******************************************************************
// 1. mgr = (EXPR)
//******************************************************************
MSAst.ParameterExpression manager = Ast.Variable(typeof(object), "with_manager");
variables.Add(manager);
statements.Add(
GlobalParent.AddDebugInfo(
Ast.Assign(
manager,
_contextManager
),
new SourceSpan(Start, _header)
)
);
//******************************************************************
// 2. exit = mgr.__exit__ # Not calling it yet
//******************************************************************
MSAst.ParameterExpression exit = Ast.Variable(typeof(object), "with_exit");
variables.Add(exit);
statements.Add(
MakeAssignment(
exit,
GlobalParent.Get(
"__exit__",
manager
)
)
);
//******************************************************************
// 3. value = mgr.__enter__()
//******************************************************************
MSAst.ParameterExpression value = Ast.Variable(typeof(object), "with_value");
variables.Add(value);
statements.Add(
GlobalParent.AddDebugInfoAndVoid(
MakeAssignment(
value,
Parent.Invoke(
new CallSignature(0),
Parent.LocalContext,
GlobalParent.Get(
"__enter__",
manager
)
)
),
new SourceSpan(Start, _header)
)
);
//******************************************************************
// 4. exc = True
//******************************************************************
MSAst.ParameterExpression exc = Ast.Variable(typeof(bool), "with_exc");
variables.Add(exc);
statements.Add(
MakeAssignment(
exc,
AstUtils.Constant(true)
)
);
//******************************************************************
// 5. The final try statement:
//
// try:
// VAR = value # Only if "as VAR" is present
// BLOCK
// except:
// # The exceptional case is handled here
// exc = False
// if not exit(*sys.exc_info()):
// raise
// # The exception is swallowed if exit() returns true
// finally:
// # The normal and non-local-goto cases are handled here
// if exc:
// exit(None, None, None)
//******************************************************************
MSAst.ParameterExpression exception;
MSAst.ParameterExpression nestedFrames = Ast.Variable(typeof(List <DynamicStackFrame>), "$nestedFrames");
variables.Add(nestedFrames);
statements.Add(
// try:
AstUtils.Try(
AstUtils.Try(// try statement body
PushLineUpdated(false, lineUpdated),
_var != null ?
(MSAst.Expression)Ast.Block(
// VAR = value
_var.TransformSet(SourceSpan.None, value, PythonOperationKind.None),
// BLOCK
_body,
AstUtils.Empty()
) :
// BLOCK
(MSAst.Expression)_body // except:, // try statement location
).Catch(exception = Ast.Variable(typeof(Exception), "exception"),
// Python specific exception handling code
TryStatement.GetTracebackHeader(
this,
exception,
GlobalParent.AddDebugInfoAndVoid(
Ast.Block(
// exc = False
MakeAssignment(
exc,
AstUtils.Constant(false)
),
Ast.Assign(
nestedFrames,
Ast.Call(AstMethods.GetAndClearDynamicStackFrames)
),
// if not exit(*sys.exc_info()):
// raise
AstUtils.IfThen(
GlobalParent.Convert(
typeof(bool),
ConversionResultKind.ExplicitCast,
GlobalParent.Operation(
typeof(bool),
PythonOperationKind.IsFalse,
MakeExitCall(exit, exception)
)
),
UpdateLineUpdated(true),
Ast.Call(
AstMethods.SetDynamicStackFrames,
nestedFrames
),
Ast.Throw(
Ast.Call(
AstMethods.MakeRethrowExceptionWorker,
exception
)
)
)
),
_body.Span
)
),
Ast.Call(
AstMethods.SetDynamicStackFrames,
nestedFrames
),
PopLineUpdated(lineUpdated),
Ast.Empty()
)
// finally:
).Finally(
// if exc:
// exit(None, None, None)
AstUtils.IfThen(
exc,
GlobalParent.AddDebugInfoAndVoid(
Ast.Block(
Ast.Dynamic(
GlobalParent.PyContext.Invoke(
new CallSignature(3) // signature doesn't include function
),
typeof(object),
new MSAst.Expression[] {
Parent.LocalContext,
exit,
AstUtils.Constant(null),
AstUtils.Constant(null),
AstUtils.Constant(null)
}
),
Ast.Empty()
),
_contextManager.Span
)
)
)
);
statements.Add(AstUtils.Empty());
return(Ast.Block(variables.ToReadOnlyCollection(), statements.ToReadOnlyCollection()));
}