67 lines
3.2 KiB
JavaScript
67 lines
3.2 KiB
JavaScript
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const assert = require('node:assert/strict');
|
|
const root = path.resolve(__dirname, '../..');
|
|
const files = [
|
|
'104/business/IEC104_Command.c',
|
|
'104/business/IEC104_Command.h',
|
|
'examples/iec104_slave_example.c',
|
|
'test/iec104_command_test.c',
|
|
];
|
|
|
|
// Preserve literals and token boundaries while ignoring only comments/whitespace.
|
|
function tokens(source) {
|
|
const input = source.replace(/\\\r?\n/g, '');
|
|
const result = [];
|
|
let offset = 0;
|
|
while (offset < input.length) {
|
|
if (/\s/.test(input[offset])) { offset++; continue; }
|
|
if (input.startsWith('//', offset)) {
|
|
const end = input.indexOf('\n', offset + 2);
|
|
offset = end < 0 ? input.length : end + 1;
|
|
continue;
|
|
}
|
|
if (input.startsWith('/*', offset)) {
|
|
const end = input.indexOf('*/', offset + 2);
|
|
assert(end >= 0, 'unterminated comment');
|
|
offset = end + 2;
|
|
continue;
|
|
}
|
|
const tail = input.slice(offset);
|
|
const literal = /^(?:u8|u|U|L)?(?:"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*')/.exec(tail);
|
|
const word = literal || /^(?:[A-Za-z_][A-Za-z_0-9]*|(?:[0-9]|\.[0-9])(?:[eEpP][+-]|[A-Za-z_0-9.])*)/.exec(tail);
|
|
const token = word || /^(?:>>=|<<=|\.\.\.|->|\+\+|--|<<|>>|<=|>=|==|!=|&&|\|\||\*=|\/=|%=|\+=|-=|&=|\^=|\|=|##|.)/.exec(tail);
|
|
assert(token, `cannot tokenize at ${offset}`);
|
|
result.push(token[0]);
|
|
offset += token[0].length;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
for (const file of files) {
|
|
const originalBytes = fs.readFileSync(path.join(__dirname, `${path.basename(file)}.before`));
|
|
const currentBytes = fs.readFileSync(path.join(root, file));
|
|
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
const original = decoder.decode(originalBytes);
|
|
const current = decoder.decode(currentBytes);
|
|
assert.deepEqual(tokens(current), tokens(original), `${file}: code tokens changed`);
|
|
assert.equal(currentBytes.subarray(0, 3).equals(Buffer.from([0xef, 0xbb, 0xbf])),
|
|
originalBytes.subarray(0, 3).equals(Buffer.from([0xef, 0xbb, 0xbf])), `${file}: BOM changed`);
|
|
assert(!current.includes('\r'), `${file}: CR line endings`);
|
|
assert(!current.includes('\ufffd'), `${file}: replacement character`);
|
|
assert(!/[\t ]+$/m.test(current), `${file}: trailing whitespace`);
|
|
assert(current.endsWith('\n'), `${file}: missing terminal newline`);
|
|
console.log(`${file}: ${tokens(current).length} unchanged tokens, UTF-8/LF OK`);
|
|
}
|
|
|
|
// The loopback update only adds standalone line comments; compare all other bytes.
|
|
const loopbackPath = 'test/slave_example_loopback_test.cjs';
|
|
const loopbackBefore = fs.readFileSync(path.join(__dirname, 'slave_example_loopback_test.cjs.before'), 'utf8');
|
|
const loopbackAfter = fs.readFileSync(path.join(root, loopbackPath), 'utf8');
|
|
const withoutCommentLines = (source) => source.split('\n').filter(line => !/^\s*\/\//.test(line)).join('\n');
|
|
assert.equal(withoutCommentLines(loopbackAfter), withoutCommentLines(loopbackBefore));
|
|
assert(!loopbackAfter.includes('\r') && !loopbackAfter.includes('\ufffd'));
|
|
assert(!/[\t ]+$/m.test(loopbackAfter) && loopbackAfter.endsWith('\n'));
|
|
new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(path.join(root, loopbackPath)));
|
|
console.log(`${loopbackPath}: only standalone comments changed, UTF-8/LF OK`);
|