jevstrudel.git / packages / mondo / test / mondo.test.mjs

mondo.test.mjs - <short description TODO> Copyright (C) 2022 Strudel contributors - see https://github.com/tidalcycles/strudel/blob/main/packages/mini/test/mini.test.mjs This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

7import { describe, expect, it } from 'vitest';
8import { MondoParser, printAst, MondoRunner } from '../mondo.mjs';
10const parser = new MondoParser();
11const p = (code) => parser.parse(code, -1);
12
13describe('mondo tokenizer', () => {
14  const parser = new MondoParser();
15  it('should tokenize with locations', () =>
16    expect(
17      parser
18        .tokenize('(one two three)')
19        .map((t) => t.value + '=' + t.loc.join('-'))
20        .join(' '),
21    ).toEqual('(=0-1 one=1-4 two=5-8 three=9-14 )=14-15'));
22  // it('should parse with loangleions', () => expect(parser.parse('(one two three)')).toEqual());
23  it('should get loangleions', () =>
24    expect(parser.get_locations('s bd rim')).toEqual([
25      [0, 1],
26      [2, 4],
27      [5, 8],
28    ]));
29});
30describe('mondo s-expressions parser', () => {
31  it('should parse an empty string', () => expect(p('')).toEqual({ type: 'list', children: [] }));
32  it('should parse a single item', () =>
33    expect(p('a')).toEqual({ type: 'list', children: [{ type: 'plain', value: 'a' }] }));
34  it('should parse an empty list', () => expect(p('()')).toEqual({ type: 'list', children: [] }));
35  it('should parse a list with 1 item', () =>
36    expect(p('(a)')).toEqual({ type: 'list', children: [{ type: 'plain', value: 'a' }] }));
37  it('should parse a list with 2 items', () =>
38    expect(p('(a b)')).toEqual({
39      type: 'list',
40      children: [
41        { type: 'plain', value: 'a' },
42        { type: 'plain', value: 'b' },
43      ],
44    }));
45  it('should parse a list with 2 items', () =>
46    expect(p('(a (b c))')).toEqual({
47      type: 'list',
48      children: [
49        { type: 'plain', value: 'a' },
50        {
51          type: 'list',
52          children: [
53            { type: 'plain', value: 'b' },
54            { type: 'plain', value: 'c' },
55          ],
56        },
57      ],
58    }));
59  it('should parse numbers', () =>
60    expect(p('(1 .2 1.2 10 22.3)')).toEqual({
61      type: 'list',
62      children: [
63        { type: 'number', value: '1' },
64        { type: 'number', value: '.2' },
65        { type: 'number', value: '1.2' },
66        { type: 'number', value: '10' },
67        { type: 'number', value: '22.3' },
68      ],
69    }));
70  it('should parse comments', () =>
71    expect(p('a // hello')).toEqual({
72      type: 'list',
73      children: [
74        { type: 'plain', value: 'a' },
75        { type: 'comment', value: '// hello' },
76      ],
77    }));
78});
79
80let desguar = (a) => {
81  return printAst(parser.parse(a), true);
82};
83
84describe('mondo sugar', () => {
85  it('should desugar []', () => expect(desguar('[a b c]')).toEqual('(square a b c)'));
86  it('should desugar [] nested', () => expect(desguar('[a [b c] d]')).toEqual('(square a (square b c) d)'));
87  it('should desugar <>', () => expect(desguar('<a b c>')).toEqual('(angle a b c)'));
88  it('should desugar <> nested', () => expect(desguar('<a <b c> d>')).toEqual('(angle a (angle b c) d)'));
89  it('should desugar mixed [] <>', () => expect(desguar('[a <b c>]')).toEqual('(square a (angle b c))'));
90  it('should desugar mixed <> []', () => expect(desguar('<a [b c]>')).toEqual('(angle a (square b c))'));
91
92  it('should desugar #', () => expect(desguar('s jazz # fast 2')).toEqual('(fast 2 (s jazz))'));
93  it('should desugar # square', () => expect(desguar('[bd cp # fast 2]')).toEqual('(fast 2 (square bd cp))'));
94  it('should desugar # twice', () => expect(desguar('s jazz # fast 2 # slow 2')).toEqual('(slow 2 (fast 2 (s jazz)))'));
95  it('should desugar # nested', () => expect(desguar('(s cp # fast 2)')).toEqual('(fast 2 (s cp))'));
96  it('should desugar # within []', () => expect(desguar('[bd cp # fast 2]')).toEqual('(fast 2 (square bd cp))'));
97  it('should desugar # within , within []', () =>
98    expect(desguar('[bd cp # fast 2, x]')).toEqual('(stack (fast 2 (square bd cp)) x)'));

it('should desugar .(.', () => expect(desguar('[jazz hh.(.fast 2)]')).toEqual('(square jazz (fast 2 hh))'));

102  it('should desugar , |', () => expect(desguar('[bd, hh | oh]')).toEqual('(stack bd (or hh oh))'));
103  it('should desugar , | of []', () =>
104    expect(desguar('[bd, hh | [oh rim]]')).toEqual('(stack bd (or hh (square oh rim)))'));
105  it('should desugar , square', () => expect(desguar('[bd, hh]')).toEqual('(stack bd hh)'));
106  it('should desugar , square 2', () => expect(desguar('[bd, hh oh]')).toEqual('(stack bd (square hh oh))'));
107  it('should desugar , square 3', () =>
108    expect(desguar('[bd cp, hh oh]')).toEqual('(stack (square bd cp) (square hh oh))'));
109  it('should desugar , angle', () => expect(desguar('<bd, hh>')).toEqual('(stack bd hh)'));
110  it('should desugar , angle 2', () => expect(desguar('<bd, hh oh>')).toEqual('(stack bd (angle hh oh))'));
111  it('should desugar , angle 3', () =>
112    expect(desguar('<bd cp, hh oh>')).toEqual('(stack (angle bd cp) (angle hh oh))'));
113  it('should desugar , ()', () => expect(desguar('(s bd, s cp)')).toEqual('(stack (s bd) (s cp))'));
114  it('should desugar * /', () => expect(desguar('[a b*2 c d/3 e]')).toEqual('(square a (* 2 b) c (/ 3 d) e)'));
115  it('should desugar []*x', () => expect(desguar('[a [b c]*3]')).toEqual('(square a (* 3 (square b c)))'));
116  it('should desugar []*<x y>', () => expect(desguar('[a b*<2 3> c]')).toEqual('(square a (* (angle 2 3) b) c)'));
117  it('should desugar x:y', () => expect(desguar('x:y')).toEqual('(: y x)'));
118  it('should desugar x:y:z', () => expect(desguar('x:y:z')).toEqual('(: z (: y x))'));
119  it('should desugar x:y*x', () => expect(desguar('bd:0*2')).toEqual('(* 2 (: 0 bd))'));
120  it('should desugar x&y:z', () => expect(desguar('bd&3:8')).toEqual('(& (: 8 3) bd)'));
121  it('should desugar a..b', () => expect(desguar('0..2')).toEqual('(.. 2 0)'));
122  /* it('should desugar x $ y', () => expect(desguar('x $ y')).toEqual('(x y)'));
123  it('should desugar x $ y z', () => expect(desguar('x $ y z')).toEqual('(x (y z))'));
124  it('should desugar x $ y . z', () => expect(desguar('x $ y . z')).toEqual('(z (x y))')); */
126  it('should desugar README example', () =>
127    expect(desguar('s [bd hh*2 (cp # crush 4) <mt ht lt>] # speed .8')).toEqual(
128      '(speed .8 (s (square bd (* 2 hh) (crush 4 cp) (angle mt ht lt))))',
129    ));
130
131  it('should desugar (#)', () => expect(desguar('(#)')).toEqual('(fn (_) _)'));
132  it('should desugar lambda', () => expect(desguar('(# fast 2)')).toEqual('(fn (_) (fast 2 _))'));
133  it('should desugar lambda call', () => expect(desguar('((# mul 2) 2)')).toEqual('((fn (_) (mul 2 _)) 2)'));
134  it('should desugar lambda with pipe', () =>
135    expect(desguar('(# fast 2 # room 1)')).toEqual('(fn (_) (room 1 (fast 2 _)))'));
136  /* const lambda = parser.parse('(lambda (_) (fast 2 _))');
137  const target = { type: 'plain', value: 'xyz' };
138  it('should desugar_lambda', () =>
139    expect(printAst(parser.desugar_lambda(lambda.children, target))).toEqual('(fast 2 xyz)')); */
140});
141
142describe('mondo arithmetic', () => {
143  let multi =
144    (op) =>
145    (init, ...rest) =>
146      rest.reduce((acc, arg) => op(acc, arg), init);
147
148  let lib = {
149    '+': multi((a, b) => a + b),
150    add: multi((a, b) => a + b),
151    '-': multi((a, b) => a - b),
152    sub: multi((a, b) => a - b),
153    '*': multi((a, b) => a * b),
154    '/': multi((a, b) => a / b),
155    mod: multi((a, b) => a % b),
156    eq: (a, b) => a === b,
157    lt: (a, b) => a < b,
158    gt: (a, b) => a > b,
159    and: (a, b) => a && b,
160    or: (a, b) => a || b,
161    not: (a) => !a,
162    run: (...args) => args[args.length - 1],
163    def: () => 0,
164    sin: Math.sin,
165    cos: Math.cos,
166    PI: Math.PI,
167    cons: (a, b) => [a, ...(Array.isArray(b) ? b : [b])],
168    car: (pair) => pair[0],
169    cdr: (pair) => pair.slice(1),
170    list: (...items) => items,
171    nil: [],
172    isnull: (items) => items.length === 0,
173    concat: (...msgs) => msgs.join(''),
174    error: (...msgs) => {
175      throw new Error(msgs.join(' '));
176    },
177  };
178  function evaluator(node, scope) {
179    if (node.type !== 'list') {
180      // is leaf
181      return scope[node.value] ?? lib[node.value] ?? node.value;
182    }
183    // is list
184    const [fn, ...args] = node.children;
185    if (typeof fn !== 'function') {
186      throw new Error(`"${fn}": expected function, got ${typeof fn} "${fn}"`);
187    }
188    return fn(...args);
189  }
190  const runner = new MondoRunner({ evaluator });
191  let evaluate = (exp, scope) => runner.run(`run ${exp}`, scope);
192  let pretty = (exp) => printAst(runner.parser.parse(exp), false);
193  //it('should eval nested expression', () => expect(runner.run('add 1 (mul 2 PI)').toFixed(2)).toEqual('7.28'));
194
195  it('eval number', () => expect(evaluate('2')).toEqual(2));
196  it('eval string', () => expect(evaluate('abc')).toEqual('abc'));
197  it('eval list', () => expect(evaluate('(+ 1 2)')).toEqual(3));
198  it('eval nested list', () => expect(evaluate('(+ 1 (+ 2 3))')).toEqual(6));
199  it('def number', () => expect(evaluate('(def a 2) a')).toEqual(2));
200  it('def + ref number', () => expect(evaluate('(def a 2) (* a a)')).toEqual(4));
201  it('def + call lambda', () => expect(evaluate('(def sqr (fn (x) (* x x))) (sqr 3)')).toEqual(9));

sicp

204  it('sicp 8.1', () => expect(evaluate('(+ 137 349)')).toEqual(486));
205  it('sicp 8.2', () => expect(evaluate('(- 1000 334)')).toEqual(666));
206  it('sicp 8.3', () => expect(evaluate('(* 5 99)')).toEqual(495));
207  it('sicp 8.4', () => expect(evaluate('(/ 10 5)')).toEqual(2));
208  it('sicp 8.5', () => expect(evaluate('(+ 2.7 10)')).toEqual(12.7));
209  it('sicp 9.1', () => expect(evaluate('(+ 21 35 12 7)')).toEqual(75));
210  it('sicp 9.2', () => expect(evaluate('(* 25 4 12)')).toEqual(1200));
211  it('sicp 9.3', () => expect(evaluate('(+ (* 3 5) (- 10 6))')).toEqual(19));
212  it('sicp 9.4', () =>
213    expect(pretty('(+ (* 3 (+ (* 2 4) (+ 3 5))) (+ (- 10 7) 6))')).toEqual(`(+ 
214 (* 3 
215  (+ 
216   (* 2 4) 
217   (+ 3 5)
218  )
219 ) 
220 (+ 
221  (- 10 7) 6
222 )
223)`)); // this is not exactly pretty printing by convention..
225  let scope = {};
226  it('sicp 11.1', () => expect(evaluate('(def size 2) (* 5 size)', scope)).toEqual(10));
227  it('sicp 11.2', () =>
228    expect(evaluate('(def pi 3.14159) (def radius 10) (* pi (* radius radius))', scope)).toEqual(314.159));
229  it('sicp 11.3', () => expect(evaluate('(def circumference (* 2 pi radius))', scope)).toEqual(0));
230  it('sicp 11.4', () => expect(evaluate('circumference', scope)).toEqual(62.8318));
231  it('sicp 13.1', () => expect(evaluate('(* (+ 2 (* 4 6)) (+ 3 5 7))')).toEqual(390));
232  it('sicp 16.1', () => expect(evaluate('(def (square x) (* x x))', scope)).toEqual(0));
233  // it('sicp 16.1', () => expect(evaluate('(def (square x) (* x x))', scope)).toEqual(0));
234  it('sicp 17.1', () => expect(evaluate('(square 21)', scope)).toEqual(441));
235  it('sicp 17.2', () => expect(evaluate('(square (+ 2 5))', scope)).toEqual(49));
236  it('sicp 17.3', () => expect(evaluate('(square (square 3))', scope)).toEqual(81));
237  it('sicp 17.4', () => expect(evaluate(`(def (sumofsquares x y) (+ (square x) (square y)))`, scope)).toEqual(0));
238  it('sicp 17.5', () => expect(evaluate(`(sumofsquares 3 4)`, scope)).toEqual(25));
239  it('sicp 17.6', () => expect(evaluate(`(def (f a) (sumofsquares (+ a 1) (* a 2))) (f 5)`, scope)).toEqual(136));
240  it('sicp 21.1', () => expect(evaluate(`(sumofsquares (+ 5 1) (* 5 2))`, scope)).toEqual(136));
241
242  it('sicp 22.1', () =>
243    expect(
244      evaluate(
245        `(def (abs x) 
246        (match 
247         ((gt x 0) x) 
248         ((eq x 0) 0)
249         ((lt x 0) (- 0 x))
250      ))`, // sicp was doing (- x), which doesnt work with our -
251        scope,
252      ),
253    ).toEqual(0));
254
255  it('sicp gt1', () => expect(evaluate(`(gt -12 0)`, scope)).toEqual(false));
256  it('sicp gt2', () => expect(evaluate(`(gt 0 -12)`, scope)).toEqual(true));
257  it('sicp lt1', () => expect(evaluate(`(lt -12 0)`, scope)).toEqual(true));
258  it('sicp lt2', () => expect(evaluate(`(lt 0 -12)`, scope)).toEqual(false));
259
260  it('sicp 24.1', () => expect(evaluate(`(abs (- 3))`, scope)).toEqual(3));
261  it('sicp 24.2', () => expect(evaluate(`(abs (+ 3))`, scope)).toEqual(3));
262  it('sicp 24.3', () => expect(evaluate(`(abs -12)`, scope)).toEqual(12));
263
264  it('sicp 24.4', () => expect(evaluate(`(def (abs x) (if (lt x 0) (- 0 x) x))`, scope)).toEqual(0));
265  it('sicp 24.5', () => expect(evaluate(`(abs -13)`, scope)).toEqual(13));
266  it('sicp 25.1', () => expect(evaluate(`(and (gt 6 5) (lt 6 10))`, scope)).toEqual(true));
267  it('sicp 25.2', () => expect(evaluate(`(and (gt 4 5) (lt 6 10))`, scope)).toEqual(false));
268
269  it('sicp ex1.1.1', () => expect(evaluate(`(def a 3)`, scope)).toEqual(0));
270  it('sicp ex1.1.2', () => expect(evaluate(`(def b (+ a 1))`, scope)).toEqual(0));
271  it('sicp ex1.1.3', () => expect(evaluate(`(+ a b (* a b))`, scope)).toEqual(19));
272  it('sicp ex1.1.4', () => expect(evaluate(`(if (and (gt b a) (lt b (* a b))) b a)`, scope)).toEqual(4));
273  it('sicp ex1.1.5', () => expect(evaluate(`(match ((eq a 4) 6) ((eq b 4) (+ 6 7 a)) (else 25))`, scope)).toEqual(16));
274  it('sicp ex1.1.6', () => expect(evaluate(`(+ 2 (if (gt b a) b a))`, scope)).toEqual(6));
275  it('sicp ex1.1.7', () =>
276    expect(evaluate(`(* (match ((gt a b) a) ((lt a b) b) (else -1)) (+ a 1))`, scope)).toEqual(16));

.. cant use "+" and "-" as standalone expressions, because they are parsed as operators...

279  it('sicp ex1.4.1', () => expect(evaluate(`(def (foo a b) ((if (gt b 0) add sub) a b))`, scope)).toEqual(0));
280  it('sicp ex1.4.1', () => expect(evaluate(`(foo 3 1)`, scope)).toEqual(4));
281  it('sicp ex1.4.2', () => expect(evaluate(`(foo 3 -1)`, scope)).toEqual(4));

1.1.7 Example: Square Roots by Newton’s Method

284  it('sicp 30.1', () =>
285    expect(evaluate(`(def (goodenuf guess x) (lt (abs (- (square guess) x)) 0.001))`, scope)).toEqual(0));
286  it('sicp 30.2', () => expect(evaluate(`(goodenuf 1 1.001)`, scope)).toEqual(true));
287  it('sicp 30.3', () => expect(evaluate(`(goodenuf 1 1.002)`, scope)).toEqual(false));
288  it('sicp 30.4', () => expect(evaluate(`(def (average x y) (/ (+ x y) 2))`, scope)).toEqual(0));
289  it('sicp 30.5', () => expect(evaluate(`(average 18 20)`, scope)).toEqual(19));
290  it('sicp 30.6', () => expect(evaluate(`(def (improve guess x) (average guess (/ x guess)))`, scope)).toEqual(0));
291  it('sicp 31.1', () =>
292    expect(
293      evaluate(
294        `(def (sqrtiter guess x) (if (goodenuf guess x)
295      guess
296      (sqrtiter (improve guess x) x)))`,
297        scope,
298      ),
299    ).toEqual(0));
300  it('sicp 31.2', () => expect(evaluate(`(def (sqrt x) (sqrtiter 1.0 x))`, scope)).toEqual(0));
301  it('sicp 31.3', () => expect(evaluate(`(sqrt 9)`, scope)).toEqual(3.00009155413138));
302  it('sicp 31.4', () => expect(evaluate(`(sqrt (+ 100 37))`, scope)).toEqual(11.704699917758145));
303  // eslint-disable-next-line no-loss-of-precision
304  it('sicp 31.5', () => expect(evaluate(`(sqrt (+ (sqrt 2) (sqrt 3)))`, scope)).toEqual(1.77392790232078925));
305  it('sicp 31.6', () => expect(evaluate(`(square (sqrt 1000))`, scope)).toEqual(1000.000369924366));

lexical scoping

308  it('sicp 39.1', () =>
309    expect(
310      evaluate(
311        `
312(def (sqrt x)
313 (def (goodenough guess)
314  (lt (abs (- (square guess) x)) 0.001)) 
315(def (improve guess)
316 (average guess (/ x guess))) 
317(def (sqrt-iter guess)
318 (if (goodenough guess) guess (sqrt-iter (improve guess))))
319(sqrtiter 1.0))
321  `,
322        scope,
323      ),
324    ).toEqual(0));

recursive fac

327  it('sicp 41.1', () => expect(evaluate(`(def (fac n) (if (eq n 1) 1 (* n (fac (- n 1)))))`, scope)).toEqual(0));
328  it('sicp 41.2', () => expect(evaluate(`(fac 4)`, scope)).toEqual(24));

iterative fac

331  it('sicp 41.3', () =>
332    expect(
333      evaluate(
334        `
335(def (factorial n) (factiter 1 1 n))
336(def (factiter product counter maxcount) 
337 (if (gt counter maxcount)
338  product
339  (factiter (* counter product)
340             (+ counter 1)
341             maxcount)))
342`,
343        scope,
344      ),
345    ).toEqual(0));
346  it('sicp 41.4', () => expect(evaluate(`(fac 4)`, scope)).toEqual(24));

46.1 (def (+ a b) (if (= a 0) b (inc (+ (dec a) b)))) (def (+ a b) (if (= a 0) b (+ (dec a) (inc b))))

Exercise 1.10 Ackermann’s function

356  it('sicp 47.1', () =>
357    expect(
358      evaluate(
359        `
360(def (A x y) (match ((eq y 0) 0) 
361((eq x 0) (* 2 y))
362((eq y 1) 2)
363(else (A (- x 1) (A x (- y 1))))))
364`,
365        scope,
366      ),
367    ).toEqual(0));
368  it('sicp 47.2', () => expect(evaluate(`(A 1 10)`, scope)).toEqual(1024));
369  it('sicp 47.3', () => expect(evaluate(`(A 2 4)`, scope)).toEqual(65536));
370  it('sicp 47.4', () => expect(evaluate(`(A 3 3)`, scope)).toEqual(65536));
371  it('sicp 47.5', () =>
372    expect(
373      evaluate(
374        `
375(def (f n) (A 0 n))
376(def (g n) (A 1 n)))
377(def (h n) (A 2 n))
378(def (k n) (* 5 n n))
379    `,
380        scope,
381      ),
382    ).toEqual(0));

Tree Recursion recursive process

386  it('sicp 48.1', () =>
387    expect(
388      evaluate(
389        `
390(def (fib n) (match ((eq n 0) 0) ((eq n 1) 1)
391(else (+ (fib (- n 1)) (fib (- n 2))))))
392(fib 7)
393    `,
394        scope,
395      ),
396    ).toEqual(13));

iterative process

399  it('sicp 48.2', () =>
400    expect(
401      evaluate(
402        `
403(def (fib n) (fibiter 1 0 n))
404(def (fibiter a b count) (if (eq count 0)
405      b
406      (fibiter (+ a b) a (- count 1))))
407(fib 7)
408    `,
409        scope,
410      ),
411    ).toEqual(13));

example: counting change

414  it('sicp 52.2', () =>
415    expect(
416      evaluate(
417        `
418(def (countchange amount) (cc amount 5))
419(def (cc amount kindsofcoins)
420 (match 
421  ((eq amount 0) 1)
422  ((or (lt amount 0) (eq kindsofcoins 0)) 0)
423  (else (+ 
424   (cc amount (- kindsofcoins 1))
425   (cc (- amount (firstdenomination kindsofcoins)) kindsofcoins)))))
427(def (firstdenomination kindsofcoins)
428(match 
429 ((eq kindsofcoins 1) 1) 
430 ((eq kindsofcoins 2) 5)
431 ((eq kindsofcoins 3) 10)
432 ((eq kindsofcoins 4) 25)
433 ((eq kindsofcoins 5) 50)))
434
435(countchange 100)
436    `,
437        scope,
438      ),
439    ).toEqual(292));

todo: pascals triangle

442  it('sicp 57.1', () =>
443    expect(
444      evaluate(
445        `
446(def (cube x) (* x x x))
447(def (p x) (sub (* 3 x) (* 4 (cube x))))
448(def (sine angle)
449(if (not (gt (abs angle) 0.1)) angle
450(p (sine (/ angle 3.0)))))
452(sine 12.15)
453    `,
454        scope,
455      ),
456    ).toEqual(-0.39980345741334));

exponentiation recursive

459  it('sicp 57.2', () =>
460    expect(
461      evaluate(
462        `
463(def (expt b n) (if (eq n 0) 1 (* b (expt b (- n 1)))))
464(expt 2 4)
465`,
466        scope,
467      ),
468    ).toEqual(16));

exponentiation iterative

471  it('sicp 58.1b', () =>
472    expect(
473      evaluate(
474        `
475(def (expt b n) (exptiter b n 1))
476(def (exptiter b counter product) (if (eq counter 0)
477      product
478      (exptiter b (- counter 1) (* b product))))
479(expt 2 5)
480  `,
481        scope,
482      ),
483    ).toEqual(32));

exponentiation fast

486  it('sicp 58.2', () =>
487    expect(
488      evaluate(
489        `
490(def (fastexpt b n) (match ((eq n 0) 1)
491((iseven n) (square (fastexpt b (/ n 2)))) (else (* b (fastexpt b (- n 1))))))
492(def (iseven n)
493(eq (mod n 2) 0))
494(fastexpt 2 5)
495   `,
496        scope,
497      ),
498    ).toEqual(32));
  • = repeated addition
501  it('sicp 60.1', () =>
502    expect(
503      evaluate(
504        `(def (mult a b) (if (eq b 0)
505        0
506        (+ a (* a (- b 1)))))
507        (mult 3 15)
508   `,
509      ),
510    ).toEqual(45));

gcd / euclid

513  it('sicp 63.1', () =>
514    expect(
515      evaluate(
516        `(def (gcd a b) (if (eq b 0)
517      a
518      (gcd b (mod a b))))
519      (gcd 20 6)
520     `,
521        scope,
522      ),
523    ).toEqual(2));

65 smallest divisor 67 fermat test ....

higher order procedures

531  it('sicp 77.1', () =>
532    expect(
533      evaluate(
534        `
535(def (sum term a next b)
536(if (gt a b) 0 (+ (term a)
537(sum term (next a) next b))))
538`,
539        scope,
540      ),
541    ).toEqual(0));
543  it('sicp 78.1', () =>
544    expect(
545      evaluate(
546        `
547(def (inc n) (+ n 1))
548(def (cube a) (* a a a))
549(def (sumcubes a b)
550(sum cube a inc b))
551(sumcubes 1 10)
552  `,
553        scope,
554      ),
555    ).toEqual(3025));
556
557  it('sicp 78.2', () =>
558    expect(
559      evaluate(
560        `
561    (def (identity x) x)
562    (def (sumintegers a b)
563    (sum identity a inc b))
564    (sumintegers 1 10)
565    `,
566        scope,
567      ),
568    ).toEqual(55));

pisum

571  it('sicp 79.1', () =>
572    expect(
573      evaluate(
574        `
575(def (pisum a b) 
576 (def (piterm x)
577  (/ 1.0 (* x (+ x 2)))) 
578(def (pinext x) (+ x 4))
579(sum piterm a pinext b))
580(* 8 (pisum 1 1000))
581`,
582        scope,
583      ),
584    ).toEqual(3.139592655589783));

integral

587  it('sicp 79.2', () =>
588    expect(
589      evaluate(
590        `
591(def (integral f a b dx) 
592 (def (adddx x) (+ x dx))
593(* (sum f (+ a (/ dx 2.0)) adddx b) dx))
594(integral cube 0 1 0.01)
595  `,
596        scope,
597      ),
598    ).toEqual(0.24998750000000042));
599  // maximum callstack...
600  //it('sicp 79.3', () => expect(evaluate(`(integral cube 0 1 0.001)`, scope)).toEqual(0.249999875000001));

lambdas

603  it('sicp 83.1', () => expect(evaluate(`((fn (x) (+ x 4)) 5)`)).toEqual(9));
605  it('sicp 83.2', () =>
606    expect(
607      evaluate(
608        `
609(def (pisum a b)
610  (sum (fn (x) (/ 1.0 (* x (+ x 2))))
611        a
612        (fn (x) (+ x 4)) 
613        b))
614(* 8 (pisum 1 1000))
615`,
616        scope,
617      ),
618    ).toEqual(3.139592655589783));
619  it('sicp 83.3', () =>
620    expect(
621      evaluate(
622        `
623(def (integral f a b dx) 
624 (* (sum f
625         (+ a (/ dx 2.0)) 
626         (fn (x) (+ x dx)) 
627         b)
628    dx))
629(integral cube 0 1 0.01)
630`,
631        scope,
632      ),
633    ).toEqual(0.24998750000000042));
634  it('sicp 84.1', () => expect(evaluate(`((fn (x y z) (+ x y (square z))) 1 2 3)`, scope)).toEqual(12));

let expressions

637  it('sicp 87.1', () =>
638    expect(
639      evaluate(
640        `
641(+ (let ((x 3))
642(+ x (* x 10))) x)
643`,
644        { x: 5 },
645      ),
646    ).toEqual(38));
647  it('sicp 87.2', () =>
648    expect(
649      evaluate(
650        `
651(let ((x 3)
652(y (+ x 2)))
653(* x y))
654  `,
655        { x: 2 },
656      ),
657    ).toEqual(12));
658  it('sicp 88.1', () =>
659    expect(
660      evaluate(
661        `
662(def (f g) (g 2))
663(f square)
664      `,
665        scope,
666      ),
667    ).toEqual(4));
668  it('sicp 88.2', () =>
669    expect(
670      evaluate(
671        `
672  (def (f g) (g 2))
673  (f (fn (z) (* z (+ z 1))))
674            `,
675        scope,
676      ),
677    ).toEqual(6));

Finding roots of equations by the half-interval method

680  it('sicp 89.1', () =>
681    expect(
682      evaluate(
683        `
684(def (search f negpoint pospoint)
685 (let ((midpoint (average negpoint pospoint)))
686 (if (closeenough negpoint pospoint) 
687  midpoint
688  (let ((testvalue (f midpoint))) 
689   (match ((positive testvalue)
690           (search f negpoint midpoint))
691          ((negative testvalue)
692           (search f midpoint pospoint)) 
693          (else midpoint))))))
695(def (closeenough x y) (lt (abs (- x y)) 0.001))
696
697(def (negative x) (lt x 0))
698(def (positive x) (gt x 0))
699
700(def (halfintervalmethod f a b) (let ((avalue (f a))
701(bvalue (f b)))
702(match ((and (negative avalue) (positive bvalue))
703(search f a b))
704((and (negative bvalue) (positive avalue))
705(search f b a)) (else
706(error "Values are not of opposite sign" a b)))))
707
708(halfintervalmethod sin 2.0 4.0)
709`,
710        scope,
711      ),
712    ).toEqual(3.14111328125));
713
714  it('sicp 89.1', () =>
715    expect(evaluate(`(halfintervalmethod (fn (x) (- (* x x x) (* 2 x) 3)) 1.0 2.0)`, scope)).toEqual(1.89306640625));

Finding fixed points of functions

718  it('sicp 92.1', () =>
719    expect(
720      evaluate(
721        `
722(def tolerance 0.00001)
723(def (fixedpoint f first-guess)
724 (def (closeenough v1 v2) (lt (abs (- v1 v2)) tolerance)) 
725 (def (try guess)
726  (let ((next (f guess)))
727  (if (closeenough guess next) next (try next))))
728 (try first-guess))
730(fixedpoint cos 1.0)
731`,
732        scope,
733      ),
734    ).toEqual(0.7390822985224023));
735  it('sicp 93.1', () =>
736    expect(evaluate(`(fixedpoint (fn (y) (+ (sin y) (cos y))) 1.0)`, scope)).toEqual(1.2587315962971173));
737  // Maximum call stack size exceeded (expected)
738  /* it('sicp 93.2', () =>
739    expect(evaluate(`(def (sqrt x) (fixedpoint (fn (y) (/ x y)) 1.0)) (sqrt 4)`, scope)).toEqual(0)); */
740  it('sicp 93.3', () =>
741    expect(evaluate(`(def (sqrt x) (fixedpoint (fn (y) (average y (/ x y))) 1.0)) (sqrt 7)`, scope)).toEqual(
742      2.6457513110645907,
743    ));
744  // Procedures as Returned Values
745  it('sicp 97.1', () =>
746    expect(evaluate(`(def (averagedamp f) (fn (x) (average x (f x)))) ((averagedamp square) 10)`, scope)).toEqual(55));
747  it('sicp 98.1', () =>
748    expect(evaluate(`(def (sqrt x) (fixedpoint (averagedamp (fn (y) (/ x y))) 1.0)) (sqrt 7)`, scope)).toEqual(
749      2.6457513110645907,
750    ));
751  it('sicp 98.2', () =>
752    expect(
753      evaluate(`(def (cuberoot x) (fixedpoint (averagedamp (fn (y) (/ x (square y)))) 1.0)) (cuberoot 7)`, scope),
754    ).toEqual(1.912934258514886));
755  it('sicp 99.1', () =>
756    expect(
757      evaluate(
758        `
759        (def (deriv g) (fn (x) (/ (- (g (+ x dx)) (g x)) dx))) 
760        (def dx 0.00001)
761        (def (cube x) (* x x x))
762        ((deriv cube) 5)
763        `,
764        scope,
765      ),
766    ).toEqual(75.00014999664018));
767  // With the aid of deriv, we can express Newton’s method as a fixed-point process:
768  it('sicp 100.1', () =>
769    expect(
770      evaluate(
771        `
772(def (newtontransform g)
773(fn (x) (- x (/ (g x) ((deriv g) x)))))
774(def (newtonsmethod g guess) (fixedpoint (newtontransform g) guess))
775(def (sqrt x) (newtonsmethod (fn (y) (- (square y) x)) 1.0))
776(sqrt 7)
777          `,
778        scope,
779      ),
780    ).toEqual(2.6457513110645907));
781  // whatever this is
782  it('sicp 101.1', () =>
783    expect(
784      evaluate(
785        `
786  (def (fixedpointoftransform g transform guess) (fixedpoint (transform g) guess))
787  (def (sqrt x) (fixedpointoftransform
788  (fn (y) (/ x y)) averagedamp 1.0))
789  (sqrt 7)
790  `,
791        scope,
792      ),
793    ).toEqual(2.6457513110645907));
794  it('sicp 101.2', () =>
795    expect(
796      evaluate(
797        `
798(def (sqrt x) (fixedpointoftransform
799(fn (y) (- (square y) x)) newtontransform 1.0))
800(sqrt 7)
801    `,
802        scope,
803      ),
804    ).toEqual(2.6457513110645907));

data abstraction

rational arithmetic

809  it('sicp 114.1', () =>
810    expect(
811      evaluate(
812        `
813(def (addrat x y)
814 (makerat (+ (* 
815   (numer x) (denom y))
816   (* (numer y) (denom x)))
817  (* (denom x) (denom y))))
818(def (subrat x y)
819 (makerat (- (* (numer x) (denom y))
820   (* (numer y) (denom x)))
821  (* (denom x) (denom y))))
822(def (mulrat x y)
823 (makerat (* (numer x) (numer y))
824  (* (denom x) (denom y)))) 
825(def (divrat x y)
826  (makerat (* (numer x) (denom y))
827   (* (denom x) (numer y))))
828(def (equalrat x y)
829  (eq (* (numer x) (denom y))
830     (* (numer y) (denom x))))
831        `,
832        scope,
833      ),
834    ).toEqual(0));

markerat number denom

837  it('sicp 117.1', () =>
838    expect(
839      evaluate(
840        `
841(def (makerat n d) (cons n d)) 
842(def (numer x) (car x))
843(def (denom x) (cdr x))
844(def (printrat x) (concat (numer x) ':' (denom x)))
845      `,
846        scope,
847      ),
848    ).toEqual(0));
850  it('sicp 117.1', () => expect(evaluate(`(def onehalf (makerat 1 2)) (printrat onehalf)`, scope)).toEqual('1:2'));
851  it('sicp 117.2', () =>
852    expect(evaluate(`(def onethird (makerat 1 3)) (printrat (addrat onehalf onethird))`, scope)).toEqual('5:6'));
853  it('sicp 117.3', () => expect(evaluate(`(printrat (mulrat onehalf onethird))`, scope)).toEqual('1:6'));
854  it('sicp 117.4', () => expect(evaluate(`(printrat (addrat onethird onethird))`, scope)).toEqual('6:9'));
855  it('sicp 118.1', () =>
856    expect(evaluate(`(def (makerat n d) (let ((g (gcd n d))) (cons (/ n g) (/ d g))))`, scope)).toEqual(0));
857  it('sicp 118.1', () => expect(evaluate(`(printrat (addrat onethird onethird))`, scope)).toEqual('2:3'));
858
859  let lscope = {};
860  // pairs with lambda
861  it('sicp 124.1', () =>
862    expect(
863      evaluate(
864        `
865(def (cons x y) 
866 (def (dispatch m)
867  (match 
868   ((eq m 0) x) 
869   ((eq m 1) y)
870   (else (error "argument not 0 or 1: CONS" m)))
871  ) dispatch)
872 (def (car z) (z 0)) 
873 (def (cdr z) (z 1))
874      `,
875        lscope,
876      ),
877    ).toEqual(0));
878  it('sicp 124.1', () => expect(evaluate(`(car (cons first second))`, lscope)).toEqual('first'));
879  it('sicp 124.2', () => expect(evaluate(`(cdr (cons first second))`, lscope)).toEqual('second'));
880  // lists
881  it('sicp 135.1', () => expect(evaluate(`(list 1 2 3 4)`)).toEqual([1, 2, 3, 4]));
882  it('sicp 137.1', () => expect(evaluate(`(car (list 1 2 3 4))`)).toEqual(1));
883  it('sicp 137.2', () => expect(evaluate(`(cdr (list 1 2 3 4))`)).toEqual([2, 3, 4]));
884  it('sicp 137.3', () => expect(evaluate(`(car (cdr (list 1 2 3 4)))`)).toEqual(2));
885  it('sicp 137.4', () => expect(evaluate(`(cons 10 (list 1 2 3 4))`)).toEqual([10, 1, 2, 3, 4]));
886  // listref
887  it('sicp 138.1', () =>
888    expect(
889      evaluate(
890        `
891(def (listref items n) (if (eq n 0) (car items) 
892 (listref (cdr items) (- n 1)))) 
893(def squares (list 1 4 9 16 25)) 
894(listref squares 3)`,
895        scope,
896      ),
897    ).toEqual(16));
898  // length recursive
899  it('sicp 138.2', () =>
900    expect(
901      evaluate(
902        `
903  (def (length items) 
904   (if (isnull items) 0
905    (+ 1 (length (cdr items))))) 
906  (def odds (list 1 3 5 7)) 
907  (length odds)`,
908      ),
909    ).toEqual(4));
910  // length iterative
911  it('sicp 139.1', () =>
912    expect(
913      evaluate(
914        `
915  (def (length items)
916  (def (lengthiter a count)
917   (if (isnull a) count
918    (lengthiter (cdr a) (+ 1 count))))
919    (lengthiter items 0))
920  (def odds (list 1 3 5 7)) 
921  (length odds)
922  `,
923        scope,
924      ),
925    ).toEqual(4));
926  // append
927  it('sicp 139.1', () =>
928    expect(
929      evaluate(
930        `
931(def (append list1 list2) 
932(if (isnull list1)
933  list2
934  (cons (car list1) (append (cdr list1) list2))))
935  (append squares odds)
936    `,
937        scope,
938      ),
939    ).toEqual([1, 4, 9, 16, 25, 1, 3, 5, 7]));
940  // (define (f x y . z) ⟨body⟩) <- tbd: variable argument count
941  // Mapping over lists
942
943  it('sicp 143.1', () =>
944    expect(
945      evaluate(
946        `
947(def (scalelist items factor) (if (isnull items) nil
948    (cons (* (car items) factor)
949          (scalelist (cdr items) factor))))
950(scalelist (list 1 2 3 4 5) 10)
951    `,
952        scope,
953      ),
954    ).toEqual([10, 20, 30, 40, 50]));
955
956  it('sicp 143.1', () =>
957    expect(
958      evaluate(
959        `
960  (def (map proc items) (if (isnull items) nil
961        (cons (proc (car items))
962              (map proc (cdr items)))))
963  (map abs (list -10 2.5 -11.6 17))
964  `,
965        scope,
966      ),
967    ).toEqual([10, 2.5, 11.6, 17]));
968  it('sicp 143.1', () => expect(evaluate(`(map (fn (x) (* x x)) (list 1 2 3 4))`, scope)).toEqual([1, 4, 9, 16]));
969  it('sicp 143.1', () =>
970    expect(
971      evaluate(
972        `
973(def (scalelist items factor) (map (fn (x) (* x factor)) items))
974(scalelist (list 1 2 3 4 5) 10)
975`,
976        scope,
977      ),
978    ).toEqual([10, 20, 30, 40, 50]));
979});