// Read-only diagnostic: exercise the current editor interpreter with mocked game APIs. // Run from scratch-gui: node scripts/diagnose-cpp.cjs const fs = require('fs'); const path = require('path'); const vm = require('vm'); const babel = require('@babel/core'); const filename = path.resolve(__dirname, '../src/components/stage-cpp/cpp-parser.js'); const transformed = babel.transformSync(fs.readFileSync(filename, 'utf8'), { filename, configFile: false, babelrc: false, plugins: ['@babel/plugin-transform-modules-commonjs'] }).code; const context = { exports: {}, require, setTimeout, console: {log() {}, warn() {}, error() {}}, window: {} }; vm.runInNewContext(transformed, context, {filename}); const {CppCodeExecutor, validateCppSyntax} = context.exports; const main = body => `int main() {\n${body}\n}`; const customer = `void test() { for (int i = 0; i < 2; i++) { } } int main() { turnLeft(-1); for (int i = 0; i < 1; i++) { test(); playerMove(i); } }`; const cases = [ ['customer_local_i', customer, {moves: [0]}], ['customer_renamed_inner_j', customer.replace('int i = 0; i < 2; i++', 'int j = 0; j < 2; j++'), {moves: [0]}], ['cout_literal', main('cout << "Hello" << endl;'), {output: ['Hello\n']}], ['cout_variable', main('int i = 7;\ncout << "i=" << i << endl;'), {output: ['i=7\n']}], ['cout_string_assignment_shape', main('cout << "value=42" << endl;'), {output: ['value=42\n']}], ['cout_increment_expression', main('int i = 0;\ncout << i++ << endl;\nplayerMove(i);'), {output: ['0\n'], moves: [1]}], ['cout_increment_literal', main('cout << "C++" << endl;'), {output: ['C++\n']}], ['cout_std_qualified', main('std::cout << "Hello" << std::endl;'), {output: ['Hello\n']}], ['main_return_zero', main('return 0;'), {error: null}], ['legal_space_before_call', main('playerMove (1);'), {valid: true, moves: [1]}], ['nested_loop_shadowing', main('for (int i = 0; i < 2; i++) {\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);\n}'), {moves: [0, 1]}], ['negative_integer_division', main('int a = -3;\na /= 2;\nplayerMove(a);'), {moves: [-1]}], ['function_return_value', 'int answer() {\nreturn 7;\n}\n' + main('int n = answer();\nplayerMove(n);'), {moves: [7]}], ['nested_if_break', main('for (int i = 0; i < 3; i++) {\nif (i == 1) {\nbreak;\n}\nplayerMove(i);\n}'), {moves: [0]}], ['floating_point', main('double x = 1.5;\ncout << x << endl;'), {output: ['1.5\n']}], ['array_index', main('int a[2] = {3, 7};\nplayerMove(a[1]);'), {moves: [7]}], ['unknown_variable', main('playerMove(missing);'), {valid: false}], ['early_return_zero', 'int stop() {\nreturn 0;\nplayerMove(9);\n}\n' + main('stop();'), {moves: []}] ]; (async () => { const results = []; for (const [name, code, expected] of cases) { const actual = {valid: false, moves: [], turns: [], output: [], error: null}; context.window = {isScriptRunOver: false, cppGameAPI: { playerMove: async n => actual.moves.push(n), turnLeft: async n => actual.turns.push(n), cmdPrint: text => actual.output.push(text) }}; const validation = await validateCppSyntax(code); actual.valid = validation.isValid; if (!actual.valid) actual.validationErrors = validation.errors; const executor = new CppCodeExecutor(code, null); if (actual.valid) { try { await executor.run(); } catch (error) {actual.error = error.message;} } const matchesExpected = Object.entries(expected).every(([key, value]) => JSON.stringify(actual[key]) === JSON.stringify(value)); results.push({name, expected, actual, matchesExpected}); } process.stdout.write(JSON.stringify(results, null, 2) + '\n'); })().catch(error => {console.error(error); process.exitCode = 1;});