예제 #1
0
        public static void Go(HaxeWriter writer, ExpressionSyntax left, SyntaxToken operatorToken, ExpressionSyntax right)
        {
            //Check for index assignments, for example dictionary[4] = 3;
            if (left is ElementAccessExpressionSyntax && operatorToken.Kind() == SyntaxKind.EqualsToken)
            {
                var elementAccess = (ElementAccessExpressionSyntax)left;

                var typeHaxe = TypeProcessor.ConvertType(Program.GetModel(left).GetTypeInfo(elementAccess.Expression).ConvertedType);

                if (!typeHaxe.StartsWith("Array<"))                 //arrays are the only thing haxe allows assignments into via indexing
                {
                    WriteIndexingExpression(writer, operatorToken, right, elementAccess);
                    return;
                }
            }


            //Check for improper nullable access, unless it's assignment or string concat, which work fine.  Note that if they're trying to add nullable number types, it won't catch it since we can't tell if it's string concatentation or addition.  But haxe will fail to build, so it should still be caught easily enough.
            if (operatorToken.Kind() != SyntaxKind.EqualsToken && operatorToken.Kind() != SyntaxKind.PlusToken)
            {
                var model = Program.GetModel(left);
                Func <ExpressionSyntax, bool> isNullable = e =>
                {
                    var t = model.GetTypeInfo(e).Type;
                    return(t != null && t.Name == "Nullable");
                };

                if (isNullable(left) || isNullable(right))
                {
                    throw new Exception("When using nullable types, you must use the .Value and .HasValue properties instead of the object directly " + Utility.Descriptor(left.Parent));
                }
            }

            if (operatorToken.Kind() == SyntaxKind.PlusEqualsToken || operatorToken.Kind() == SyntaxKind.MinusEqualsToken)
            {
                //Check for event subscription/removal
                var leftSymbol = Program.GetModel(left).GetSymbolInfo(left);
                if (leftSymbol.Symbol is IEventSymbol)
                {
                    Core.Write(writer, left);
                    if (operatorToken.Kind() == SyntaxKind.PlusEqualsToken)
                    {
                        writer.Write(".Add(");
                    }
                    else
                    {
                        writer.Write(".Remove(");
                    }
                    Core.Write(writer, right);
                    writer.Write(")");
                    return;
                }
            }

            if (operatorToken.Kind() == SyntaxKind.AsKeyword)
            {
                var leftStr = Utility.TryGetIdentifier(left);

                if (leftStr == null)
                {
                    throw new Exception("The \"as\" keyword can only be used on simple names.  Declare it as a local variable. " + Utility.Descriptor(left.Parent));
                }

                var typeHaxe = TypeProcessor.ConvertType(right);

                writer.Write("(Std.is(" + leftStr + ", " + typeHaxe + ") ? cast(" + leftStr + ", " + typeHaxe + ") : null)");
            }
            else if (operatorToken.Kind() == SyntaxKind.IsKeyword)
            {
                writer.Write("Std.is(");
                Core.Write(writer, left);
                writer.Write(", ");
                writer.Write(TypeProcessor.RemoveGenericArguments(TypeProcessor.ConvertType(right)));
                writer.Write(")");
            }
            else if (operatorToken.Kind() == SyntaxKind.QuestionQuestionToken)
            {
                writer.Write("Cs2Hx.Coalesce(");
                Core.Write(writer, left);
                writer.Write(", ");
                Core.Write(writer, right);
                writer.Write(")");
            }
            else
            {
                var leftType  = Program.GetModel(left).GetTypeInfo(left);
                var rightType = Program.GetModel(right).GetTypeInfo(right);

                if ((operatorToken.Kind() == SyntaxKind.EqualsEqualsToken || operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken) &&
                    leftType.ConvertedType.SpecialType == SpecialType.System_Boolean &&
                    rightType.ConvertedType.SpecialType == SpecialType.System_Boolean)
                {
                    //Anytime we == or != booleans, we need to take special care when dealing with the js target.  haxe seems to leave some variables as undefined, which works fine as booleans in most comparisons, but not when comparing against each other such as "x == false"
                    if (operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken)
                    {
                        writer.Write("!");
                    }
                    writer.Write("Cs2Hx.BoolCompare(");
                    Core.Write(writer, left);
                    writer.Write(", ");
                    Core.Write(writer, right);
                    writer.Write(")");
                    return;
                }

                Action <ExpressionSyntax, ExpressionSyntax> write = (e, otherSide) =>
                {
                    var type      = Program.GetModel(left).GetTypeInfo(e);
                    var otherType = Program.GetModel(left).GetTypeInfo(otherSide);
                    //Check for enums being converted to strings by string concatenation
                    if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.TypeKind == TypeKind.Enum)
                    {
                        writer.Write(type.Type.ContainingNamespace.FullNameWithDot().ToLower());
                        writer.Write(WriteType.TypeName(type.Type.As <INamedTypeSymbol>()));
                        writer.Write(".ToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.SpecialType == SpecialType.System_Char && otherType.Type.SpecialType != SpecialType.System_Char && otherType.Type.SpecialType != SpecialType.System_Int32)
                    {
                        //Chars are integers in haxe, so when string-concatening them we must convert them to strings
                        writer.Write("Cs2Hx.CharToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && !(e is BinaryExpressionSyntax) && type.Type.SpecialType == SpecialType.System_String && CouldBeNullString(Program.GetModel(e), e))
                    {
                        //In .net, concatenating a null string does not alter the output. However, in haxe's js target, it produces the "null" string. To counter this, we must check non-const strings.
                        writer.Write("system.Cs2Hx.NullCheck(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else
                    {
                        Core.Write(writer, e);
                    }
                };

                write(left, right);
                writer.Write(" ");
                writer.Write(operatorToken.ToString());                 //we can do this since haxe operators work just like C# operators
                writer.Write(" ");
                write(right, left);
            }
        }