const fs = require('fs');
|
const path = require('path');
|
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
const SOURCE_FILE = path.join(PROJECT_ROOT, 'CB_IMGPSScanImp.pas.bk');
|
const JSON_DIR = path.join(PROJECT_ROOT, 'doc', 'curtis', 'prompt', 'scanimpl_analysis');
|
const REVIEW_FILE = path.join(PROJECT_ROOT, 'CB_IMGPSScanImp_Remain_Review.pas');
|
|
// --- 動態 Delphi 語法解析器 ---
|
function findDelphiMethodBounds(lines, matcher, startIndexHint = 0) {
|
// 取得函式宣告的前半段特徵,並統一轉換為小寫比對 (避免 Delphi 的大小寫差異)
|
// 例如: "procedure TCB_IMGPSScanX.initkscan"
|
const rawSignature = matcher.split('(')[0].trim().toLowerCase().replace(';', '');
|
|
// 針對有參數跟沒參數的,進一步過濾乾淨
|
const signature = rawSignature.split(':')[0].trim();
|
|
let startIdx = -1;
|
let endIdx = -1;
|
|
// 1. 找尋起點 (從 startIndexHint 開始找,支援屬性 getter/setter 重名問題)
|
for (let i = startIndexHint; i < lines.length; i++) {
|
const line = lines[i].toLowerCase();
|
|
// 確保是宣告實作區段,而非 interface 宣告
|
if (line.includes(signature) &&
|
(line.startsWith('procedure ') || line.startsWith('function ') || line.includes(' procedure ') || line.includes(' function '))) {
|
|
// 避免找到 interface 區段的宣告 (通常以分號結尾且無 begin)
|
// 簡單判斷:如果是實作區,往下找幾行應該會有 begin, var, const 等宣告
|
let isImpl = false;
|
for(let scan = i; scan < Math.min(i + 20, lines.length); scan++) {
|
if (lines[scan].toLowerCase().match(/\b(begin|var|const|type)\b/)) {
|
isImpl = true;
|
break;
|
}
|
}
|
if (isImpl) {
|
startIdx = i;
|
break;
|
}
|
}
|
}
|
|
if (startIdx === -1) return null;
|
|
// 2. 透過括號與 Begin/End 深度計算法找尋終點 end;
|
let openCount = 0;
|
let foundBegin = false;
|
let inAsm = false;
|
|
for (let i = startIdx; i < lines.length; i++) {
|
let line = lines[i].toLowerCase();
|
|
// 剃除字串與註解,避免干擾括號計算
|
line = line.replace(/'[^']*'/g, '').replace(/\/\/.*$/, '').replace(/\{[^}]*\}/g, '').replace(/\(\*.*?\*\)/g, '');
|
|
// 分割出乾淨的字詞
|
const words = line.split(/[\s;()\[\]=]+/g).filter(Boolean);
|
|
for (const word of words) {
|
if (word === 'asm') inAsm = true;
|
if (word === 'begin' || word === 'case' || word === 'try' || word === 'asm') {
|
openCount++;
|
foundBegin = true;
|
}
|
if (word === 'end') {
|
openCount--;
|
if (inAsm && openCount === 0) inAsm = false;
|
}
|
}
|
|
// 當發現了 begin 且深度歸零,代表這個方法完全結束了
|
if (foundBegin && openCount === 0) {
|
endIdx = i;
|
break;
|
}
|
}
|
|
return { lIndex: startIdx + 1, rIndex: endIdx + 1 };
|
}
|
|
function runTest() {
|
console.log("==================================================");
|
console.log(" CB_IMGPSScanImp 動態全域解析與拆解測試腳本 (自動校正行號)");
|
console.log("==================================================\n");
|
|
if (!fs.existsSync(SOURCE_FILE)) {
|
console.error(`[錯誤] 找不到來源備份檔: ${SOURCE_FILE}`);
|
return;
|
}
|
|
let sourceLines = fs.readFileSync(SOURCE_FILE, 'utf-8').split(/\r?\n/);
|
let parsedTargets = [];
|
let totalUpdated = 0;
|
let missingCount = 0;
|
|
if (!fs.existsSync(JSON_DIR)) {
|
console.error(`[錯誤] 找不到 JSON 目錄: ${JSON_DIR}`);
|
return;
|
}
|
|
const files = fs.readdirSync(JSON_DIR).filter(f => f.endsWith('.json'));
|
console.log(`>> 找到 ${files.length} 個 JSON 分類檔,開始進行語法比對與深度解析...`);
|
|
// 略過 interface 區段,避免把 interface 的宣告誤當作實作
|
let implementationStartLine = 0;
|
for(let i=0; i<sourceLines.length; i++){
|
if(sourceLines[i].toLowerCase().trim() === 'implementation') {
|
implementationStartLine = i;
|
break;
|
}
|
}
|
|
// --- 0.2.2.1: 測試與動態驗證 (Verification & Correction Phase) ---
|
for (const file of files) {
|
const filePath = path.join(JSON_DIR, file);
|
let hasChanges = false;
|
try {
|
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
const items = Array.isArray(content) ? content : [content];
|
|
for (let item of items) {
|
// 呼叫 AST 語法掃描器動態找尋真實行號 (從 implementation 之後開始找)
|
const actualBounds = findDelphiMethodBounds(sourceLines, item.matcher, implementationStartLine);
|
|
if (actualBounds && actualBounds.lIndex > 0 && actualBounds.rIndex >= actualBounds.lIndex) {
|
// 若偵測到的與原本不同的,自動更新並標記變更
|
if (item.lIndex != actualBounds.lIndex || item.rIndex != actualBounds.rIndex) {
|
item.lIndex = actualBounds.lIndex.toString();
|
item.rIndex = actualBounds.rIndex.toString();
|
hasChanges = true;
|
totalUpdated++;
|
}
|
// 轉換為 0-based index 加入待刪除清單
|
parsedTargets.push({
|
...item,
|
startIdx: actualBounds.lIndex - 1,
|
endIdx: actualBounds.rIndex - 1
|
});
|
} else {
|
console.warn(`[警告] 無法在原始碼中定位 Matcher 或其包含無效範圍: ${item.matcher}`);
|
missingCount++;
|
}
|
}
|
|
// 若有修正,回寫覆蓋原本的 JSON 檔
|
if (hasChanges) {
|
fs.writeFileSync(filePath, JSON.stringify(items, null, 2), 'utf-8');
|
}
|
} catch (e) {
|
console.error(`[錯誤] 解析 JSON 檔案 ${file} 失敗:`, e.message);
|
}
|
}
|
|
console.log(`\n>> 驗證完成。`);
|
console.log(` 成功動態定位: ${parsedTargets.length} 個區段。`);
|
console.log(` 自動校正誤差: ${totalUpdated} 個。`);
|
console.log(` 無法定位遺漏: ${missingCount} 個。\n`);
|
|
// --- 0.2.2.2: 執行區段移除並審核 (Removal Phase) ---
|
console.log("[階段 2] 執行區段移除...");
|
|
// 依照 startIdx 由大到小排序 (由尾部往上刪),保證刪除時上方行號不會產生位移跑版
|
parsedTargets.sort((a, b) => b.startIdx - a.startIdx);
|
|
let removedCount = 0;
|
for (const target of parsedTargets) {
|
const deleteCount = target.endIdx - target.startIdx + 1;
|
if (deleteCount > 0) {
|
sourceLines.splice(target.startIdx, deleteCount);
|
removedCount++;
|
}
|
}
|
|
// 寫入完整剩餘結果至實體檔案
|
fs.writeFileSync(REVIEW_FILE, sourceLines.join('\n'), 'utf-8');
|
console.log(`✅ 成功移除 ${removedCount} 個方法區段。`);
|
console.log(`✅ 移除後的剩餘程式碼已儲存至: \n ${REVIEW_FILE}`);
|
console.log(` 您可以開啟該檔案進行人工審核 (裡面應該只剩下全域變數與宣告區段)。`);
|
}
|
|
runTest();
|