about summary refs log tree commit diff stats
path: root/js/baba-yaga/tests/math_namespace.test.js
blob: c892bbb52e782a72cb9c6290584756efa796741c (plain) (blame)
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
const assert = require('assert');
const { createLexer } = require('../src/core/lexer');
const { createParser } = require('../src/core/parser');
const { createInterpreter } = require('../src/core/interpreter');

function interpret(code) {
  const lexer = createLexer(code);
  const tokens = lexer.allTokens();
  const parser = createParser(tokens);
  const ast = parser.parse();
  const interpreter = createInterpreter(ast);
  interpreter.interpret();
  return interpreter;
}

describe('Math Namespace', () => {
  it('should support basic numeric ops: abs/floor/ceil/round/trunc', () => {
    const code = `
      a : math.abs -3;
      b : math.floor 2.9;
      c : math.ceil 2.1;
      d : math.round 2.5;
      e : math.trunc -2.9;
    `;
    const itp = interpret(code);
    assert.strictEqual(itp.scope.get('a').value, 3);
    assert.strictEqual(itp.scope.get('b').value, 2);
    assert.strictEqual(itp.scope.get('c').value, 3);
    assert.strictEqual(itp.scope.get('d').value, 3);
    assert.strictEqual(itp.scope.get('e').value, -2);
  });

  it('should support min/max/clamp', () => {
    const code = `
      a : math.min 10 3;
      b : math.max 10 3;
      c : math.clamp 15 0 10;
    `;
    const itp = interpret(code);
    assert.strictEqual(itp.scope.get('a').value, 3);
    assert.strictEqual(itp.scope.get('b').value, 10);
    assert.strictEqual(itp.scope.get('c').value, 10);
  });

  it('should support pow/sqrt/exp/log (with domain checks)', () => {
    const code = `
      p : math.pow 2 8;
      s : math.sqrt 9;
      e : math.exp 1;
      l : math.log 2.718281828;
    `;
    const itp = interpret(code);
    assert.strictEqual(itp.scope.get('p').value, 256);
    assert.strictEqual(itp.scope.get('s').value, 3);
    assert.ok(Math.abs(itp.scope.get('e').value - Math.E) < 1e-9);
    assert.ok(Math.abs(itp.scope.get('l').value - 1) < 1e-6);

    assert.throws(() => interpret('x : math.sqrt -1;'));
    assert.throws(() => interpret('x : math.log 0;'));
  });

  it('should support trig and conversions', () => {
    const code = `
      c : math.cos 0;
      s : math.sin 0;
      a : math.atan2 1 1;
      d : math.deg PI;
      r : math.rad 180;
    `;
    const itp = interpret(code);
    assert.ok(Math.abs(itp.scope.get('c').value - 1) < 1e-12);
    assert.ok(Math.abs(itp.scope.get('s').value - 0) < 1e-12);
    assert.ok(Math.abs(itp.scope.get('a').value - Math.PI/4) < 1e-6);
    assert.ok(Math.abs(itp.scope.get('d').value - 180) < 1e-12);
    assert.ok(Math.abs(itp.scope.get('r').value - Math.PI) < 1e-6);
  });

  it('should support random and randomInt', () => {
    const code = `
      one : math.randomInt 1 1;
    `;
    const itp = interpret(code);
    assert.strictEqual(itp.scope.get('one').value, 1);
  });

  it('should accept Int where Float is expected, and Number as supertype', () => {
    const code = `
      // Float-typed parameter accepts Int (widening)
      f : (x: Float) -> Float -> x;
      r1 : f 2;

      // Number supertype accepts Int or Float
      idN : (x: Number) -> Number -> x;
      n1 : idN 2;
      n2 : idN 2.5;

      // Return type Number accepts either Int or Float (use dummy arg since zero-arg call syntax is not supported)
      retN1 : (z: Int) -> Number -> 3;
      retN2 : (z: Int) -> Number -> 3.5;
      v1 : retN1 0;
      v2 : retN2 0;
    `;
    const itp = interpret(code);
    assert.strictEqual(itp.scope.get('r1').value, 2);
    assert.strictEqual(itp.scope.get('n1').value, 2);
    assert.ok(Math.abs(itp.scope.get('n2').value - 2.5) < 1e-12);
    assert.strictEqual(itp.scope.get('v1').value, 3);
    assert.ok(Math.abs(itp.scope.get('v2').value - 3.5) < 1e-12);
  });
});