diff options
Diffstat (limited to 'js/baba-yaga/tests/math_namespace.test.js')
-rw-r--r-- | js/baba-yaga/tests/math_namespace.test.js | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/js/baba-yaga/tests/math_namespace.test.js b/js/baba-yaga/tests/math_namespace.test.js new file mode 100644 index 0000000..c892bbb --- /dev/null +++ b/js/baba-yaga/tests/math_namespace.test.js @@ -0,0 +1,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); + }); +}); + + |