-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathTest.js
181 lines (167 loc) · 7.46 KB
/
Test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Test.js
// (c) 2007 B. Crowell and M. Khafateh, GPL 2 license
//
// This file provides a constructor, com.lightandmatter.Test.
//
// to do:
// fix the ones that actually fail
// After implementing lc_series operator, use it in tests to make sure specific coefficients of series expansions come out right.
var com;
if (!com) {com = {};}
if (!com.lightandmatter) {com.lightandmatter = {};}
com.lightandmatter.Test =
function (output_element,lexer,parser) {
// testing input
var testing_lines = [
// In the following, we can have:
// [string,native] ... test that computing string gives an output that's equal to the native javascript typpe (number or boolean)
// [string,string] ... test that the two strings evaluate to the same result
// [string] ... test that the computation doesn't result in null or undefined
// [string,null] ... tests that the computation does result in null or NaN
// [] ... do nothing (placeholder for end of list, to avoid forgetting commas)
// The tolerance for comparisons is set by an optional third argument, eps.
// The magnitude of the difference between the results should be no more than eps.
// Eps defaults to 10^-12.
["2+2",4],
["1<3",true],
["1>3",false],
["2d",null],
["1+d"],
["1/d"],
["d+d","2*d"],
["d<0",false],
["d<-1",false],
["-d>-1",true],
["d<10^-10",true],
["d^2<d",true],
["sqrt d > d",true],
["2*d>d",true],
["a=6*7;a+5",47], // semicolon operator and side-effects
["zzz=1;zzz==1",true], // nopromote flag
["f x=x^2;f(f(2))",16], // composition of functions
["sqrt(-1)","i"],
["((1+i)/(sqrt 2))^8",1],
["(1,2,3)==(1,2,3)",true],
["(1,2,3)==(1,2,4)",false],
["((1,2),(3,4))==((1,2),(3,4))",true],
["((1,2),3)==(1,2,3)",false], // closed_array, parens have extra significance beyond grouping in the case of arrays
["array[1/(1-d)]==[[0,1],[1,1],[2,1],[3,1],[4,1]]",true],
["array(exp(d))==[[0,1],[1,1],[2,0.5],[3,0.16666666666666666],[4,0.041666666666666664]]",true],
["array cos sin cos sin d==[[0,0.6663669686453381],[2,0.20143030141979187],[4,-0.029816455545224607],[6,-0.05417070055498075],[8,0.047167710820525295]]",true],
["(1+d)^pi"],
["d^pi",null],
["[sqrt(d+d^2)]^2","d+d^2"],
// "foo",
// "2d", // the parser doesn't return anything for this line
[] // end of list
];
function html_armor(s) {
if (typeof(s)!=='string') {return s;}
return s.replace(new RegExp('<',"g"),'<');
}
function unstring_if_possible(s) {
if (typeof(s)!=='string') {return s;}
if (s=='0') {return 0;} // In some browsers, parseFloat() returns 0 on error, so eliminate that case.
// parseFloat ignores trailing stuff that it can't parse, so check for that:
if (s.match(/[di\(\)]/)) {return s;}
var n = parseFloat(s);
if (n===0 || isNaN(n)) {n= s;}
return n;
}
function do_parse(lexer,parser,line) {
lexer.change_text(line);
parser.parse(lexer.tokens,lexer.props);
var result = "";
var parser_errors = "";
try {
//result = parser.toString();
result = parser.tree_to_string(parser.tree);
}
catch (e) {
parser_errors += e;
}
return [result,parser_errors];
}
// this variable hold the input and output for the whole session.
// I tried to make it work like the private variable "terminal" in Terminal.js
// whatever is in debug2 will be overwritten. It could easily be changed to append
// results to the debug2 div.
var testing_output = "";
var nn = com.lightandmatter.Num;
var n_passed = 0;
var n_tried = 0;
for (var i in testing_lines) {
test = testing_lines[i];
if (test.length>0) {
n_tried += 1;
line = test[0];
var x,y,rx,ry;
x = do_parse(lexer,parser,line);
rx = x[0];
rx = unstring_if_possible(rx);
var parser_errors = x[1];
var unequal = false;
var diff;
ry = null;
if (test.length>=2) { // comparing against a second expression
var eps = 1e-12; // tolerance for comparisons, see explanation above
if (test.length>=3) {eps=test[2];}
if (typeof(test[1])=='string') {
y = do_parse(lexer,parser,test[1]);
ry = y[0];
parser_errors += y[1];
ry = unstring_if_possible(ry);
if (typeof(rx)=='string' || typeof(ry)=='string') {
unequal = rx.toString() != ry.toString();
}
else {
diff = com.lightandmatter.Num.binop('-',rx,ry);
//document.getElementById("debug").innerHTML += 'diff='+diff+nn.num_type(diff);
if (typeof(diff)=='number') {diff=Math.abs(diff);} else {diff=diff.abs();}
unequal = nn.binop('>',diff,eps);
}
}
if (test[1]===null) {
unequal = rx!==null && !(typeof(rx)=='number' && isNaN(rx));
}
if (typeof(test[1])=='boolean') {
unequal = (rx!=test[1]);
}
if (typeof(test[1])=='number') {
ry = test[1];
if (typeof(test[1])=='number') {
if (rx!==null && ry!==null) {
diff = com.lightandmatter.Num.binop('-',rx,ry);
if (typeof(diff)=='number') {diff=Math.abs(diff);} else {diff=diff.abs();}
unequal = nn.binop('>',diff,eps);
}
else {
unequal = true;
}
}
}
}
// TODO - add style
testing_output += "testing " + html_armor(line);
if (test.length>=2) {testing_output += ' = '+html_armor(test[1]);}
if (unequal) {
testing_output += "<br/>Unequal expressions, "+rx+" and "+ry+", types "+
typeof(rx)+','+typeof(ry)+
"**************** fail *******************<br/>";
}
else {
testing_output += '...pass -- ';
n_passed += 1;
}
//testing_output += "<br/>";
if (parser_errors) {
testing_output += "Parser Exception: " + parser_errors + "<br/>";
}
testing_output += rx;
if (test.length>=2 && typeof(test[1])!='boolean') {testing_output += ' = '+ry;}
testing_output += "<br/>";
output_element.innerHTML = testing_output;
}
}
return [n_passed,n_tried];
};