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 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();