- The hard-wired anchor definition computations.
- Image loading.
- The time computation for the steps (the two variants of the @ feature).
- Producing different variables when using the underscore _.
- Correct disambiguation in the rule with backtrack=true.
A first test for numbers might look like this:
[Test]
public void Test_number() {
Assert.AreEqual(1.0, GetParser("1").number(), 1e-10);
Assert.AreEqual(1.0, GetParser("1.").number(), 1e-10);
Assert.AreEqual(10.0, GetParser("1.E1").number(), 1e-10);
Assert.AreEqual(0.01, GetParser("1.E-2").number(), 1e-10);
Assert.AreEqual(10.1, GetParser("1.01E1").number(), 1e-10);
Assert.AreEqual(0.010001, GetParser("1.0001E-2").number(), 1e-10);
}
public void Test_number() {
Assert.AreEqual(1.0, GetParser("1").number(), 1e-10);
Assert.AreEqual(1.0, GetParser("1.").number(), 1e-10);
Assert.AreEqual(10.0, GetParser("1.E1").number(), 1e-10);
Assert.AreEqual(0.01, GetParser("1.E-2").number(), 1e-10);
Assert.AreEqual(10.1, GetParser("1.01E1").number(), 1e-10);
Assert.AreEqual(0.010001, GetParser("1.0001E-2").number(), 1e-10);
}
Then, a test might just parse all sorts of scalar expressions:
[Test]
public void TestJustParse_scalarexpr() {
GetParser("a+b").scalarexpr();
GetParser("a+b*c").scalarexpr();
...public void TestJustParse_scalarexpr() {
GetParser("a+b").scalarexpr();
GetParser("a+b*c").scalarexpr();
}
However, this test will not work - it throws an exception about an "unexpected EOF". Why? Well, the grammar knows that scalar expressions can never be at the very end of an input file. So, it treats an EOF right after a scalar expression as an error. We can repair this by providing enough following symbols for the LL parser so that it remains happy. In our case, a semicolon suffices, as each constraint - which might have a scalar expressions at the end - is terminated by that symbol. So, the following actually tests parsing of scalar expressions:
[Test]
public void TestJustParse_scalarexpr() {
GetParser("a+b ;").scalarexpr();
GetParser("a+b*c ;").scalarexpr();
GetParser("(a-b)*c ;").scalarexpr();
GetParser("a+b*-c*d ;").scalarexpr();
GetParser("(a-b*c)/-(-d) ;").scalarexpr();
GetParser(".i(a+2*b) ;").scalarexpr();
GetParser("(3+.d(-a-b*2))-d ;").scalarexpr();
GetParser("-.a([0,1],[1,1]) ;").scalarexpr();
GetParser("_+_ ;").scalarexpr();
GetParser("3*.t ;").scalarexpr();
GetParser("(.iv-.t)/2 ;").scalarexpr();
}
public void TestJustParse_scalarexpr() {
GetParser("a+b ;").scalarexpr();
GetParser("a+b*c ;").scalarexpr();
GetParser("(a-b)*c ;").scalarexpr();
GetParser("a+b*-c*d ;").scalarexpr();
GetParser("(a-b*c)/-(-d) ;").scalarexpr();
GetParser(".i(a+2*b) ;").scalarexpr();
GetParser("(3+.d(-a-b*2))-d ;").scalarexpr();
GetParser("-.a([0,1],[1,1]) ;").scalarexpr();
GetParser("_+_ ;").scalarexpr();
GetParser("3*.t ;").scalarexpr();
GetParser("(.iv-.t)/2 ;").scalarexpr();
}
Next, we have to check that the correct model is returned - and here we get a problem: How do we compare two models? There are two possibilities: We can either compare each property, or we implement an Equals method in each class. As we might want to compare model parts also later (e.g. to find out that a constraint is superfluous; or for constant folding), I opt for the Equals methods. So each expression (at least) now gets three methods - here is an example for class BinaryScalarExpr:
public bool Equals(BinaryScalarExpr obj) {
return Equals((object)obj);
}
public override bool Equals(object obj) {
BinaryScalarExpr o = obj as BinaryScalarExpr;
return o != null
&& o._lhs.Equals(_lhs)
&& o._operator.Equals(_operator)
&& o._rhs.Equals(_rhs);
}
public override int GetHashCode() {
return _lhs.GetHashCode() + (int)_operator + _rhs.GetHashCode();
}
return Equals((object)obj);
}
public override bool Equals(object obj) {
BinaryScalarExpr o = obj as BinaryScalarExpr;
return o != null
&& o._lhs.Equals(_lhs)
&& o._operator.Equals(_operator)
&& o._rhs.Equals(_rhs);
}
public override int GetHashCode() {
return _lhs.GetHashCode() + (int)_operator + _rhs.GetHashCode();
}
The tests I write look like this. They are a sort of mixture between single-purpose tests (where you would put only exactly one operator or the like into an expression) and integrating tests - and are quite boring to write ...
ScalarExpr x = CreateParserForSyntacticTests("a+b*-c/d ;").scalarexpr();
Assert.AreEqual(
new BinaryScalarExpr(new ScalarVariable("a"),
BinaryScalarOperator.PLUS,
new BinaryScalarExpr(
new BinaryScalarExpr(new ScalarVariable("b"),
BinaryScalarOperator.TIMES,
new UnaryScalarExpr(UnaryScalarOperator.MINUS,
new ScalarVariable("c")
)
),
BinaryScalarOperator.DIVIDE,
new ScalarVariable("d")
)
), x);
Assert.AreEqual(
new BinaryScalarExpr(new ScalarVariable("a"),
BinaryScalarOperator.PLUS,
new BinaryScalarExpr(
new BinaryScalarExpr(new ScalarVariable("b"),
BinaryScalarOperator.TIMES,
new UnaryScalarExpr(UnaryScalarOperator.MINUS,
new ScalarVariable("c")
)
),
BinaryScalarOperator.DIVIDE,
new ScalarVariable("d")
)
), x);
I wrote about 20 of them up to now - that should suffice for my enterprise. If I find errors, I'll add more. Tests for the five specific items mentioned at the beginning are still missing, I confess.
Remark: During testing I found - I think - some quite serious restrictions in my grammar. For example, I cannot write 2*[0,1] for a multiplication of a scalar and a vector, much less a*[b,c]. I'll leave it at that right now:
- Either one can work around the restrictions - this is an animation program, not a generic mathematical equation solver;
- or I introduce the {...} braces for vector expressions (if that helps);
- or I find some other clever way ...
No comments:
Post a Comment