UNPKG

21.7 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * Javascript code in this page
4 *
5 * Copyright 2017 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * Javascript code in this page
21 */
22'use strict';
23
24var _function = require('../../core/function');
25
26var _ps_parser = require('../../core/ps_parser');
27
28var _stream = require('../../core/stream');
29
30describe('function', function () {
31 beforeEach(function () {
32 jasmine.addMatchers({
33 toMatchArray: function toMatchArray(util, customEqualityTesters) {
34 return {
35 compare: function compare(actual, expected) {
36 var result = {};
37 if (actual.length !== expected.length) {
38 result.pass = false;
39 result.message = 'Array length: ' + actual.length + ', expected: ' + expected.length;
40 return result;
41 }
42 result.pass = true;
43 for (var i = 0; i < expected.length; i++) {
44 var a = actual[i],
45 b = expected[i];
46 if (Array.isArray(b)) {
47 if (a.length !== b.length) {
48 result.pass = false;
49 break;
50 }
51 for (var j = 0; j < a.length; j++) {
52 var suba = a[j],
53 subb = b[j];
54 if (suba !== subb) {
55 result.pass = false;
56 break;
57 }
58 }
59 } else {
60 if (a !== b) {
61 result.pass = false;
62 break;
63 }
64 }
65 }
66 return result;
67 }
68 };
69 }
70 });
71 });
72 describe('PostScriptParser', function () {
73 function parse(program) {
74 var stream = new _stream.StringStream(program);
75 var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
76 return parser.parse();
77 }
78 it('parses empty programs', function () {
79 var output = parse('{}');
80 expect(output.length).toEqual(0);
81 });
82 it('parses positive numbers', function () {
83 var number = 999;
84 var program = parse('{ ' + number + ' }');
85 var expectedProgram = [number];
86 expect(program).toMatchArray(expectedProgram);
87 });
88 it('parses negative numbers', function () {
89 var number = -999;
90 var program = parse('{ ' + number + ' }');
91 var expectedProgram = [number];
92 expect(program).toMatchArray(expectedProgram);
93 });
94 it('parses negative floats', function () {
95 var number = 3.3;
96 var program = parse('{ ' + number + ' }');
97 var expectedProgram = [number];
98 expect(program).toMatchArray(expectedProgram);
99 });
100 it('parses operators', function () {
101 var program = parse('{ sub }');
102 var expectedProgram = ['sub'];
103 expect(program).toMatchArray(expectedProgram);
104 });
105 it('parses if statements', function () {
106 var program = parse('{ { 99 } if }');
107 var expectedProgram = [3, 'jz', 99];
108 expect(program).toMatchArray(expectedProgram);
109 });
110 it('parses ifelse statements', function () {
111 var program = parse('{ { 99 } { 44 } ifelse }');
112 var expectedProgram = [5, 'jz', 99, 6, 'j', 44];
113 expect(program).toMatchArray(expectedProgram);
114 });
115 it('handles missing brackets', function () {
116 expect(function () {
117 parse('{');
118 }).toThrow(new Error('Unexpected symbol: found undefined expected 1.'));
119 });
120 it('handles junk after the end', function () {
121 var number = 3.3;
122 var program = parse('{ ' + number + ' }#');
123 var expectedProgram = [number];
124 expect(program).toMatchArray(expectedProgram);
125 });
126 });
127 describe('PostScriptEvaluator', function () {
128 function evaluate(program) {
129 var stream = new _stream.StringStream(program);
130 var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
131 var code = parser.parse();
132 var evaluator = new _function.PostScriptEvaluator(code);
133 var output = evaluator.execute();
134 return output;
135 }
136 it('pushes stack', function () {
137 var stack = evaluate('{ 99 }');
138 var expectedStack = [99];
139 expect(stack).toMatchArray(expectedStack);
140 });
141 it('handles if with true', function () {
142 var stack = evaluate('{ 1 {99} if }');
143 var expectedStack = [99];
144 expect(stack).toMatchArray(expectedStack);
145 });
146 it('handles if with false', function () {
147 var stack = evaluate('{ 0 {99} if }');
148 var expectedStack = [];
149 expect(stack).toMatchArray(expectedStack);
150 });
151 it('handles ifelse with true', function () {
152 var stack = evaluate('{ 1 {99} {77} ifelse }');
153 var expectedStack = [99];
154 expect(stack).toMatchArray(expectedStack);
155 });
156 it('handles ifelse with false', function () {
157 var stack = evaluate('{ 0 {99} {77} ifelse }');
158 var expectedStack = [77];
159 expect(stack).toMatchArray(expectedStack);
160 });
161 it('handles nested if', function () {
162 var stack = evaluate('{ 1 {1 {77} if} if }');
163 var expectedStack = [77];
164 expect(stack).toMatchArray(expectedStack);
165 });
166 it('abs', function () {
167 var stack = evaluate('{ -2 abs }');
168 var expectedStack = [2];
169 expect(stack).toMatchArray(expectedStack);
170 });
171 it('adds', function () {
172 var stack = evaluate('{ 1 2 add }');
173 var expectedStack = [3];
174 expect(stack).toMatchArray(expectedStack);
175 });
176 it('boolean and', function () {
177 var stack = evaluate('{ true false and }');
178 var expectedStack = [false];
179 expect(stack).toMatchArray(expectedStack);
180 });
181 it('bitwise and', function () {
182 var stack = evaluate('{ 254 1 and }');
183 var expectedStack = [254 & 1];
184 expect(stack).toMatchArray(expectedStack);
185 });
186 it('calculates the inverse tangent of a number', function () {
187 var stack = evaluate('{ 90 atan }');
188 var expectedStack = [Math.atan(90)];
189 expect(stack).toMatchArray(expectedStack);
190 });
191 it('handles bitshifting ', function () {
192 var stack = evaluate('{ 50 2 bitshift }');
193 var expectedStack = [200];
194 expect(stack).toMatchArray(expectedStack);
195 });
196 it('calculates the ceiling value', function () {
197 var stack = evaluate('{ 9.9 ceiling }');
198 var expectedStack = [10];
199 expect(stack).toMatchArray(expectedStack);
200 });
201 it('copies', function () {
202 var stack = evaluate('{ 99 98 2 copy }');
203 var expectedStack = [99, 98, 99, 98];
204 expect(stack).toMatchArray(expectedStack);
205 });
206 it('calculates the cosine of a number', function () {
207 var stack = evaluate('{ 90 cos }');
208 var expectedStack = [Math.cos(90)];
209 expect(stack).toMatchArray(expectedStack);
210 });
211 it('converts to int', function () {
212 var stack = evaluate('{ 9.9 cvi }');
213 var expectedStack = [9];
214 expect(stack).toMatchArray(expectedStack);
215 });
216 it('converts negatives to int', function () {
217 var stack = evaluate('{ -9.9 cvi }');
218 var expectedStack = [-9];
219 expect(stack).toMatchArray(expectedStack);
220 });
221 it('converts to real', function () {
222 var stack = evaluate('{ 55.34 cvr }');
223 var expectedStack = [55.34];
224 expect(stack).toMatchArray(expectedStack);
225 });
226 it('divides', function () {
227 var stack = evaluate('{ 6 5 div }');
228 var expectedStack = [1.2];
229 expect(stack).toMatchArray(expectedStack);
230 });
231 it('maps division by zero to infinity', function () {
232 var stack = evaluate('{ 6 0 div }');
233 var expectedStack = [Infinity];
234 expect(stack).toMatchArray(expectedStack);
235 });
236 it('duplicates', function () {
237 var stack = evaluate('{ 99 dup }');
238 var expectedStack = [99, 99];
239 expect(stack).toMatchArray(expectedStack);
240 });
241 it('accepts an equality', function () {
242 var stack = evaluate('{ 9 9 eq }');
243 var expectedStack = [true];
244 expect(stack).toMatchArray(expectedStack);
245 });
246 it('rejects an inequality', function () {
247 var stack = evaluate('{ 9 8 eq }');
248 var expectedStack = [false];
249 expect(stack).toMatchArray(expectedStack);
250 });
251 it('exchanges', function () {
252 var stack = evaluate('{ 44 99 exch }');
253 var expectedStack = [99, 44];
254 expect(stack).toMatchArray(expectedStack);
255 });
256 it('handles exponentiation', function () {
257 var stack = evaluate('{ 10 2 exp }');
258 var expectedStack = [100];
259 expect(stack).toMatchArray(expectedStack);
260 });
261 it('pushes false onto the stack', function () {
262 var stack = evaluate('{ false }');
263 var expectedStack = [false];
264 expect(stack).toMatchArray(expectedStack);
265 });
266 it('calculates the floor value', function () {
267 var stack = evaluate('{ 9.9 floor }');
268 var expectedStack = [9];
269 expect(stack).toMatchArray(expectedStack);
270 });
271 it('handles greater than or equal to', function () {
272 var stack = evaluate('{ 10 9 ge }');
273 var expectedStack = [true];
274 expect(stack).toMatchArray(expectedStack);
275 });
276 it('rejects less than for greater than or equal to', function () {
277 var stack = evaluate('{ 8 9 ge }');
278 var expectedStack = [false];
279 expect(stack).toMatchArray(expectedStack);
280 });
281 it('handles greater than', function () {
282 var stack = evaluate('{ 10 9 gt }');
283 var expectedStack = [true];
284 expect(stack).toMatchArray(expectedStack);
285 });
286 it('rejects less than or equal for greater than', function () {
287 var stack = evaluate('{ 9 9 gt }');
288 var expectedStack = [false];
289 expect(stack).toMatchArray(expectedStack);
290 });
291 it('divides to integer', function () {
292 var stack = evaluate('{ 2 3 idiv }');
293 var expectedStack = [0];
294 expect(stack).toMatchArray(expectedStack);
295 });
296 it('divides to negative integer', function () {
297 var stack = evaluate('{ -2 3 idiv }');
298 var expectedStack = [0];
299 expect(stack).toMatchArray(expectedStack);
300 });
301 it('duplicates index', function () {
302 var stack = evaluate('{ 4 3 2 1 2 index }');
303 var expectedStack = [4, 3, 2, 1, 3];
304 expect(stack).toMatchArray(expectedStack);
305 });
306 it('handles less than or equal to', function () {
307 var stack = evaluate('{ 9 10 le }');
308 var expectedStack = [true];
309 expect(stack).toMatchArray(expectedStack);
310 });
311 it('rejects greater than for less than or equal to', function () {
312 var stack = evaluate('{ 10 9 le }');
313 var expectedStack = [false];
314 expect(stack).toMatchArray(expectedStack);
315 });
316 it('calculates the natural logarithm', function () {
317 var stack = evaluate('{ 10 ln }');
318 var expectedStack = [Math.log(10)];
319 expect(stack).toMatchArray(expectedStack);
320 });
321 it('calculates the base 10 logarithm', function () {
322 var stack = evaluate('{ 100 log }');
323 var expectedStack = [2];
324 expect(stack).toMatchArray(expectedStack);
325 });
326 it('handles less than', function () {
327 var stack = evaluate('{ 9 10 lt }');
328 var expectedStack = [true];
329 expect(stack).toMatchArray(expectedStack);
330 });
331 it('rejects greater than or equal to for less than', function () {
332 var stack = evaluate('{ 10 9 lt }');
333 var expectedStack = [false];
334 expect(stack).toMatchArray(expectedStack);
335 });
336 it('performs the modulo operation', function () {
337 var stack = evaluate('{ 4 3 mod }');
338 var expectedStack = [1];
339 expect(stack).toMatchArray(expectedStack);
340 });
341 it('multiplies two numbers (positive result)', function () {
342 var stack = evaluate('{ 9 8 mul }');
343 var expectedStack = [72];
344 expect(stack).toMatchArray(expectedStack);
345 });
346 it('multiplies two numbers (negative result)', function () {
347 var stack = evaluate('{ 9 -8 mul }');
348 var expectedStack = [-72];
349 expect(stack).toMatchArray(expectedStack);
350 });
351 it('accepts an inequality', function () {
352 var stack = evaluate('{ 9 8 ne }');
353 var expectedStack = [true];
354 expect(stack).toMatchArray(expectedStack);
355 });
356 it('rejects an equality', function () {
357 var stack = evaluate('{ 9 9 ne }');
358 var expectedStack = [false];
359 expect(stack).toMatchArray(expectedStack);
360 });
361 it('negates', function () {
362 var stack = evaluate('{ 4.5 neg }');
363 var expectedStack = [-4.5];
364 expect(stack).toMatchArray(expectedStack);
365 });
366 it('boolean not', function () {
367 var stack = evaluate('{ true not }');
368 var expectedStack = [false];
369 expect(stack).toMatchArray(expectedStack);
370 });
371 it('bitwise not', function () {
372 var stack = evaluate('{ 12 not }');
373 var expectedStack = [-13];
374 expect(stack).toMatchArray(expectedStack);
375 });
376 it('boolean or', function () {
377 var stack = evaluate('{ true false or }');
378 var expectedStack = [true];
379 expect(stack).toMatchArray(expectedStack);
380 });
381 it('bitwise or', function () {
382 var stack = evaluate('{ 254 1 or }');
383 var expectedStack = [254 | 1];
384 expect(stack).toMatchArray(expectedStack);
385 });
386 it('pops stack', function () {
387 var stack = evaluate('{ 1 2 pop }');
388 var expectedStack = [1];
389 expect(stack).toMatchArray(expectedStack);
390 });
391 it('rolls stack right', function () {
392 var stack = evaluate('{ 1 3 2 2 4 1 roll }');
393 var expectedStack = [2, 1, 3, 2];
394 expect(stack).toMatchArray(expectedStack);
395 });
396 it('rolls stack left', function () {
397 var stack = evaluate('{ 1 3 2 2 4 -1 roll }');
398 var expectedStack = [3, 2, 2, 1];
399 expect(stack).toMatchArray(expectedStack);
400 });
401 it('rounds a number', function () {
402 var stack = evaluate('{ 9.52 round }');
403 var expectedStack = [10];
404 expect(stack).toMatchArray(expectedStack);
405 });
406 it('calculates the sine of a number', function () {
407 var stack = evaluate('{ 90 sin }');
408 var expectedStack = [Math.sin(90)];
409 expect(stack).toMatchArray(expectedStack);
410 });
411 it('calculates a square root (integer)', function () {
412 var stack = evaluate('{ 100 sqrt }');
413 var expectedStack = [10];
414 expect(stack).toMatchArray(expectedStack);
415 });
416 it('calculates a square root (float)', function () {
417 var stack = evaluate('{ 99 sqrt }');
418 var expectedStack = [Math.sqrt(99)];
419 expect(stack).toMatchArray(expectedStack);
420 });
421 it('subtracts (positive result)', function () {
422 var stack = evaluate('{ 6 4 sub }');
423 var expectedStack = [2];
424 expect(stack).toMatchArray(expectedStack);
425 });
426 it('subtracts (negative result)', function () {
427 var stack = evaluate('{ 4 6 sub }');
428 var expectedStack = [-2];
429 expect(stack).toMatchArray(expectedStack);
430 });
431 it('pushes true onto the stack', function () {
432 var stack = evaluate('{ true }');
433 var expectedStack = [true];
434 expect(stack).toMatchArray(expectedStack);
435 });
436 it('truncates a number', function () {
437 var stack = evaluate('{ 35.004 truncate }');
438 var expectedStack = [35];
439 expect(stack).toMatchArray(expectedStack);
440 });
441 it('calculates an exclusive or value', function () {
442 var stack = evaluate('{ 3 9 xor }');
443 var expectedStack = [10];
444 expect(stack).toMatchArray(expectedStack);
445 });
446 });
447 describe('PostScriptCompiler', function () {
448 function check(code, domain, range, samples) {
449 var compiler = new _function.PostScriptCompiler();
450 var compiledCode = compiler.compile(code, domain, range);
451 if (samples === null) {
452 expect(compiledCode).toBeNull();
453 } else {
454 expect(compiledCode).not.toBeNull();
455 var fn = new Function('src', 'srcOffset', 'dest', 'destOffset', compiledCode);
456 for (var i = 0; i < samples.length; i++) {
457 var out = new Float32Array(samples[i].output.length);
458 fn(samples[i].input, 0, out, 0);
459 expect(Array.prototype.slice.call(out, 0)).toMatchArray(samples[i].output);
460 }
461 }
462 }
463 it('check compiled add', function () {
464 check([0.25, 0.5, 'add'], [], [0, 1], [{
465 input: [],
466 output: [0.75]
467 }]);
468 check([0, 'add'], [0, 1], [0, 1], [{
469 input: [0.25],
470 output: [0.25]
471 }]);
472 check([0.5, 'add'], [0, 1], [0, 1], [{
473 input: [0.25],
474 output: [0.75]
475 }]);
476 check([0, 'exch', 'add'], [0, 1], [0, 1], [{
477 input: [0.25],
478 output: [0.25]
479 }]);
480 check([0.5, 'exch', 'add'], [0, 1], [0, 1], [{
481 input: [0.25],
482 output: [0.75]
483 }]);
484 check(['add'], [0, 1, 0, 1], [0, 1], [{
485 input: [0.25, 0.5],
486 output: [0.75]
487 }]);
488 check(['add'], [0, 1], [0, 1], null);
489 });
490 it('check compiled sub', function () {
491 check([0.5, 0.25, 'sub'], [], [0, 1], [{
492 input: [],
493 output: [0.25]
494 }]);
495 check([0, 'sub'], [0, 1], [0, 1], [{
496 input: [0.25],
497 output: [0.25]
498 }]);
499 check([0.5, 'sub'], [0, 1], [0, 1], [{
500 input: [0.75],
501 output: [0.25]
502 }]);
503 check([0, 'exch', 'sub'], [0, 1], [-1, 1], [{
504 input: [0.25],
505 output: [-0.25]
506 }]);
507 check([0.75, 'exch', 'sub'], [0, 1], [-1, 1], [{
508 input: [0.25],
509 output: [0.5]
510 }]);
511 check(['sub'], [0, 1, 0, 1], [-1, 1], [{
512 input: [0.25, 0.5],
513 output: [-0.25]
514 }]);
515 check(['sub'], [0, 1], [0, 1], null);
516 check([1, 'dup', 3, 2, 'roll', 'sub', 'sub'], [0, 1], [0, 1], [{
517 input: [0.75],
518 output: [0.75]
519 }]);
520 });
521 it('check compiled mul', function () {
522 check([0.25, 0.5, 'mul'], [], [0, 1], [{
523 input: [],
524 output: [0.125]
525 }]);
526 check([0, 'mul'], [0, 1], [0, 1], [{
527 input: [0.25],
528 output: [0]
529 }]);
530 check([0.5, 'mul'], [0, 1], [0, 1], [{
531 input: [0.25],
532 output: [0.125]
533 }]);
534 check([1, 'mul'], [0, 1], [0, 1], [{
535 input: [0.25],
536 output: [0.25]
537 }]);
538 check([0, 'exch', 'mul'], [0, 1], [0, 1], [{
539 input: [0.25],
540 output: [0]
541 }]);
542 check([0.5, 'exch', 'mul'], [0, 1], [0, 1], [{
543 input: [0.25],
544 output: [0.125]
545 }]);
546 check([1, 'exch', 'mul'], [0, 1], [0, 1], [{
547 input: [0.25],
548 output: [0.25]
549 }]);
550 check(['mul'], [0, 1, 0, 1], [0, 1], [{
551 input: [0.25, 0.5],
552 output: [0.125]
553 }]);
554 check(['mul'], [0, 1], [0, 1], null);
555 });
556 it('check compiled max', function () {
557 check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1], [{
558 input: [0.5],
559 output: [0.5]
560 }]);
561 check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1], [{
562 input: [1],
563 output: [0.75]
564 }]);
565 check(['dup', 0.75, 'gt', 5, 'jz', 'pop', 0.75], [0, 1], [0, 1], null);
566 });
567 it('check pop/roll/index', function () {
568 check([1, 'pop'], [0, 1], [0, 1], [{
569 input: [0.5],
570 output: [0.5]
571 }]);
572 check([1, 3, -1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], [{
573 input: [0.25, 0.5],
574 output: [0.5, 1, 0.25]
575 }]);
576 check([1, 3, 1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], [{
577 input: [0.25, 0.5],
578 output: [1, 0.25, 0.5]
579 }]);
580 check([1, 3, 1.5, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], null);
581 check([1, 1, 'index'], [0, 1], [0, 1, 0, 1, 0, 1], [{
582 input: [0.5],
583 output: [0.5, 1, 0.5]
584 }]);
585 check([1, 3, 'index', 'pop'], [0, 1], [0, 1], null);
586 check([1, 0.5, 'index', 'pop'], [0, 1], [0, 1], null);
587 });
588 it('check input boundaries', function () {
589 check([], [0, 0.5], [0, 1], [{
590 input: [1],
591 output: [0.5]
592 }]);
593 check([], [0.5, 1], [0, 1], [{
594 input: [0],
595 output: [0.5]
596 }]);
597 check(['dup'], [0.5, 0.75], [0, 1, 0, 1], [{
598 input: [0],
599 output: [0.5, 0.5]
600 }]);
601 check([], [100, 1001], [0, 10000], [{
602 input: [1000],
603 output: [1000]
604 }]);
605 });
606 it('check output boundaries', function () {
607 check([], [0, 1], [0, 0.5], [{
608 input: [1],
609 output: [0.5]
610 }]);
611 check([], [0, 1], [0.5, 1], [{
612 input: [0],
613 output: [0.5]
614 }]);
615 check(['dup'], [0, 1], [0.5, 1, 0.75, 1], [{
616 input: [0],
617 output: [0.5, 0.75]
618 }]);
619 check([], [0, 10000], [100, 1001], [{
620 input: [1000],
621 output: [1000]
622 }]);
623 });
624 it('compile optimized', function () {
625 var compiler = new _function.PostScriptCompiler();
626 var code = [0, 'add', 1, 1, 3, -1, 'roll', 'sub', 'sub', 1, 'mul'];
627 var compiledCode = compiler.compile(code, [0, 1], [0, 1]);
628 expect(compiledCode).toEqual('dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));');
629 });
630 });
631});
\No newline at end of file