Skip to content

Commit

Permalink
Version up + yet another detection improvements regarding types consi…
Browse files Browse the repository at this point in the history
…stency with small refactoring.
  • Loading branch information
jwaliszko committed Jan 5, 2015
1 parent f400378 commit 8f462a3
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,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.2.4.0")]
[assembly: AssemblyFileVersion("2.2.4.0")]
[assembly: AssemblyVersion("2.2.5.0")]
[assembly: AssemblyFileVersion("2.2.5.0")]
186 changes: 186 additions & 0 deletions src/ExpressiveAnnotations.Tests/ParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,96 @@ public void verify_various_parsing_errors()
e.Message);
}

try
{
parser.Parse<object>("0 + null").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 3:
... + null ...
^--- Operator '+' cannot be applied to operands of type 'System.Int32' and 'null'.",
e.Message);
}

try
{
parser.Parse<object>("0 / null").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 3:
... / null ...
^--- Operator '/' cannot be applied to operands of type 'System.Int32' and 'null'.",
e.Message);
}

try
{
parser.Parse<object>("true && null").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... && null ...
^--- Operator '&&' cannot be applied to operands of type 'System.Boolean' and 'null'.",
e.Message);
}

try
{
parser.Parse<object>("true || null").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... || null ...
^--- Operator '||' cannot be applied to operands of type 'System.Boolean' and 'null'.",
e.Message);
}

try
{
parser.Parse<object>("!null").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 1:
... !null ...
^--- Operator '!' cannot be applied to operand of type 'null'.",
e.Message);
}

try
{
parser.Parse<object>("'abc' * 'abc'").Invoke(null);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 7:
... * 'abc' ...
^--- Operator '*' cannot be applied to operands of type 'System.String' and 'System.String'.",
e.Message);
}

try
{
parser.Parse<object>("1 + 2 + 'abc' - 'abc' > 0").Invoke(null);
Expand Down Expand Up @@ -971,6 +1061,22 @@ ... Max(1.1, 1.2, 'a') ...
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("Date + Date != null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... + Date != null ...
^--- Operator '+' cannot be applied to operands of type 'System.DateTime' and 'System.DateTime'.",
e.Message);
}

try
{
var model = new Model();
Expand Down Expand Up @@ -1130,6 +1236,86 @@ ... Max(1.1, 1.2, 'a') ...
^--- Operator '>' cannot be applied to operands of type 'System.Object' and 'System.Object'.",
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("null > null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... > null ...
^--- Operator '>' cannot be applied to operands of type 'null' and 'null'.",
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("null + null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... + null ...
^--- Operator '+' cannot be applied to operands of type 'null' and 'null'.",
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("null / null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... / null ...
^--- Operator '/' cannot be applied to operands of type 'null' and 'null'.",
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("null && null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... && null ...
^--- Operator '&&' cannot be applied to operands of type 'null' and 'null'.",
e.Message);
}

try
{
var model = new Model();
parser.Parse<Model>("null || null").Invoke(model);
Assert.Fail();
}
catch (Exception e)
{
Assert.IsTrue(e is InvalidOperationException);
Assert.AreEqual(
@"Parse error on line 1, column 6:
... || null ...
^--- Operator '||' cannot be applied to operands of type 'null' and 'null'.",
e.Message);
}
}

private class Model
Expand Down
44 changes: 34 additions & 10 deletions src/ExpressiveAnnotations/Analysis/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ private Expression ParseOrExp()
ReadToken();
var arg2 = ParseOrExp();

AssertArgsNotNull(arg1, arg2, oper);
if (!arg1.Type.IsBool() || !arg2.Type.IsBool())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'.", oper.Value, arg1.Type, arg2.Type),
Expand All @@ -338,6 +339,7 @@ private Expression ParseAndExp()
ReadToken();
var arg2 = ParseAndExp();

AssertArgsNotNull(arg1, arg2, oper);
if (!arg1.Type.IsBool() || !arg2.Type.IsBool())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'.", oper.Value, arg1.Type, arg2.Type),
Expand All @@ -357,14 +359,7 @@ private Expression ParseRelExp()

if (oper.Type != TokenType.EQ && oper.Type != TokenType.NEQ)
{
if (!arg1.IsNull() && arg2.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and 'null'.", oper.Value, arg1.Type),
oper.Location);
if (arg1.IsNull() && !arg2.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type 'null' and '{1}'.", oper.Value, arg1.Type),
oper.Location);
AssertArgsNotNull(arg1, arg2, oper);
if (!(arg1.Type.IsNumeric() && arg2.Type.IsNumeric()) && !(arg1.Type.IsDateTime() && arg2.Type.IsDateTime()))
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'.", oper.Value, arg1.Type, arg2.Type),
Expand Down Expand Up @@ -412,6 +407,10 @@ private Expression ParseNotExp()
ReadToken();
var arg = ParseNotExp(); // allow multiple negations

if (arg.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operand of type 'null'.", oper.Value),
oper.Location);
if (!arg.Type.IsBool())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operand of type '{1}'.", oper.Value, arg.Type),
Expand All @@ -435,14 +434,20 @@ private Expression ParseAddExpInternal(Expression arg1)
{
if (!new[] {TokenType.ADD, TokenType.SUB}.Contains(PeekType()))
return arg1;
var oper = PeekToken();
var oper = PeekToken();
ReadToken();
var sign = UnifySign();
var arg2 = ParseMulExp();

if (sign == TokenType.SUB)
arg2 = InverseNumber(arg2);

if(!arg1.Type.IsString() && !arg2.Type.IsString())
AssertArgsNotNull(arg1, arg2, oper);
if ((!arg1.Type.IsString() && !arg1.Type.IsNumeric() && !arg1.IsNull()) || (!arg2.Type.IsString() && !arg2.Type.IsNumeric() && !arg2.IsNull()))
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'.", oper.Value, arg1.Type, arg2.Type),
oper.Location);
if (oper.Type == TokenType.SUB)
if (arg1.Type.IsString() || arg2.Type.IsString())
throw new ParseErrorException(
Expand All @@ -454,7 +459,7 @@ private Expression ParseAddExpInternal(Expression arg1)
{
case TokenType.ADD:
return ParseAddExpInternal(
(arg1.Type == typeof (string) || arg2.Type == typeof (string))
(arg1.Type.IsString() || arg2.Type.IsString())
? Expression.Add(
Expression.Convert(arg1, typeof (object)),
Expression.Convert(arg2, typeof (object)),
Expand Down Expand Up @@ -491,9 +496,12 @@ private Expression ParseMulExpInternal(Expression arg1)
arg2 = InverseNumber(arg2);

if (!arg1.Type.IsNumeric() || !arg2.Type.IsNumeric())
{
AssertArgsNotNull(arg1, arg2, oper);
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'.", oper.Value, arg1.Type, arg2.Type),
oper.Location);
}

Helper.MakeTypesCompatible(arg1, arg2, out arg1, out arg2);
switch (oper.Type)
Expand Down Expand Up @@ -811,6 +819,22 @@ private void AssertParamsEquality(int expected, int actual, string funcName)
null);
}

private void AssertArgsNotNull(Expression arg1, Expression arg2, Token oper)
{
if (arg1.IsNull() && arg2.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type 'null' and 'null'.", oper.Value),
oper.Location);
if (!arg1.IsNull() && arg2.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type '{1}' and 'null'.", oper.Value, arg1.Type),
oper.Location);
if (arg1.IsNull() && !arg2.IsNull())
throw new ParseErrorException(
string.Format("Operator '{0}' cannot be applied to operands of type 'null' and '{1}'.", oper.Value, arg2.Type),
oper.Location);
}

private Expression ConvertArgument(Expression arg, Type type, string funcName, int argIdx, Location argPos)
{
try
Expand Down
4 changes: 2 additions & 2 deletions src/ExpressiveAnnotations/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,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.2.7.0")]
[assembly: AssemblyFileVersion("2.2.7.0")]
[assembly: AssemblyVersion("2.2.8.0")]
[assembly: AssemblyFileVersion("2.2.8.0")]

0 comments on commit 8f462a3

Please sign in to comment.