72 lines
3.8 KiB
JavaScript
72 lines
3.8 KiB
JavaScript
// 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;});
|