diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj index dd2fef1d06..4a65766564 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj @@ -100,6 +100,7 @@ + diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/ConstructorInfo.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/ConstructorInfo.cs index 415982fcfd..ba9ce7b48d 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/ConstructorInfo.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/ConstructorInfo.cs @@ -9,45 +9,60 @@ namespace ICSharpCode.PythonBinding { public class PythonConstructorInfo { - ConstructorDeclaration constructor; - List fields = new List(); - - PythonConstructorInfo(ConstructorDeclaration constructor, List fields) + private ConstructorDeclaration constructor; + private List fields = new List(); + private List properties = new List(); + + private PythonConstructorInfo(ConstructorDeclaration constructor, + List fields, List properties) { this.constructor = constructor; this.fields = fields; + this.properties = properties; } - + /// - /// Gets the constructor information from a type declaration. Returns null if there is no - /// constructor defined or if there are no fields defined. + /// Gets the constructor information from a type declaration. /// + /// Returns null if there is no constructor defined or + /// if there are no fields/properties defined. public static PythonConstructorInfo GetConstructorInfo(TypeDeclaration type) { List fields = new List(); + List properties = new List(); ConstructorDeclaration constructor = null; foreach (INode node in type.Children) { ConstructorDeclaration currentConstructor = node as ConstructorDeclaration; FieldDeclaration field = node as FieldDeclaration; + PropertyDeclaration property = node as PropertyDeclaration; if (currentConstructor != null) { constructor = currentConstructor; } else if (field != null) { fields.Add(field); + } else if (property != null) { + if (property.HasGetRegion && property.GetRegion.Block.IsNull + && property.HasSetRegion && property.SetRegion.Block.IsNull) { + properties.Add(property); // basically anonymous backed property + } } } - - if ((fields.Count > 0) || (constructor != null)) { - return new PythonConstructorInfo(constructor, fields); + + if ((properties.Count > 0) || (fields.Count > 0) || (constructor != null)) { + return new PythonConstructorInfo(constructor, fields, properties); } return null; } - + public ConstructorDeclaration Constructor { get { return constructor; } } - + public List Fields { get { return fields; } } + + public List Properties { + get { return properties; } + } } -} +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/NRefactoryToPythonConverter.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/NRefactoryToPythonConverter.cs index 0b1455a6c6..8d882ca9fa 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/NRefactoryToPythonConverter.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/NRefactoryToPythonConverter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Text; +using System.Text.RegularExpressions; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Parser; @@ -21,43 +22,62 @@ namespace ICSharpCode.PythonBinding { string indentString = "\t"; PythonCodeBuilder codeBuilder; - + // Holds the constructor for the class being converted. This is used to identify class fields. PythonConstructorInfo constructorInfo; - + // Holds the parameters of the current method. This is used to identify // references to fields or parameters. List methodParameters = new List(); MethodDeclaration currentMethod; + bool methodHasOutputParameters = false; // Holds the names of any parameters defined for this class. List propertyNames = new List(); - + SupportedLanguage language; List entryPointMethods; SpecialNodesInserter specialNodesInserter; INode currentNode; List xmlDocComments = new List(); - + + // helpers for managing current types + bool clrImported = false; + TypeDeclaration currentType; + List importLibraries = new List(); + Dictionary typeNameMap = new Dictionary(); + + // flag for accumulating statements which should generated before and + // after the current statement + bool accumulatePostfixStatements = false; + List postfixStatements = new List(); + PrefixExpressionExtractor prefixExpressionExtractor = new PrefixExpressionExtractor(); + + // flag during yield statement to suppress child return statements + private bool suppressReturn = false; + + // flag during case statement to suppress child break statements + private bool suppressBreak = false; + static readonly string Docstring = "\"\"\""; public NRefactoryToPythonConverter(SupportedLanguage language) { this.language = language; } - + public NRefactoryToPythonConverter() { } - + /// /// Gets or sets the source language that will be converted to python. /// public SupportedLanguage SupportedLanguage { get { return language; } } - + /// /// Creates either C# to Python or VB.NET to Python converter based on the filename extension that is to be converted. /// @@ -69,7 +89,7 @@ namespace ICSharpCode.PythonBinding } return null; } - + /// /// Only C# (.cs) or VB.NET (.vb) files can be converted. /// @@ -82,7 +102,7 @@ namespace ICSharpCode.PythonBinding } return false; } - + /// /// Gets or sets the string that will be used to indent the generated Python code. /// @@ -108,7 +128,7 @@ namespace ICSharpCode.PythonBinding return parser.CompilationUnit; } } - + /// /// Converts the source code to Python. /// @@ -116,7 +136,7 @@ namespace ICSharpCode.PythonBinding { return Convert(source, language); } - + /// /// Converts the source code to Python. /// @@ -124,19 +144,39 @@ namespace ICSharpCode.PythonBinding { // Convert to NRefactory code DOM. CompilationUnit unit = GenerateCompilationUnit(source, language); - + SpecialOutputVisitor specialOutputVisitor = new SpecialOutputVisitor(this); specialNodesInserter = new SpecialNodesInserter(unit.UserData as List, specialOutputVisitor); - + + clrImported = false; + importLibraries = new List(); + typeNameMap = new Dictionary(); + // Convert to Python code. entryPointMethods = new List(); codeBuilder = new PythonCodeBuilder(); codeBuilder.IndentString = indentString; + InsertStandardImports(); unit.AcceptVisitor(this, null); - + return codeBuilder.ToString().Trim(); } - + + /// + /// Standard imports to maximize code compatibility + /// + private void InsertStandardImports() + { + codeBuilder.AppendIndentedLine("import System"); + + typeNameMap.Add("System.Int32", "int"); + typeNameMap.Add("System.String", "str"); + typeNameMap.Add("System.Object", "object"); + typeNameMap.Add("Int32", "int"); + typeNameMap.Add("String", "str"); + typeNameMap.Add("Object", "object"); + } + /// /// Gets a list of possible entry point methods found when converting the /// python source code. @@ -144,7 +184,7 @@ namespace ICSharpCode.PythonBinding public ReadOnlyCollection EntryPointMethods { get { return entryPointMethods.AsReadOnly(); } } - + /// /// Generates code to call the main entry point. /// @@ -159,7 +199,7 @@ namespace ICSharpCode.PythonBinding code.Append("None"); } code.Append(')'); - + return code.ToString(); } @@ -212,30 +252,86 @@ namespace ICSharpCode.PythonBinding return "=="; } } - + public override object TrackedVisitAddHandlerStatement(AddHandlerStatement addHandlerStatement, object data) { Console.WriteLine("VisitAddHandlerStatement"); return null; } - + public override object TrackedVisitAddressOfExpression(AddressOfExpression addressOfExpression, object data) { Console.WriteLine("VisitAddressOfExpression"); return null; } - + public override object TrackedVisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) { - Console.WriteLine("VisitAnonymousMethodExpression"); + bool isReference = accumulatePostfixStatements && !(anonymousMethodExpression.Parent is Statement); + if (!isReference) + Append("def "); + + // handle known special cases. Namely variable initialize + if (anonymousMethodExpression.Parent is ObjectCreateExpression) { + // Use the CreateType type to generate the anonymous method name + var objectCreationExpr = anonymousMethodExpression.Parent as ObjectCreateExpression; + var match = Regex.Match(GetTypeName(objectCreationExpr.CreateType), @"\.?(\w+)$", RegexOptions.Singleline); + string methodName = (match.Success) ? match.Groups[0].ToString() + : Regex.Replace(GetTypeName(objectCreationExpr.CreateType), @"\W", ""); + if (methodName.StartsWith("System")) methodName = methodName.Substring(6); + methodName += "_" + anonymousMethodExpression.StartLocation.Line.ToString() + + "_" + anonymousMethodExpression.StartLocation.Column.ToString(); + Append(methodName); + } else if (anonymousMethodExpression.Parent is VariableDeclaration) { + // this will still generate a "func = func" statement after definition but is harmless in python + var varDecl = anonymousMethodExpression.Parent as VariableDeclaration; + Append(varDecl.Name); + //varDecl.Name.AcceptVisitor(this, data); + } else { + string methodName = "anonMethod_" + anonymousMethodExpression.StartLocation.Line.ToString() + + "_" + anonymousMethodExpression.StartLocation.Column.ToString(); + Append(methodName); + } + + // method name reference written during normal passes + // definition written in prefix pass + if (isReference) + return null; + + // Add the parameters. + Append("("); + for (int i = 0; i < anonymousMethodExpression.Parameters.Count; ++i) { + if (i > 0) { + Append(", "); + } + Append(anonymousMethodExpression.Parameters[i].ParameterName); + } + Append("):"); + + // merge parameters with parent method with anonymous due to closures + // push old parameters on stack while we write out the inline method + var oldMethodParameters = methodParameters; + methodParameters = new List(anonymousMethodExpression.Parameters); + methodParameters.AddRange(methodParameters); + AppendLine(); + + IncreaseIndent(); + AppendDocstring(xmlDocComments); + if (anonymousMethodExpression.Body.Children.Count > 0) { + anonymousMethodExpression.Body.AcceptVisitor(this, data); + } else { + AppendIndentedPassStatement(); + } + DecreaseIndent(); + methodParameters = oldMethodParameters; return null; } - + public override object TrackedVisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data) { string arrayType = GetTypeName(arrayCreateExpression.CreateType); if (arrayCreateExpression.ArrayInitializer.CreateExpressions.Count == 0) { - Append("Array.CreateInstance(" + arrayType); + Append(GetTypeName("System.Array") + ".CreateInstance(" + arrayType); if (arrayCreateExpression.Arguments.Count > 0) { foreach (Expression expression in arrayCreateExpression.Arguments) { Append(", "); @@ -246,8 +342,8 @@ namespace ICSharpCode.PythonBinding Append(", 0)"); } } else { - Append("Array[" + arrayType + "]"); - + Append(GetTypeName("System.Array") + "[" + arrayType + "]"); + // Add initializers. Append("(("); bool firstItem = true; @@ -263,11 +359,14 @@ namespace ICSharpCode.PythonBinding } return null; } - + public override object TrackedVisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) { switch (assignmentExpression.Op) { case AssignmentOperatorType.Assign: + if (assignmentExpression.Right is InvocationExpression) { + return CreateInvocationAssignment(assignmentExpression, data); + } return CreateSimpleAssignment(assignmentExpression, "=", data); case AssignmentOperatorType.Add: if (IsAddEventHandler(assignmentExpression)) { @@ -301,15 +400,15 @@ namespace ICSharpCode.PythonBinding case AssignmentOperatorType.Power: return CreateSimpleAssignment(assignmentExpression, "**=", data); } - + return null; } - + public override object TrackedVisitAttribute(ICSharpCode.NRefactory.Ast.Attribute attribute, object data) { return null; } - + public override object TrackedVisitAttributeSection(AttributeSection attributeSection, object data) { return null; @@ -325,7 +424,7 @@ namespace ICSharpCode.PythonBinding Append("self"); return null; } - + public override object TrackedVisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) { binaryOperatorExpression.Left.AcceptVisitor(this, data); @@ -335,7 +434,7 @@ namespace ICSharpCode.PythonBinding binaryOperatorExpression.Right.AcceptVisitor(this, data); return null; } - + /// /// Visits the statement's children. /// @@ -343,13 +442,16 @@ namespace ICSharpCode.PythonBinding { return blockStatement.AcceptChildren(this, data); } - + public override object TrackedVisitBreakStatement(BreakStatement breakStatement, object data) { - AppendIndentedLine("break"); + if (suppressBreak) + AppendIndentedLine("pass #break"); // use pass as noop + else + AppendIndentedLine("break"); return null; } - + public override object TrackedVisitCaseLabel(CaseLabel caseLabel, object data) { return null; @@ -362,38 +464,38 @@ namespace ICSharpCode.PythonBinding { return castExpression.Expression.AcceptVisitor(this, data); } - + public override object TrackedVisitCatchClause(CatchClause catchClause, object data) { Console.WriteLine("VisitCatchClause"); return null; } - + public override object TrackedVisitCheckedExpression(CheckedExpression checkedExpression, object data) { Console.WriteLine("VisitCheckedExpression"); return null; } - + public override object TrackedVisitCheckedStatement(CheckedStatement checkedStatement, object data) { Console.WriteLine("VisitCheckedStatement"); return null; } - + public override object TrackedVisitClassReferenceExpression(ClassReferenceExpression classReferenceExpression, object data) { Console.WriteLine("VisitClassReferenceExpression"); return null; } - + public override object TrackedVisitCompilationUnit(CompilationUnit compilationUnit, object data) { // Visit the child items of the compilation unit. compilationUnit.AcceptChildren(this, data); return null; } - + /// /// An ternary operator expression: /// @@ -407,53 +509,53 @@ namespace ICSharpCode.PythonBinding { // Add true part. conditionalExpression.TrueExpression.AcceptVisitor(this, data); - + // Add condition. Append(" if "); conditionalExpression.Condition.AcceptVisitor(this, data); - + // Add false part. Append(" else "); conditionalExpression.FalseExpression.AcceptVisitor(this, data); return null; } - + public override object TrackedVisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) { CreateConstructor(constructorInfo); return null; } - + public override object TrackedVisitConstructorInitializer(ConstructorInitializer constructorInitializer, object data) { Console.WriteLine("VisitConstructorInitializer"); return null; } - + public override object TrackedVisitContinueStatement(ContinueStatement continueStatement, object data) { AppendIndentedLine("continue"); return null; } - + public override object TrackedVisitDeclareDeclaration(DeclareDeclaration declareDeclaration, object data) { Console.WriteLine("VisitDeclareDeclaration"); return null; } - + public override object TrackedVisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data) { Console.WriteLine("VisitDefaultValueExpression"); return null; } - + public override object TrackedVisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, object data) { Console.WriteLine("VisitDelegateDeclaration"); return null; } - + public override object TrackedVisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data) { AppendIndentedLine("def __del__(self):"); @@ -462,24 +564,27 @@ namespace ICSharpCode.PythonBinding DecreaseIndent(); return null; } - + public override object TrackedVisitDirectionExpression(DirectionExpression directionExpression, object data) { - Console.WriteLine("VisitDirectionExpression"); + directionExpression.Expression.AcceptVisitor(this, data); return null; } - + public override object TrackedVisitDoLoopStatement(DoLoopStatement doLoopStatement, object data) { AppendIndented("while "); - doLoopStatement.Condition.AcceptVisitor(this, data); + if (doLoopStatement.Condition.IsNull) + Append("True"); + else + doLoopStatement.Condition.AcceptVisitor(this, data); Append(":"); AppendLine(); - + IncreaseIndent(); doLoopStatement.EmbeddedStatement.AcceptVisitor(this, data); DecreaseIndent(); - + return null; } @@ -490,69 +595,69 @@ namespace ICSharpCode.PythonBinding elseIfSection.Condition.AcceptVisitor(this, data); Append(":"); AppendLine(); - + // Convert else if body statements. IncreaseIndent(); elseIfSection.EmbeddedStatement.AcceptVisitor(this, data); DecreaseIndent(); - + return null; } - + public override object TrackedVisitEmptyStatement(EmptyStatement emptyStatement, object data) { Console.WriteLine("VisitEmptyStatement"); return null; } - + public override object TrackedVisitEndStatement(EndStatement endStatement, object data) { Console.WriteLine("VistEndStatement"); return null; } - + public override object TrackedVisitEraseStatement(EraseStatement eraseStatement, object data) { Console.WriteLine("VisitEraseStatement"); return null; } - + public override object TrackedVisitErrorStatement(ErrorStatement errorStatement, object data) { Console.WriteLine("VisitErrorStatement"); return null; } - + public override object TrackedVisitEventAddRegion(EventAddRegion eventAddRegion, object data) { Console.WriteLine("VisitEventAddRegion"); return null; } - + public override object TrackedVisitEventDeclaration(EventDeclaration eventDeclaration, object data) { Console.WriteLine("VisitEventDeclaration"); return null; } - + public override object TrackedVisitEventRaiseRegion(EventRaiseRegion eventRaiseRegion, object data) { Console.WriteLine("VisitEventRaiseRegion"); return null; } - + public override object TrackedVisitEventRemoveRegion(EventRemoveRegion eventRemoveRegion, object data) { Console.WriteLine("VisitEventRemoveRegion"); return null; } - + public override object TrackedVisitExitStatement(ExitStatement exitStatement, object data) { AppendIndentedLine("break"); return null; } - + public override object TrackedVisitExpressionStatement(ExpressionStatement expressionStatement, object data) { // Convert the expression. @@ -561,40 +666,44 @@ namespace ICSharpCode.PythonBinding AppendLine(); return null; } - + public override object TrackedVisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) { return null; } - + public override object TrackedVisitFixedStatement(FixedStatement fixedStatement, object data) { Console.WriteLine("VisitFixedStatement"); return null; } - + + /// + /// Convert `foreach (var x in items)` to `for x in items:` + /// + /// + /// + /// public override object TrackedVisitForeachStatement(ForeachStatement foreachStatement, object data) { - // Convert the for loop's initializers. + // while statement does not work with lambda expressions + // and is more pythonic AppendIndented(String.Empty); - CreateInitStatement(foreachStatement); + Append("for "); + Append(foreachStatement.VariableName); + Append(" in "); + AppendForeachVariableName(foreachStatement); + Append(":"); AppendLine(); - - // Convert the for loop's test expression. - AppendIndentedLine("while enumerator.MoveNext():"); - - // Move the initializer in the foreach loop to the - // first line of the for loop's body. IncreaseIndent(); - AppendIndentedLine(foreachStatement.VariableName + " = enumerator.Current"); - + // Visit the for loop's body. foreachStatement.EmbeddedStatement.AcceptVisitor(this, data); DecreaseIndent(); return null; } - + /// /// Converts from an NRefactory VB.NET for next loop: /// @@ -614,7 +723,7 @@ namespace ICSharpCode.PythonBinding Append(" = "); forNextStatement.Start.AcceptVisitor(this, data); AppendLine(); - + // Convert the for loop's test expression. AppendIndented("while "); Append(variableName); @@ -622,11 +731,11 @@ namespace ICSharpCode.PythonBinding forNextStatement.End.AcceptVisitor(this, data); Append(":"); AppendLine(); - + // Visit the for loop's body. IncreaseIndent(); forNextStatement.EmbeddedStatement.AcceptVisitor(this, data); - + // Convert the for loop's increment statement. AppendIndented(variableName); Append(" = "); @@ -639,63 +748,271 @@ namespace ICSharpCode.PythonBinding } AppendLine(); DecreaseIndent(); - + return null; } - + /// /// Converts from an NRefactory for loop: /// /// for (int i = 0; i < 5; i = i + 1) /// /// to Python's: - /// - /// i = 0 - /// while i < 5: + /// for i in xrange(0,5,1): + /// or + /// i = 0 + /// while i < 5: /// public override object TrackedVisitForStatement(ForStatement forStatement, object data) { - // Convert the for loop's initializers. - foreach (Statement statement in forStatement.Initializers) { - statement.AcceptVisitor(this, data); + // Check if forStatement can be represented as simple range + if (!TryWriteSimpleForStatement(forStatement, data)) { + // Convert the for loop's initializers. + foreach (Statement statement in forStatement.Initializers) { + statement.AcceptVisitor(this, data); + } + + // Convert the for loop's test expression. + AppendIndented("while "); + if (forStatement.Condition.IsNull) + Append("True"); + else + forStatement.Condition.AcceptVisitor(this, data); + Append(":"); + AppendLine(); + + // Visit the for loop's body. + IncreaseIndent(); + forStatement.EmbeddedStatement.AcceptVisitor(this, data); + + // Convert the for loop's increment statement. + foreach (Statement statement in forStatement.Iterator) { + statement.AcceptVisitor(this, data); + } + DecreaseIndent(); + } + return null; + } + + /// + /// Handle the fairly common integer increment loop + /// + /// The complexity of this method is to guarantee that + /// it is only used on integer based variables and that we know that + /// its one of the common loop conditions + /// Statement to handle + /// Additional value for visitor + /// Returns true if the simple for statement was written + private bool TryWriteSimpleForStatement(ForStatement forStatement, object data) + { + if (forStatement.Initializers.Count != 1 + || forStatement.Iterator.Count != 1 + || forStatement.Condition.IsNull + ) { + return false; } + string variableName = null; + Expression variableExpression = null; + Expression startExpression = null; + Expression endExpression = null; + object stepExpression = null; + bool negateStepExpression = false; - // Convert the for loop's test expression. - AppendIndented("while "); - forStatement.Condition.AcceptVisitor(this, data); - Append(":"); + // Locate the initializer variable and determine if xrange applies + var localVariableDecl = forStatement.Initializers[0] as LocalVariableDeclaration; + var initializerStatement = forStatement.Initializers[0] as ExpressionStatement; + if (localVariableDecl != null && localVariableDecl.Variables.Count == 1) { + // validate the data type is an integer + var variableDecl = localVariableDecl.Variables[0] as VariableDeclaration; + if (!IsIntegerValue(localVariableDecl.TypeReference)) + return false; + variableName = variableDecl.Name; + startExpression = variableDecl.Initializer; + } else if (initializerStatement != null) { + var initializerExpression = initializerStatement.Expression as AssignmentExpression; + if (initializerExpression == null || initializerExpression.Op != AssignmentOperatorType.Assign) + return false; + var initializerVariable = initializerExpression.Left as IdentifierExpression; + if (initializerVariable == null) + return false; + variableName = initializerVariable.Identifier; + variableExpression = initializerVariable; + if (initializerExpression.Right is PrimitiveExpression) { + var primativeExpr = initializerExpression.Right as PrimitiveExpression; + if (!IsIntegerValue(primativeExpr.Value.GetType())) return false; + } + startExpression = initializerExpression.Right; + } else { + return false; + } + // get the step size from the iterator statement and determine increment direction + var iteratorStatement = forStatement.Iterator[0] as ExpressionStatement; + if (iteratorStatement == null) + return false; + var iteratorExpression = iteratorStatement.Expression as UnaryOperatorExpression; + if (iteratorExpression != null) { + switch (iteratorExpression.Op) { + case UnaryOperatorType.Increment: + case UnaryOperatorType.PostIncrement: + stepExpression = "1"; + break; + case UnaryOperatorType.Decrement: + case UnaryOperatorType.PostDecrement: + stepExpression = "1"; + negateStepExpression = true; + break; + default: + return false; + } + var iteratorVariable = iteratorExpression.Expression as IdentifierExpression; + if (iteratorVariable == null || iteratorVariable.Identifier != variableName) + return false; + } else { + var stepAssignmentExpression = iteratorStatement.Expression as AssignmentExpression; + if (stepAssignmentExpression == null || + (stepAssignmentExpression.Op != AssignmentOperatorType.Add && + stepAssignmentExpression.Op != AssignmentOperatorType.Subtract && + stepAssignmentExpression.Op != AssignmentOperatorType.Assign)) + return false; + negateStepExpression = stepAssignmentExpression.Op == AssignmentOperatorType.Subtract; + + var iteratorVariable = stepAssignmentExpression.Left as IdentifierExpression; + if (iteratorVariable == null || iteratorVariable.Identifier != variableName) + return false; + if (stepAssignmentExpression.Right is PrimitiveExpression + || stepAssignmentExpression.Right is IdentifierExpression + || stepAssignmentExpression.Right is MemberReferenceExpression + ) { + // handles += and -= + stepExpression = stepAssignmentExpression.Right; + } else if (stepAssignmentExpression.Op == AssignmentOperatorType.Assign + && stepAssignmentExpression.Right is BinaryOperatorExpression + ) { + // Handle i = i +/- step + var stepRightSubCondition = stepAssignmentExpression.Right as BinaryOperatorExpression; + if ((stepRightSubCondition.Op != BinaryOperatorType.Add && + stepRightSubCondition.Op != BinaryOperatorType.Subtract) + ) { + return false; + } + var stepLastVariable = stepRightSubCondition.Left as IdentifierExpression; + if (stepLastVariable == null || stepLastVariable.Identifier != variableName) { + return false; + } + negateStepExpression = (stepRightSubCondition.Op == BinaryOperatorType.Subtract); + if (!(stepRightSubCondition.Right is PrimitiveExpression + || stepRightSubCondition.Right is IdentifierExpression + || stepRightSubCondition.Right is MemberReferenceExpression + )) { + return false; + } + stepExpression = stepRightSubCondition.Right; + + } else { + return false; + } + } + + // determine the max value from the condition test + var condition = forStatement.Condition as BinaryOperatorExpression; + if (condition == null) + return false; + + // verify simple inequality and step direct is correct + if (!((condition.Op == BinaryOperatorType.GreaterThan && negateStepExpression) + || (condition.Op == BinaryOperatorType.LessThan && !negateStepExpression))) + return false; + + var compareVariable = condition.Left as IdentifierExpression; + if (compareVariable == null || compareVariable.Identifier != variableName) + return false; + endExpression = condition.Right; + + // Write out the for loop + AppendIndented("for "); + if (variableExpression != null) + variableExpression.AcceptVisitor(this, null); + else + Append(variableName); + Append(" in xrange("); + startExpression.AcceptVisitor(this, null); + Append(","); + endExpression.AcceptVisitor(this, null); + Append(","); + if (negateStepExpression) Append("-"); + if (stepExpression is string) { + Append(stepExpression.ToString()); + } else { + ((Expression)stepExpression).AcceptVisitor(this, null); + } + Append("):"); AppendLine(); - + // Visit the for loop's body. IncreaseIndent(); forStatement.EmbeddedStatement.AcceptVisitor(this, data); - - // Convert the for loop's increment statement. - foreach (Statement statement in forStatement.Iterator) { - statement.AcceptVisitor(this, data); - } DecreaseIndent(); - - return null; + return true; + } + + /// + /// Test if the TypeReference is an integer type + /// + /// TypeReference to test + /// True if the type is an integer type + private static bool IsIntegerValue(TypeReference typeReference) + { + if (typeReference.IsArrayType) + return false; + if (!typeReference.Type.StartsWith("System.")) + return false; + return IsIntegerValue(Type.GetType(typeReference.Type, false, true)); + } + + /// + /// Test if the Type is an integer type + /// + /// Type to test + /// True if the type is an integer type + private static bool IsIntegerValue(Type type) + { + if (type == null || !type.IsValueType || !type.IsPrimitive || type.IsArray || type.IsGenericType) + return false; + TypeCode code = Type.GetTypeCode(type); + switch (code) { + case TypeCode.Byte: + case TypeCode.Char: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + break; + default: + return false; + } + return true; } - + public override object TrackedVisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, object data) { Console.WriteLine("VisitGotoCaseStatement"); return null; } - + public override object TrackedVisitGotoStatement(GotoStatement gotoStatement, object data) { Console.WriteLine("VisitGotoStatement"); return null; } - + public override object TrackedVisitIdentifierExpression(IdentifierExpression identifierExpression, object data) { string name = identifierExpression.Identifier; if (IsField(name)) { - Append("self._" + name); + AppendFieldReferenceName(name); } else if (IsProperty(name) && !IsMethodParameter(name)) { Append("self." + name); } else { @@ -703,7 +1020,7 @@ namespace ICSharpCode.PythonBinding } return null; } - + public override object TrackedVisitIfElseStatement(IfElseStatement ifElseStatement, object data) { // Convert condition. @@ -711,7 +1028,7 @@ namespace ICSharpCode.PythonBinding ifElseStatement.Condition.AcceptVisitor(this, data); Append(":"); AppendLine(); - + // Convert true statements. IncreaseIndent(); foreach (Statement statement in ifElseStatement.TrueStatement) { @@ -725,7 +1042,7 @@ namespace ICSharpCode.PythonBinding elseIfSection.AcceptVisitor(this, data); } } - + // Convert false statements. if (ifElseStatement.HasElseStatements) { AppendIndentedLine("else:"); @@ -738,7 +1055,7 @@ namespace ICSharpCode.PythonBinding return null; } - + public override object TrackedVisitIndexerExpression(IndexerExpression indexerExpression, object data) { indexerExpression.TargetObject.AcceptVisitor(this, data); @@ -752,26 +1069,77 @@ namespace ICSharpCode.PythonBinding return null; } - + public override object TrackedVisitInnerClassTypeReference(InnerClassTypeReference innerClassTypeReference, object data) { Console.WriteLine("VisitInnerClassTypeReference"); return null; } - + public override object TrackedVisitInterfaceImplementation(InterfaceImplementation interfaceImplementation, object data) { Console.WriteLine("VisitInterfaceImplementation"); return null; } + private object CreateInvocationAssignment(AssignmentExpression assignmentExpression, object data) + { + assignmentExpression.Left.AcceptVisitor(this, data); + var invocationExpression = assignmentExpression.Right as InvocationExpression; + if (invocationExpression == null) { + Append(" = "); + assignmentExpression.Right.AcceptVisitor(this, data); + return null; + } + + CreateInvocationAssignmentParameters(invocationExpression, data, false); + invocationExpression.AcceptVisitor(this, data); + return null; + } + + private void CreateInvocationAssignmentParameters(InvocationExpression invocationExpression, object data, bool firstArg) + { + // out and ref values are passed via return tuple + // should verify that the objects are not clr.Reference + foreach (var param in invocationExpression.Arguments) { + var directionExpression = param as DirectionExpression; + if (directionExpression == null) continue; + if (directionExpression.FieldDirection == FieldDirection.Ref || + directionExpression.FieldDirection == FieldDirection.Out) { + if (!firstArg) Append(", "); + directionExpression.AcceptVisitor(this, data); + firstArg = false; + } + } + Append(" = "); + } + + private bool HasReferences(IEnumerable parameters) + { + foreach (var param in parameters) { + var expression = param as DirectionExpression; + if (expression == null) continue; + if (expression.FieldDirection == FieldDirection.Out || expression.FieldDirection == FieldDirection.Ref) + return true; + } + return false; + } + public override object TrackedVisitInvocationExpression(InvocationExpression invocationExpression, object data) { + // Special case for searching if single line statement with + if (HasReferences(invocationExpression.Arguments)) { + var expression = invocationExpression.Parent as ExpressionStatement; + if (expression != null && expression.Parent is BlockStatement) { + CreateInvocationAssignmentParameters(invocationExpression, data, true); + } + } + MemberReferenceExpression memberRefExpression = invocationExpression.TargetObject as MemberReferenceExpression; IdentifierExpression identifierExpression = invocationExpression.TargetObject as IdentifierExpression; if (memberRefExpression != null) { memberRefExpression.TargetObject.AcceptVisitor(this, data); - Append("." + memberRefExpression.MemberName); + Append("." + memberRefExpression.MemberName); } else if (identifierExpression != null) { if ((currentMethod != null) && IsStatic(currentMethod)) { Append(GetTypeName(currentMethod) + "."); @@ -780,11 +1148,15 @@ namespace ICSharpCode.PythonBinding } Append(identifierExpression.Identifier); } - + // Create method parameters Append("("); bool firstParam = true; foreach (Expression param in invocationExpression.Arguments) { + if (param is DirectionExpression) { + if (((DirectionExpression)param).FieldDirection == FieldDirection.Out) + continue; + } if (firstParam) { firstParam = false; } else { @@ -795,13 +1167,13 @@ namespace ICSharpCode.PythonBinding Append(")"); return null; } - + public override object TrackedVisitLabelStatement(LabelStatement labelStatement, object data) { Console.WriteLine("VisitLabelStatement"); return null; } - + /// /// The variable declaration is not created if the variable has no initializer. /// @@ -809,12 +1181,12 @@ namespace ICSharpCode.PythonBinding { foreach (VariableDeclaration variableDeclaration in localVariableDeclaration.Variables) { if (!variableDeclaration.Initializer.IsNull) { - + AddTypeToArrayInitializerIfMissing(variableDeclaration); - + // Create variable declaration. AppendIndented(variableDeclaration.Name + " = "); - + // Generate the variable initializer. variableDeclaration.Initializer.AcceptVisitor(this, data); AppendLine(); @@ -822,13 +1194,32 @@ namespace ICSharpCode.PythonBinding } return null; } - + public override object TrackedVisitLockStatement(LockStatement lockStatement, object data) { - Console.WriteLine("VisitLockStatement"); + AppendIndented(GetTypeName("System.Threading.Monitor")); + Append(".Enter("); + lockStatement.LockExpression.AcceptVisitor(this, data); + Append(")"); + AppendLine(); + + AppendIndentedLine("try:"); + IncreaseIndent(); + lockStatement.EmbeddedStatement.AcceptVisitor(this, data); + DecreaseIndent(); + + // Convert finally block. + AppendIndentedLine("finally:"); + IncreaseIndent(); + AppendIndented(GetTypeName("System.Threading.Monitor")); + Append(".Exit("); + lockStatement.LockExpression.AcceptVisitor(this, data); + Append(")"); + AppendLine(); + DecreaseIndent(); return null; } - + public override object TrackedVisitMemberInitializerExpression(MemberInitializerExpression memberInitializerExpression, object data) { Append(memberInitializerExpression.Name); @@ -836,7 +1227,7 @@ namespace ICSharpCode.PythonBinding memberInitializerExpression.Expression.AcceptVisitor(this, data); return null; } - + /// /// Adds a CodeMemberMethod to the current class being visited. /// @@ -844,14 +1235,19 @@ namespace ICSharpCode.PythonBinding { // Add method name. currentMethod = methodDeclaration; + + var oldMethodHasOutputParameters = methodHasOutputParameters; + methodHasOutputParameters = false; + string methodName = methodDeclaration.Name; AppendIndented("def " + methodName); - + // Add the parameters. AddParameters(methodDeclaration); + var oldMethodParameters = methodParameters; methodParameters = methodDeclaration.Parameters; AppendLine(); - + IncreaseIndent(); AppendDocstring(xmlDocComments); if (methodDeclaration.Body.Children.Count > 0) { @@ -859,23 +1255,25 @@ namespace ICSharpCode.PythonBinding } else { AppendIndentedPassStatement(); } - + DecreaseIndent(); AppendLine(); - + if (IsStatic(methodDeclaration)) { AppendIndentedLine(methodDeclaration.Name + " = staticmethod(" + methodDeclaration.Name + ")"); AppendLine(); - + // Save Main entry point method. SaveMethodIfMainEntryPoint(methodDeclaration); } - + + methodParameters = oldMethodParameters; + methodHasOutputParameters = oldMethodHasOutputParameters; currentMethod = null; - + return null; } - + public override object TrackedVisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, object data) { Append(namedArgumentExpression.Name); @@ -883,7 +1281,7 @@ namespace ICSharpCode.PythonBinding namedArgumentExpression.Expression.AcceptVisitor(this, data); return null; } - + /// /// Visits the namespace declaration and all child nodes. /// @@ -891,14 +1289,14 @@ namespace ICSharpCode.PythonBinding { return namespaceDeclaration.AcceptChildren(this, data); } - + /// /// Converts an NRefactory's ObjectCreateExpression to a code dom's /// CodeObjectCreateExpression. /// public override object TrackedVisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) { - Append(objectCreateExpression.CreateType.Type); + Append(GetTypeName(objectCreateExpression.CreateType)); if (IsGenericType(objectCreateExpression)) { AppendGenericTypes(objectCreateExpression); } @@ -913,7 +1311,7 @@ namespace ICSharpCode.PythonBinding expression.AcceptVisitor(this, data); firstParameter = false; } - + // Add object initializers. bool firstInitializer = true; foreach (Expression expression in objectCreateExpression.ObjectInitializer.CreateExpressions) { @@ -923,166 +1321,214 @@ namespace ICSharpCode.PythonBinding expression.AcceptVisitor(this, data); firstInitializer = false; } - + Append(")"); return null; } - + public override object TrackedVisitOnErrorStatement(OnErrorStatement onErrorStatement, object data) { return null; } - + public override object TrackedVisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) { Console.WriteLine("VisitOperatorDeclaration"); return null; } - + public override object TrackedVisitOptionDeclaration(OptionDeclaration optionDeclaration, object data) { Console.WriteLine("VisitOptionDeclaration"); return null; } - + public override object TrackedVisitExternAliasDirective(ExternAliasDirective externAliasDirective, object data) { Console.WriteLine("ExternAliasDirective"); return null; } - + public override object TrackedVisitParameterDeclarationExpression(ParameterDeclarationExpression parameterDeclarationExpression, object data) { Console.WriteLine("VisitParameterDeclarationExpression"); return null; } - + public override object TrackedVisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) { - Append("(" ); + Append("("); parenthesizedExpression.Expression.AcceptVisitor(this, data); Append(")"); return null; } - + public override object TrackedVisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) { Console.WriteLine("VisitPointerReferenceExpression"); return null; } - + public override object TrackedVisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) { if (primitiveExpression.Value == null) { Append("None"); } else if (primitiveExpression.Value is Boolean) { Append(primitiveExpression.Value.ToString()); + } else if (primitiveExpression.Value is Single + || primitiveExpression.Value is UInt16 + || primitiveExpression.Value is UInt32 + || primitiveExpression.Value is Byte + ) // special handling for primatives that do not directly map to Python + { + var t = primitiveExpression.Value.GetType(); + Append(string.Format("{0}({1})", t.Name, primitiveExpression.Value)); } else { Append(primitiveExpression.StringValue); } return null; } - + public override object TrackedVisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) { string propertyName = propertyDeclaration.Name; propertyNames.Add(propertyName); + bool isAnonymous = (propertyDeclaration.HasGetRegion && propertyDeclaration.GetRegion.Block.IsNull + && propertyDeclaration.HasSetRegion && propertyDeclaration.SetRegion.Block.IsNull); + // Add get statements. if (propertyDeclaration.HasGetRegion) { AppendIndentedLine("def get_" + propertyName + "(self):"); IncreaseIndent(); - propertyDeclaration.GetRegion.Block.AcceptVisitor(this, data); + if (isAnonymous) { + AppendIndentedLine("return self._" + propertyDeclaration.Name.ToLower()); // maybe do some type casting? + } else if (propertyDeclaration.GetRegion.Block.IsNull) { + AppendIndentedLine("pass"); + } else { + propertyDeclaration.GetRegion.Block.AcceptVisitor(this, data); + } DecreaseIndent(); AppendLine(); } - + // Add set statements. if (propertyDeclaration.HasSetRegion) { AppendIndentedLine("def set_" + propertyName + "(self, value):"); IncreaseIndent(); - propertyDeclaration.SetRegion.Block.AcceptVisitor(this, data); + if (isAnonymous) { + AppendIndentedLine("self._" + propertyDeclaration.Name.ToLower() + " = value"); // maybe do some type casting? + } else if (propertyDeclaration.GetRegion.Block.IsNull) { + AppendIndentedLine("pass"); + } else { + propertyDeclaration.SetRegion.Block.AcceptVisitor(this, data); + } DecreaseIndent(); AppendLine(); } - + AppendPropertyDecorator(propertyDeclaration); AppendLine(); - + return null; } - + public override object TrackedVisitPropertyGetRegion(PropertyGetRegion propertyGetRegion, object data) { Console.WriteLine("VisitPropertyGetRegion"); return null; } - + public override object TrackedVisitPropertySetRegion(PropertySetRegion propertySetRegion, object data) { Console.WriteLine("VisitPropertySetRegion"); return null; } - + public override object TrackedVisitRaiseEventStatement(RaiseEventStatement raiseEventStatement, object data) { Console.WriteLine("VisitRaiseEventStatement"); return null; } - + public override object TrackedVisitReDimStatement(ReDimStatement reDimStatement, object data) { Console.WriteLine("VisitReDimStatement"); return null; } - + public override object TrackedVisitRemoveHandlerStatement(RemoveHandlerStatement removeHandlerStatement, object data) { Console.WriteLine("VisitRemoveHandlerStatement"); return null; } - + public override object TrackedVisitResumeStatement(ResumeStatement resumeStatement, object data) { Console.WriteLine("VisitResumeStatement"); return null; } - + /// /// Converts a NRefactory ReturnStatement to a code dom's /// CodeMethodReturnStatement. /// public override object TrackedVisitReturnStatement(ReturnStatement returnStatement, object data) { - AppendIndented("return "); - returnStatement.Expression.AcceptVisitor(this, data); - AppendLine(); + // yield statements will generate the correct line start and end + if (suppressReturn) { + returnStatement.Expression.AcceptVisitor(this, data); + } else { + AppendIndented("return "); + + // python returns out and ref generally as a return tuple + if (methodHasOutputParameters) + Append("("); + + returnStatement.Expression.AcceptVisitor(this, data); + + if (methodHasOutputParameters) { + if (currentMethod != null) { + foreach (var param in currentMethod.Parameters) { + if (param.ParamModifier == ParameterModifiers.Out + || param.ParamModifier == ParameterModifiers.Ref) { + Append(", "); + Append(param.ParameterName); + } + } + } + Append(")"); + } + AppendLine(); + } return null; } - + public override object TrackedVisitSizeOfExpression(SizeOfExpression sizeOfExpression, object data) { Console.WriteLine("VisitSizeOfExpression"); return null; } - + public override object TrackedVisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data) { return null; } - + public override object TrackedVisitStopStatement(StopStatement stopStatement, object data) { return null; } - + public override object TrackedVisitSwitchSection(SwitchSection switchSection, object data) { return null; } - + public override object TrackedVisitSwitchStatement(SwitchStatement switchStatement, object data) { + var oldSuppressBreak = suppressBreak; + this.suppressBreak = true; bool firstSection = true; foreach (SwitchSection section in switchStatement.SwitchSections) { // Create if/elif/else condition. @@ -1092,17 +1538,19 @@ namespace ICSharpCode.PythonBinding IncreaseIndent(); CreateSwitchCaseBody(section); DecreaseIndent(); - + firstSection = false; } + + suppressBreak = oldSuppressBreak; return null; } - + public override object TrackedVisitTemplateDefinition(TemplateDefinition templateDefinition, object data) { return null; } - + public override object TrackedVisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) { Append("self"); @@ -1119,7 +1567,7 @@ namespace ICSharpCode.PythonBinding AppendLine(); return null; } - + /// /// Converts an NRefactory try-catch statement to a code dom /// try-catch statement. @@ -1131,29 +1579,29 @@ namespace ICSharpCode.PythonBinding IncreaseIndent(); tryCatchStatement.StatementBlock.AcceptVisitor(this, data); DecreaseIndent(); - + // Convert catches. foreach (CatchClause catchClause in tryCatchStatement.CatchClauses) { AppendIndented("except "); - Append(catchClause.TypeReference.Type); + Append(GetTypeName(catchClause.TypeReference)); Append(", " + catchClause.VariableName + ":"); AppendLine(); - + // Convert catch child statements. IncreaseIndent(); catchClause.StatementBlock.AcceptVisitor(this, data); DecreaseIndent(); } - + // Convert finally block. AppendIndentedLine("finally:"); IncreaseIndent(); tryCatchStatement.FinallyBlock.AcceptVisitor(this, data); DecreaseIndent(); - + return null; } - + /// /// Visits a class. /// @@ -1165,63 +1613,80 @@ namespace ICSharpCode.PythonBinding AppendLine(); IncreaseIndent(); AppendDocstring(xmlDocComments); - if (typeDeclaration.Children.Count > 0) { - // Look for fields or a constructor for the type. - constructorInfo = PythonConstructorInfo.GetConstructorInfo(typeDeclaration); - if (constructorInfo != null) { - if (constructorInfo.Constructor != null) { - // Generate constructor later when VisitConstructorDeclaration method is called. - // This allows the constructor comments to be converted in the right place. - } else { - CreateConstructor(constructorInfo); + + this.currentType = typeDeclaration; + if (typeDeclaration.Type == ClassType.Enum) { + CreateEnumeration(typeDeclaration); + } else { + if (typeDeclaration.Children.Count > 0) { + // Look for fields or a constructor for the type. + constructorInfo = PythonConstructorInfo.GetConstructorInfo(typeDeclaration); + if (constructorInfo != null) { + if (constructorInfo.Constructor != null) { + // Generate constructor later when VisitConstructorDeclaration method is called. + // This allows the constructor comments to be converted in the right place. + } else { + CreateConstructor(constructorInfo); + } } + + // Visit the rest of the class. + typeDeclaration.AcceptChildren(this, data); + } else { + AppendIndentedPassStatement(); } - - // Visit the rest of the class. - typeDeclaration.AcceptChildren(this, data); - } else { - AppendIndentedPassStatement(); } + this.currentType = null; DecreaseIndent(); return null; } - + public override object TrackedVisitTypeOfExpression(TypeOfExpression typeOfExpression, object data) { - codeBuilder.InsertIndentedLine("import clr\r\n"); + // clr was added to list of standard imports + if (!clrImported) { + codeBuilder.InsertIndentedLine("import clr"); + clrImported = true; + } Append("clr.GetClrType("); Append(GetTypeName(typeOfExpression.TypeReference)); Append(")"); return null; } - + public override object TrackedVisitTypeOfIsExpression(TypeOfIsExpression typeOfIsExpression, object data) { - Console.WriteLine("VisitTypeOfIsExpression"); + Append("isinstance("); + typeOfIsExpression.Expression.AcceptVisitor(this, data); + Append(","); + typeOfIsExpression.TypeReference.AcceptVisitor(this, data); + Append(")"); return null; } - + public override object TrackedVisitTypeReference(TypeReference typeReference, object data) { Console.WriteLine("VisitTypeReference"); return null; } - + public override object TrackedVisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) { Append(GetTypeName(typeReferenceExpression.TypeReference)); return null; } - + public override object TrackedVisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) { switch (unaryOperatorExpression.Op) { + // Change i++ or ++i to i += 1 case UnaryOperatorType.PostIncrement: - case UnaryOperatorType.Increment: - // Change i++ or ++i to i += 1 return CreateIncrementStatement(unaryOperatorExpression); + case UnaryOperatorType.Increment: + return CreateIncrementStatement(unaryOperatorExpression, prefix: true); case UnaryOperatorType.Decrement: + return CreateDecrementStatement(unaryOperatorExpression, prefix: true); case UnaryOperatorType.PostDecrement: // Change --i or i-- to i -= 1. return CreateDecrementStatement(unaryOperatorExpression); @@ -1236,27 +1701,27 @@ namespace ICSharpCode.PythonBinding } return null; } - + public override object TrackedVisitUncheckedExpression(UncheckedExpression uncheckedExpression, object data) { return null; } - + public override object TrackedVisitUncheckedStatement(UncheckedStatement uncheckedStatement, object data) { return null; } - + public override object TrackedVisitUnsafeStatement(UnsafeStatement unsafeStatement, object data) { return null; } - + public override object TrackedVisitUsing(Using @using, object data) { return null; } - + /// /// Converts using declarations into Python import statements. /// @@ -1264,16 +1729,83 @@ namespace ICSharpCode.PythonBinding { // Add import statements for each using. foreach (Using @using in usingDeclaration.Usings) { - AppendIndentedLine("from " + @using.Name + " import *"); + if (@using.IsAlias) { + typeNameMap[@using.Alias.Type] = @using.Name; + var match = Regex.Match(@using.Alias.Type, @"^(?.*)\.(?\w+)$", RegexOptions.Singleline); + if (match.Success) + AppendIndentedLine("from " + match.Groups["lib"] + " import " + + match.Groups["name"] + " as " + @using.Name); + else + AppendIndentedLine(@using.Name + " = " + @using.Alias.Type); + } else { + AppendIndentedLine("from " + @using.Name + " import *"); + importLibraries.Insert(0, @using.Name + "."); // insert in reverse order + } + } return null; } - + public override object TrackedVisitUsingStatement(UsingStatement usingStatement, object data) { + // first check if using is either LocalVariable or Variable assignment and try to get the variable name + string variableName = null; + bool skipInitializer = false; + var localVariableDecl = usingStatement.ResourceAcquisition as LocalVariableDeclaration; + var initializerStatement = usingStatement.ResourceAcquisition as ExpressionStatement; + if (localVariableDecl != null && localVariableDecl.Variables.Count == 1) { + // validate the data type is an integer + var variableDecl = localVariableDecl.Variables[0]; + variableName = variableDecl.Name; + } else if (initializerStatement != null) { + var initializerExpression = initializerStatement.Expression as AssignmentExpression; + if (initializerExpression != null && initializerExpression.Op == AssignmentOperatorType.Assign) { + var initializerVariable = initializerExpression.Left as IdentifierExpression; + if (initializerVariable != null) + variableName = initializerVariable.Identifier; + } else if (initializerStatement.Expression is ObjectCreateExpression) { + var createObjectStatement = initializerStatement.Expression as ObjectCreateExpression; + // create an anonymous variable placeholder + variableName = "anon" + createObjectStatement.StartLocation.Line.ToString(); + AppendIndented(variableName); + Append(" = "); + createObjectStatement.AcceptVisitor(this, data); + AppendLine(); + skipInitializer = true; + } + } + + if (!skipInitializer) + usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + + AppendIndentedLine("try:"); + IncreaseIndent(); + if (IsEmptyStatement(usingStatement.EmbeddedStatement)) + AppendIndentedLine("pass"); + else + usingStatement.EmbeddedStatement.AcceptVisitor(this, data); + DecreaseIndent(); + + // Convert finally block. + AppendIndentedLine("finally:"); + IncreaseIndent(); + if (!string.IsNullOrEmpty(variableName)) { + AppendIndented(variableName); + Append(".Dispose()"); + AppendLine(); + } else { + AppendIndentedLine("pass"); + } + DecreaseIndent(); return null; } - + + private static bool IsEmptyStatement(Statement statement) + { + return statement.IsNull + || (statement is BlockStatement && statement.Children.Count == 0); + } + public override object TrackedVisitVariableDeclaration(VariableDeclaration variableDeclaration, object data) { AppendIndented(variableDeclaration.Name + " = "); @@ -1281,24 +1813,91 @@ namespace ICSharpCode.PythonBinding AppendLine(); return null; } - + public override object TrackedVisitWithStatement(WithStatement withStatement, object data) { return null; } - + public override object TrackedVisitYieldStatement(YieldStatement yieldStatement, object data) { + if (yieldStatement.IsYieldBreak) + AppendIndentedLine("raise StopIteration"); + else if (yieldStatement.IsYieldReturn) { + // suppress the return in child statement + var oldSuppressReturn = suppressReturn; + suppressReturn = true; + AppendIndented("yield "); + yieldStatement.Statement.AcceptVisitor(this, data); + suppressReturn = oldSuppressReturn; + AppendLine(); + } return null; } - + public override object TrackedVisitCollectionInitializerExpression(CollectionInitializerExpression collectionInitializerExpression, object data) { return null; } - + public override object TrackedVisitLambdaExpression(LambdaExpression lambdaExpression, object data) { + if (!lambdaExpression.ExpressionBody.IsNull) // simple lambda otherwise anonymous method + { + Append("lambda "); + for (int i = 0; i < lambdaExpression.Parameters.Count; ++i) { + if (i > 0) { + Append(", "); + } + Append(lambdaExpression.Parameters[i].ParameterName); + } + Append(": "); + + // merge parameters with parent method with lambda due to closures + var oldMethodParameters = methodParameters; + methodParameters = new List(lambdaExpression.Parameters); + methodParameters.AddRange(methodParameters); + + lambdaExpression.ExpressionBody.AcceptVisitor(this, null); + + methodParameters = oldMethodParameters; + } else if (!lambdaExpression.StatementBody.IsNull) { + // handle known special cases. Namely variable initialize + string methodName = "lambdaMethod_" + lambdaExpression.StartLocation.Line.ToString() + + "_" + lambdaExpression.StartLocation.Column.ToString(); + + // method name reference written during normal passes + // definition written in prefix pass + if (accumulatePostfixStatements && !(lambdaExpression.Parent is Statement)) { + Append(methodName); + } else { + // merge parameters with parent method with lambda due to closures + var oldMethodParameters = methodParameters; + methodParameters = new List(lambdaExpression.Parameters); + methodParameters.AddRange(methodParameters); + + Append("def "); + Append(methodName); + Append("("); + // Add the parameters. + for (int i = 0; i < lambdaExpression.Parameters.Count; ++i) { + if (i > 0) { + Append(", "); + } + Append(lambdaExpression.Parameters[i].ParameterName); + } + Append("):"); + AppendLine(); + + IncreaseIndent(); + AppendDocstring(xmlDocComments); + lambdaExpression.StatementBody.AcceptVisitor(this, data); + DecreaseIndent(); + + methodParameters = oldMethodParameters; + } + } + return null; } @@ -1313,102 +1912,102 @@ namespace ICSharpCode.PythonBinding Append(memberReferenceExpression.MemberName); return null; } - + public override object TrackedVisitQueryExpression(QueryExpression queryExpression, object data) { return null; } - + public override object TrackedVisitQueryExpressionFromClause(QueryExpressionFromClause queryExpressionFromClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionGroupClause(QueryExpressionGroupClause queryExpressionGroupClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionJoinClause(QueryExpressionJoinClause queryExpressionJoinClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionLetClause(QueryExpressionLetClause queryExpressionLetClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionOrderClause(QueryExpressionOrderClause queryExpressionOrderClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionOrdering(QueryExpressionOrdering queryExpressionOrdering, object data) { return null; } - + public override object TrackedVisitQueryExpressionSelectClause(QueryExpressionSelectClause queryExpressionSelectClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionWhereClause(QueryExpressionWhereClause queryExpressionWhereClause, object data) { return null; } - + public override object TrackedVisitExpressionRangeVariable(ExpressionRangeVariable expressionRangeVariable, object data) { return null; } - + public override object TrackedVisitQueryExpressionAggregateClause(QueryExpressionAggregateClause queryExpressionAggregateClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionDistinctClause(QueryExpressionDistinctClause queryExpressionDistinctClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionGroupJoinVBClause(QueryExpressionGroupJoinVBClause queryExpressionGroupJoinVBClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionGroupVBClause(QueryExpressionGroupVBClause queryExpressionGroupVBClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionJoinConditionVB(QueryExpressionJoinConditionVB queryExpressionJoinConditionVB, object data) { return null; } - + public override object TrackedVisitQueryExpressionJoinVBClause(QueryExpressionJoinVBClause queryExpressionJoinVBClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionLetVBClause(QueryExpressionLetVBClause queryExpressionLetVBClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionPartitionVBClause(QueryExpressionPartitionVBClause queryExpressionPartitionVBClause, object data) { return null; } - + public override object TrackedVisitQueryExpressionSelectVBClause(QueryExpressionSelectVBClause queryExpressionSelectVBClause, object data) { return null; } - + /// /// Appends any comments that appear before this node. /// @@ -1417,33 +2016,64 @@ namespace ICSharpCode.PythonBinding xmlDocComments.Clear(); currentNode = node; specialNodesInserter.AcceptNodeStart(node); + + // Search the Method Statements for prefix/postfix/lambda expressions + // and move the prefix/lambda to line before current statement + // move the postfix statements to line after current statement + if (node is MethodDeclaration) { + accumulatePostfixStatements = true; + } + if (accumulatePostfixStatements && (node is Statement) && !(node is BlockStatement)) { + bool oldAccumulatePostfixStatements = accumulatePostfixStatements; + accumulatePostfixStatements = false; + node.AcceptVisitor(prefixExpressionExtractor, null); + if (prefixExpressionExtractor.Statements.Count > 0) { + CreatePostfixStatements(prefixExpressionExtractor.Statements); + prefixExpressionExtractor.Statements.Clear(); + } + accumulatePostfixStatements = oldAccumulatePostfixStatements; + } } - + + protected override void EndVisit(INode node) + { + if (node is MethodDeclaration) { + accumulatePostfixStatements = false; + if (this.postfixStatements.Count > 0) + this.postfixStatements.Clear(); + } else if (node is Statement) { + if (this.postfixStatements.Count > 0) { + CreatePostfixStatements(this.postfixStatements); + this.postfixStatements.Clear(); + } + } + } + #region IOutputFormatter - + int IOutputFormatter.IndentationLevel { get { return codeBuilder.Indent; } set { ; } } - + string IOutputFormatter.Text { get { return String.Empty; } } - + bool IOutputFormatter.IsInMemberBody { get { return false; } set { ; } } - + void IOutputFormatter.NewLine() { } - + void IOutputFormatter.Indent() { - + } - + void IOutputFormatter.PrintComment(Comment comment, bool forceWriteInPreviousBlock) { if (comment.CommentType == CommentType.SingleLine) { @@ -1458,17 +2088,17 @@ namespace ICSharpCode.PythonBinding } } } - + void IOutputFormatter.PrintPreprocessingDirective(PreprocessingDirective directive, bool forceWriteInPreviousBlock) { } - + void IOutputFormatter.PrintBlankLine(bool forceWriteInPreviousBlock) { } - + #endregion - + /// /// Checks that the field declaration has an initializer that /// sets an initial value. @@ -1484,9 +2114,9 @@ namespace ICSharpCode.PythonBinding /// This converts "i++" and "++i" to "i = i + 1" since python /// does not support post increment expressions. /// - object CreateIncrementStatement(UnaryOperatorExpression unaryOperatorExpression) + object CreateIncrementStatement(UnaryOperatorExpression unaryOperatorExpression, bool prefix = false) { - return CreateIncrementStatement(unaryOperatorExpression, 1, GetBinaryOperator(BinaryOperatorType.Add)); + return CreateIncrementStatement(unaryOperatorExpression, 1, GetBinaryOperator(BinaryOperatorType.Add), prefix); } /// @@ -1494,25 +2124,31 @@ namespace ICSharpCode.PythonBinding /// This converts "i--" and "--i" to "i -= 1" since python /// does not support post increment expressions. /// - object CreateDecrementStatement(UnaryOperatorExpression unaryOperatorExpression) + object CreateDecrementStatement(UnaryOperatorExpression unaryOperatorExpression, bool prefix = false) { - return CreateIncrementStatement(unaryOperatorExpression, 1, GetBinaryOperator(BinaryOperatorType.Subtract)); + return CreateIncrementStatement(unaryOperatorExpression, 1, GetBinaryOperator(BinaryOperatorType.Subtract), prefix); } - + /// /// Converts a post or pre increment expression to an assign statement. /// This converts "i++" and "++i" to "i += 1" since python /// does not support post increment expressions. /// - object CreateIncrementStatement(UnaryOperatorExpression unaryOperatorExpression, int increment, string binaryOperator) + object CreateIncrementStatement(UnaryOperatorExpression unaryOperatorExpression, int increment, string binaryOperator, bool prefix = false) { - unaryOperatorExpression.Expression.AcceptVisitor(this, null); - Append(" " + binaryOperator + "= "); - Append(increment.ToString()); - + if (accumulatePostfixStatements && !(unaryOperatorExpression.Parent is Statement)) { + unaryOperatorExpression.Expression.AcceptVisitor(this, null); + + // prefix handled by the PrefixExpressionExtractor + if (!prefix) postfixStatements.Add(unaryOperatorExpression); + } else { + unaryOperatorExpression.Expression.AcceptVisitor(this, null); + Append(" " + binaryOperator + "= "); + Append(increment.ToString()); + } return null; } - + /// /// Creates the statement used to initialize the for loop. The /// initialize statement will be "enumerator = variableName.GetEnumerator()" @@ -1526,7 +2162,7 @@ namespace ICSharpCode.PythonBinding return null; } - + /// /// Gets the name of the variable that is used in the /// foreach loop as the item being iterated and appends the code. @@ -1544,7 +2180,7 @@ namespace ICSharpCode.PythonBinding memberRefExpression.AcceptVisitor(this, null); } } - + /// /// Determines whether the identifier refers to a field in the /// current class. @@ -1568,7 +2204,61 @@ namespace ICSharpCode.PythonBinding } return false; } - + + void AppendFieldReferenceName(string name) + { + // Check the current class's fields. + bool nameWritten = false; + if (constructorInfo != null) { + foreach (FieldDeclaration field in constructorInfo.Fields) { + foreach (VariableDeclaration variable in field.Fields) { + if (variable.Name == name) { + var fieldDecl = field.TypeReference.Parent as FieldDeclaration; + if (fieldDecl == null && ((field.Modifier & Modifiers.Const) != 0)) { + variable.Initializer.AcceptVisitor(this, null); + } else { + var typeDecl = fieldDecl.Parent as TypeDeclaration; + if (typeDecl == null) typeDecl = currentType; // use current type + if (typeDecl != null) { + if (field.TypeReference.IsNull || IsStatic(field)) + Append(typeDecl.Name + "." + name); + else if (IsPrivate(field)) + Append("self._" + name); + else + Append("self." + name); // no underscore for public methods + } + } + nameWritten = true; + } + } + } + } + if (!nameWritten) { + Append("self._" + name); + } + } + + bool IsStaticField(string name) + { + // Check the current method's parameters. + if (IsMethodParameter(name)) { + return false; + } + + // Check the current class's fields. + if (constructorInfo != null) { + foreach (FieldDeclaration field in constructorInfo.Fields) { + foreach (VariableDeclaration variable in field.Fields) { + if (variable.Name == name) { + if (field.TypeReference.IsNull || (field.Modifier & (Modifiers.Const | Modifiers.Static)) != 0) + return true; + } + } + } + } + return false; + } + bool IsMethodParameter(string name) { foreach (ParameterDeclarationExpression param in methodParameters) { @@ -1578,12 +2268,12 @@ namespace ICSharpCode.PythonBinding } return false; } - + bool IsProperty(string name) { return propertyNames.Contains(name); } - + /// /// Creates an attach statement (i.e. button.Click += ButtonClick) /// or remove statement (i.e. button.Click -= ButtonClick) @@ -1595,7 +2285,7 @@ namespace ICSharpCode.PythonBinding CreateDelegateCreateExpression(eventHandlerExpression); return null; } - + /// /// Converts an expression to a CodeEventReferenceExpression /// (i.e. the "button.Click" part of "button.Click += ButtonClick". @@ -1607,7 +2297,7 @@ namespace ICSharpCode.PythonBinding memberRef.AcceptVisitor(this, null); return null; } - + /// /// Creates an event handler expression /// (i.e. the "ButtonClick" part of "button.Click += ButtonClick") @@ -1627,15 +2317,23 @@ namespace ICSharpCode.PythonBinding } return null; } - + /// /// Determines whether the assignment expression is actually an /// event handler attach statement. /// static bool IsAddEventHandler(AssignmentExpression assignmentExpression) { - return (assignmentExpression.Op == AssignmentOperatorType.Add) && - (assignmentExpression.Left is MemberReferenceExpression); + if ((assignmentExpression.Op == AssignmentOperatorType.Add) + && (assignmentExpression.Left is MemberReferenceExpression) + && ((assignmentExpression.Right is IdentifierExpression) + || (assignmentExpression.Right is ObjectCreateExpression) + || (assignmentExpression.Right is MemberReferenceExpression) + ) + ) { + return true; + } + return false; } /// @@ -1644,47 +2342,60 @@ namespace ICSharpCode.PythonBinding /// static bool IsRemoveEventHandler(AssignmentExpression assignmentExpression) { - return (assignmentExpression.Op == AssignmentOperatorType.Subtract) && - (assignmentExpression.Left is MemberReferenceExpression); + if ((assignmentExpression.Op == AssignmentOperatorType.Subtract) + && (assignmentExpression.Left is MemberReferenceExpression) + && ((assignmentExpression.Right is IdentifierExpression) + || (assignmentExpression.Right is ObjectCreateExpression) + || (assignmentExpression.Right is MemberReferenceExpression) + ) + ) { + return true; + } + return false; } - + void Append(string code) { codeBuilder.Append(code); } - + void AppendIndented(string code) { codeBuilder.AppendIndented(code); } - + void AppendIndentedPassStatement() { AppendIndentedLine("pass"); } - + void AppendIndentedLine(string code) { codeBuilder.AppendIndentedLine(code); } - + void AppendLine() { codeBuilder.AppendLine(); } - + void IncreaseIndent() { codeBuilder.IncreaseIndent(); } - + void DecreaseIndent() { codeBuilder.DecreaseIndent(); } - + void CreateConstructor(PythonConstructorInfo constructorInfo) { + // handle static fields + foreach (var field in constructorInfo.Fields) { + CreateStaticFieldInitialization(field, null); + } + if (constructorInfo.Constructor != null) { AppendIndented("def __init__"); AddParameters(constructorInfo.Constructor); @@ -1693,28 +2404,27 @@ namespace ICSharpCode.PythonBinding AppendIndented("def __init__(self):"); } AppendLine(); - + // Add fields at start of constructor. IncreaseIndent(); AppendDocstring(xmlDocComments); - if (constructorInfo.Fields.Count > 0) { - foreach (FieldDeclaration field in constructorInfo.Fields) { - CreateFieldInitialization(field); - } + foreach (var field in constructorInfo.Fields) { + CreateFieldInitialization(field); + } + foreach (var property in constructorInfo.Properties) { + CreatePropertyInitialization(property); } - + if (!IsEmptyConstructor(constructorInfo.Constructor)) { constructorInfo.Constructor.Body.AcceptVisitor(this, null); AppendLine(); - } else if (constructorInfo.Fields.Count == 0) { - AppendIndentedPassStatement(); } else { - AppendLine(); + AppendIndentedPassStatement(); } - + DecreaseIndent(); } - + /// /// Returns true if the constructor has no statements in its body. /// @@ -1725,25 +2435,125 @@ namespace ICSharpCode.PythonBinding } return true; } - + + void CreateEnumeration(TypeDeclaration typeDeclaration) + { + object current = 0; + foreach (INode node in typeDeclaration.Children) { + var field = node as FieldDeclaration; + if (field != null) { + object last = CreateStaticFieldInitialization(field, current); + if (last != null) current = last; + } + } + if (current.Equals(0)) { + AppendIndentedLine("pass"); + } + } + /// /// Creates a field initialization statement. /// void CreateFieldInitialization(FieldDeclaration field) { - foreach (VariableDeclaration variable in field.Fields) { + if (field.TypeReference.IsNull || (field.Modifier & (Modifiers.Const | Modifiers.Static)) != 0) + return; + foreach (VariableDeclaration variable in field.Fields) { // Ignore field if it has no initializer. if (FieldHasInitialValue(variable)) { AddTypeToArrayInitializerIfMissing(variable); - + string oldVariableName = variable.Name; - variable.Name = "self._" + variable.Name; + if (IsPrivate(field)) + variable.Name = "self._" + variable.Name; + else + variable.Name = "self." + variable.Name; VisitVariableDeclaration(variable, null); variable.Name = oldVariableName; } } } - + /// + /// Creates a field initialization statement. + /// + object CreateStaticFieldInitialization(FieldDeclaration field, object value) + { + if (!field.TypeReference.IsNull && (field.Modifier & (Modifiers.Const | Modifiers.Static)) == 0) + return value; + + foreach (VariableDeclaration variable in field.Fields) { + // Ignore field if it has no initializer. + if (FieldHasInitialValue(variable)) { + AddTypeToArrayInitializerIfMissing(variable); + VisitVariableDeclaration(variable, null); + value = variable; + } else { + var line = variable.Name + " = "; + if (variable.TypeReference.IsNull && value != null) { + var declaration = value as VariableDeclaration; + if (declaration != null) { + line += declaration.Name; + line += " + 1"; + } else { + line += value.ToString(); + } + value = variable; + } else if (variable.TypeReference.IsArrayType || !variable.TypeReference.Type.StartsWith("System.")) { + line += "None"; + } else { + Type type = Type.GetType(variable.TypeReference.Type, false, true); + if (type != null && type.IsValueType) { + //object default_value = Activator.CreateInstance(type); + line += GetTypeName(variable.TypeReference) + "()"; + value = variable; + } else { + line += "None"; + } + } + AppendIndentedLine(line); + } + } + return value; + } + + bool IsValueType(TypeReference typeRef) + { + if (typeRef.IsArrayType) + return true; + if (!typeRef.Type.StartsWith("System.")) + return false; + Type type = Type.GetType(typeRef.Type, false, true); + return (type != null && type.IsValueType); + } + + /// + /// Creates a property initialization statement. + /// + void CreatePropertyInitialization(PropertyDeclaration property) + { + + if (property.Initializer == null || property.Initializer.IsNull) { + AppendIndented("self._" + property.Name.ToLower() + " = "); + if (property.TypeReference.IsArrayType || !property.TypeReference.Type.StartsWith("System.")) { + Append("None"); + } else { + Type type = Type.GetType(property.TypeReference.Type, false, true); + if (type != null && type.IsValueType) { + //object default_value = Activator.CreateInstance(type); + Append(GetTypeName(property.TypeReference)); + Append("()"); + } else { + Append("None"); + } + } + AppendLine(); + } else { + AppendIndented("self._" + property.Name.ToLower() + " = "); + property.Initializer.AcceptVisitor(this, null); + AppendLine(); + } + } + void AddTypeToArrayInitializerIfMissing(VariableDeclaration variable) { ArrayCreateExpression arrayCreate = variable.Initializer as ArrayCreateExpression; @@ -1751,7 +2561,7 @@ namespace ICSharpCode.PythonBinding arrayCreate.CreateType = variable.TypeReference; } } - + bool IsArrayMissingTypeToCreate(ArrayCreateExpression arrayCreate) { if (arrayCreate != null) { @@ -1759,7 +2569,7 @@ namespace ICSharpCode.PythonBinding } return false; } - + /// /// Adds the method or constructor parameters. /// @@ -1772,10 +2582,19 @@ namespace ICSharpCode.PythonBinding Append("self, "); } for (int i = 0; i < parameters.Count; ++i) { + var param = parameters[i]; + if (param.ParamModifier == ParameterModifiers.Out + || param.ParamModifier == ParameterModifiers.Ref) { + // track both ref and output for return statement + methodHasOutputParameters = true; + // drop output parameters from declarations + if (param.ParamModifier == ParameterModifiers.Out) + continue; + } if (i > 0) { Append(", "); } - Append(parameters[i].ParameterName); + Append(param.ParameterName); } } else { if (!IsStatic(method)) { @@ -1784,21 +2603,32 @@ namespace ICSharpCode.PythonBinding } Append("):"); } - - bool IsStatic(ParametrizedNode method) + + bool IsStatic(AttributedNode method) { - return (method.Modifier & Modifiers.Static) == Modifiers.Static; + return method == null || (method.Modifier & (Modifiers.Const | Modifiers.Static)) != 0; } - + + bool IsPrivate(AttributedNode node) + { + return node != null && (((node.Modifier & (Modifiers.Private)) == Modifiers.Private) + || ((node.Modifier & (Modifiers.Public | Modifiers.Internal)) == 0)); + } + /// /// Creates assignments of the form: /// i = 1 /// object CreateSimpleAssignment(AssignmentExpression assignmentExpression, string op, object data) { - assignmentExpression.Left.AcceptVisitor(this, data); - Append(" " + op + " "); - assignmentExpression.Right.AcceptVisitor(this, data); + if (accumulatePostfixStatements && !(assignmentExpression.Parent is Statement)) { + assignmentExpression.Left.AcceptVisitor(this, null); + postfixStatements.Add(assignmentExpression); + } else { + assignmentExpression.Left.AcceptVisitor(this, data); + Append(" " + op + " "); + assignmentExpression.Right.AcceptVisitor(this, data); + } return null; } @@ -1813,7 +2643,7 @@ namespace ICSharpCode.PythonBinding expression.AcceptVisitor(this, null); return null; } - + /// /// Converts a switch case statement to an if/elif/else in Python. /// @@ -1843,11 +2673,11 @@ namespace ICSharpCode.PythonBinding } firstLabel = false; } - + Append(":"); AppendLine(); } - + /// /// Creates the switch test condition /// @@ -1859,7 +2689,7 @@ namespace ICSharpCode.PythonBinding Append(" == "); label.Label.AcceptVisitor(this, null); } - + /// /// Creates the statements inside a switch case statement. /// @@ -1880,7 +2710,7 @@ namespace ICSharpCode.PythonBinding AppendIndentedLine("pass"); } } - + /// /// Gets the supported language either C# or VB.NET /// @@ -1892,7 +2722,7 @@ namespace ICSharpCode.PythonBinding } return SupportedLanguage.CSharp; } - + /// /// Saves the method declaration if it is a main entry point. /// @@ -1902,7 +2732,7 @@ namespace ICSharpCode.PythonBinding entryPointMethods.Add(method); } } - + /// /// Returns true if the object being created is a generic. /// @@ -1910,7 +2740,7 @@ namespace ICSharpCode.PythonBinding { return expression.CreateType.GenericTypes.Count > 0; } - + /// /// Appends the types used when creating a generic surrounded by square brackets. /// @@ -1924,14 +2754,14 @@ namespace ICSharpCode.PythonBinding } TypeReference typeRef = typeRefs[i]; if (typeRef.IsArrayType) { - Append("Array[" + GetTypeName(typeRef) + "]"); + Append(GetTypeName("System.Array") + "[" + GetTypeName(typeRef) + "]"); } else { Append(GetTypeName(typeRef)); } } Append("]"); } - + /// /// If the type is String or Int32 then it returns "str" and "int". /// @@ -1942,6 +2772,14 @@ namespace ICSharpCode.PythonBinding string GetTypeName(TypeReference typeRef) { string name = typeRef.Type; + + if (typeNameMap.ContainsKey(name)) { + return typeNameMap[name]; + } + foreach (string library in importLibraries) { + if (name.StartsWith(library)) + return name.Substring(library.Length); + } if (name == typeof(String).FullName) { return "str"; } else if ((name == typeof(int).FullName) || ((name == typeof(int).Name))) { @@ -1955,19 +2793,46 @@ namespace ICSharpCode.PythonBinding } return name; } - + + /// + /// Converts name to appropriately scoped version based on imports + /// + /// If the type is a keyword (e.g. uint) then the TypeRef.Type returns + /// the full type name. It returns the short type name if the type is not a keyword. So + /// this method will strip the namespace from the name. + /// + private string GetTypeName(string name) + { + if (typeNameMap.ContainsKey(name)) { + return typeNameMap[name]; + } + foreach (string library in importLibraries) { + if (name.StartsWith(library)) + return name.Substring(library.Length); + } + return name; + } + /// /// Gets the type name that defines the method. /// - static string GetTypeName(MethodDeclaration methodDeclaration) + string GetTypeName(MethodDeclaration methodDeclaration) { TypeDeclaration type = methodDeclaration.Parent as TypeDeclaration; - return type.Name; + string name = type.Name; + if (typeNameMap.ContainsKey(name)) { + return typeNameMap[name]; + } + foreach (string library in importLibraries) { + if (name.StartsWith(library)) + return name.Substring(library.Length); + } + return name; } - + void AppendMultilineComment(Comment comment) { - string[] lines = comment.CommentText.Split(new char[] {'\n'}); + string[] lines = comment.CommentText.Split(new char[] { '\n' }); for (int i = 0; i < lines.Length; ++i) { string line = "# " + lines[i].Trim(); if ((i == 0) && !comment.CommentStartsLine) { @@ -1986,7 +2851,7 @@ namespace ICSharpCode.PythonBinding codeBuilder.AppendToPreviousLine(" #" + comment.CommentText); } } - + void AppendDocstring(List xmlDocComments) { if (xmlDocComments.Count > 1) { @@ -2007,7 +2872,7 @@ namespace ICSharpCode.PythonBinding AppendIndentedLine(Docstring + xmlDocComments[0].CommentText + Docstring); } } - + /// /// Returns true if the node is a type declaration or a method since these can have /// python docstrings. @@ -2016,19 +2881,19 @@ namespace ICSharpCode.PythonBinding { return (node is TypeDeclaration) || (node is MethodDeclaration) || (node is ConstructorDeclaration); } - + void AppendPropertyDecorator(PropertyDeclaration propertyDeclaration) { string propertyName = propertyDeclaration.Name; AppendIndented(propertyName); Append(" = property("); - + bool addedParameter = false; if (propertyDeclaration.HasGetRegion) { Append("fget=get_" + propertyName); addedParameter = true; } - + if (propertyDeclaration.HasSetRegion) { if (addedParameter) { Append(", "); @@ -2038,7 +2903,7 @@ namespace ICSharpCode.PythonBinding Append(")"); AppendLine(); } - + void AppendBaseTypes(List baseTypes) { Append("("); @@ -2047,6 +2912,8 @@ namespace ICSharpCode.PythonBinding } else { for (int i = 0; i < baseTypes.Count; ++i) { TypeReference typeRef = baseTypes[i]; + if (IsValueType(typeRef)) // fix for enumerations + continue; if (i > 0) { Append(", "); } @@ -2055,5 +2922,22 @@ namespace ICSharpCode.PythonBinding } Append("):"); } + + /// + /// Generate the PostFix increment statements + /// + /// list of statements to write + private void CreatePostfixStatements(IEnumerable list) + { + bool oldAccumulatePostfixStatements = accumulatePostfixStatements; + accumulatePostfixStatements = false; + foreach (var expression in list) { + AppendIndented(String.Empty); + expression.AcceptVisitor(this, null); + AppendLine(); + } + accumulatePostfixStatements = oldAccumulatePostfixStatements; + } + } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PrefixExpressionExtractor.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PrefixExpressionExtractor.cs new file mode 100644 index 0000000000..e9f360e512 --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PrefixExpressionExtractor.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; + +namespace ICSharpCode.PythonBinding +{ + /// + /// This Vistor is used to locate Prefix expressions and insert them + /// in statement block prior to owned statement. This is probably expensive + /// but without caching on statement writes no easy way to do it. + /// May be faster to do once per Method to see if any Prefix exist at all + /// and then disable checks while in that method + /// + internal class PrefixExpressionExtractor : NodeTrackingAstVisitor + { + readonly List statements = new List(); + private int statementRecursion; + public List Statements + { + get { return statements; } + } + + public void Reset() + { + statementRecursion = 0; + } + + protected override void BeginVisit(INode node) + { + if (node is Statement) + statementRecursion++; + base.BeginVisit(node); + } + protected override void EndVisit(INode node) + { + if (node is Statement) + statementRecursion--; + base.EndVisit(node); + } + public override object TrackedVisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) + { + // only accumulate if current statement is active. And also not immediate parent + if (statementRecursion == 1 && !(unaryOperatorExpression.Parent is Statement)) { + switch (unaryOperatorExpression.Op) { + case UnaryOperatorType.Increment: + case UnaryOperatorType.Decrement: + statements.Add(unaryOperatorExpression); + break; + } + } + return base.TrackedVisitUnaryOperatorExpression(unaryOperatorExpression, data); + } + public override object TrackedVisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) + { + if (statementRecursion == 1 && !(anonymousMethodExpression.Parent is Statement)) { + statements.Add(anonymousMethodExpression); + } + return null; + } + public override object TrackedVisitLambdaExpression(LambdaExpression lambdaExpression, object data) + { + if (statementRecursion == 1 && !(lambdaExpression.Parent is Statement)) { + if (!lambdaExpression.StatementBody.IsNull) + statements.Add(lambdaExpression); + } + return base.TrackedVisitLambdaExpression(lambdaExpression, data); + } + } +} \ No newline at end of file