curtis
9小時前 0756bf12d10cf1b7f78c571de0a9ad69cbaeb7ca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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();