diff --git a/examples/spec/tco_brackets.js b/examples/spec/tco_brackets.js new file mode 100644 index 0000000..300a3f5 --- /dev/null +++ b/examples/spec/tco_brackets.js @@ -0,0 +1,114 @@ +import { test, summary } from './helpers.js'; + +console.log('TCO Bracket Edge Cases\n'); + +function indexCallResult(n) { + if (n <= 0) return [42]; + return indexCallResult(n - 1); +} +test('f() then index (small)', indexCallResult(5)[0], 42); + +function indexByCall(n) { + if (n <= 0) return [99]; + return indexByCall(n - 1); +} +function zeroFn() { + return 0; +} +test('f()[g()] (small)', indexByCall(3)[zeroFn()], 99); + +function returnBracketAccess(arr) { + return arr[0]; +} +test('bare bracket access', returnBracketAccess([7]), 7); + +function indexArrByCall(arr) { + return arr[zeroFn()]; +} +test('arr[f()] access', indexArrByCall([55]), 55); + +const methods = { + greet() { + return 'hello'; + }, + farewell() { + return 'bye'; + } +}; +function computedCall(key) { + return methods[key](); +} +test('obj[key]() simple', computedCall('greet'), 'hello'); +test('obj[key]() simple 2', computedCall('farewell'), 'bye'); + +const ops = { + dec(n) { + return recurseViaComputed(n - 1); + } +}; +function recurseViaComputed(n) { + if (n <= 0) return 'done'; + return ops['dec'](n); +} +test('obj[key]() deep recursion', recurseViaComputed(100000), 'done'); + +function callWithBracketArg(arr) { + return identity(arr[0]); +} +function identity(x) { + return x; +} +test('f(a[0]) bracket in arg', callWithBracketArg([33]), 33); + +function callWithBracketBinopArg(arr) { + return identity(arr[0] + arr[1]); +} +test('f(a[0]+a[1]) binop inside args', callWithBracketBinopArg([10, 20]), 30); + +function recurseWithBracketArg(arr, i) { + if (i >= arr.length) return 0; + return recurseWithBracketArg(arr, i + 1); +} +test('deep with bracket arg', recurseWithBracketArg(new Array(100000), 0), 0); + +const handlers = {}; +for (let i = 0; i < 10; i++) { + handlers['h' + i] = function (n) { + if (n <= 0) return 'handled'; + return handlers['h' + i](n - 1); + }; +} +test('obj[key+expr]() result', handlers['h0'](50), 'handled'); + +const dispatch = { + action_run(n) { + if (n <= 0) return 'ran'; + return dispatch['action' + '_' + 'run'](n - 1); + } +}; +test('obj[a+b]() deep (may fail without bracket fix)', dispatch['action_run'](100000), 'ran'); + +const table = []; +for (let i = 0; i < 20; i++) { + table[i] = function () { + return i; + }; +} +function callFromTable(a) { + return table[a * 2 + 1](); +} +test('table[a*2+1]() computed', callFromTable(3), 7); + +function ternaryBracket(arr, flag) { + return flag ? identity(arr[0]) : identity(arr[1]); +} +test('ternary with bracket args true', ternaryBracket([10, 20], true), 10); +test('ternary with bracket args false', ternaryBracket([10, 20], false), 20); + +function ternaryComputedVsPlain(flag, n) { + if (n <= 0) return 'end'; + return flag ? ops['dec'](n) : ternaryComputedVsPlain(flag, n - 1); +} +test('ternary computed vs plain', ternaryComputedVsPlain(false, 100000), 'end'); + +summary(); diff --git a/examples/spec/tco_shift.js b/examples/spec/tco_shift.js new file mode 100644 index 0000000..7ea4a02 --- /dev/null +++ b/examples/spec/tco_shift.js @@ -0,0 +1,86 @@ +import { test, summary } from './helpers.js'; + +console.log('TCO Shift/Comparison Operator Tests\n'); + +function identity(x) { + return x; +} + +function shiftLeft(n) { + if (n <= 0) return identity(1) << 3; + return shiftLeft(n - 1); +} +test('f() << N result', shiftLeft(5), 8); + +function shiftRight(n) { + if (n <= 0) return identity(64) >> 2; + return shiftRight(n - 1); +} +test('f() >> N result', shiftRight(5), 16); + +function shiftRightUnsigned(n) { + if (n <= 0) return identity(-1) >>> 24; + return shiftRightUnsigned(n - 1); +} +test('f() >>> N result', shiftRightUnsigned(5), 255); + +function lessThan(n) { + if (n <= 0) return identity(3) < 5; + return lessThan(n - 1); +} +test('f() < N result', lessThan(3), true); + +function greaterThan(n) { + if (n <= 0) return identity(10) > 5; + return greaterThan(n - 1); +} +test('f() > N result', greaterThan(3), true); + +function lessEq(n) { + if (n <= 0) return identity(5) <= 5; + return lessEq(n - 1); +} +test('f() <= N result', lessEq(3), true); + +function greaterEq(n) { + if (n <= 0) return identity(5) >= 10; + return greaterEq(n - 1); +} +test('f() >= N result', greaterEq(3), false); + +function deepShiftLeft(n) { + if (n <= 0) return identity(1) << 3; + return deepShiftLeft(n - 1); +} +test('deep f()<> 2; + return deepShiftRight(n - 1); +} +test('deep f()>>N value correct', deepShiftRight(500), 16); + +function deepUnsignedShift(n) { + if (n <= 0) return identity(-1) >>> 24; + return deepUnsignedShift(n - 1); +} +test('deep f()>>>N value correct', deepUnsignedShift(500), 255); + +function recurseShiftArg(n) { + if (n <= 0) return 'done'; + return recurseShiftArg((n - 1) >> 0); +} +test('shift inside arg (tail-eligible)', recurseShiftArg(100000), 'done'); + +function ternaryShift(n) { + return n <= 0 ? identity(1) << 4 : ternaryShift(n - 1); +} +test('ternary with shift in then-branch', ternaryShift(5), 16); + +function ternaryBothShift(flag) { + return flag ? identity(1) << 2 : identity(1) >> 1; +} +test('ternary both shift true', ternaryBothShift(true), 4); +test('ternary both shift false', ternaryBothShift(false), 0); + +summary();