From bbcabab453a735b7c19cec5f98b3c27a00411d30 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Tue, 8 Oct 2024 23:44:09 -0400 Subject: [PATCH 01/15] Remove non-det --- scripts/autocomplete.sh | 1 - scripts/updateAutocompleteDocs.js | 1 - src/__tests__/non-det-interpreter.ts | 1044 ------------------------ src/__tests__/non-det-sicp-examples.ts | 209 ----- src/alt-langs/scheme/scheme-mapper.ts | 2 +- src/constants.ts | 1 - src/createContext.ts | 5 - src/editors/ace/docTooltip/index.ts | 2 - src/interpreter/interpreter-non-det.ts | 698 ---------------- src/repl/index.ts | 2 - src/repl/repl-non-det.ts | 129 --- src/repl/utils.ts | 2 +- src/runner/sourceRunner.ts | 8 +- src/schedulers.ts | 26 - src/stdlib/non-det.prelude.ts | 28 - src/types.ts | 8 +- tsconfig.prod.json | 1 + 17 files changed, 6 insertions(+), 2161 deletions(-) delete mode 100644 src/__tests__/non-det-interpreter.ts delete mode 100644 src/__tests__/non-det-sicp-examples.ts delete mode 100644 src/interpreter/interpreter-non-det.ts delete mode 100644 src/repl/repl-non-det.ts delete mode 100644 src/stdlib/non-det.prelude.ts diff --git a/scripts/autocomplete.sh b/scripts/autocomplete.sh index 5a996deba..4b61c68d2 100755 --- a/scripts/autocomplete.sh +++ b/scripts/autocomplete.sh @@ -108,7 +108,6 @@ targets = [ "source_2_lazy", "source_3", "source_3_concurrent", - "source_3_non-det", "source_4", "source_4_explicit-control" "External libraries", diff --git a/scripts/updateAutocompleteDocs.js b/scripts/updateAutocompleteDocs.js index 0cfb7cd61..8d3ab860c 100644 --- a/scripts/updateAutocompleteDocs.js +++ b/scripts/updateAutocompleteDocs.js @@ -18,7 +18,6 @@ const TARGETS = [ "source_2_typed", "source_3", "source_3_concurrent", - "source_3_non-det", "source_3_typed", "source_4", "source_4_typed", diff --git a/src/__tests__/non-det-interpreter.ts b/src/__tests__/non-det-interpreter.ts deleted file mode 100644 index 8f35874cd..000000000 --- a/src/__tests__/non-det-interpreter.ts +++ /dev/null @@ -1,1044 +0,0 @@ -/* tslint:disable:max-line-length */ -import { IOptions, parseError, Result, resume, runInContext } from '../index' -import { mockContext } from '../mocks/context' -import { Context, Finished, SuspendedNonDet, Variant } from '../types' - -test('Empty code returns undefined', async () => { - await testDeterministicCode('', undefined) -}) - -test('Unary operations', async () => { - await testDeterministicCode('-12 - 8;', -20) - await testDeterministicCode('!true;', false) - await testDeterministicCode('!(false);', true) -}) - -test('Unary operations with non deterministic terms', async () => { - await testNonDeterministicCode('-amb(12, 24) - 8;', [-20, -32]) - await testNonDeterministicCode('!amb(true, false);', [false, true]) -}) - -test('Unary operations on the wrong type should cause error', async () => { - await testDeterministicCode('!100;', 'Line 1: Expected boolean, got number.', true) -}) - -test('Binary operations', async () => { - await testDeterministicCode('1 + 4 - 10 * 5;', -45) - await testDeterministicCode('"hello" + " world" + "!";', 'hello world!') - await testDeterministicCode('(23 % 3) * (10 / 2);', 10) -}) - -test('Binary operations with non deterministic terms', async () => { - await testNonDeterministicCode('1 + amb(4) - 10 * 5;', [-45]) - await testNonDeterministicCode('amb("hello", "bye") + " world" + "!";', [ - 'hello world!', - 'bye world!' - ]) - await testNonDeterministicCode('amb((23 % 3), 7) * amb((10 / 2), 19 - 5);', [10, 28, 35, 98]) -}) - -test('Binary operations on the wrong types should cause error', async () => { - await testDeterministicCode( - 'false + 4;', - 'Line 1: Expected string or number on left hand side of operation, got boolean.', - true - ) -}) - -test('Assignment', async () => { - await testDeterministicCode('let a = 5; a = 10; a;', 10) -}) - -test('Assignment with non deterministic terms', async () => { - await testNonDeterministicCode('let a = amb(1, 2); a = amb(4, 5); a;', [4, 5, 4, 5]) - - await testNonDeterministicCode( - `let num = 5; - function reassign_num() { num = 10; return num; } - amb(reassign_num(), num);`, - [10, 5] - ) -}) - -test('Re-assignment to constant should cause error', async () => { - await testDeterministicCode( - `const f = 10; { f = 20; }`, - 'Line 1: Cannot assign new value to constant f.', - true - ) -}) - -test('Accessing un-declared variable should cause error', async () => { - await testDeterministicCode(`let g = 100; f;`, 'Name f not declared.', true) -}) - -test('If-else and conditional expressions with non deterministic terms', async () => { - await testNonDeterministicCode('amb(false, true) ? 4 - 10 : 6;', [6, -6]) - await testNonDeterministicCode( - `if (amb(true, false)) { - -100; - } else { - 200 / 2; - 210; - }`, - [-100, 210] - ) - await testNonDeterministicCode( - `if (amb(100 * 2 === 2, 40 % 2 === 0)) { - amb(false, 'test' === 'test') ? amb(false === false, false) ? "hello" : false : amb(5, "world"); - } else { - 9 * 10 / 5; - }`, - [18, 5, 'world', 'hello', false] - ) -}) - -test('Conditional expression with non boolean predicate should cause error', async () => { - await testDeterministicCode( - '100 ? 5 : 5;', - 'Line 1: Expected boolean as condition, got number.', - true - ) -}) - -test('Logical expressions', async () => { - await testDeterministicCode(`true && (false || true) && (true && false);`, false) - - await testDeterministicCode( - `function foo() { return foo(); }\ - true || foo();`, - true - ) - - await testDeterministicCode( - `function foo() { return foo(); }\ - false && foo();`, - false - ) -}) - -test('Logical expressions with non deterministic terms', async () => { - await testNonDeterministicCode( - `amb(true, false) && amb(false, true) || amb(false); - `, - [false, true, false] - ) -}) - -test('Function applications', async () => { - await testDeterministicCode( - `function f() {} f(); - `, - undefined - ) - - await testDeterministicCode( - `function factorial(n) { - return n === 0 ? 1 : n * factorial(n - 1); - } - factorial(5); - `, - 120 - ) - - await testDeterministicCode( - 'function f(x) { function subfunction(y) { return y * 2; } return x * subfunction(10); } f(6);', - 120 - ) - - await testDeterministicCode( - `function noReturnStatement_returnsUndefined() { - 20 + 40 - 6; - 5 - 5; - list(); - reverse(list(1)); - }`, - undefined - ) - - await testDeterministicCode(`const a = 2; a();`, 'Line 1: Calling non-function value 2.', true) - - await testDeterministicCode( - `(function() {})();`, - 'Line 1: Function expressions are not allowed', - true - ) - - await testDeterministicCode( - `function ignoreStatementsAfterReturn(n) { - return n; return n * 2; - } - ignoreStatementsAfterReturn(5); - `, - 5 - ) -}) - -test('Applying functions with wrong number of arguments should cause error', async () => { - await testDeterministicCode( - `function foo(a, b) { - return a + b; - } - foo(1); - `, - `Line 4: Expected 2 arguments, but got 1.`, - true - ) -}) - -test('Builtin list functions', async () => { - await testDeterministicCode('pair(false, 10);', [false, 10]) - await testDeterministicCode('list();', null) - await testDeterministicCode('list(1);', [1, null]) - await testDeterministicCode('head(list(1));', 1) - await testDeterministicCode('tail(list(1));', null) -}) - -test('Builtin list functions with non deterministic terms', async () => { - await testNonDeterministicCode('pair(amb(false, true), 10);', [ - [false, 10], - [true, 10] - ]) - await testNonDeterministicCode('list(amb());', []) - await testNonDeterministicCode('list(amb(1,2));', [ - [1, null], - [2, null] - ]) - await testNonDeterministicCode('head(amb(list(100), list(20, 30)));', [100, 20]) -}) - -test('Prelude list functions', async () => { - await testDeterministicCode('is_null(null);', true) - await testDeterministicCode('is_null(list(null));', false) - await testDeterministicCode( - `function increment(n) { return n + 1; } - map(increment, list(100, 101, 200)); - `, - [101, [102, [201, null]]] - ) - await testDeterministicCode('append(list(5), list(6,20));', [5, [6, [20, null]]]) - await testDeterministicCode('append(list(4,5), list());', [4, [5, null]]) - await testDeterministicCode('reverse(list("hello", true, 0));', [0, [true, ['hello', null]]]) -}) - -test('Empty amb application', async () => { - await testNonDeterministicCode('amb();', []) -}) - -test('Simple amb application', async () => { - await testNonDeterministicCode('amb(1, 4 + 5, 3 - 10);', [1, 9, -7]) -}) - -test('Functions with non deterministic terms', async () => { - await testNonDeterministicCode( - `function foo() { - return amb(true, false) ? 'a string' : amb(10, 20); - } - foo();`, - ['a string', 10, 20] - ) -}) - -test('Functions as amb arguments', async () => { - await testNonDeterministicCode( - ' const is_even = num => (num % 2) === 0;\ - const add_five = num => num + 5;\ - const nondet_func = amb(is_even, add_five, num => !is_even(num));\ - nondet_func(5);\ - ', - [false, 10, true] - ) -}) - -test('Combinations of amb', async () => { - await testNonDeterministicCode('list(amb(1, 2, 3), amb("a", "b"));', [ - [1, ['a', null]], - [1, ['b', null]], - [2, ['a', null]], - [2, ['b', null]], - [3, ['a', null]], - [3, ['b', null]] - ]) -}) - -test('Require operator', async () => { - await testNonDeterministicCode( - ' \ - function int_between(low, high) { \ - return low > high ? amb() : amb(low, int_between(low + 1, high)); \ - } \ - let integer = int_between(5, 10);\ - require(integer % 3 === 0); \ - integer;\ - ', - [6, 9] - ) - - await testNonDeterministicCode( - `const f = an_integer_between(1, 10); require(f > 3, true); f;`, - ['Line 1: Expected 1 arguments, but got 2.'], - true - ) -}) - -test('Cut operator', async () => { - await testNonDeterministicCode( - `const f = amb(1, 2, 3); cut(); f + amb(4, 5, 6); - `, - [5, 6, 7] - ) - - await testNonDeterministicCode( - `const f = amb(1, 2, 3); const g = amb(4, 5, 6); cut(); f + g; - `, - [5] - ) -}) - -/* Deterministic block scoping tests taken from block-scoping.ts */ - -test('Block statements', async () => { - await testDeterministicCode( - ` - function test(){ - const x = true; - { - const x = false; - } - return x; - } - test(); - `, - true - ) - - await testDeterministicCode( - ` - function test(){ - let x = true; - if(true) { - let x = false; - } else { - let x = false; - } - return x; - } - test(); - `, - true - ) - - await testDeterministicCode( - ` - const v = f(); - function f() { - return 1; - } - v; - `, - 'Name f declared later in current scope but not yet assigned', - true - ) - - await testDeterministicCode( - ` - const a = 1; - function f() { - display(a); - const a = 5; - } - f(); - `, - 'Name a declared later in current scope but not yet assigned', - true - ) - - await testDeterministicCode( - ` - const a = 1; - { - a + a; - const a = 10; - } - `, - 'Name a declared later in current scope but not yet assigned', - true - ) - - await testDeterministicCode( - ` - let variable = 1; - function test(){ - variable = 100; - let variable = true; - return variable; - } - test(); - `, - 'Name variable not declared.', - true - ) -}) - -test('ambR application', async () => { - await testNonDeterministicCode('ambR();', [], false, true) - - await testNonDeterministicCode('ambR(1, 2, 3, 4, 5);', [1, 2, 3, 4, 5], false, true) - - await testNonDeterministicCode( - 'ambR(ambR(4, 5, 6, 7), ambR(3, 8));', - [4, 5, 6, 7, 3, 8], - false, - true - ) -}) - -test('Deterministic arrays', async () => { - await testDeterministicCode(`[];`, []) - - await testDeterministicCode(`const a = [[1, 2], [3, [4]]]; a;`, [ - [1, 2], - [3, [4]] - ]) - - await testDeterministicCode(`const a = [[[[6]]]]; a[0][0][0][0];`, 6) - - await testDeterministicCode(`const f = () => 2; const a = [1, f(), 3]; a;`, [1, 2, 3]) - - await testDeterministicCode( - `[1, 1, 1][4.4];`, - 'Line 1: Expected array index as prop, got other number.', - true - ) - - await testDeterministicCode( - `[1, 1, 1]["str"] = 2;`, - 'Line 1: Expected array index as prop, got string.', - true - ) - - await testDeterministicCode(`4[0];`, 'Line 1: Expected object or array, got number.', true) -}) - -test('Non-deterministic array values', async () => { - await testNonDeterministicCode(`const a = [amb(1, 2), amb(3, 4)]; a;`, [ - [1, 3], - [1, 4], - [2, 3], - [2, 4] - ]) - - await testNonDeterministicCode(`const a = [1, 2, 3, 4]; a[2] = amb(10, 11, 12); a;`, [ - [1, 2, 10, 4], - [1, 2, 11, 4], - [1, 2, 12, 4] - ]) -}) - -test('Non-deterministic array objects', async () => { - await testNonDeterministicCode( - `const a = [1, 2]; const b = [3, 4]; - amb(a, b)[1] = 99; a; - `, - [ - [1, 99], - [1, 2] - ] - ) - - await testNonDeterministicCode( - `const a = [1, 2]; const b = [3, 4]; - amb(a, b)[1] = 99; b; - `, - [ - [3, 4], - [3, 99] - ] - ) -}) - -test('Non-deterministic array properties', async () => { - await testNonDeterministicCode( - ` - const a = [100, 101, 102, 103]; - a[amb(0, 1, 2, 3)] = 999; a; - `, - [ - [999, 101, 102, 103], - [100, 999, 102, 103], - [100, 101, 999, 103], - [100, 101, 102, 999] - ] - ) -}) - -test('Material Conditional', async () => { - await testDeterministicCode(`implication(true, true);`, true) - await testDeterministicCode(`implication(true, false);`, false) - await testDeterministicCode(`implication(false, true);`, true) - await testDeterministicCode(`implication(false, false);`, true) -}) - -test('Material Biconditional', async () => { - await testDeterministicCode(`bi_implication(true, true);`, true) - await testDeterministicCode(`bi_implication(true, false);`, false) - await testDeterministicCode(`bi_implication(false, true);`, false) - await testDeterministicCode(`bi_implication(false, false);`, true) -}) - -test('While loops', async () => { - await testDeterministicCode( - ` - let i = 2; - while (false) { - i = i - 1; - } - i;`, - 2 - ) - - await testDeterministicCode( - ` - let i = 5; - while (i > 0) { - i = i - 1; - }`, - 0 - ) - - await testDeterministicCode( - ` - let i = 5; - let j = 0; - while (i > 0 && j < 5) { - i = i - 1; - j = j + 2; - }`, - 6 - ) - - await testDeterministicCode( - ` - let i = 2; - while (i) { - i = i - 1; - } - i;`, - 'Line 3: Expected boolean as condition, got number.', - true - ) -}) - -test('Let statement should be block scoped in body of while loop', async () => { - await testDeterministicCode( - ` - let i = 2; - let x = 5; - while (i > 0) { - i = i - 1; - let x = 10; - } - x;`, - 5 - ) - - await testDeterministicCode( - ` - let i = 2; - while (i > 0) { - i = i - 1; - let x = 5; - } - x;`, - 'Name x not declared.', - true - ) -}) - -test('Nested while loops', async () => { - await testDeterministicCode( - ` - let count = 0; - let i = 1; - while (i > 0) { - let j = 2; - while (j > 0) { - let k = 4; - while (k > 0) { - count = count + 1; - k = k - 1; - } - j = j - 1; - } - i = i - 1; - } - count;`, - 8 - ) -}) - -test('Break statement in while loop body', async () => { - await testDeterministicCode( - ` - let i = 5; - while (i > 0) { - i = i - 1; - break; - } - i;`, - 4 - ) -}) - -test('Continue statement in while loop body', async () => { - await testDeterministicCode( - ` - let i = 5; - let j = 0; - while (i > 0 && j < 5) { - i = i - 1; - continue; - j = j + 2; - } - j;`, - 0 - ) -}) - -test('Return statement in while loop body', async () => { - await testDeterministicCode( - ` - function loopTest(i, j) { - while (i > 0 && j > i) { - return i * j; - i = i - 1; - j = j + i; - } - } - loopTest(5, 10);`, - 50 - ) -}) - -test('Non-deterministic while loop condition', async () => { - await testNonDeterministicCode( - ` - let i = amb(3, 4); - let j = 0; - while (i > 0) { - i = i - 1; - j = j + 1; - } - j;`, - [3, 4] - ) - - await testNonDeterministicCode( - ` - let i = 1; - let j = 2; - let count = 0; - while (amb(i, j) > 0) { - i = i - 1; - j = j - 1; - count = count + 1; - } - count;`, - [1, 2, 2, 1, 2, 2] - ) // chosen variables: (i,i), (i,j,i), (i,j,j), (j,i), (j,j,i), (j,j,j) -}) - -test('Non-deterministic while loop body', async () => { - /* number of total program values = - (number of values from cartesian product of the statements in loop body)^ - (number of loop iterations) - */ - - await testNonDeterministicCode( - ` - let i = 3; - let count = 0; - while (i > 0) { - count = count + amb(0, 1); - i = i - 1; - } - count;`, - [0, 1, 1, 2, 1, 2, 2, 3] - ) - - await testNonDeterministicCode( - ` - let i = 2; - let count = 0; - while (i > 0) { - count = count + amb(0, 1); - count = count + amb(0, 1); - i = i - 1; - } - count;`, - [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4] - ) -}) - -test('For loops', async () => { - await testDeterministicCode( - ` - let i = 0; - for (i; i < 0; i = i + 1) { - } - i; - `, - 0 - ) - - await testDeterministicCode( - ` - for (let i = 0; i < 5; i = i + 1) { - i; - } - `, - 4 - ) - - await testDeterministicCode( - ` - let count = 0; - for (let i = 5; i > 0; i = i - 2) { - count = count + 1; - } - count; - `, - 3 - ) - - await testDeterministicCode( - ` - for (let i = 0; 2; i = i + 1) { - } - `, - 'Line 2: Expected boolean as condition, got number.', - true - ) -}) - -test('Let statement should be block scoped in head of for loop', async () => { - await testDeterministicCode( - ` - for (let i = 2; i > 0; i = i - 1) { - } - i; - `, - 'Name i not declared.', - true - ) -}) - -test('Let statement should be block scoped in body of for loop', async () => { - await testDeterministicCode( - ` - let x = 0; - for (x; x < 10; x = x + 1) { - let x = 1; - } - x;`, - 10 - ) - - await testDeterministicCode( - ` - for (let i = 2; i > 0; i = i - 1) { - let x = 5; - } - x; - `, - 'Name x not declared.', - true - ) -}) - -test('Loop control variable should be copied into for loop body', async () => { - await testDeterministicCode( - ` - let arr = []; - for (let i = 0; i < 5; i = i + 1) { - arr[i] = () => i; - } - arr[3]();`, - 3 - ) -}) - -test('Assignment to loop control variable', async () => { - await testDeterministicCode( - ` - for (let i = 0; i < 2; i = i + 1){ - i = i + 1; - } - `, - 'Line 3: Assignment to a for loop variable in the for loop is not allowed.', - true - ) - - await testDeterministicCode( - ` - let i = 0; - for (i; i < 2; i = i + 1){ - i = i + 1; - } - i;`, - 2 - ) -}) - -test('Nested for loops', async () => { - await testDeterministicCode( - ` - let count = 0; - for (let i = 0; i < 1; i = i + 1) { - for (let j = 0; j < 2; j = j + 1) { - for (let k = 0; k < 4; k = k + 1) { - count = count + 1; - } - } - } - count; - `, - 8 - ) -}) - -test('Break statement in for loop body', async () => { - await testDeterministicCode( - ` - let count = 0; - for (let i = 0; i < 5; i = i + 1) { - break; - count = count + 1; - } - count;`, - 0 - ) -}) - -test('Continue statement in for loop body', async () => { - await testDeterministicCode( - ` - let count = 0; - for (let i = 0; i < 5; i = i + 1) { - continue; - count = count + 1; - } - count;`, - 0 - ) -}) - -test('Return statement in for loop body', async () => { - await testDeterministicCode( - ` - let count = 0; - function loopTest(x) { - for (let i = 0; i < 5; i = i + 1) { - return x; - count = count + 1; - } - } - loopTest(10);`, - 10 - ) -}) - -test('Non-deterministic for loop initializer', async () => { - await testNonDeterministicCode( - ` - let j = 0; - for (let i = amb(3, 4); i > 0; i = i - 1) { - j = j + 1; - } - j;`, - [3, 4] - ) -}) - -test('Non-deterministic for loop condition', async () => { - await testNonDeterministicCode( - ` - let count = 0; - for (let i = 2; i > amb(0, 1); i = i - 1) { - count = count + 1; - } - count;`, - [2, 2, 1, 2, 2, 1] - ) // chosen conditions: (0, 0, 0), (0, 0, 1), (0, 1), (1, 0, 0), (1, 0, 1), (1, 1) -}) - -test('Non-deterministic for loop update', async () => { - await testNonDeterministicCode( - ` - let count = 0; - for (let i = 2; i > 0; i = i - amb(1, 2)) { - count = count + 1; - } - count;`, - [2, 2, 1] - ) // chosen updates: (1, 1), (1, 2), (2) -}) - -test('Non-deterministic for loop body', async () => { - /* number of total program values = - (number of values from cartesian product of the statements in loop body)^ - (number of loop iterations) - */ - - await testNonDeterministicCode( - ` - let count = 0; - for (let i = 3; i > 0; i = i - 1) { - count = count + amb(0, 1); - } - count;`, - [0, 1, 1, 2, 1, 2, 2, 3] - ) - - await testNonDeterministicCode( - ` - let count = 0; - for (let i = 2; i > 0; i = i - 1) { - count = count + amb(0, 1); - count = count + amb(0, 1); - } - count;`, - [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4] - ) - - await testNonDeterministicCode( - ` - const N = 2; - let arr = []; - for (let r = 0; r < N; r = r + 1) { - arr[r] = []; - for (let c = 0; c < N; c = c + 1) { - arr[r][c] = an_integer_between(1, N); - } - } - require(arr[0][0] === 2); - arr;`, - [ - [ - [2, 1], - [1, 1] - ], - [ - [2, 1], - [1, 2] - ], - [ - [2, 1], - [2, 1] - ], - [ - [2, 1], - [2, 2] - ], - [ - [2, 2], - [1, 1] - ], - [ - [2, 2], - [1, 2] - ], - [ - [2, 2], - [2, 1] - ], - [ - [2, 2], - [2, 2] - ] - ] - ) -}) - -// ---------------------------------- Helper functions ------------------------------------------- - -const nonDetTestOptions = { - executionMethod: 'interpreter' -} as Partial - -export async function testDeterministicCode( - code: string, - expectedValue: any, - hasError: boolean = false -) { - /* a deterministic program is equivalent to a non deterministic program - that returns a single value */ - await testNonDeterministicCode(code, [expectedValue], hasError) -} - -/* Assumes the error message (if any) is at the last index of expectedValues */ -export async function testNonDeterministicCode( - code: string, - expectedValues: any[], - hasError: boolean = false, - random: boolean = false -) { - const context: Context = makeNonDetContext() - let result: Result = await runInContext(code, context, nonDetTestOptions) - - const results: any[] = [] - const numOfRuns = hasError ? expectedValues.length - 1 : expectedValues.length - for (let i = 0; i < numOfRuns; i++) { - if (random) { - results.push((result as SuspendedNonDet).value) - } else { - expect((result as SuspendedNonDet).value).toEqual(expectedValues[i]) - } - - expect(result.status).toEqual('suspended-non-det') - result = await resume(result) - } - - if (random) { - verifyRandomizedTest(results, expectedValues) - } - - if (hasError) { - verifyError(result, expectedValues, context) - } else { - verifyFinalResult(result) - } -} - -/* Checks the final result obtained for a test - * Assumes the test is not erroneous - */ -function verifyFinalResult(result: Result) { - // all non deterministic programs have a final result whose value is undefined - expect(result.status).toEqual('finished') - expect((result as Finished).value).toEqual(undefined) -} - -/* Checks the error obtained for an erroneous test - * The error message is captured as the test's final result - */ -function verifyError(result: Result, expectedValues: any[], context: Context) { - expect(result.status).toEqual('error') - const message: string = parseError(context.errors) - expect(message).toEqual(expectedValues[expectedValues.length - 1]) -} - -/* Compares expected and obtained results after a test is run - * Assumes the test involves randomization - */ -function verifyRandomizedTest(results: any[], expectedValues: any[]) { - results.sort() - expectedValues.sort() - expect(results).toEqual(expectedValues) -} - -function makeNonDetContext() { - const context = mockContext(3, Variant.NON_DET) - context.executionMethod = 'interpreter' - return context -} diff --git a/src/__tests__/non-det-sicp-examples.ts b/src/__tests__/non-det-sicp-examples.ts deleted file mode 100644 index 5c8ee8896..000000000 --- a/src/__tests__/non-det-sicp-examples.ts +++ /dev/null @@ -1,209 +0,0 @@ -/* This file uses programs from SICP JS 4.3 as tests for the non deterministic interpreter */ -import { testNonDeterministicCode } from './non-det-interpreter' - -test('An element of', async () => { - await testNonDeterministicCode(`an_element_of(list(1, 2, list(3, 4)));`, [1, 2, [3, [4, null]]]) -}) - -test('An integer between', async () => { - await testNonDeterministicCode('an_integer_between(5, 10);', [5, 6, 7, 8, 9, 10]) -}) - -test('Pythagorean triple', async () => { - await testNonDeterministicCode( - `function a_pythagorean_triple_between(low, high) { - const i = an_integer_between(low, high); - const j = an_integer_between(i, high); - const k = an_integer_between(j, high); - require(i * i + j * j === k * k); - return list(i, j, k); - } - a_pythagorean_triple_between(3, 5);`, - [[3, [4, [5, null]]]] - ) -}) - -test('Multiple dwelling problem', async () => { - await testNonDeterministicCode( - ` - function distinct(items) { - return is_null(items) - ? true - : is_null(tail(items)) - ? true - : is_null(member(head(items), tail(items))) - ? distinct(tail(items)) - : false; - } - function multiple_dwelling() { - const baker = amb(1, 2, 3, 4, 5); - const cooper = amb(1, 2, 3, 4, 5); - const fletcher = amb(1, 2, 3, 4, 5); - const miller = amb(1, 2, 3, 4, 5); - const smith = amb(1, 2, 3, 4, 5); - require(distinct(list(baker, cooper, fletcher, miller, smith))); - require(!(baker === 5)); - require(!(cooper === 1)); - require(!(fletcher === 5)); - require(!(fletcher === 1)); - require((miller > cooper)); - require(!(math_abs(smith - fletcher) === 1)); - require(!(math_abs(fletcher - cooper) === 1)); - return list(list("baker", baker), - list("cooper", cooper), - list("fletcher", fletcher), - list("miller", miller), - list("smith", smith)); - } - multiple_dwelling();`, - [ - [ - ['baker', [3, null]], - [ - ['cooper', [2, null]], - [ - ['fletcher', [4, null]], - [ - ['miller', [5, null]], - [['smith', [1, null]], null] - ] - ] - ] - ] - ] - ) -}) - -test('Language parsing', async () => { - await testNonDeterministicCode( - ` - let unparsed = null; - - const nouns = list("noun", "student", "professor", "cat", "class"); - const verbs = list("verb", "studies", "lectures", "eats", "sleeps"); - const articles = list("article", "the", "a"); - const prepositions = list("prep", "for", "to", "in", "by", "with"); - - function parse_word(word_list) { - require(! is_null(unparsed)); - require(member(head(unparsed), tail(word_list)) !== null); - const found_word = head(unparsed); - unparsed = tail(unparsed); - return list(head(word_list), found_word); - } - - function parse_prepositional_phrase() { - return list("prep-phrase", - parse_word(prepositions), - parse_noun_phrase()); - } - - function parse_verb_phrase() { - function maybe_extend(verb_phrase) { - return amb(verb_phrase, - maybe_extend(list("verb-phrase", - verb_phrase, - parse_prepositional_phrase()))); - } - return maybe_extend(parse_word(verbs)); - } - - function parse_simple_noun_phrase() { - return list("simple-noun-phrase", - parse_word(articles), - parse_word(nouns)); - } - - function parse_noun_phrase() { - function maybe_extend(noun_phrase) { - return amb(noun_phrase, - maybe_extend(list("noun-phrase", - noun_phrase, - parse_prepositional_phrase()))); - } - return maybe_extend(parse_simple_noun_phrase()); - } - - function parse_sentence() { - return list("sentence", - parse_noun_phrase(), - parse_verb_phrase()); - } - - function parse_input(input) { - unparsed = input; - const sent = parse_sentence(); - require(is_null(unparsed)); - return sent; - } - - parse_input(list("the", "student", "with", "the", "cat", "sleeps", "in", "the", "class")); - `, - [ - [ - 'sentence', - [ - [ - 'noun-phrase', - [ - [ - 'simple-noun-phrase', - [ - ['article', ['the', null]], - [['noun', ['student', null]], null] - ] - ], - [ - [ - 'prep-phrase', - [ - ['prep', ['with', null]], - [ - [ - 'simple-noun-phrase', - [ - ['article', ['the', null]], - [['noun', ['cat', null]], null] - ] - ], - null - ] - ] - ], - null - ] - ] - ], - [ - [ - 'verb-phrase', - [ - ['verb', ['sleeps', null]], - [ - [ - 'prep-phrase', - [ - ['prep', ['in', null]], - [ - [ - 'simple-noun-phrase', - [ - ['article', ['the', null]], - [['noun', ['class', null]], null] - ] - ], - null - ] - ] - ], - null - ] - ] - ], - null - ] - ] - ] - ] - ) -}) diff --git a/src/alt-langs/scheme/scheme-mapper.ts b/src/alt-langs/scheme/scheme-mapper.ts index 468c33870..cb1c3c43f 100644 --- a/src/alt-langs/scheme/scheme-mapper.ts +++ b/src/alt-langs/scheme/scheme-mapper.ts @@ -7,7 +7,7 @@ import { List, Pair } from "../../stdlib/list" import { Representation } from "../mapper" export function mapResultToScheme(res: Result): Result { - if (res.status === "finished" || res.status === "suspended-non-det") { + if (res.status === "finished") { return { ...res, value: decodeValue(res.value), diff --git a/src/constants.ts b/src/constants.ts index e33dbcc44..8847cb6a8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -38,7 +38,6 @@ export const sourceLanguages: Language[] = [ { chapter: Chapter.SOURCE_3, variant: Variant.DEFAULT }, { chapter: Chapter.SOURCE_3, variant: Variant.TYPED }, { chapter: Chapter.SOURCE_3, variant: Variant.CONCURRENT }, - { chapter: Chapter.SOURCE_3, variant: Variant.NON_DET }, { chapter: Chapter.SOURCE_4, variant: Variant.DEFAULT }, { chapter: Chapter.SOURCE_4, variant: Variant.TYPED }, { chapter: Chapter.SOURCE_4, variant: Variant.GPU }, diff --git a/src/createContext.ts b/src/createContext.ts index 2f5ab7dca..64db352ed 100644 --- a/src/createContext.ts +++ b/src/createContext.ts @@ -19,7 +19,6 @@ import { list_to_vector } from './stdlib/list' import { listPrelude } from './stdlib/list.prelude' import { localImportPrelude } from './stdlib/localImport.prelude' import * as misc from './stdlib/misc' -import { nonDetPrelude } from './stdlib/non-det.prelude' import * as parser from './stdlib/parser' import * as pylib from './stdlib/pylib' import * as stream from './stdlib/stream' @@ -849,10 +848,6 @@ function importPrelude(context: Context) { prelude += streamPrelude } - if (context.variant === Variant.NON_DET) { - prelude += nonDetPrelude - } - if (context.chapter <= +Chapter.SCHEME_1 && context.chapter >= +Chapter.FULL_SCHEME) { // Scheme preludes // scheme 1 is the "highest" scheme chapter, so we can just check if it's less than or equal to scheme 1 diff --git a/src/editors/ace/docTooltip/index.ts b/src/editors/ace/docTooltip/index.ts index fca24c46f..2373e165b 100644 --- a/src/editors/ace/docTooltip/index.ts +++ b/src/editors/ace/docTooltip/index.ts @@ -5,7 +5,6 @@ import * as source_2 from './source_2.json' import * as source_2_typed from './source_2_typed.json' import * as source_3 from './source_3.json' import * as source_3_concurrent from './source_3_concurrent.json' -import * as source_3_non_det from './source_3_non-det.json' import * as source_3_typed from './source_3_typed.json' import * as source_4 from './source_4.json' import * as source_4_typed from './source_4_typed.json' @@ -49,7 +48,6 @@ export const SourceDocumentation = { '2_typed': resolveImportInconsistency(source_2_typed), '3': resolveImportInconsistency(source_3), '3_concurrent': resolveImportInconsistency(source_3_concurrent), - '3_non-det': resolveImportInconsistency(source_3_non_det), '3_typed': resolveImportInconsistency(source_3_typed), '4': resolveImportInconsistency(source_4), '4_typed': resolveImportInconsistency(source_4_typed), diff --git a/src/interpreter/interpreter-non-det.ts b/src/interpreter/interpreter-non-det.ts deleted file mode 100644 index c4a47b7a6..000000000 --- a/src/interpreter/interpreter-non-det.ts +++ /dev/null @@ -1,698 +0,0 @@ -/* tslint:disable:max-classes-per-file */ -import * as es from 'estree' -import { cloneDeep } from 'lodash' - -import { CUT, UNKNOWN_LOCATION } from '../constants' -import Heap from '../cse-machine/heap' -import { uniqueId } from '../cse-machine/utils' -import * as errors from '../errors/errors' -import { RuntimeSourceError } from '../errors/runtimeSourceError' -import { Context, Environment, Node, Value } from '../types' -import { conditionalExpression, literal, primitive } from '../utils/ast/astCreator' -import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators' -import * as rttc from '../utils/rttc' -import Closure from './closure' - -class BreakValue {} - -class ContinueValue {} - -class ReturnValue { - constructor(public value: Value) {} -} - -const createEnvironment = ( - context: Context, - closure: Closure, - args: Value[], - callExpression?: es.CallExpression -): Environment => { - const environment: Environment = { - name: closure.functionName, // TODO: Change this - tail: closure.environment, - head: {}, - heap: new Heap(), - id: uniqueId(context) - } - if (callExpression) { - environment.callExpression = { - ...callExpression, - arguments: args.map(primitive) - } - } - closure.node.params.forEach((param, index) => { - const ident = param as es.Identifier - environment.head[ident.name] = args[index] - }) - return environment -} - -const createBlockEnvironment = (context: Context, name = 'blockEnvironment'): Environment => { - return { - name, - tail: currentEnvironment(context), - head: {}, - heap: new Heap(), - thisContext: context, - id: uniqueId(context) - } -} - -const handleRuntimeError = (context: Context, error: RuntimeSourceError): never => { - context.errors.push(error) - context.runtime.environments = context.runtime.environments.slice( - -context.numberOfOuterEnvironments - ) - throw error -} - -const DECLARED_BUT_NOT_YET_ASSIGNED = Symbol('Used to implement declaration') - -function declareIdentifier(context: Context, name: string, node: Node) { - const environment = currentEnvironment(context) - if (environment.head.hasOwnProperty(name)) { - const descriptors = Object.getOwnPropertyDescriptors(environment.head) - - return handleRuntimeError( - context, - new errors.VariableRedeclaration(node, name, descriptors[name].writable) - ) - } - environment.head[name] = DECLARED_BUT_NOT_YET_ASSIGNED - return environment -} - -function declareVariables(context: Context, node: es.VariableDeclaration) { - for (const declaration of node.declarations) { - declareIdentifier(context, (declaration.id as es.Identifier).name, node) - } -} - -function declareFunctionAndVariableIdentifiers(context: Context, node: es.BlockStatement) { - for (const statement of node.body) { - switch (statement.type) { - case 'VariableDeclaration': - declareVariables(context, statement) - break - case 'FunctionDeclaration': - if (statement.id === null) { - throw new Error( - 'Encountered a FunctionDeclaration node without an identifier. This should have been caught when parsing.' - ) - } - declareIdentifier(context, statement.id.name, statement) - break - } - } -} - -function defineVariable(context: Context, name: string, value: Value, constant = false) { - const environment = context.runtime.environments[0] - - if (environment.head[name] !== DECLARED_BUT_NOT_YET_ASSIGNED) { - return handleRuntimeError( - context, - new errors.VariableRedeclaration(context.runtime.nodes[0]!, name, !constant) - ) - } - - Object.defineProperty(environment.head, name, { - value, - writable: !constant, - enumerable: true - }) - - return environment -} - -function undefineVariable(context: Context, name: string) { - const environment = context.runtime.environments[0] - - Object.defineProperty(environment.head, name, { - value: DECLARED_BUT_NOT_YET_ASSIGNED, - writable: true, - enumerable: true - }) -} - -const currentEnvironment = (context: Context) => context.runtime.environments[0] -const popEnvironment = (context: Context) => context.runtime.environments.shift() -const pushEnvironment = (context: Context, environment: Environment) => - context.runtime.environments.unshift(environment) - -const getVariable = (context: Context, name: string, ensureVariableAssigned: boolean) => { - let environment: Environment | null = context.runtime.environments[0] - while (environment) { - if (environment.head.hasOwnProperty(name)) { - if (environment.head[name] === DECLARED_BUT_NOT_YET_ASSIGNED) { - if (ensureVariableAssigned) { - return handleRuntimeError( - context, - new errors.UnassignedVariable(name, context.runtime.nodes[0]) - ) - } else { - return DECLARED_BUT_NOT_YET_ASSIGNED - } - } else { - return environment.head[name] - } - } else { - environment = environment.tail - } - } - return handleRuntimeError(context, new errors.UndefinedVariable(name, context.runtime.nodes[0])) -} - -const setVariable = (context: Context, name: string, value: any) => { - let environment: Environment | null = context.runtime.environments[0] - while (environment) { - if (environment.head.hasOwnProperty(name)) { - if (environment.head[name] === DECLARED_BUT_NOT_YET_ASSIGNED) { - break - } - const descriptors = Object.getOwnPropertyDescriptors(environment.head) - if (descriptors[name].writable) { - environment.head[name] = value - return undefined - } - return handleRuntimeError( - context, - new errors.ConstAssignment(context.runtime.nodes[0]!, name) - ) - } else { - environment = environment.tail - } - } - return handleRuntimeError(context, new errors.UndefinedVariable(name, context.runtime.nodes[0])) -} - -const checkNumberOfArguments = ( - context: Context, - callee: Closure, - args: Value[], - exp: es.CallExpression -) => { - if (callee.node.params.length !== args.length) { - return handleRuntimeError( - context, - new errors.InvalidNumberOfArguments(exp, callee.node.params.length, args.length) - ) - } - return undefined -} - -/** - * Returns a random integer for a given interval (inclusive). - */ -function randomInt(min: number, max: number): number { - return Math.floor(Math.random() * (max - min + 1) + min) -} - -function* getAmbRArgs(context: Context, call: es.CallExpression) { - const args: Node[] = cloneDeep(call.arguments) - while (args.length > 0) { - const r = randomInt(0, args.length - 1) - const arg: Node = args.splice(r, 1)[0] - - yield* evaluate(arg, context) - } -} - -function* getArgs(context: Context, call: es.CallExpression) { - const args = cloneDeep(call.arguments) - return yield* cartesianProduct(context, args as es.Expression[], []) -} - -/* Given a list of non deterministic nodes, this generator returns every - * combination of values of these nodes */ -function* cartesianProduct( - context: Context, - nodes: es.Expression[], - nodeValues: Value[] -): IterableIterator { - if (nodes.length === 0) { - yield nodeValues - } else { - const currentNode = nodes.shift()! // we need the postfix ! to tell compiler that nodes array is nonempty - const nodeValueGenerator = evaluate(currentNode, context) - for (const nodeValue of nodeValueGenerator) { - nodeValues.push(nodeValue) - yield* cartesianProduct(context, nodes, nodeValues) - nodeValues.pop() - } - nodes.unshift(currentNode) - } -} - -function* getAmbArgs(context: Context, call: es.CallExpression) { - for (const arg of call.arguments) { - yield* evaluate(arg, context) - } -} - -function transformLogicalExpression(node: es.LogicalExpression): es.ConditionalExpression { - if (node.operator === '&&') { - return conditionalExpression(node.left, node.right, literal(false), node.loc) - } else { - return conditionalExpression(node.left, literal(true), node.right, node.loc) - } -} - -function* reduceIf( - node: es.IfStatement | es.ConditionalExpression, - context: Context -): IterableIterator { - const testGenerator = evaluate(node.test, context) - for (const test of testGenerator) { - const error = rttc.checkIfStatement(node, test, context.chapter) - if (error) { - return handleRuntimeError(context, error) - } - yield test ? node.consequent : node.alternate! - } -} - -export type Evaluator = (node: T, context: Context) => IterableIterator - -function* evaluateBlockSatement(context: Context, node: es.BlockStatement) { - declareFunctionAndVariableIdentifiers(context, node) - yield* evaluateSequence(context, node.body) -} - -function* evaluateSequence(context: Context, sequence: es.Statement[]): IterableIterator { - if (sequence.length === 0) { - return yield undefined - } - const firstStatement = sequence[0] - const sequenceValGenerator = evaluate(firstStatement, context) - if (sequence.length === 1) { - yield* sequenceValGenerator - } else { - sequence.shift() - let shouldUnshift = true - for (const sequenceValue of sequenceValGenerator) { - // prevent unshifting of cut operator - shouldUnshift = sequenceValue !== CUT - - if ( - sequenceValue instanceof ReturnValue || - sequenceValue instanceof BreakValue || - sequenceValue instanceof ContinueValue - ) { - yield sequenceValue - continue - } - - const res = yield* evaluateSequence(context, sequence) - if (res === CUT) { - // prevent unshifting of statements before cut - shouldUnshift = false - break - } - } - - if (shouldUnshift) sequence.unshift(firstStatement) - else return CUT - } -} - -function* evaluateConditional(node: es.IfStatement | es.ConditionalExpression, context: Context) { - const branchGenerator = reduceIf(node, context) - for (const branch of branchGenerator) { - yield* evaluate(branch, context) - } -} - -/** - * WARNING: Do not use object literal shorthands, e.g. - * { - * *Literal(node: es.Literal, ...) {...}, - * *ThisExpression(node: es.ThisExpression, ..._ {...}, - * ... - * } - * They do not minify well, raising uncaught syntax errors in production. - * See: https://github.com/webpack/webpack/issues/7566 - */ -// tslint:disable:object-literal-shorthand -// prettier-ignore -export const evaluators: { [nodeType: string]: Evaluator } = { - /** Simple Values */ - Literal: function*(node: es.Literal, _context: Context) { - yield node.value - }, - - ArrowFunctionExpression: function*(node: es.ArrowFunctionExpression, context: Context) { - yield Closure.makeFromArrowFunction(node, currentEnvironment(context), context) - }, - - ArrayExpression: function*(node: es.ArrayExpression, context: Context) { - const arrayGenerator = cartesianProduct(context, node.elements as es.Expression[], []) - for (const array of arrayGenerator) { - yield array.slice() // yield a new array to avoid modifying previous ones - } - }, - - Identifier: function*(node: es.Identifier, context: Context) { - return yield getVariable(context, node.name, true) - }, - - CallExpression: function*(node: es.CallExpression, context: Context) { - const callee = node.callee; - if (rttc.isIdentifier(callee)) { - switch (callee.name) { - case 'amb': - return yield* getAmbArgs(context, node) - case 'ambR': - return yield* getAmbRArgs(context, node) - case 'cut': - return yield CUT - } - } - - const calleeGenerator = evaluate(node.callee, context) - for (const calleeValue of calleeGenerator) { - const argsGenerator = getArgs(context, node) - for (const args of argsGenerator) { - yield* apply(context, calleeValue, args, node, undefined) - } - } - }, - - UnaryExpression: function*(node: es.UnaryExpression, context: Context) { - const argGenerator = evaluate(node.argument, context) - for (const argValue of argGenerator) { - const error = rttc.checkUnaryExpression(node, node.operator, argValue, context.chapter) - if (error) { - return handleRuntimeError(context, error) - } - yield evaluateUnaryExpression(node.operator, argValue) - } - return - }, - - BinaryExpression: function*(node: es.BinaryExpression, context: Context) { - const pairGenerator = cartesianProduct(context, [node.left, node.right], []) - for (const pair of pairGenerator) { - const leftValue = pair[0] - const rightValue = pair[1] - const error = rttc.checkBinaryExpression(node, node.operator, context.chapter, leftValue, rightValue) - if (error) { - return handleRuntimeError(context, error) - } - yield evaluateBinaryExpression(node.operator, leftValue, rightValue) - } - return - }, - - ConditionalExpression: function*(node: es.ConditionalExpression, context: Context) { - yield* evaluateConditional(node, context) - }, - - LogicalExpression: function*(node: es.LogicalExpression, context: Context) { - const conditional: es.ConditionalExpression = transformLogicalExpression(node) - yield* evaluateConditional(conditional, context) - }, - - VariableDeclaration: function*(node: es.VariableDeclaration, context: Context) { - const declaration = node.declarations[0] - const constant = node.kind === 'const' - const id = declaration.id as es.Identifier - const valueGenerator = evaluate(declaration.init!, context) - for (const value of valueGenerator) { - defineVariable(context, id.name, value, constant) - yield value - undefineVariable(context, id.name) - } - return undefined - }, - - MemberExpression: function*(node: es.MemberExpression, context: Context) { - // es.PrivateIdentifier is a ES2022 feature - const pairGenerator = cartesianProduct(context, [node.property as es.Expression, node.object as es.Expression], []) - for (const pair of pairGenerator) { - const prop = pair[0] - const obj = pair[1] - - const error = rttc.checkMemberAccess(node, obj, prop) - if (error) { - return yield handleRuntimeError(context, error) - } - - yield obj[prop] - } - - return - }, - - AssignmentExpression: function*(node: es.AssignmentExpression, context: Context) { - if (node.left.type === 'MemberExpression') { - // es.PrivateIdentifier is a ES2022 feature - const tripleGenerator = cartesianProduct(context, [node.right, node.left.property as es.Expression, node.left.object as es.Expression], []) - for (const triple of tripleGenerator) { - const val = triple[0] - const prop = triple[1] - const obj = triple[2] - - const error = rttc.checkMemberAccess(node, obj, prop) - if (error) { - return yield handleRuntimeError(context, error) - } - const originalElementValue = obj[prop] - obj[prop] = val - yield val - obj[prop] = originalElementValue - } - - return - } - - const id = node.left as es.Identifier - const originalValue = getVariable(context, id.name, false) - const valueGenerator = evaluate(node.right, context) - for (const value of valueGenerator) { - setVariable(context, id.name, value) - yield value - setVariable(context, id.name, originalValue) - } - return - }, - - FunctionDeclaration: function*(node: es.FunctionDeclaration, context: Context) { - const id = node.id - if (id === null) { - throw new Error("Encountered a FunctionDeclaration node without an identifier. This should have been caught when parsing.") - } - const closure = new Closure(node, currentEnvironment(context), context) - defineVariable(context, id.name, closure, true) - yield undefined - undefineVariable(context, id.name) - }, - - IfStatement: function*(node: es.IfStatement, context: Context) { - yield* evaluateConditional(node, context) - }, - - ExpressionStatement: function*(node: es.ExpressionStatement, context: Context) { - return yield* evaluate(node.expression, context) - }, - - ContinueStatement: function*(_node: es.ContinueStatement, _context: Context) { - yield new ContinueValue() - }, - - BreakStatement: function*(_node: es.BreakStatement, _context: Context) { - yield new BreakValue() - }, - - WhileStatement: function*(node: es.WhileStatement, context: Context) { - let value: Value // tslint:disable-line - function* loop(): Value { - const testGenerator = evaluate(node.test, context) - for (const test of testGenerator) { - const error = rttc.checkIfStatement(node.test, test, context.chapter) - if (error) return handleRuntimeError(context, error) - - if (test && - !(value instanceof ReturnValue) && - !(value instanceof BreakValue) - ) { - const iterationValueGenerator = evaluate(cloneDeep(node.body), context) - for (const iterationValue of iterationValueGenerator) { - value = iterationValue - yield* loop(); - } - } else { - if (value instanceof BreakValue || value instanceof ContinueValue) { - yield undefined - } else { - yield value - } - } - } - } - - yield* loop(); - }, - - ForStatement: function*(node: es.ForStatement, context: Context) { - let value: Value - function* loop(): Value { - const testGenerator = evaluate(node.test!, context) - for (const test of testGenerator) { - const error = rttc.checkIfStatement(node.test!, test, context.chapter) - if (error) return handleRuntimeError(context, error) - - if (test && - !(value instanceof ReturnValue) && - !(value instanceof BreakValue) - ) { - const iterationEnvironment = createBlockEnvironment(context, 'forBlockEnvironment') - pushEnvironment(context, iterationEnvironment) - for (const name in loopEnvironment.head) { - if (loopEnvironment.head.hasOwnProperty(name)) { - declareIdentifier(context, name, node) - defineVariable(context, name, loopEnvironment.head[name], true) - } - } - - const iterationValueGenerator = evaluate(cloneDeep(node.body), context) - for (const iterationValue of iterationValueGenerator) { - value = iterationValue - popEnvironment(context) - const updateNode = evaluate(node.update!, context) - for (const _update of updateNode) { - yield* loop(); - } - - pushEnvironment(context, iterationEnvironment) - } - popEnvironment(context) - } else { - if (value instanceof BreakValue || value instanceof ContinueValue) { - yield undefined - } else { - yield value - } - } - } - } - - // Create a new block scope for the loop variables - const loopEnvironment = createBlockEnvironment(context, 'forLoopEnvironment') - pushEnvironment(context, loopEnvironment) - - const initNode = node.init! - if (initNode.type === 'VariableDeclaration') { - declareVariables(context, initNode) - } - - const initNodeGenerator = evaluate(node.init!, context) - for (const _init of initNodeGenerator) { - const loopGenerator = loop() - for (const loopValue of loopGenerator) { - popEnvironment(context) - yield loopValue - pushEnvironment(context, loopEnvironment) - } - } - - popEnvironment(context) - }, - - ReturnStatement: function*(node: es.ReturnStatement, context: Context) { - const returnExpression = node.argument! - const returnValueGenerator = evaluate(returnExpression, context) - for (const returnValue of returnValueGenerator) { - yield new ReturnValue(returnValue) - } - }, - - BlockStatement: function*(node: es.BlockStatement, context: Context) { - // Create a new environment (block scoping) - const environment = createBlockEnvironment(context, 'blockEnvironment') - pushEnvironment(context, environment) - - const resultGenerator = evaluateBlockSatement(context, node) - for (const result of resultGenerator) { - popEnvironment(context) - yield result - pushEnvironment(context, environment) - } - popEnvironment(context) - }, - - Program: function*(node: es.BlockStatement, context: Context) { - context.numberOfOuterEnvironments += 1 - const environment = createBlockEnvironment(context, 'programEnvironment') - pushEnvironment(context, environment) - return yield* evaluateBlockSatement(context, node) - } -} -// tslint:enable:object-literal-shorthand - -export function* evaluate(node: Node, context: Context) { - const result = yield* evaluators[node.type](node, context) - return result -} - -export function* apply( - context: Context, - fun: Closure | Value, - args: Value[], - node: es.CallExpression, - thisContext?: Value -) { - if (fun instanceof Closure) { - checkNumberOfArguments(context, fun, args, node!) - const environment = createEnvironment(context, fun, args, node) - environment.thisContext = thisContext - pushEnvironment(context, environment) - const applicationValueGenerator = evaluateBlockSatement( - context, - cloneDeep(fun.node.body) as es.BlockStatement - ) - - // This function takes a value that may be a ReturnValue. - // If so, it returns the value wrapped in the ReturnValue. - // If not, it returns the default value. - function unwrapReturnValue(result: any, defaultValue: any) { - if (result instanceof ReturnValue) { - return result.value - } else { - return defaultValue - } - } - - for (const applicationValue of applicationValueGenerator) { - popEnvironment(context) - yield unwrapReturnValue(applicationValue, undefined) - pushEnvironment(context, environment) - } - - popEnvironment(context) - } else if (typeof fun === 'function') { - try { - yield fun.apply(thisContext, args) - } catch (e) { - // Recover from exception - context.runtime.environments = context.runtime.environments.slice( - -context.numberOfOuterEnvironments - ) - - const loc = node.loc ?? UNKNOWN_LOCATION - if (!(e instanceof RuntimeSourceError || e instanceof errors.ExceptionError)) { - // The error could've arisen when the builtin called a source function which errored. - // If the cause was a source error, we don't want to include the error. - // However if the error came from the builtin itself, we need to handle it. - return handleRuntimeError(context, new errors.ExceptionError(e, loc)) - } - throw e - } - } else { - return handleRuntimeError(context, new errors.CallingNonFunctionValue(fun, node)) - } - - return -} - -export { evaluate as nonDetEvaluate } diff --git a/src/repl/index.ts b/src/repl/index.ts index 51dfe4393..72065bc95 100644 --- a/src/repl/index.ts +++ b/src/repl/index.ts @@ -3,11 +3,9 @@ import { Command } from '@commander-js/extra-typings' import { getReplCommand } from './repl' -import { nonDetCommand } from './repl-non-det' import { transpilerCommand } from './transpiler' new Command() .addCommand(transpilerCommand) .addCommand(getReplCommand(), { isDefault: true }) - .addCommand(nonDetCommand) .parseAsync() diff --git a/src/repl/repl-non-det.ts b/src/repl/repl-non-det.ts deleted file mode 100644 index 0f5d09a73..000000000 --- a/src/repl/repl-non-det.ts +++ /dev/null @@ -1,129 +0,0 @@ -import type fslib from 'fs/promises' -import * as repl from 'repl' // 'repl' here refers to the module named 'repl' in index.d.ts -import { inspect } from 'util' -import { Command } from '@commander-js/extra-typings' - -import { createContext, type IOptions, parseError, type Result, resume, runInContext } from '..' -import { CUT, TRY_AGAIN } from '../constants' -import Closure from '../interpreter/closure' -import { Chapter, Context, SuspendedNonDet, Variant } from '../types' - -const NO_MORE_VALUES_MESSAGE: string = 'There are no more values of: ' -let previousInput: string | undefined // stores the input which is then shown when there are no more values for the program -let previousResult: Result // stores the result obtained when execution is suspended - -function _handleResult( - result: Result, - context: Context, - callback: (err: Error | null, result: any) => void -) { - if (result.status === 'finished' || result.status === 'suspended-non-det') { - previousResult = result - - if (result.value === CUT) result.value = undefined - callback(null, result.value) - } else { - const error = new Error(parseError(context.errors)) - // we do not display the stack trace, because the stack trace points to code within this REPL - // program, rather than the erroneous line in the user's program. Such a trace is too low level - // to be helpful. - error.stack = undefined - callback(error, undefined) - return - } -} - -function _try_again_message(): string | undefined { - if (previousInput) { - const message: string = NO_MORE_VALUES_MESSAGE + previousInput - previousInput = undefined - - return message - } else { - return undefined - } -} - -function _resume( - toResume: SuspendedNonDet, - context: Context, - callback: (err: Error | null, result: any) => void -) { - Promise.resolve(resume(toResume)).then((result: Result) => { - if (result.status === 'finished') result.value = _try_again_message() - _handleResult(result, context, callback) - }) -} - -function _try_again(context: Context, callback: (err: Error | null, result: any) => void) { - if (previousResult && previousResult.status === 'suspended-non-det') { - _resume(previousResult, context, callback) - } else { - callback(null, _try_again_message()) - } -} - -function _run( - cmd: string, - context: Context, - options: Partial, - callback: (err: Error | null, result: any) => void -) { - if (cmd.trim() === TRY_AGAIN) { - _try_again(context, callback) - } else { - previousInput = cmd.trim() - runInContext(cmd, context, options).then(result => { - _handleResult(result, context, callback) - }) - } -} - -function _startRepl(chapter: Chapter = Chapter.SOURCE_1, useSubst: boolean, prelude = '') { - // use defaults for everything - const context = createContext(chapter, Variant.NON_DET) - const options: Partial = { - executionMethod: 'interpreter', - useSubst - } - runInContext(prelude, context, options).then(preludeResult => { - if (preludeResult.status === 'finished' || preludeResult.status === 'suspended-non-det') { - console.dir(preludeResult.value, { depth: null }) - - repl.start( - // the object being passed as argument fits the interface ReplOptions in the repl module. - { - eval: (cmd, unusedContext, unusedFilename, callback) => { - _run(cmd, context, options, callback) - }, - // set depth to a large number so that `parse()` output will not be folded, - // setting to null also solves the problem, however a reference loop might crash - writer: output => { - return output instanceof Closure || typeof output === 'function' - ? output.toString() - : inspect(output, { - depth: 1000, - colors: true - }) - } - } - ) - } else { - throw new Error(parseError(context.errors)) - } - }) -} - -export const nonDetCommand = new Command('non-det') - .option('--useSubst') - .argument('') - .action(async (fileName, { useSubst }) => { - if (fileName !== undefined) { - const fs: typeof fslib = require('fs/promises') - const data = await fs.readFile(fileName, 'utf-8') - - _startRepl(Chapter.SOURCE_3, false, data) - } else { - _startRepl(Chapter.SOURCE_3, !!useSubst) - } - }) diff --git a/src/repl/utils.ts b/src/repl/utils.ts index bd4ddeeb7..aed3ab3e3 100644 --- a/src/repl/utils.ts +++ b/src/repl/utils.ts @@ -65,7 +65,7 @@ export function validChapterVariant(language: Language) { } export function handleResult(result: Result, context: Context, verboseErrors: boolean) { - if (result.status === 'finished' || result.status === 'suspended-non-det') { + if (result.status === 'finished') { if (result.representation !== undefined) { return result.representation } diff --git a/src/runner/sourceRunner.ts b/src/runner/sourceRunner.ts index 4ac64147d..af9fea5a2 100644 --- a/src/runner/sourceRunner.ts +++ b/src/runner/sourceRunner.ts @@ -12,13 +12,12 @@ import { transpileToGPU } from '../gpu/gpu' import { isPotentialInfiniteLoop } from '../infiniteLoops/errors' import { testForInfiniteLoop } from '../infiniteLoops/runtime' import { evaluateProgram as evaluate } from '../interpreter/interpreter' -import { nonDetEvaluate } from '../interpreter/interpreter-non-det' import { transpileToLazy } from '../lazy/lazy' import preprocessFileImports from '../modules/preprocessor' import { defaultAnalysisOptions } from '../modules/preprocessor/analyzer' import { defaultLinkerOptions } from '../modules/preprocessor/linker' import { parse } from '../parser/parser' -import { AsyncScheduler, NonDetScheduler, PreemptiveScheduler } from '../schedulers' +import { AsyncScheduler, PreemptiveScheduler } from '../schedulers' import { callee, getEvaluationSteps, @@ -117,10 +116,7 @@ function runSubstitution( function runInterpreter(program: es.Program, context: Context, options: IOptions): Promise { let it = evaluate(program, context) let scheduler: Scheduler - if (context.variant === Variant.NON_DET) { - it = nonDetEvaluate(program, context) - scheduler = new NonDetScheduler() - } else if (options.scheduler === 'async') { + if (options.scheduler === 'async') { scheduler = new AsyncScheduler() } else { scheduler = new PreemptiveScheduler(options.steps) diff --git a/src/schedulers.ts b/src/schedulers.ts index 00a7d7c52..94a521f7c 100644 --- a/src/schedulers.ts +++ b/src/schedulers.ts @@ -35,32 +35,6 @@ export class AsyncScheduler implements Scheduler { } } -export class NonDetScheduler implements Scheduler { - public run(it: IterableIterator, context: Context): Promise { - return new Promise((resolve, _reject) => { - try { - const itValue = it.next() - if (itValue.done) { - resolve({ status: 'finished', context, value: itValue.value }) - } else { - resolve({ - status: 'suspended-non-det', - it, - scheduler: this, - context, - value: itValue.value - } as Result) - } - } catch (e) { - checkForStackOverflow(e, context) - resolve({ status: 'error' }) - } finally { - context.runtime.isRunning = false - } - }) - } -} - export class PreemptiveScheduler implements Scheduler { constructor(public steps: number) {} diff --git a/src/stdlib/non-det.prelude.ts b/src/stdlib/non-det.prelude.ts deleted file mode 100644 index 06b2c1e06..000000000 --- a/src/stdlib/non-det.prelude.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const nonDetPrelude = ` - function require(predicate) { - return predicate ? "Satisfied require" : amb(); - } - - function an_element_of(items) { - require(!is_null(items)); - return amb(head(items), an_element_of(tail(items))); - } - - function an_integer_between(low, high) { - return low > high ? amb() : amb(low, an_integer_between(low + 1, high)); - } - - /* Material Conditional */ - /* if P, then Q */ - /* P --> Q */ - function implication(P, Q) { - return !P || Q; - } - - /* Material Biconditional */ - /* (if P, then Q) AND (if Q, then P) */ - /* P <--> Q */ - function bi_implication(P, Q) { - return implication(P, Q) && implication(Q, P); - } -` diff --git a/src/types.ts b/src/types.ts index 7dd4374ea..5a18e638a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,7 +95,6 @@ export enum Variant { NATIVE = 'native', WASM = 'wasm', LAZY = 'lazy', - NON_DET = 'non-det', CONCURRENT = 'concurrent', GPU = 'gpu', EXPLICIT_CONTROL = 'explicit-control' @@ -289,17 +288,12 @@ export interface Suspended { context: Context } -export type SuspendedNonDet = Omit & { status: 'suspended-non-det' } & { - value: Value - representation?: Representation // never used, only kept for consistency with Finished -} - export interface SuspendedCseEval { status: 'suspended-cse-eval' context: Context } -export type Result = Suspended | SuspendedNonDet | Finished | Error | SuspendedCseEval +export type Result = Suspended | Finished | Error | SuspendedCseEval export interface Scheduler { run(it: IterableIterator, context: Context): Promise diff --git a/tsconfig.prod.json b/tsconfig.prod.json index 0cc257b56..35361500d 100644 --- a/tsconfig.prod.json +++ b/tsconfig.prod.json @@ -14,5 +14,6 @@ "sicp_publish", "src/**/__tests__/**", "src/**/__mocks__/**", + "src/testing" ] } From 47b83cfbaba5cae494548cd653637157f9e2699a Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Wed, 9 Oct 2024 00:27:32 -0400 Subject: [PATCH 02/15] Continue removing non-det --- scripts/jsdoc.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/scripts/jsdoc.sh b/scripts/jsdoc.sh index 881d62fba..e03cf1659 100755 --- a/scripts/jsdoc.sh +++ b/scripts/jsdoc.sh @@ -130,21 +130,6 @@ run() { ${LIB}/pairmutator.js \ ${LIB}/concurrency.js - # Source §3 Non-Det - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_3_NON-DET.md \ - -d ${DST}/"source_3_non-det"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js \ - ${LIB}/non-det.js - # Source §3 Typed ${JSDOC} -r -t ${TMPL} \ From b77b2ec8b0d36ea35f68e8b1fe803171c9f7ad37 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 10 Oct 2024 23:05:07 -0400 Subject: [PATCH 03/15] Update eslint and fix linting errors --- .eslintignore | 3 - .eslintrc.json | 65 ------ .gitignore | 1 + eslint.config.mjs | 75 ++++++ package.json | 9 +- src/__tests__/stringify.ts | 2 +- src/cse-machine/interpreter.ts | 27 +-- src/editors/ace/modes/source.ts | 2 +- src/errors/errors.ts | 2 +- src/infiniteLoops/detect.ts | 1 + src/modules/loader/loaders.ts | 5 +- .../preprocessor/__tests__/analyzer.ts | 2 +- src/parser/source/typed/typeParser.ts | 51 ++-- src/repl/__tests__/repl.ts | 5 +- src/runner/__tests__/files.ts | 2 +- src/stepper/__tests__/stepper.ts | 2 +- src/validator/validator.ts | 2 +- yarn.lock | 220 ++++++++---------- 18 files changed, 243 insertions(+), 233 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 2e9a9a8e1..000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -/dist/ -/src/alt-langs/ -/src/py-slang/ diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 345d40c66..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true - }, - "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier" - ], - "ignorePatterns": ["src/**/__tests__/**", "src/**/__mocks__/**"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "tsconfig.json", - "sourceType": "module" - }, - "plugins": [ - "import", - "@typescript-eslint" - ], - "rules": { - "import/no-duplicates": ["warn", { "prefer-inline": true }], - "import/order": "warn", - "@typescript-eslint/no-base-to-string": "off", // TODO: Remove - "prefer-const": "off", // TODO: Remove - "no-var": "off", // TODO: Remove - "@typescript-eslint/ban-ts-comment": "warn", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/camelcase": "off", - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/interface-name-prefix": "off", - "@typescript-eslint/no-duplicate-type-constituents": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-implied-eval": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-non-null-asserted-optional-chain": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-redundant-type-constituents": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/require-await": "error", - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/unbound-method": "off", - "prefer-rest-params": "off" - }, - "overrides": [{ - "files": ["src/**/__tests__/**", "src/**/__mocks__/**"], - "plugins": ["import"], - "rules": { - "import/order": "warn" - } - }] -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 469d91d2e..88711fcd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules *.js +!eslint.config.js !docs/jsdoc/templates/**/*.js !docs/lib/**/*.js *.map diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..fd7ab611c --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,75 @@ +import tseslint from 'typescript-eslint' +import globals from 'globals' +import importPlugin from 'eslint-plugin-import' + +/** + * @type {import('eslint').Linter.FlatConfig[]} + */ +export default [ + { + // global ignores + ignores: ['dist', 'src/alt-langs', 'src/py-slang', 'src/__tests__/sicp', '**/*.snap'] + }, + ...tseslint.configs.recommended, + { + files: ['**/*.ts*'], + languageOptions: { + globals: { + ...globals.node, + ...globals.es2016, + ...globals.browser + }, + parser: tseslint.parser, + parserOptions: { + project: './tsconfig.json' + } + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + import: importPlugin + }, + rules: { + 'import/no-duplicates': ['warn', { 'prefer-inline': true }], + 'import/order': 'warn', + '@typescript-eslint/no-base-to-string': 'off', // TODO: Remove + 'prefer-const': 'off', // TODO: Remove + 'no-var': 'off', // TODO: Remove + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/camelcase': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/no-duplicate-type-constituents': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-implied-eval': 'off', + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-redundant-type-constituents': 'off', + '@typescript-eslint/no-unnecessary-type-assertion': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/restrict-plus-operands': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/unbound-method': 'off', + 'prefer-rest-params': 'off' + } + }, + { + files: ['**/*.js', 'src/repl/*.ts'], + rules: { + '@typescript-eslint/no-require-imports': 'off' + } + } +] diff --git a/package.json b/package.json index ca7b393fe..390b40b6b 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "scripts": { "build": "yarn docs && yarn build:slang", "build:slang": "tsc --project tsconfig.prod.json", - "eslint": "eslint --ext \".ts,.tsx\" src", + "eslint": "eslint src", "format": "prettier --write \"src/**/*.{ts,tsx}\"", "format:ci": "prettier --list-different \"src/**/*.{ts,tsx}\"", "test": "jest", @@ -72,14 +72,12 @@ "@types/lodash": "^4.14.202", "@types/node": "^20.0.0", "@types/offscreencanvas": "^2019.7.0", - "@typescript-eslint/eslint-plugin": "^7.3.1", - "@typescript-eslint/parser": "^7.3.1", "ace-builds": "^1.4.12", "coveralls": "^3.1.0", "escodegen": "^2.0.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", + "globals": "^15.11.0", "husky": "^8.0.1", "jest": "^29.0.0", "jest-environment-jsdom": "^29.0.0", @@ -88,7 +86,8 @@ "jsdom": "^19.0.0", "prettier": "^2.1.2", "ts-jest": "^29.0.0", - "typescript": "^4.0.3" + "typescript": "^4.0.3", + "typescript-eslint": "^8.8.1" }, "jest": { "snapshotFormat": { diff --git a/src/__tests__/stringify.ts b/src/__tests__/stringify.ts index 60b10301f..27b846b8b 100644 --- a/src/__tests__/stringify.ts +++ b/src/__tests__/stringify.ts @@ -405,7 +405,7 @@ test('String representation of non literal objects is nice', () => { test('String representation of non literal objects in nested object is nice', () => { const errorMsg: string = 'This is an error' const errorObj: Error = new Error(errorMsg) - const nestedObj: Object = { + const nestedObj = { data: [1, [2, errorObj], 3] } return expect(stringify(nestedObj)).toMatchInlineSnapshot( diff --git a/src/cse-machine/interpreter.ts b/src/cse-machine/interpreter.ts index 15c98912f..9b767eb99 100644 --- a/src/cse-machine/interpreter.ts +++ b/src/cse-machine/interpreter.ts @@ -96,7 +96,7 @@ export class Control extends Stack { super() this.numEnvDependentItems = 0 // Load program into control stack - program ? this.push(program) : null + if (program) this.push(program) } public canAvoidEnvInstr(): boolean { @@ -851,20 +851,17 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = { control: Control, stash: Stash ) { - command.declaration - ? defineVariable( - context, - command.symbol, - stash.peek(), - command.constant, - command.srcNode as es.VariableDeclaration - ) - : setVariable( - context, - command.symbol, - stash.peek(), - command.srcNode as es.AssignmentExpression - ) + if (command.declaration) { + defineVariable( + context, + command.symbol, + stash.peek(), + command.constant, + command.srcNode as es.VariableDeclaration + ) + } else { + setVariable(context, command.symbol, stash.peek(), command.srcNode as es.AssignmentExpression) + } }, [InstrType.UNARY_OP]: function ( diff --git a/src/editors/ace/modes/source.ts b/src/editors/ace/modes/source.ts index 2b402101d..44a5b01cf 100644 --- a/src/editors/ace/modes/source.ts +++ b/src/editors/ace/modes/source.ts @@ -21,7 +21,7 @@ import { SourceDocumentation } from '../docTooltip' export function HighlightRulesSelector( id: number, variant: Variant = Variant.DEFAULT, - external: String = 'NONE', + external: string = 'NONE', externalLibraries: ( | { caption: string diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 3ea1c41d7..b18dafac7 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -10,7 +10,7 @@ import { RuntimeSourceError } from './runtimeSourceError' //Wrap build-in function error in SourceError export class BuiltInFunctionError extends RuntimeSourceError { - constructor(private explanation: String) { + constructor(private explanation: string) { super(undefined) this.explanation = explanation } diff --git a/src/infiniteLoops/detect.ts b/src/infiniteLoops/detect.ts index af9b98416..680e0034d 100644 --- a/src/infiniteLoops/detect.ts +++ b/src/infiniteLoops/detect.ts @@ -7,6 +7,7 @@ import { getOriginalName } from './instrument' import * as st from './state' import { shallowConcretize } from './symbolic' +// eslint-disable-next-line @typescript-eslint/no-require-imports const runAltErgo: any = require('@joeychenofficial/alt-ergo-modified') const options = { diff --git a/src/modules/loader/loaders.ts b/src/modules/loader/loaders.ts index e9c6d508b..939dec1bb 100644 --- a/src/modules/loader/loaders.ts +++ b/src/modules/loader/loaders.ts @@ -122,8 +122,9 @@ export const memoizedGetModuleDocsAsync = getMemoizedDocsImporter() const bundleAndTabImporter = wrapImporter<{ default: ModuleBundle }>( typeof window !== 'undefined' && process.env.NODE_ENV !== 'test' - ? (new Function('path', 'return import(`${path}?q=${Date.now()}`)') as any) - : p => Promise.resolve(require(p)) + ? (new Function('path', 'return import(path)') as any) + : // eslint-disable-next-line @typescript-eslint/no-require-imports + p => Promise.resolve(require(p)) ) export async function loadModuleBundleAsync( diff --git a/src/modules/preprocessor/__tests__/analyzer.ts b/src/modules/preprocessor/__tests__/analyzer.ts index 40078d6cc..522102dd3 100644 --- a/src/modules/preprocessor/__tests__/analyzer.ts +++ b/src/modules/preprocessor/__tests__/analyzer.ts @@ -1,3 +1,4 @@ +import type { Program } from 'estree' import createContext from '../../../createContext' import { DuplicateImportNameError, @@ -11,7 +12,6 @@ import parseProgramsAndConstructImportGraph from '../linker' import analyzeImportsAndExports from '../analyzer' import { parse } from '../../../parser/parser' import { mockContext } from '../../../mocks/context' -import type { Program } from 'estree' import loadSourceModules from '../../loader' import type { SourceFiles as Files } from '../../moduleTypes' import { objectKeys } from '../../../utils/misc' diff --git a/src/parser/source/typed/typeParser.ts b/src/parser/source/typed/typeParser.ts index da509f65f..ca6f9c570 100644 --- a/src/parser/source/typed/typeParser.ts +++ b/src/parser/source/typed/typeParser.ts @@ -223,7 +223,11 @@ const tsPlugin = (BaseParser: any) => { this.awaitPos = 0 // Do not save awaitIdentPos to allow checking awaits nested in parameters while (this.type !== tokTypes.parenR) { - first ? (first = false) : this.expect(tokTypes.comma) + if (first) { + first = false + } else { + this.expect(tokTypes.comma) + } if (allowTrailingComma && this.afterTrailingComma(tokTypes.parenR, true)) { lastIsComma = true break @@ -325,7 +329,9 @@ const tsPlugin = (BaseParser: any) => { } parseTSTypeAnnotation(eatColon = true) { - eatColon && this.expect(tokTypes.colon) + if (eatColon) { + this.expect(tokTypes.colon) + } const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc) this._parseTSTypeAnnotation(node) return this.finishNode(node, 'TSTypeAnnotation') @@ -481,7 +487,11 @@ const tsPlugin = (BaseParser: any) => { this.eat(tokTypes.bracketL) let first = true while (!this.eat(tokTypes.bracketR)) { - first ? (first = false) : this.expect(tokTypes.comma) + if (first) { + first = false + } else { + this.expect(tokTypes.comma) + } switch (this.type) { case tokTypes.name: const elem = this.parseTSTypeReference() @@ -563,10 +573,13 @@ const tsPlugin = (BaseParser: any) => { return false } case tokTypes.braceL: - case tokTypes.bracketL: - this.type === tokTypes.braceL - ? this.parseObj(/* isPattern */ true) - : this.parseBindingAtom() + case tokTypes.bracketL: { + if (this.type === tokTypes.braceL) { + this.parseObj(/* isPattern */ true) + } else { + this.parseBindingAtom() + } + switch (this.type) { case tokTypes.colon: case tokTypes.comma: @@ -578,6 +591,7 @@ const tsPlugin = (BaseParser: any) => { default: return false } + } default: return false } @@ -608,7 +622,7 @@ const tsPlugin = (BaseParser: any) => { parseTSUnionType(first: any) { const node = first ? this.startNodeAtNode(first) : this.startNode() const types = [] - first && types.push(first) + if (first) types.push(first) while (this.eat(tokTypes.bitwiseOR)) { types.push(this._parseTSIntersectionTypeOrPrimaryType()) } @@ -622,7 +636,7 @@ const tsPlugin = (BaseParser: any) => { parseTSIntersectionType(first: any) { const node = first ? this.startNodeAtNode(first) : this.startNode() const types = [] - first && types.push(first) + if (first) types.push(first) while (this.eat(tokTypes.bitwiseAND)) { types.push(this._parsePrimaryType()) } @@ -817,7 +831,11 @@ const tsPlugin = (BaseParser: any) => { let first = true this.next() while (!this.eat(tokTypes.relational)) { - first ? (first = false) : this.expect(tokTypes.comma) + if (first) { + first = false + } else { + this.expect(tokTypes.comma) + } if (this._isEndOfTypeParameters()) { break } @@ -834,7 +852,11 @@ const tsPlugin = (BaseParser: any) => { this.next() // < let first = true while ((this.value && !this._isEndOfTypeParameters()) || this.type === tokTypes.comma) { - first ? (first = false) : this.expect(tokTypes.comma) + if (first) { + first = false + } else { + this.expect(tokTypes.comma) + } params.push(this._parseTSType()) } @@ -934,7 +956,7 @@ const tsPlugin = (BaseParser: any) => { if (this.type === tokTypes.colon) { node.typeAnnotation = this.parseTSTypeAnnotation(true) } - this.eat(tokTypes.comma) || this.eat(tokTypes.semi) + if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi) return this.finishNode(node, 'TSMethodSignature') } @@ -956,7 +978,7 @@ const tsPlugin = (BaseParser: any) => { if (this.type === tokTypes.colon) { node.typeAnnotation = this.parseTSTypeAnnotation(true) } - this.eat(tokTypes.comma) || this.eat(tokTypes.semi) + if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi) return this.finishNode(node, 'TSPropertySignature') } @@ -972,7 +994,7 @@ const tsPlugin = (BaseParser: any) => { node.index = index this.expect(tokTypes.bracketR) node.typeAnnotation = this.parseTSTypeAnnotation(true) - this.eat(tokTypes.comma) || this.eat(tokTypes.semi) + if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi) return this.finishNode(node, 'TSIndexSignature') } @@ -1041,6 +1063,7 @@ const tsPlugin = (BaseParser: any) => { } // acorn-class-fields plugin is needed, else parsing of some function types will not work +// eslint-disable-next-line @typescript-eslint/no-require-imports const TypeParser = Parser.extend(tsPlugin as any, require('acorn-class-fields')) export default TypeParser diff --git a/src/repl/__tests__/repl.ts b/src/repl/__tests__/repl.ts index bf2b4b35e..b3276464d 100644 --- a/src/repl/__tests__/repl.ts +++ b/src/repl/__tests__/repl.ts @@ -1,5 +1,5 @@ -import type { SourceFiles } from '../../modules/moduleTypes' import * as repl from 'repl' +import type { SourceFiles } from '../../modules/moduleTypes' import { Chapter } from '../../types' import { asMockedFunc } from '../../utils/testing' import { getReplCommand } from '../repl' @@ -20,7 +20,8 @@ jest.mock('fs/promises', () => ({ jest.mock('path', () => { const actualPath = jest.requireActual('path') - const newResolve = (...args: string[]) => actualPath.resolve('/', ...args) + const { resolve } = jest.requireActual('path/posix') + const newResolve = (...args: string[]) => resolve('/', ...args) return { ...actualPath, resolve: newResolve diff --git a/src/runner/__tests__/files.ts b/src/runner/__tests__/files.ts index a7ec831e6..4a355cd6a 100644 --- a/src/runner/__tests__/files.ts +++ b/src/runner/__tests__/files.ts @@ -77,7 +77,7 @@ describe('runFilesInContext', () => { describe('compileFiles', () => { let context = mockContext(Chapter.SOURCE_4) - beforeEach(async () => { + beforeEach(() => { context = mockContext(Chapter.SOURCE_4) }) diff --git a/src/stepper/__tests__/stepper.ts b/src/stepper/__tests__/stepper.ts index e0d654131..0e6f6a3dd 100644 --- a/src/stepper/__tests__/stepper.ts +++ b/src/stepper/__tests__/stepper.ts @@ -50,7 +50,7 @@ describe('Test codify works on non-circular abstract syntax graphs', () => { }) describe('Test codify works on circular abstract syntax graphs', () => { - test('functions', async () => { + test('functions', () => { const code = ` x => x(); ` diff --git a/src/validator/validator.ts b/src/validator/validator.ts index 14fd14962..88b88e587 100644 --- a/src/validator/validator.ts +++ b/src/validator/validator.ts @@ -170,7 +170,7 @@ export function checkForUndefinedVariables( ) { const preludes = context.prelude ? getFunctionDeclarationNamesInProgram(parse(context.prelude, context)!) - : new Set() + : new Set() const env = context.runtime.environments[0].head || {} diff --git a/yarn.lock b/yarn.lock index fd614451d..dff0c64b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1054,7 +1054,12 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0": + version "4.11.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" + integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== + +"@eslint-community/regexpp@^4.6.1": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== @@ -1507,11 +1512,6 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@^7.0.12": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -1552,11 +1552,6 @@ resolved "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz" integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg== -"@types/semver@^7.5.0": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -1579,91 +1574,86 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz#0d8f38a6c8a1802139e62184ee7a68ed024f30a1" - integrity sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw== +"@typescript-eslint/eslint-plugin@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz#9364b756d4d78bcbdf6fd3e9345e6924c68ad371" + integrity sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ== dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "7.3.1" - "@typescript-eslint/type-utils" "7.3.1" - "@typescript-eslint/utils" "7.3.1" - "@typescript-eslint/visitor-keys" "7.3.1" - debug "^4.3.4" + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.8.1" + "@typescript-eslint/type-utils" "8.8.1" + "@typescript-eslint/utils" "8.8.1" + "@typescript-eslint/visitor-keys" "8.8.1" graphemer "^1.4.0" - ignore "^5.2.4" + ignore "^5.3.1" natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.3.1.tgz#c4ba7dc2744318a5e4506596cbc3a0086255c526" - integrity sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw== +"@typescript-eslint/parser@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.8.1.tgz#5952ba2a83bd52024b872f3fdc8ed2d3636073b8" + integrity sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow== dependencies: - "@typescript-eslint/scope-manager" "7.3.1" - "@typescript-eslint/types" "7.3.1" - "@typescript-eslint/typescript-estree" "7.3.1" - "@typescript-eslint/visitor-keys" "7.3.1" + "@typescript-eslint/scope-manager" "8.8.1" + "@typescript-eslint/types" "8.8.1" + "@typescript-eslint/typescript-estree" "8.8.1" + "@typescript-eslint/visitor-keys" "8.8.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz#73fd0cb4211a7be23e49e5b6efec8820caa6ec36" - integrity sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag== +"@typescript-eslint/scope-manager@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz#b4bea1c0785aaebfe3c4ab059edaea1c4977e7ff" + integrity sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA== dependencies: - "@typescript-eslint/types" "7.3.1" - "@typescript-eslint/visitor-keys" "7.3.1" + "@typescript-eslint/types" "8.8.1" + "@typescript-eslint/visitor-keys" "8.8.1" -"@typescript-eslint/type-utils@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz#cbf90d3d7e788466aa8a5c0ab3f46103f098aa0d" - integrity sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw== +"@typescript-eslint/type-utils@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz#31f59ec46e93a02b409fb4d406a368a59fad306e" + integrity sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA== dependencies: - "@typescript-eslint/typescript-estree" "7.3.1" - "@typescript-eslint/utils" "7.3.1" + "@typescript-eslint/typescript-estree" "8.8.1" + "@typescript-eslint/utils" "8.8.1" debug "^4.3.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" -"@typescript-eslint/types@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.3.1.tgz#ae104de8efa4227a462c0874d856602c5994413c" - integrity sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw== +"@typescript-eslint/types@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.8.1.tgz#ebe85e0fa4a8e32a24a56adadf060103bef13bd1" + integrity sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q== -"@typescript-eslint/typescript-estree@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz#598848195fad34c7aa73f548bd00a4d4e5f5e2bb" - integrity sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ== +"@typescript-eslint/typescript-estree@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz#34649f4e28d32ee49152193bc7dedc0e78e5d1ec" + integrity sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg== dependencies: - "@typescript-eslint/types" "7.3.1" - "@typescript-eslint/visitor-keys" "7.3.1" + "@typescript-eslint/types" "8.8.1" + "@typescript-eslint/visitor-keys" "8.8.1" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/utils@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.3.1.tgz#fc28fd508ccf89495012561b7c02a6fdad162460" - integrity sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ== +"@typescript-eslint/utils@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.8.1.tgz#9e29480fbfa264c26946253daa72181f9f053c9d" + integrity sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "7.3.1" - "@typescript-eslint/types" "7.3.1" - "@typescript-eslint/typescript-estree" "7.3.1" - semver "^7.5.4" + "@typescript-eslint/scope-manager" "8.8.1" + "@typescript-eslint/types" "8.8.1" + "@typescript-eslint/typescript-estree" "8.8.1" -"@typescript-eslint/visitor-keys@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz#6ddef14a3ce2a79690f01176f5305c34d7b93d8c" - integrity sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw== +"@typescript-eslint/visitor-keys@8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz#0fb1280f381149fc345dfde29f7542ff4e587fc5" + integrity sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag== dependencies: - "@typescript-eslint/types" "7.3.1" - eslint-visitor-keys "^3.4.1" + "@typescript-eslint/types" "8.8.1" + eslint-visitor-keys "^3.4.3" "@ungap/structured-clone@^1.2.0": version "1.2.0" @@ -1861,11 +1851,6 @@ array-includes@^3.1.7: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - array.prototype.findlastindex@^1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" @@ -2545,13 +2530,6 @@ diff-sequences@^29.6.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2770,11 +2748,6 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" - integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== - eslint-import-resolver-node@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" @@ -2970,7 +2943,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.2.12, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -3258,6 +3231,11 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" +globals@^15.11.0: + version "15.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.11.0.tgz#b96ed4c6998540c6fb824b24b5499216d2438d6e" + integrity sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw== + globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -3265,18 +3243,6 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - glsl-tokenizer@^2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz#1c2e78c16589933c274ba278d0a63b370c5fee1a" @@ -3460,11 +3426,16 @@ ieee754@^1.1.13: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0, ignore@^5.2.4: +ignore@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" @@ -4506,7 +4477,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -4541,13 +4512,6 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -4562,6 +4526,13 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.6" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" @@ -4929,11 +4900,6 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" @@ -5324,6 +5290,11 @@ semver@^7.3.5, semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -5698,7 +5669,7 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -ts-api-utils@^1.0.1: +ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== @@ -5812,6 +5783,15 @@ typed-array-length@^1.0.5: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typescript-eslint@^8.8.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.8.1.tgz#b375c877b2184d883b6228170bc66f0fca847c9a" + integrity sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ== + dependencies: + "@typescript-eslint/eslint-plugin" "8.8.1" + "@typescript-eslint/parser" "8.8.1" + "@typescript-eslint/utils" "8.8.1" + typescript@^4.0.3: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" From 7a8133a310f2a81d90c9192ff9dcce74a3721a4d Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 10 Oct 2024 23:09:39 -0400 Subject: [PATCH 04/15] Fix bug where cleaning docs would remove the gitignore --- .gitignore | 4 ++++ docs/source/.gitignore | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 docs/source/.gitignore diff --git a/.gitignore b/.gitignore index 88711fcd1..cf605489f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ node_modules *.js !eslint.config.js + +# docs !docs/jsdoc/templates/**/*.js !docs/lib/**/*.js +docs/source + *.map dist/ .idea/ diff --git a/docs/source/.gitignore b/docs/source/.gitignore deleted file mode 100644 index 241e560df..000000000 --- a/docs/source/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* - From af91f2be463ebdf682c25e5da7186c77ab16d19c Mon Sep 17 00:00:00 2001 From: Lee Yi Date: Fri, 11 Oct 2024 12:01:38 -0400 Subject: [PATCH 05/15] Add ts-check and tseslint helper as requested --- eslint.config.mjs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index fd7ab611c..b1f8bad2b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,11 +1,12 @@ +// @ts-check + import tseslint from 'typescript-eslint' import globals from 'globals' + +// @ts-expect-error import importPlugin from 'eslint-plugin-import' -/** - * @type {import('eslint').Linter.FlatConfig[]} - */ -export default [ +export default tseslint.config( { // global ignores ignores: ['dist', 'src/alt-langs', 'src/py-slang', 'src/__tests__/sicp', '**/*.snap'] @@ -72,4 +73,4 @@ export default [ '@typescript-eslint/no-require-imports': 'off' } } -] +) From a06bb4302f54a853346733dfefce680785017d16 Mon Sep 17 00:00:00 2001 From: Lee Yi Date: Fri, 11 Oct 2024 12:02:55 -0400 Subject: [PATCH 06/15] Add explanation for ts-expect-error --- eslint.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index b1f8bad2b..0ba26feca 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -3,7 +3,7 @@ import tseslint from 'typescript-eslint' import globals from 'globals' -// @ts-expect-error +// @ts-expect-error no type definitions for plugin import importPlugin from 'eslint-plugin-import' export default tseslint.config( From cf9ea38af651066af1dbc59612d1a409407905b6 Mon Sep 17 00:00:00 2001 From: Lee Yi Date: Fri, 11 Oct 2024 13:34:07 -0400 Subject: [PATCH 07/15] Remove jsdoc stuff pertaining to non-det --- docs/lib/non-det.js | 89 ----------------------- docs/md/README_3_NON-DET.md | 105 --------------------------- docs/md/README_NON-DET.md | 10 --- docs/md/README_top.md | 2 - docs/specs/Makefile | 2 +- docs/specs/source_3_nondet.tex | 85 ---------------------- docs/specs/source_nondet.tex | 16 ---- docs/specs/source_nondet_library.tex | 7 -- 8 files changed, 1 insertion(+), 315 deletions(-) delete mode 100644 docs/lib/non-det.js delete mode 100644 docs/md/README_3_NON-DET.md delete mode 100644 docs/md/README_NON-DET.md delete mode 100644 docs/specs/source_3_nondet.tex delete mode 100644 docs/specs/source_nondet.tex delete mode 100644 docs/specs/source_nondet_library.tex diff --git a/docs/lib/non-det.js b/docs/lib/non-det.js deleted file mode 100644 index eb4a0761e..000000000 --- a/docs/lib/non-det.js +++ /dev/null @@ -1,89 +0,0 @@ -// \texttt{non-det.js START} \begin{lstlisting} - -/** - * Given n values, creates a choice point whose value is chosen, - * at run-time, from the set e1, e2, ..., en.
- * If n is 0, it forces the language processor to backtrack to - * the most recent amb expression without returning a value. - * @param {value} e1,e2,...en - given values - * @returns {value} a value from the given values chosen sequentially - */ -function amb(e1, e2, ...en) {} - -/** - * Given n values, creates a choice point whose value is chosen, - * at run-time, randomly from the set e1, e2, ..., en.
- * If n is 0, it forces the language processor to backtrack to - * the most recent amb expression without returning a value.
- * Functions similarly to the amb operator but makes choices randomly - * instead of sequentially. - * @param {value} e1,e2,...en - given values - * @returns {value} a value from the given values chosen randomly - */ -function ambR(e1, e2, ...en) {} - -/** - * Prevents the language processor from backtracking any further - * beyond the current statement. - */ -function cut() {} - -/** - * Forces the language processor to backtrack to the most recent - * amb expression, if and only if pred evaluates - * to false. - * @param {boolean} pred - given predicate - * @returns {string} - a message indicating that the given predicate - * is true - */ -function require(pred) { - return pred ? "Satisfied require" : amb(); -} - - -/** - * Returns true if and only if P and Q - * satisfy the boolean equation P --> Q. - * @param {boolean} P - antecedent of the conditional - * @param {boolean} Q - consequent of the conditional - * @return {boolean} - a boolean according to the truth table of - * Material Conditional - */ -function implication(P, Q) { - return !P || Q; -} - -/** - * Returns true if and only if P and Q - * satisfy the boolean equation P <--> Q. - * @param {boolean} P - antecedent and consequent of the conditional - * @param {boolean} Q - antecedent and consequent of the conditional - * @return {boolean} - a boolean according to the truth table of - * Material Biconditional - */ -function bi_implication(P, Q) { - return implication(P, Q) && implication(Q, P); -} - -/** - * Nondeterministically returns an element from a given list. - * @param {list} xs - given list - * @returns {value} - an element from xs - */ -function an_element_of(xs) { - require(!is_null(xs)); - return amb(head(xs), an_element_of(tail(xs))); -} - -/** - * Nondeterministically returns an integer between n and - * m (inclusively). - * @param {number} n - lower bound - * @param {number} m - upper bound - * @returns {number} - a number between n and m (inclusive) - */ -function an_integer_between(n, m) { - return n > m ? amb() : amb(n, an_integer_between(n + 1, m)); -} - -// \end{lstlisting} // \texttt{non-det.js END} diff --git a/docs/md/README_3_NON-DET.md b/docs/md/README_3_NON-DET.md deleted file mode 100644 index 3869e0023..000000000 --- a/docs/md/README_3_NON-DET.md +++ /dev/null @@ -1,105 +0,0 @@ -Source §3 Non-Det is a small programming language, designed for the fourth chapter -of the textbook -Structure and Interpretation -of Computer Programs, JavaScript Adaptation (SICP JS). - -## What is nondeterministic programming? -Source 3 Non-Det is a version of Source 3 with a built-in search mechanism. -Programmers can specify sets of values, and requirements that the values must satisfy. -The program evaluator then automatically identifies the values that meet the requirements. - -## What names are predeclared in Source §3 Non-Det? - -On the right, you see all predeclared names of Source §3 Non-Det, in alphabetical -order. Click on a name to see how it is defined and used. They come in these groups: -
    -
  • - AUXILIARY: Auxiliary constants and functions -
  • -
  • - MISC: Miscellaneous constants and functions -
  • -
  • - MATH: Mathematical constants and functions -
  • -
  • - LISTS: Support for lists -
  • -
  • - PAIRMUTATORS: Mutating pairs -
  • -
  • - ARRAYS: Support for arrays -
  • -
  • - STREAMS: Support for streams -
  • -
  • - NON-DET: Support for nondeterminism -
  • -
- -## What can you do in Source §3 Non-Det? - -You can use all features of -Source §3 and all -features that are introduced in -chapter 4.3 of the -textbook. - -Below are the features that Source §3 Non-Det adds to Source §3. - -### amb operator -A set of values can be specified with the amb operator. - -In the following example, we specify two possible choices for a value we want:
-amb('hello', 'bye') + 'world!'; // 'hello world!' - -To obtain the next value, 'bye', enter the command try_again in the playground REPL. -This will give the result 'bye world!'. - -### ambR operator -The ambR operator functions similarly to amb but makes choices randomly -instead of sequentially. - -Upon running the above example, we may randomly obtain the result 'hello world!' first and 'bye world! second
-or 'bye world! first and 'hello world!' second. - -### require function -Requirements can be specified with the require function. - -In the following example, we add the requirement that the number chosen should be greater than 3:
-const f = amb(1, 2, 3, 4, 5, 6); require(f > 3); f; // 4 - -To obtain the next value 5, enter the command try_again in the playground REPL.
-Entering try_again once more will give the final value of 6. - -### cut operator -In order to obtain only the first possible value which satisfies given requirements,
-the cut operator can be used to prevent backtracking beyond the current statement. - -In the following example, we are able to obtain only a single value:
-const f = amb(1, 2, 3, 4, 5, 6); require(f > 3); cut(); f; // 4 - -Entering try_again in the playground REPL will not give the subsequent values that were specified, -5 and 6. - -### implication function -The implication function can be used to model logical implication between two boolean expressions. - -### bi_implication function -The bi_implication function can be used to model logical bi-implication between two boolean expressions. - -### an_element_of function -The an_element_of function can be used to nondeterministically obtain an element from a list.
-It functions similarly to amb but takes in a list of choices as argument instead of the choices being arguments themselves. - -### an_integer_between function -The an_integer_between function can be used to nondeterministically obtain an integer between a specified range (inclusively). - -## You want the definitive specs? - -For our development team, we are maintaining a definitive description -of the language, called the -Specification of Source §3 Non-Det. -Feel free to take a peek! diff --git a/docs/md/README_NON-DET.md b/docs/md/README_NON-DET.md deleted file mode 100644 index e55223fd4..000000000 --- a/docs/md/README_NON-DET.md +++ /dev/null @@ -1,10 +0,0 @@ -The functions in NON-DET provide useful functions for nondeterministic programs, -as introduced in -section -4.3 Nondeterministic computing -of the textbook -Structure and Interpretation -of Computer Programs, JavaScript Adaptation (SICP JS). - -On the right, you see all predeclared NON-DET functions, -in alphabetical order. Click on a name to see how it is defined and used. diff --git a/docs/md/README_top.md b/docs/md/README_top.md index 94ead7bd5..95568ece6 100644 --- a/docs/md/README_top.md +++ b/docs/md/README_top.md @@ -70,8 +70,6 @@ the Source Academy. #### Specification of Source §2 Typed -#### Specification of Source §3 Non-Det - #### Specification of Source §3 Concurrent #### Specification of Source §3 Typed diff --git a/docs/specs/Makefile b/docs/specs/Makefile index f3fcbdedf..81b8ede94 100644 --- a/docs/specs/Makefile +++ b/docs/specs/Makefile @@ -1,6 +1,6 @@ PDFLATEX = latexmk -pdf -SPECSNUMS = 1 1_lazy 1_wasm 1_type_inference 1_infinite_loop_detection 1_typed 2 2_lazy 2_typed 3_type_inference 3 3_nondet 3_concurrent 3_typed 4 4_explicitcontrol 4_gpu 4_typed styleguide 2_stepper studio_2 python_1 +SPECSNUMS = 1 1_lazy 1_wasm 1_type_inference 1_infinite_loop_detection 1_typed 2 2_lazy 2_typed 3_type_inference 3 3_concurrent 3_typed 4 4_explicitcontrol 4_gpu 4_typed styleguide 2_stepper studio_2 python_1 SPECS = $(SPECSNUMS:%=source_%) diff --git a/docs/specs/source_3_nondet.tex b/docs/specs/source_3_nondet.tex deleted file mode 100644 index 0cd286d23..000000000 --- a/docs/specs/source_3_nondet.tex +++ /dev/null @@ -1,85 +0,0 @@ -\input source_header.tex - -\begin{document} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \docheader{2021}{Source}{\S 3 Non-Det}{Martin Henz, Arsalan Cheema, Anubhav} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\input source_intro.tex - -Source \S 3 Non-Det is a nondeterministic variant of Source \S 3. - -\section{Changes} - -Source \S 3 Non-Det modifies Source \S 3 by adding an automatic search mechanism into the language semantics. The language allows programmers to specify \emph{choices} and \emph{requirements}. At runtime, the search mechanism automatically resolves these requirements, and identifies all those choices that satisfy them. \newline - -This is achieved by supporting a new type of expression, called the \emph{choice point} expression. Every choice point expression is described by a (possibly empty) set of \emph{expressions} (known as the set of \emph{choices}). At runtime, a choice point expression can assume any value from its set of choices. A program can also include instructions that force the language processor to \emph{backtrack} to any choice point expression. \emph{Backtracking} means that the choice point expression will take on a different value from its set of choices, and the program will then be re-evaluated with this new choice. \newline - -A Source \S 3 Non-Det program can contain any number of choice points. Evaluating a program can hence have an exponential number of outcomes - one outcome for every combination of possible choices. The language's search mechanism, however, only returns one outcome at a time. The programmer can \emph{command} the search mechanism to re-evaluate the program with a different combination of choices. On the Source Academy Playground, this command is implemented as the magic phrase \verb#try_again#, -which has to be entered into the REPL. \newline - -In the Source Academy implementation, the Source \S 3 Non-Det evaluator can only process one program at a time. If the evaluator is given a new program \textbf{before} all the outcomes of an existing program have been processed, then all remaining outcomes will be ignored. \newline - -Unlike the implementations of Source \S 3, the implementation of Source \S 3 Non-Det does not use a constant amount of memory for loops. Instead, memory usage increases linearly with the number of iterations. This is because each iteration can be nondeterministic. Each iteration's state must be kept in memory so that the language processor can backtrack to every iteration. Similarly, no tail call optimization is done, because every function application's state must be kept in memory to allow backtracking. - -Source \S 3 Non-Det currently does not support import directives. - -\input source_bnf.tex - -\newpage - -\input source_3_bnf_without_import.tex - -\newpage - -\input source_return - -\input source_import - -\input source_boolean_operators - -\input source_loops - -\input source_names_lang - -\input source_numbers - -\input source_strings - -\input source_arrays - -\input source_comments - -\input source_typing_3 - -\input source_standard - -\input source_misc - -\input source_math - -\input source_nondet - -\input source_lists - -\input source_pair_mutators - -\input source_array_support - -\input source_streams - -\input source_js_differences - -\newpage - -\input source_list_library - -\newpage - -\input source_stream_library - -\newpage - -\input source_nondet_library - - \end{document} diff --git a/docs/specs/source_nondet.tex b/docs/specs/source_nondet.tex deleted file mode 100644 index 7620d5293..000000000 --- a/docs/specs/source_nondet.tex +++ /dev/null @@ -1,16 +0,0 @@ -\subsection*{Nondeterminism support} - -The following primitives and builtins are supported: - -\begin{itemize} -\item \lstinline{amb(e1, e2, ..., en)}: \textit{primitive}, creates a choice point whose value is chosen, at runtime, -from the set \lstinline{e1, e2, ..., en}. Note: although the usage of this primitive syntactically resembles a function application, \textit{amb} is not a function value. It is simply an operator, like the binary and unary operators. The values from the set \lstinline{(e1, e2, ..., en)} are chosen sequentially, from left-to-right. -\item \lstinline{amb()}: applying \textit{amb} without any arguments forces the language processor to backtrack to the most recent \textit{amb expression}. -\item \lstinline{ambR(e1, e2, ..., en)}: \textit{primitive}, creates a choice point. This is similar to \lstinline{amb}, but the values from the set \lstinline{(e1, e2, ..., en)} are chosen randomly and not sequentially. -\item \lstinline{cut()}: \textit{primitive}, prevents the language processor from backtracking any further beyond the current statement. Note: \textit{cut} is also not a function value. It is simply an operator, like the binary and unary operators. -\item \lstinline{require(pred)}: \textit{builtin}, forces the language processor to backtrack to the most recent \textit{amb expression}, if and only if \lstinline{pred} is false. -\item \lstinline{an_element_of(xs)}: \textit{builtin}, nondeterministically returns an element from the list \lstinline{xs}. -\item \lstinline{an_integer_between(n, m)}: \textit{builtin}, nondeterministically returns an integer between integers \lstinline{n} and \lstinline{m} (inclusively). -\item \lstinline{implication(p, q)}: \textit{builtin} returns true if and only if the boolean values \lstinline{p} and \lstinline{q} satisfy the equation $P \Rightarrow Q$ -\item \lstinline{bi_implication(p, q)}: \textit{builtin} returns true if and only if the boolean values \lstinline{p} and \lstinline{q} satisfy the equation $P \Leftrightarrow Q$ -\end{itemize} diff --git a/docs/specs/source_nondet_library.tex b/docs/specs/source_nondet_library.tex deleted file mode 100644 index eaf5f170f..000000000 --- a/docs/specs/source_nondet_library.tex +++ /dev/null @@ -1,7 +0,0 @@ -\section*{Appendix: Nondeterminism library} - -Those nondeterminism functions that are not primitives are pre-declared as follows: - -\input ../lib/non-det.js - - From cabdfe168a98ed1d29cc467620107caad7332074 Mon Sep 17 00:00:00 2001 From: Lee Yi Date: Fri, 11 Oct 2024 13:41:32 -0400 Subject: [PATCH 08/15] Remove from non-det from the script --- scripts/jsdoc.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/scripts/jsdoc.sh b/scripts/jsdoc.sh index e03cf1659..d40364a60 100755 --- a/scripts/jsdoc.sh +++ b/scripts/jsdoc.sh @@ -268,14 +268,6 @@ run() { -d ${DST}/CONCURRENCY/ \ ${LIB}/concurrency.js - # NON-DET - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_NON-DET.md \ - -d ${DST}/NON-DET/ \ - ${LIB}/non-det.js - # MCE ${JSDOC} -r -t ${TMPL} \ From 49b60007cc6066c35c9fc20c0b4648363c7f6fa4 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 12 Oct 2024 21:10:21 -0400 Subject: [PATCH 09/15] Replace docs scripts with javascript code --- .gitignore | 1 - package.json | 6 +- ...teAutocompleteDocs.js => autocomplete.mjs} | 46 +- scripts/autocomplete.sh | 125 ------ scripts/docs.mjs | 397 ++++++++++++++++++ scripts/jsdoc.sh | 316 -------------- 6 files changed, 428 insertions(+), 463 deletions(-) rename scripts/{updateAutocompleteDocs.js => autocomplete.mjs} (72%) delete mode 100755 scripts/autocomplete.sh create mode 100644 scripts/docs.mjs delete mode 100755 scripts/jsdoc.sh diff --git a/.gitignore b/.gitignore index cf605489f..8dc876373 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ node_modules *.js -!eslint.config.js # docs !docs/jsdoc/templates/**/*.js diff --git a/package.json b/package.json index 390b40b6b..b3a7b4c47 100644 --- a/package.json +++ b/package.json @@ -58,9 +58,9 @@ "format:ci": "prettier --list-different \"src/**/*.{ts,tsx}\"", "test": "jest", "test-coverage": "jest --coverage", - "docs": "yarn jsdoc && yarn autocomplete", - "jsdoc": "./scripts/jsdoc.sh", - "autocomplete": "node ./scripts/updateAutocompleteDocs.js", + "docs": "node ./scripts/docs.mjs docs", + "jsdoc": "node ./scripts/docs.mjs", + "autocomplete": "node ./scripts/docs.mjs autocomplete", "build_sicp_package": "./scripts/build_sicp_package.sh", "publish_sicp_package": "./scripts/publish_sicp_package.sh", "benchmark": "jest --runInBand --testPathPattern='.*benchmark.*' --testPathIgnorePatterns='/dist/'", diff --git a/scripts/updateAutocompleteDocs.js b/scripts/autocomplete.mjs similarity index 72% rename from scripts/updateAutocompleteDocs.js rename to scripts/autocomplete.mjs index 8d3ab860c..f0f2aa0fa 100644 --- a/scripts/updateAutocompleteDocs.js +++ b/scripts/autocomplete.mjs @@ -1,7 +1,8 @@ -const fs = require('fs'); -const path = require('path'); -const jsdom = require('jsdom'); -const { JSDOM } = jsdom; +// @ts-check + +import fs from 'fs/promises'; +import pathlib from 'path'; +import { JSDOM } from 'jsdom'; const CONST_DECL = "const"; const FUNC_DECL = "func"; @@ -74,11 +75,11 @@ function processFunction(namespace, element, document) { namespace[name] = { title, description: html, meta: FUNC_DECL }; } -function processDirGlobals(target) { - const inFile = path.join(BASE_DIR, target, SRC_FILENAME); +async function processDirGlobals(target) { + const inFile = pathlib.join(BASE_DIR, target, SRC_FILENAME); let document; try { - const contents = fs.readFileSync(inFile); + const contents = await fs.readFile(inFile); document = new JSDOM(contents.toString()).window.document; } catch (err) { console.error(inFile, "failed", err); @@ -93,17 +94,26 @@ function processDirGlobals(target) { const functions = document.getElementsByClassName('function-entry') Array.prototype.forEach.call(functions, ele => processFunction(names, ele, document)); - fs.mkdirSync(OUT_DIR, { recursive: true }) - - const outFile = path.join(OUT_DIR, target + '.json'); - fs.writeFileSync(outFile, JSON.stringify(names, null, 2), 'utf-8'); + const outFile = pathlib.join(OUT_DIR, target + '.json'); + await fs.writeFile(outFile, JSON.stringify(names, null, 2), 'utf-8') } -if (fs.existsSync(BASE_DIR)) { - TARGETS.forEach(processDirGlobals); -} else { - console.error(` - Error: path to jsdoc html is invalid. - Ensure that this script is run from the project root and documentation has been generated\ - `); +export default async function autocomplete() { + try { + await fs.access(BASE_DIR, fs.constants.R_OK) + } catch (error) { + if (error.code === 'ENOENT') { + console.error(` + Error: path to jsdoc html is invalid. + Ensure that this script is run from the project root and documentation has been generated\ + `); + } else { + console.error(error) + } + } + + await fs.mkdir(OUT_DIR, { recursive: true }) + await Promise.all(TARGETS.map(processDirGlobals)) + console.log('Finished processing autocomplete documentation') } + diff --git a/scripts/autocomplete.sh b/scripts/autocomplete.sh deleted file mode 100755 index 4b61c68d2..000000000 --- a/scripts/autocomplete.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 - -from lxml import etree, html -from pathlib import Path -import os -import re -import json - -CONST_DECL = "const" -FUNC_DECL = "func" - - -base_dir = f"docs{os.path.sep}source" -src_filename = "global.html" -out_dir = f"src{os.path.sep}editors{os.path.sep}ace{os.path.sep}docTooltip" - - -def new_title_node(title): - node = etree.Element("h4") - node.text = title - return node - - -def build_description_html(description_div): - description_html = html.tostring(description_div).decode("utf-8") - - # There's a whole bunch of newlines between sections for some reason. - # Main reason for this is to avoid double newlines inside code blocks. - description_html = re.sub("\n+", "\n", description_html) - - return description_html - - -def process_constant(namespace, element): - header = element.find('./h4') - raw_name = "".join(header.itertext()) - fields = raw_name.split()[1:] # first result just says (constant) - - name = header.attrib["id"] - title = "".join(fields) - if not title: # Some runes have no title - title = name - - title_node = new_title_node(title) - - description_node = element.find('./div[@class="description"]') - - description_div = etree.Element("div") - description_div.append(title_node) - description_div.append(description_node) - - description_html = build_description_html(description_div) - - namespace[name] = {"title": title, - "description": description_html, "meta": CONST_DECL} - - -def process_function(namespace, element): - header = element.find('./h4') - title = "".join(header.itertext()) - name = header.attrib["id"] - - title_node = new_title_node(title) - - description_node = element.find('./div[@class="description"]') - - description_div = etree.Element("div") - description_div.append(title_node) - description_div.append(description_node) - - description_html = build_description_html(description_div) - - namespace[name] = {"title": title, - "description": description_html, "meta": FUNC_DECL} - - -def process_dir_globals(target): - infile = os.path.join(base_dir, target, src_filename) - with open(infile, encoding="utf-8") as f: - contents = "\n".join(f.readlines()) - try: - tree = etree.HTML(contents) - except Exception as e: - print(infile, "failed", e) - return - - names = {} - - constants = tree.findall('.//div[@class="constant-entry"]') - for c in constants: - process_constant(names, c) - - functions = tree.findall('.//div[@class="function-entry"]') - for f in functions: - process_function(names, f) - - Path(out_dir).mkdir(parents=True, exist_ok=True) - outfile = os.path.join(out_dir, target + ".json") - with open(outfile, "w", encoding="utf-8") as f: - json.dump(names, f, indent=2) - -# Folder names for jsdoc html -targets = [ - "source_1", - "source_1_lazy", - "source_1_wasm", - "source_2", - "source_2_lazy", - "source_3", - "source_3_concurrent", - "source_4", - "source_4_explicit-control" - "External libraries", -] - -for target in targets: - if not os.path.exists(base_dir): - print("""\ - Error: path to jsdoc html is invalid. - Ensure that this script is run from the project root and documentation has been generated\ - """) - exit(1) - - process_dir_globals(target) - diff --git a/scripts/docs.mjs b/scripts/docs.mjs new file mode 100644 index 000000000..02437e7cb --- /dev/null +++ b/scripts/docs.mjs @@ -0,0 +1,397 @@ +// @ts-check + +import { spawn } from 'child_process' +import pathlib from 'path' +import fs from 'fs/promises' +import { Command } from 'commander' +import process from 'process' +import autocomplete from './autocomplete.mjs' + +const configs = { + "landing": { + "readme": "README_top.md", + "dst": "", + "libs": [ + "empty.js" + ] + }, + "Source §1": { + "readme": "README_1.md", + "dst": "source_1/", + "libs": [ + "misc.js", + "math.js" + ] + }, + "Source §1 Lazy": { + "readme": "README_1_LAZY.md", + "dst": "source_1_lazy/", + "libs": [ + "misc.js", + "math.js" + ] + }, + "Source §1 Typed": { + "readme": "README_1_TYPED.md", + "dst": "source_1_typed/", + "libs": [ + "misc.js", + "math.js" + ] + }, + "Source §1 WebAssembly": { + "readme": "README_1_WASM.md", + "dst": "source_1_wasm/", + "libs": [ + "empty.js" + ] + }, + "Source §2": { + "readme": "README_2.md", + "dst": "source_2/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js" + ] + }, + "Source §2 Lazy": { + "readme": "README_2_LAZY.md", + "dst": "source_2_lazy/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js" + ] + }, + "Source §2 Typed": { + "readme": "README_2_TYPED.md", + "dst": "source_2_typed/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js" + ] + }, + "Source §3": { + "readme": "README_3.md", + "dst": "source_3/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js" + ] + }, + "Source §3 Concurrent": { + "readme": "README_3_CONCURRENT.md", + "dst": "source_3_concurrent/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js", + "concurrency.js" + ] + }, + "Source §3 Typed": { + "readme": "README_3_TYPED.md", + "dst": "source_3_typed/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js" + ] + }, + "Source §4": { + "readme": "README_4.md", + "dst": "source_4/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js", + "mce.js" + ] + }, + "Source §4 Explicit-Control": { + "readme": "README_4_EXPLICIT-CONTROL.md", + "dst": "source_4_explicit-control/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js", + "mce.js", + "continuation.js" + ] + }, + "Source §4 GPU": { + "readme": "README_4_GPU.md", + "dst": "source_4_gpu/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js" + ] + }, + "Source §4 Typed": { + "readme": "README_4_TYPED.md", + "dst": "source_4_typed/", + "libs": [ + "auxiliary.js", + "misc.js", + "math.js", + "list.js", + "stream.js", + "array.js", + "pairmutator.js", + "mce.js" + ] + }, + "AUXILLARY": { + "readme": "README_AUXILIARY.md", + "dst": "AUXILIARY/", + "libs": [ + "auxiliary.js" + ] + }, + "MISC": { + "readme": "README_MISC.md", + "dst": "MISC/", + "libs": [ + "misc.js" + ] + }, + "MATH": { + "readme": "README_MATH.md", + "dst": "MATH/", + "libs": [ + "math.js" + ] + }, + "LIST": { + "readme": "README_LISTS.md", + "dst": "LISTS/", + "libs": [ + "list.js" + ] + }, + "STREAMS": { + "readme": "README_STREAMS.md", + "dst": "STREAMS/", + "libs": [ + "stream.js" + ] + }, + "ARRAYS": { + "readme": "README_ARRAYS.md", + "dst": "ARRAYS/", + "libs": [ + "array.js" + ] + }, + "PAIRMUTATIONS": { + "readme": "README_PAIRMUTATORS.md", + "dst": "PAIRMUTATORS/", + "libs": [ + "pairmutator.js" + ] + }, + "CONCURRENCY": { + "readme": "README_CONCURRENCY.md", + "dst": "CONCURRENCY/", + "libs": [ + "concurrency.js" + ] + }, + "MCE": { + "readme": "README_MCE.md", + "dst": "MCE/", + "libs": [ + "mce.js" + ] + }, + "CONTINUATION": { + "readme": "README_CONTINUATION.md", + "dst": "CONTINUATION/", + "libs": [ + "continuation.js" + ] + }, + "EV3": { + "readme": "EV3_README.md", + "dst": "EV3/", + "libs": [ + "ev3.js" + ] + }, + "EXTERNAL": { + "readme": "README_EXTERNAL.md", + "dst": "External libraries", + "libs": [ + "ev3.js" + ] + } +} + +const config_file = 'docs/jsdoc/conf.json' +const readmes = 'docs/md' +const libraries = 'docs/lib' +const out_dir = 'docs/source' +const jsdoc = "node_modules/jsdoc/jsdoc.js" +const template_location = "docs/jsdoc/templates/template" +const specs_dir = "docs/specs" + +async function run() { + await fs.mkdir(out_dir, { recursive: true }) + + const promises = Object.entries(configs).map(([name, config]) => { + const args = [ + jsdoc, + '-r', + '-t', template_location, + '-c', config_file, + '-R', pathlib.join(readmes, config["readme"]), + '-d', pathlib.join(out_dir, config["dst"]), + ...config.libs.map(each => pathlib.join(libraries, each)) + ] + + const proc = spawn('node', args) + proc.on('spawn', () => console.log(`Building ${name}`)) + return new Promise((resolve, reject) => { + proc.on('exit', c => { + if (c === 0) { + console.log(`Finished ${name}`) + } else { + console.error(`Error occurred with ${name}: jsdoc exited with ${c}`) + } + resolve(c) + }) + + proc.on('error', e => { + console.error(`Error occurred with ${name}: ${e}`) + reject(e) + }) + }) + }) + + const retcodes = await Promise.all(promises) + const nonzeroRetcode = retcodes.find(c => c !== 0) + + if (nonzeroRetcode !== undefined) process.exit(nonzeroRetcode) +} + +async function prepare() { + await run() + + // Copy images in images directory to out_dir + await fs.readdir('docs/images') + .then(images => Promise.all(images.map(async img => { + const srcPath = pathlib.join('docs/images', img) + const dstPath = pathlib.join(out_dir, img) + await fs.copyFile(srcPath, dstPath) + console.log(`Copied ${srcPath} to ${dstPath}`) + }))) + + const makeProc = spawn('make', { cwd: specs_dir }) + + const makeretcode = await new Promise((resolve, reject) => { + makeProc.on('exit', resolve) + makeProc.on('error', reject) + }) + + if (makeretcode !== 0) process.exit(makeretcode) + + // Copy pdf files that make produced to out_dir + await fs.readdir(specs_dir) + .then(files => Promise.all(files + .filter(file => pathlib.extname(file) === '.pdf') + .map(async file => { + const srcPath = pathlib.join(specs_dir, file) + const dstPath = pathlib.join(out_dir, file) + await fs.copyFile(srcPath, dstPath) + console.log(`Copied ${srcPath} to ${dstPath}`) + }) + )) +} + +async function clean() { + await fs.rm(out_dir, { recursive: true }) + console.log(`Cleared ${out_dir}`) +} + +/** + * Check that the commands are being run from the root of the git repository + */ +async function checkGitRoot() { + const gitProc = spawn('git', ['rev-parse', '--show-toplevel'], { stdio: ['ignore', 'pipe', 'inherit'] }) + gitProc.on('error', e => { + console.error(e) + process.exit(1) + }) + + const gitRoot = await new Promise((resolve, reject) => { + gitProc.stdout.on('data', data => { + resolve(data.toString().trim()) + }) + + gitProc.stdout.on('error', reject) + }) + + const procDir = pathlib.relative(gitRoot, '') + if (procDir !== '') { + console.error('Please run this command from the git root directory') + process.exit(1) + } +} + +await new Command() + .hook('preAction', checkGitRoot) + .addCommand( + new Command('run') + .description('Run JSDOC and build documentation') + .action(run), + { isDefault: true } + ) + .addCommand(new Command('prepare').action(prepare)) + .addCommand( + new Command('clean') + .description('Clear the output directory') + .action(clean) + ) + .addCommand( + new Command('autocomplete') + .description('Update autocomplete documentation') + .action(autocomplete) + ) + .addCommand( + new Command('docs') + .description('Execute the \'run\' command and then the \'autocomplete\' command') + .action(() => run().then(autocomplete)) + ) + .parseAsync() + \ No newline at end of file diff --git a/scripts/jsdoc.sh b/scripts/jsdoc.sh deleted file mode 100755 index d40364a60..000000000 --- a/scripts/jsdoc.sh +++ /dev/null @@ -1,316 +0,0 @@ -#! /usr/bin/env bash - -set -e - -JSDOC="node_modules/.bin/jsdoc" -TMPL="docs/jsdoc/templates/template" -DST="docs/source" -MD="docs/md" -LIB="docs/lib" -SPECS="docs/specs" - -main() { - - if [ "$1" == "prepare" ]; then - prepare - elif [ "$1" == "clean" ]; then - clean - elif [[ "$(git rev-parse --show-toplevel 2> /dev/null)" -ef "$PWD" ]]; then - run - else - echo "Please run this command from the git root directory." - false # exit 1 - fi -} - -run() { - - # Source landing page - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_top.md \ - -d ${DST}/ \ - ${LIB}/empty.js - - # Source §1 - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_1.md \ - -d ${DST}/"source_1"/ \ - ${LIB}/misc.js \ - ${LIB}/math.js - - # Source §1 Lazy - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_1_LAZY.md \ - -d ${DST}/"source_1_lazy"/ \ - ${LIB}/misc.js \ - ${LIB}/math.js - - # Source §1 Typed - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_1_TYPED.md \ - -d ${DST}/"source_1_typed"/ \ - ${LIB}/misc.js \ - ${LIB}/math.js - - # Source §1 WebAssembly - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_1_WASM.md \ - -d ${DST}/"source_1_wasm"/ \ - ${LIB}/empty.js - - # Source §2 - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_2.md \ - -d ${DST}/"source_2"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js - - # Source §2 Lazy - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_2_LAZY.md \ - -d ${DST}/"source_2_lazy"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js - - # Source §2 Typed - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_2_TYPED.md \ - -d ${DST}/"source_2_typed"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js - - # Source §3 - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_3.md \ - -d ${DST}/"source_3"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js - - # Source §3 Concurrent - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_3_CONCURRENT.md \ - -d ${DST}/"source_3_concurrent"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js \ - ${LIB}/concurrency.js - - # Source §3 Typed - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_3_TYPED.md \ - -d ${DST}/"source_3_typed"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js - - # Source §4 - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_4.md \ - -d ${DST}/"source_4"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js \ - ${LIB}/mce.js - - # Source §4 Explicit-Control - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_4_EXPLICIT-CONTROL.md \ - -d ${DST}/"source_4_explicit-control"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js \ - ${LIB}/mce.js \ - ${LIB}/continuation.js - - # Source §4 GPU - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_4_GPU.md \ - -d ${DST}/"source_4_gpu"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js - - # Source §4 Typed - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_4_TYPED.md \ - -d ${DST}/"source_4_typed"/ \ - ${LIB}/auxiliary.js \ - ${LIB}/misc.js \ - ${LIB}/math.js \ - ${LIB}/list.js \ - ${LIB}/stream.js \ - ${LIB}/array.js \ - ${LIB}/pairmutator.js \ - ${LIB}/mce.js - - # AUXILIARY - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_AUXILIARY.md \ - -d ${DST}/AUXILIARY/ \ - ${LIB}/auxiliary.js - - # MISC - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_MISC.md \ - -d ${DST}/MISC/ \ - ${LIB}/misc.js - - # MATH - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_MATH.md \ - -d ${DST}/MATH/ \ - ${LIB}/math.js - - # LISTS - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_LISTS.md \ - -d ${DST}/LISTS/ \ - ${LIB}/list.js - - # STREAMS - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_STREAMS.md \ - -d ${DST}/STREAMS/ \ - ${LIB}/stream.js - - # ARRAYS - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_ARRAYS.md \ - -d ${DST}/ARRAYS/ \ - ${LIB}/array.js - - # PAIRMUTATORS - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_PAIRMUTATORS.md \ - -d ${DST}/PAIRMUTATORS/ \ - ${LIB}/pairmutator.js - - # CONCURRENCY - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_CONCURRENCY.md \ - -d ${DST}/CONCURRENCY/ \ - ${LIB}/concurrency.js - - # MCE - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_MCE.md \ - -d ${DST}/MCE/ \ - ${LIB}/mce.js - - # CONTINUATION - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_CONTINUATION.md \ - -d ${DST}/CONTINUATION/ \ - ${LIB}/continuation.js - - # EV3 - - ${JSDOC} -r -t ${TMPL} \ - -c docs/jsdoc/conf.json \ - -d "${DST}/EV3/" \ - -R ${MD}/EV3_README.md \ - ${LIB}/ev3.js - - # External - - ${JSDOC} -r -t ${TMPL}/ \ - -c docs/jsdoc/conf.json \ - -R ${MD}/README_EXTERNAL.md \ - -d ${DST}/"External libraries"/ \ - ${LIB}/ev3.js -} - -prepare() { - run - cp -r docs/images ${DST} ; \ - cd ${SPECS}; make; cp *.pdf ../source; cd ../.. -} - -clean() { - - rm -rf ${DST}/* - -} - -main $1 From 474bd649e787b5e4fd936cc9776d38e2bbd46e70 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 12 Oct 2024 22:57:11 -0400 Subject: [PATCH 10/15] Misc change --- scripts/docs.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/docs.mjs b/scripts/docs.mjs index 02437e7cb..983f45a19 100644 --- a/scripts/docs.mjs +++ b/scripts/docs.mjs @@ -325,6 +325,7 @@ async function prepare() { }) if (makeretcode !== 0) process.exit(makeretcode) + console.log('Finished running make') // Copy pdf files that make produced to out_dir await fs.readdir(specs_dir) @@ -359,6 +360,10 @@ async function checkGitRoot() { resolve(data.toString().trim()) }) + gitProc.on('exit', c => { + if (c !== 0) reject(c) + }) + gitProc.stdout.on('error', reject) }) From d419bdd144da3834a2f5c34426582a9a4668ef46 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 12 Oct 2024 23:09:52 -0400 Subject: [PATCH 11/15] Get make to output to stdio --- scripts/docs.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docs.mjs b/scripts/docs.mjs index 983f45a19..5678af467 100644 --- a/scripts/docs.mjs +++ b/scripts/docs.mjs @@ -317,7 +317,7 @@ async function prepare() { console.log(`Copied ${srcPath} to ${dstPath}`) }))) - const makeProc = spawn('make', { cwd: specs_dir }) + const makeProc = spawn('make', { cwd: specs_dir, stdio: [0, 1, 2]}) const makeretcode = await new Promise((resolve, reject) => { makeProc.on('exit', resolve) From a0a2e3a725937c371f4bd6984bea2d464fa628a6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 12 Oct 2024 23:32:40 -0400 Subject: [PATCH 12/15] Update to use the proper functions from child_process --- scripts/autocomplete.mjs | 1 + scripts/docs.mjs | 47 +++++++++++++++++----------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/scripts/autocomplete.mjs b/scripts/autocomplete.mjs index f0f2aa0fa..54db56635 100644 --- a/scripts/autocomplete.mjs +++ b/scripts/autocomplete.mjs @@ -110,6 +110,7 @@ export default async function autocomplete() { } else { console.error(error) } + process.exit(1) } await fs.mkdir(OUT_DIR, { recursive: true }) diff --git a/scripts/docs.mjs b/scripts/docs.mjs index 5678af467..5469edfdd 100644 --- a/scripts/docs.mjs +++ b/scripts/docs.mjs @@ -1,6 +1,6 @@ // @ts-check -import { spawn } from 'child_process' +import { execFile, fork, spawn } from 'child_process' import pathlib from 'path' import fs from 'fs/promises' import { Command } from 'commander' @@ -270,19 +270,17 @@ async function run() { await fs.mkdir(out_dir, { recursive: true }) const promises = Object.entries(configs).map(([name, config]) => { - const args = [ - jsdoc, + const proc = fork(jsdoc, [ '-r', '-t', template_location, '-c', config_file, '-R', pathlib.join(readmes, config["readme"]), '-d', pathlib.join(out_dir, config["dst"]), ...config.libs.map(each => pathlib.join(libraries, each)) - ] + ]) - const proc = spawn('node', args) proc.on('spawn', () => console.log(`Building ${name}`)) - return new Promise((resolve, reject) => { + return new Promise(resolve => { proc.on('exit', c => { if (c === 0) { console.log(`Finished ${name}`) @@ -294,7 +292,7 @@ async function run() { proc.on('error', e => { console.error(`Error occurred with ${name}: ${e}`) - reject(e) + resolve(1) }) }) }) @@ -317,11 +315,13 @@ async function prepare() { console.log(`Copied ${srcPath} to ${dstPath}`) }))) - const makeProc = spawn('make', { cwd: specs_dir, stdio: [0, 1, 2]}) - - const makeretcode = await new Promise((resolve, reject) => { + const makeProc = spawn('make', { cwd: specs_dir, stdio: 'inherit' }) + const makeretcode = await new Promise(resolve => { makeProc.on('exit', resolve) - makeProc.on('error', reject) + makeProc.on('error', () => { + console.error('Failed to start make') + process.exit(1) + }) }) if (makeretcode !== 0) process.exit(makeretcode) @@ -349,24 +349,17 @@ async function clean() { * Check that the commands are being run from the root of the git repository */ async function checkGitRoot() { - const gitProc = spawn('git', ['rev-parse', '--show-toplevel'], { stdio: ['ignore', 'pipe', 'inherit'] }) - gitProc.on('error', e => { - console.error(e) - process.exit(1) - }) + const gitRoot = await new Promise(resolve => { + execFile('git', ['rev-parse', '--show-toplevel'], (err, stdout, stderr) => { + const possibleError = err || stderr + if (possibleError) { + console.error(possibleError) + process.exit(1) + } - const gitRoot = await new Promise((resolve, reject) => { - gitProc.stdout.on('data', data => { - resolve(data.toString().trim()) - }) - - gitProc.on('exit', c => { - if (c !== 0) reject(c) - }) + resolve(stdout.trim()) + })}) - gitProc.stdout.on('error', reject) - }) - const procDir = pathlib.relative(gitRoot, '') if (procDir !== '') { console.error('Please run this command from the git root directory') From d9b4ba6d1febc9392ce31ca9899ed1c49e7dc9cc Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 12 Oct 2024 23:54:55 -0400 Subject: [PATCH 13/15] Add documentation and silent option for prepare --- scripts/autocomplete.mjs | 1 + scripts/docs.mjs | 35 ++++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/scripts/autocomplete.mjs b/scripts/autocomplete.mjs index 54db56635..5709c5101 100644 --- a/scripts/autocomplete.mjs +++ b/scripts/autocomplete.mjs @@ -100,6 +100,7 @@ async function processDirGlobals(target) { export default async function autocomplete() { try { + // Check that the BASE_DIR exists and that we can read from it await fs.access(BASE_DIR, fs.constants.R_OK) } catch (error) { if (error.code === 'ENOENT') { diff --git a/scripts/docs.mjs b/scripts/docs.mjs index 5469edfdd..49688195c 100644 --- a/scripts/docs.mjs +++ b/scripts/docs.mjs @@ -270,12 +270,14 @@ async function run() { await fs.mkdir(out_dir, { recursive: true }) const promises = Object.entries(configs).map(([name, config]) => { + // Use fork to start a new instance of nodejs and run jsdoc + // for each configuration const proc = fork(jsdoc, [ '-r', '-t', template_location, '-c', config_file, - '-R', pathlib.join(readmes, config["readme"]), - '-d', pathlib.join(out_dir, config["dst"]), + '-R', pathlib.join(readmes, config.readme), + '-d', pathlib.join(out_dir, config.dst), ...config.libs.map(each => pathlib.join(libraries, each)) ]) @@ -285,25 +287,27 @@ async function run() { if (c === 0) { console.log(`Finished ${name}`) } else { - console.error(`Error occurred with ${name}: jsdoc exited with ${c}`) + console.error(`Error occurred with ${name}: jsdoc exited with code ${c}`) } resolve(c) }) proc.on('error', e => { - console.error(`Error occurred with ${name}: ${e}`) + console.error(`Error occurred with ${name}: `, e) resolve(1) }) }) }) + // If some instance returned a non zero return code, + // exit with that return code const retcodes = await Promise.all(promises) const nonzeroRetcode = retcodes.find(c => c !== 0) if (nonzeroRetcode !== undefined) process.exit(nonzeroRetcode) } -async function prepare() { +async function prepare({ silent }) { await run() // Copy images in images directory to out_dir @@ -312,14 +316,19 @@ async function prepare() { const srcPath = pathlib.join('docs/images', img) const dstPath = pathlib.join(out_dir, img) await fs.copyFile(srcPath, dstPath) - console.log(`Copied ${srcPath} to ${dstPath}`) + console.debug(`Copied ${srcPath} to ${dstPath}`) }))) - const makeProc = spawn('make', { cwd: specs_dir, stdio: 'inherit' }) + const makeProc = spawn('make', { cwd: specs_dir, stdio: [ + 'ignore', + silent ? 'ignore' : 'inherit', + 'inherit' + ]}) + const makeretcode = await new Promise(resolve => { makeProc.on('exit', resolve) - makeProc.on('error', () => { - console.error('Failed to start make') + makeProc.on('error', e => { + console.error('Failed to start make: ', e) process.exit(1) }) }) @@ -335,7 +344,7 @@ async function prepare() { const srcPath = pathlib.join(specs_dir, file) const dstPath = pathlib.join(out_dir, file) await fs.copyFile(srcPath, dstPath) - console.log(`Copied ${srcPath} to ${dstPath}`) + console.debug(`Copied ${srcPath} to ${dstPath}`) }) )) } @@ -375,7 +384,11 @@ await new Command() .action(run), { isDefault: true } ) - .addCommand(new Command('prepare').action(prepare)) + .addCommand( + new Command('prepare') + .option('--silent', 'Run make without outputting to stdout') + .action(prepare) + ) .addCommand( new Command('clean') .description('Clear the output directory') From 5685d4df56f87b7d2c174d09274654dc6e115fff Mon Sep 17 00:00:00 2001 From: Lee Yi Date: Wed, 16 Oct 2024 15:31:52 -0400 Subject: [PATCH 14/15] Make autocomplete exit with error code --- scripts/autocomplete.mjs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/autocomplete.mjs b/scripts/autocomplete.mjs index 5709c5101..34de8e977 100644 --- a/scripts/autocomplete.mjs +++ b/scripts/autocomplete.mjs @@ -83,7 +83,7 @@ async function processDirGlobals(target) { document = new JSDOM(contents.toString()).window.document; } catch (err) { console.error(inFile, "failed", err); - return; + return err; } const names = {}; @@ -96,6 +96,7 @@ async function processDirGlobals(target) { const outFile = pathlib.join(OUT_DIR, target + '.json'); await fs.writeFile(outFile, JSON.stringify(names, null, 2), 'utf-8') + return undefined } export default async function autocomplete() { @@ -115,7 +116,11 @@ export default async function autocomplete() { } await fs.mkdir(OUT_DIR, { recursive: true }) - await Promise.all(TARGETS.map(processDirGlobals)) + + // Exit with error code if the there was some error + const errors = await Promise.all(TARGETS.map(processDirGlobals)) + if (errors.find(each => each !== undefined)) process.exit(1) + console.log('Finished processing autocomplete documentation') } From 04ac630ebf1598ca26544bfeaad9fe74ab74c348 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 17 Oct 2024 20:58:34 -0400 Subject: [PATCH 15/15] Put the query parameter back for module importing --- src/modules/loader/loaders.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/modules/loader/loaders.ts b/src/modules/loader/loaders.ts index 939dec1bb..86085b807 100644 --- a/src/modules/loader/loaders.ts +++ b/src/modules/loader/loaders.ts @@ -27,9 +27,6 @@ function wrapImporter(func: (p: string) => Promise) { For the browser, we use the function constructor to hide the import calls from webpack so that webpack doesn't try to compile them away. - - Browsers automatically cache import() calls, so we add a query parameter with the - current time to always invalidate the cache and handle the memoization ourselves */ return async (p: string): Promise => { try { @@ -122,7 +119,7 @@ export const memoizedGetModuleDocsAsync = getMemoizedDocsImporter() const bundleAndTabImporter = wrapImporter<{ default: ModuleBundle }>( typeof window !== 'undefined' && process.env.NODE_ENV !== 'test' - ? (new Function('path', 'return import(path)') as any) + ? (new Function('path', 'return import(`${path}?q=${Date.now()}`)') as any) : // eslint-disable-next-line @typescript-eslint/no-require-imports p => Promise.resolve(require(p)) )