internal override MSAst.Expression Transform(AstGenerator ag) { MSAst.Expression destination = ag.TransformAsObject(_dest); if (_expressions.Length == 0) { MSAst.Expression result; if (destination != null) { result = Ast.Call( AstGenerator.GetHelperMethod("PrintNewlineWithDest"), ag.LocalContext, destination ); } else { result = Ast.Call( AstGenerator.GetHelperMethod("PrintNewline"), ag.LocalContext ); } return ag.AddDebugInfo(result, Span); } else { // Create list for the individual statements List<MSAst.Expression> statements = new List<MSAst.Expression>(); // Store destination in a temp, if we have one if (destination != null) { MSAst.ParameterExpression temp = ag.GetTemporary("destination"); statements.Add( ag.MakeAssignment(temp, destination) ); destination = temp; } for (int i = 0; i < _expressions.Length; i++) { string method = (i < _expressions.Length - 1 || _trailingComma) ? "PrintComma" : "Print"; Expression current = _expressions[i]; MSAst.MethodCallExpression mce; if (destination != null) { mce = Ast.Call( AstGenerator.GetHelperMethod(method + "WithDest"), ag.LocalContext, destination, ag.TransformAsObject(current) ); } else { mce = Ast.Call( AstGenerator.GetHelperMethod(method), ag.LocalContext, ag.TransformAsObject(current) ); } statements.Add(mce); } statements.Add(AstUtils.Empty()); return ag.AddDebugInfo(Ast.Block(statements.ToArray()), Span); } }
private MSAst.Expression AssignComplex(AstGenerator ag, MSAst.Expression right) { // Python assignment semantics: // - only evaluate RHS once. // - evaluates assignment from left to right // - does not evaluate getters. // // So // a=b[c]=d=f() // should be: // $temp = f() // a = $temp // b[c] = $temp // d = $temp List<MSAst.Expression> statements = new List<MSAst.Expression>(); // 1. Create temp variable for the right value MSAst.ParameterExpression right_temp = ag.GetTemporary("assignment"); // 2. right_temp = right statements.Add( ag.MakeAssignment(right_temp, right) ); // Do left to right assignment foreach (Expression e in _left) { if (e == null) { continue; } // 3. e = right_temp MSAst.Expression transformed = e.TransformSet(ag, Span, right_temp, Operators.None); if (transformed == null) { throw PythonOps.SyntaxError(String.Format("can't assign to {0}", e.NodeName), ag.Context.SourceUnit, e.Span, -1); } statements.Add(transformed); } // 4. Create and return the resulting suite statements.Add(Ast.Empty()); return ag.AddDebugInfo( Ast.Block(statements.ToArray()), Span ); }
internal override MSAst.Expression TransformSet(AstGenerator ag, SourceSpan span, MSAst.Expression right, PythonOperationKind op) { // if we just have a simple named multi-assignment (e.g. a, b = 1,2) // then go ahead and step over the entire statement at once. If we have a // more complex statement (e.g. a.b, c.d = 1, 2) then we'll step over the // sets individually as they could be property sets the user wants to step // into. TODO: Enable stepping of the right hand side? bool emitIndividualSets = false; foreach (Expression e in _items) { if (IsComplexAssignment(e)) { emitIndividualSets = true; break; } } SourceSpan rightSpan = SourceSpan.None; SourceSpan leftSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; SourceSpan totalSpan = SourceSpan.None; if (emitIndividualSets) { rightSpan = span; leftSpan = SourceSpan.None; totalSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; } // 1. Evaluate the expression and assign the value to the temp. MSAst.ParameterExpression right_temp = ag.GetTemporary("unpacking"); // 2. Add the assignment "right_temp = right" into the suite/block MSAst.Expression assignStmt1 = AstGenerator.MakeAssignment(right_temp, right); // 3. Call GetEnumeratorValues on the right side (stored in temp) MSAst.Expression enumeratorValues = Ast.Call( AstGenerator.GetHelperMethod("GetEnumeratorValues"), // method // arguments ag.LocalContext, right_temp, AstUtils.Constant(_items.Length) ); // 4. Create temporary variable for the array MSAst.ParameterExpression array_temp = ag.GetTemporary("array", typeof(object[])); // 5. Assign the value of the method call (mce) into the array temp // And add the assignment "array_temp = Ops.GetEnumeratorValues(...)" into the block MSAst.Expression assignStmt2 = ag.MakeAssignment( array_temp, enumeratorValues, rightSpan ); ReadOnlyCollectionBuilder<MSAst.Expression> sets = new ReadOnlyCollectionBuilder<MSAst.Expression>(_items.Length + 1); for (int i = 0; i < _items.Length; i ++) { // target = array_temp[i] Expression target = _items[i]; if (target == null) { continue; } // 6. array_temp[i] MSAst.Expression element = Ast.ArrayAccess( array_temp, // array expression AstUtils.Constant(i) // index ); // 7. target = array_temp[i], and add the transformed assignment into the list of sets MSAst.Expression set = target.TransformSet( ag, emitIndividualSets ? // span target.Span : SourceSpan.None, element, PythonOperationKind.None ); sets.Add(set); } // 9. add the sets as their own block so they can be marked as a single span, if necessary. sets.Add(AstUtils.Empty()); MSAst.Expression itemSet = ag.AddDebugInfo(Ast.Block(sets.ToReadOnlyCollection()), leftSpan); // 10. Free the temps ag.FreeTemp(array_temp); ag.FreeTemp(right_temp); // 11. Return the suite statement (block) return ag.AddDebugInfo(Ast.Block(assignStmt1, assignStmt2, itemSet, AstUtils.Empty()), totalSpan); }
internal override MSAst.Expression TransformSet(AstGenerator ag, SourceSpan span, MSAst.Expression right, Operators op) { if (op != Operators.None) { ag.AddError("augmented assign to sequence prohibited", Span); return null; } // if we just have a simple named multi-assignment (e.g. a, b = 1,2) // then go ahead and step over the entire statement at once. If we have a // more complex statement (e.g. a.b, c.d = 1, 2) then we'll step over the // sets individually as they could be property sets the user wants to step // into. TODO: Enable stepping of the right hand side? bool emitIndividualSets = false; foreach (Expression e in _items) { if (IsComplexAssignment(e)) { emitIndividualSets = true; break; } } SourceSpan rightSpan = SourceSpan.None; SourceSpan leftSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; SourceSpan totalSpan = SourceSpan.None; if (emitIndividualSets) { rightSpan = span; leftSpan = SourceSpan.None; totalSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; } MSAst.Expression[] statements = new MSAst.Expression[4]; // 1. Evaluate the expression and assign the value to the temp. MSAst.ParameterExpression right_temp = ag.GetTemporary("unpacking"); // 2. Add the assignment "right_temp = right" into the suite/block statements[0] = ag.MakeAssignment(right_temp, right); // 3. Call GetEnumeratorValues on the right side (stored in temp) MSAst.Expression enumeratorValues = Ast.Call( AstGenerator.GetHelperMethod("GetEnumeratorValues"), // method // arguments AstUtils.CodeContext(), right_temp, Ast.Constant(_items.Length) ); // 4. Create temporary variable for the array MSAst.ParameterExpression array_temp = ag.GetTemporary("array", typeof(object[])); // 5. Assign the value of the method call (mce) into the array temp // And add the assignment "array_temp = Ops.GetEnumeratorValues(...)" into the block statements[1] = ag.MakeAssignment( array_temp, enumeratorValues, rightSpan ); MSAst.Expression[] sets = new MSAst.Expression[_items.Length + 1]; for (int i = 0; i < _items.Length; i ++) { // target = array_temp[i] Expression target = _items[i]; if (target == null) { continue; } // 6. array_temp[i] MSAst.Expression element = Ast.ArrayAccess( array_temp, // array expression Ast.Constant(i) // index ); // 7. target = array_temp[i], and add the transformed assignment into the list of sets MSAst.Expression set = target.TransformSet( ag, emitIndividualSets ? // span target.Span : SourceSpan.None, element, Operators.None ); if (set == null) { throw PythonOps.SyntaxError(string.Format("can't assign to {0}", target.NodeName), ag.Context.SourceUnit, target.Span, -1); } sets[i] = set; } // 9. add the sets as their own block so they can be marked as a single span, if necessary. sets[_items.Length] = Ast.Empty(); statements[2] = ag.AddDebugInfo(Ast.Block(sets), leftSpan); // 10. Free the temps ag.FreeTemp(array_temp); ag.FreeTemp(right_temp); // 11. Return the suite statement (block) statements[3] = Ast.Empty(); return ag.AddDebugInfo(Ast.Block(statements), totalSpan); }
/// <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> internal override MSAst.Expression Transform(AstGenerator ag) { // Five statements in the result... MSAst.Expression[] statements = new MSAst.Expression[6]; //****************************************************************** // 1. mgr = (EXPR) //****************************************************************** MSAst.ParameterExpression manager = ag.GetTemporary("with_manager"); statements[0] = ag.MakeAssignment( manager, ag.Transform(_contextManager), new SourceSpan(Start, _header) ); //****************************************************************** // 2. exit = mgr.__exit__ # Not calling it yet //****************************************************************** MSAst.ParameterExpression exit = ag.GetTemporary("with_exit"); statements[1] = ag.MakeAssignment( exit, Binders.Get( ag.BinderState, typeof(object), "__exit__", manager ) ); //****************************************************************** // 3. value = mgr.__enter__() //****************************************************************** MSAst.ParameterExpression value = ag.GetTemporary("with_value"); statements[2] = ag.MakeAssignment( value, Binders.Invoke( ag.BinderState, typeof(object), new CallSignature(0), Binders.Get( ag.BinderState, typeof(object), "__enter__", manager ) ) ); //****************************************************************** // 4. exc = True //****************************************************************** MSAst.ParameterExpression exc = ag.GetTemporary("with_exc", typeof(bool)); statements[3] = ag.MakeAssignment( exc, Ast.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 = ag.GetTemporary("exception", typeof(Exception)); statements[4] = // try: AstUtils.Try(// try statement body _var != null ? ag.AddDebugInfo( Ast.Block( // VAR = value _var.TransformSet(ag, SourceSpan.None, value, Operators.None), // BLOCK ag.Transform(_body), Ast.Empty() ), _body.Span ) : // BLOCK ag.Transform(_body) // except:, // try statement location ).Catch(exception, Ast.Block( // Python specific exception handling code Ast.Call( AstGenerator.GetHelperMethod("ClearDynamicStackFrames") ), // exc = False ag.MakeAssignment( exc, Ast.Constant(false) ), // if not exit(*sys.exc_info()): // raise AstUtils.IfThen( Binders.Convert( ag.BinderState, typeof(bool), ConversionResultKind.ExplicitCast, Binders.Operation( ag.BinderState, typeof(object), StandardOperators.Not, MakeExitCall(ag, exit, exception) ) ), Ast.Rethrow() ), Ast.Empty() ) // finally: ).Finally( // if exc: // exit(None, None, None) AstUtils.IfThen( exc, ag.AddDebugInfo( Ast.Dynamic( new PythonInvokeBinder( ag.BinderState, new CallSignature(3) // signature doesn't include function ), typeof(object), new MSAst.Expression[] { AstUtils.CodeContext(), exit, Ast.Constant(null), Ast.Constant(null), Ast.Constant(null) } ), _contextManager.Span ) ) ); statements[4] = ag.AddDebugInfo(statements[4], Span); statements[5] = Ast.Empty(); return ag.AddDebugInfo(Ast.Block(statements), _body.Span); }
/// <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> internal override MSAst.Expression Transform(AstGenerator ag) { MSAst.ParameterExpression lineUpdated = ag.GetTemporary("$lineUpdated_with", typeof(bool)); // Five statements in the result... ReadOnlyCollectionBuilder<MSAst.Expression> statements = new ReadOnlyCollectionBuilder<MSAst.Expression>(6); //****************************************************************** // 1. mgr = (EXPR) //****************************************************************** MSAst.ParameterExpression manager = ag.GetTemporary("with_manager"); statements.Add( ag.MakeAssignment( manager, ag.Transform(_contextManager), new SourceSpan(Start, _header) ) ); //****************************************************************** // 2. exit = mgr.__exit__ # Not calling it yet //****************************************************************** MSAst.ParameterExpression exit = ag.GetTemporary("with_exit"); statements.Add( AstGenerator.MakeAssignment( exit, ag.Get( typeof(object), "__exit__", manager ) ) ); //****************************************************************** // 3. value = mgr.__enter__() //****************************************************************** MSAst.ParameterExpression value = ag.GetTemporary("with_value"); statements.Add( ag.AddDebugInfoAndVoid( AstGenerator.MakeAssignment( value, ag.Invoke( typeof(object), new CallSignature(0), ag.Get( typeof(object), "__enter__", manager ) ) ), new SourceSpan(Start, _header) ) ); //****************************************************************** // 4. exc = True //****************************************************************** MSAst.ParameterExpression exc = ag.GetTemporary("with_exc", typeof(bool)); statements.Add( AstGenerator.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 = ag.GetTemporary("exception", typeof(Exception)); MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>)); statements.Add( // try: AstUtils.Try( AstUtils.Try(// try statement body ag.PushLineUpdated(false, lineUpdated), _var != null ? Ast.Block( // VAR = value _var.TransformSet(ag, SourceSpan.None, value, PythonOperationKind.None), // BLOCK ag.Transform(_body), AstUtils.Empty() ) : // BLOCK ag.Transform(_body) // except:, // try statement location ).Catch(exception, // Python specific exception handling code TryStatement.GetTracebackHeader( ag, exception, ag.AddDebugInfoAndVoid( Ast.Block( // exc = False AstGenerator.MakeAssignment( exc, AstUtils.Constant(false) ), Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // if not exit(*sys.exc_info()): // raise AstUtils.IfThen( ag.Convert( typeof(bool), ConversionResultKind.ExplicitCast, ag.Operation( typeof(bool), PythonOperationKind.Not, MakeExitCall(ag, exit, exception) ) ), ag.UpdateLineUpdated(true), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), Ast.Throw( Ast.Call( AstGenerator.GetHelperMethod("MakeRethrowExceptionWorker"), exception ) ) ) ), _body.Span ) ), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), ag.PopLineUpdated(lineUpdated), Ast.Empty() ) // finally: ).Finally( // if exc: // exit(None, None, None) AstUtils.IfThen( exc, ag.AddDebugInfoAndVoid( Ast.Block( Ast.Dynamic( ag.PyContext.Invoke( new CallSignature(3) // signature doesn't include function ), typeof(object), new MSAst.Expression[] { ag.LocalContext, exit, AstUtils.Constant(null), AstUtils.Constant(null), AstUtils.Constant(null) } ), Ast.Empty() ), _contextManager.Span ) ) ) ); statements.Add(AstUtils.Empty()); return Ast.Block(statements.ToReadOnlyCollection()); }