diff --git a/src/ExpressiveAnnotations.Tests/ParserTest.cs b/src/ExpressiveAnnotations.Tests/ParserTest.cs index 3935ce5..6cf5b24 100644 --- a/src/ExpressiveAnnotations.Tests/ParserTest.cs +++ b/src/ExpressiveAnnotations.Tests/ParserTest.cs @@ -343,7 +343,8 @@ public void verify_logic_without_context() Assert.True(parser.Parse("false ? false : true").Invoke()); Assert.True(parser.Parse("(true ? 1 : 2) == 1").Invoke()); - Assert.True(parser.Parse("(false ? 1 : 2) == 2").Invoke()); + Assert.True(parser.Parse("(false ? 1 : 2.2) == 2.2").Invoke()); + Assert.True(parser.Parse("(true ? null : null) == null").Invoke()); Assert.True(parser.Parse("(1 > 0 ? true : false) ? (1 > 0 ? true : false) : (false ? false : false)").Invoke()); Assert.True(parser.Parse("(1 > 0 ? false : true) ? (false ? false : false) : (1 > 0 ? true : false)").Invoke()); @@ -580,6 +581,8 @@ public void verify_logic_with_context() Assert.Equal(expectedMethods.Length, parsedMethods.Count); Assert.True(!expectedMethods.Except(parsedMethods).Any()); + Assert.True(parser.Parse("(true ? Number : -1.1) == 0").Invoke(model)); + Assert.True(parser.Parse("Array[0] != null && Array[1] != null").Invoke(model)); Assert.True(parser.Parse("Array[0].Number + Array[0].Array[0].Number + Array[1].Number + Array[1].Array[0].Number == 0").Invoke(model)); @@ -1561,10 +1564,22 @@ public void verify_various_parsing_errors() Assert.Equal("Expected subproperty identifier. Unexpected end of expression.", e.Error); Assert.Equal(new Location(1, 6), e.Location, new LocationComparer()); - e = Assert.Throws(() => parser.Parse("[0][1 > 0 ? 0*2.0 : 0^2] == 1")); - Assert.Equal("Argument types must match.", e.Error); + e = Assert.Throws(() => parser.Parse("[0][1 > 0 ? 0*2.0 : ''] == 1")); + Assert.Equal("Type of conditional expression cannot be determined because there is no implicit conversion between 'System.Double' and 'System.String'.", e.Error); + Assert.Equal(new Location(1, 11), e.Location, new LocationComparer()); + + e = Assert.Throws(() => parser.Parse("[0][1 > 0 ? '' : 0*2.0] == 1")); + Assert.Equal("Type of conditional expression cannot be determined because there is no implicit conversion between 'System.String' and 'System.Double'.", e.Error); Assert.Equal(new Location(1, 11), e.Location, new LocationComparer()); + e = Assert.Throws(() => parser.Parse("true ? null : 1")); + Assert.Equal("Type of conditional expression cannot be determined because there is no implicit conversion between 'null' and 'System.Int32'.", e.Error); + Assert.Equal(new Location(1, 6), e.Location, new LocationComparer()); + + e = Assert.Throws(() => parser.Parse("true ? 1 : null")); + Assert.Equal("Type of conditional expression cannot be determined because there is no implicit conversion between 'System.Int32' and 'null'.", e.Error); + Assert.Equal(new Location(1, 6), e.Location, new LocationComparer()); + e = Assert.Throws(() => parser.Parse("Collection[true ? 0 : 1].Collection[true ? [0][0] : 1].Unknown == -2")); Assert.Equal("Only public properties, constants and enums are accepted. Identifier 'Unknown' not known.", e.Error); Assert.Equal(new Location(1, 56), e.Location, new LocationComparer()); diff --git a/src/ExpressiveAnnotations/Analysis/Expr.cs b/src/ExpressiveAnnotations/Analysis/Expr.cs index f6a53e1..d387a59 100644 --- a/src/ExpressiveAnnotations/Analysis/Expr.cs +++ b/src/ExpressiveAnnotations/Analysis/Expr.cs @@ -259,7 +259,7 @@ public Expression Multiply(Expression arg1, Expression arg2, Token oper) var type1 = arg1.Type; var type2 = arg2.Type; TypeAdapter.MakeTypesCompatible(arg1, arg2, out arg1, out arg2, oper.Type); - Wall.Mul(arg1, arg2, type1, type2, oper); + Wall.Mul(arg1, arg2, type1, type2, oper); try { @@ -364,7 +364,11 @@ public Expression OnesComplement(Expression arg, Token oper) public Expression Condition(Expression arg1, Expression arg2, Expression arg3, Token start, Token oper) { Wall.OfType(arg1, start.Location); - Wall.TypesMatch(arg2, arg3, oper.Location); + + var type2 = arg2.Type; + var type3 = arg3.Type; + TypeAdapter.MakeTypesCompatible(arg2, arg3, out arg2, out arg3, oper.Type); + Wall.Cond(arg2, arg3, type2, type3, oper.Location); return Expression.Condition(arg1, arg2, arg3); } diff --git a/src/ExpressiveAnnotations/Analysis/TypeWall.cs b/src/ExpressiveAnnotations/Analysis/TypeWall.cs index d0bfc72..9afad2b 100644 --- a/src/ExpressiveAnnotations/Analysis/TypeWall.cs +++ b/src/ExpressiveAnnotations/Analysis/TypeWall.cs @@ -91,11 +91,17 @@ public void Unary(Expression arg, Token oper) $"Operator '{oper.Value}' cannot be applied to operand of type 'null'.", ExprString, oper.Location); } - public void TypesMatch(Expression arg1, Expression arg2, Location pos) + public void Cond(Expression arg1, Expression arg2, Type type1, Type type2, Location pos) { + if (arg1.IsNullLiteral() && !arg2.IsNullLiteral()) + throw new ParseErrorException( + $"Type of conditional expression cannot be determined because there is no implicit conversion between 'null' and '{type2}'.", ExprString, pos); + if (!arg1.IsNullLiteral() && arg2.IsNullLiteral()) + throw new ParseErrorException( + $"Type of conditional expression cannot be determined because there is no implicit conversion between '{type1}' and 'null'.", ExprString, pos); if (arg1.Type != arg2.Type) throw new ParseErrorException( - "Argument types must match.", ExprString, pos); + $"Type of conditional expression cannot be determined because there is no implicit conversion between '{type1}' and '{type2}'.", ExprString, pos); } public void OfType(Expression arg, Location pos) diff --git a/src/ExpressiveAnnotations/Properties/AssemblyInfo.cs b/src/ExpressiveAnnotations/Properties/AssemblyInfo.cs index 3078548..9b5d8e8 100644 --- a/src/ExpressiveAnnotations/Properties/AssemblyInfo.cs +++ b/src/ExpressiveAnnotations/Properties/AssemblyInfo.cs @@ -42,5 +42,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.7.3.0")] -[assembly: AssemblyFileVersion("2.7.3.0")] +[assembly: AssemblyVersion("2.7.4.0")] +[assembly: AssemblyFileVersion("2.7.4.0")]