Initial commit: ABAP indexing pipeline (ingest, parser, summarize, index, query, wiki)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# 정의부(declaration) 수집 설계 — 2026-09-21
|
||||
|
||||
## 왜 필요한가
|
||||
|
||||
이 인덱스의 최종 용도는 **비슷한 로직을 새로 짤 때 기존 코드를 찾아 복사해 오는 것**이다.
|
||||
그런데 인덱스에 담긴 조각(logic_chunk)의 코드 원문은 **로직만**이다. ABAP 은 내부테이블·스트럭처·
|
||||
상수·필드심볼을 TOP 인클루드나 FORM 머리에 따로 선언하므로, 로직만 복사해 붙여넣으면
|
||||
`GT_LOAD 는 선언되지 않았습니다` 류의 문법 오류가 난다.
|
||||
|
||||
그래서 **정의부를 인덱싱 시점에 따로 수집**한다. 조각 코드에는 계속 로직만 둔다(검색 품질을
|
||||
지키기 위해서다 — 선언 수백 줄이 조각에 섞이면 조각의 뜻이 흐려진다).
|
||||
|
||||
수집한 정의부를 **쓸지 말지는 판단하지 않는다.** 붙여넣는 쪽의 LLM 이 정한다:
|
||||
이미 같은 선언이 있으면 버리고, 이름이 겹치면 바꾸고, 타입만 빌려 쓰기도 한다.
|
||||
인덱스는 "이 조각을 가져가려면 이것들이 함께 필요하다"는 **재료와 분류**만 정확히 넘긴다.
|
||||
|
||||
## 무엇을 수집하는가
|
||||
|
||||
`parser/declarations.py` 가 선언 문장을 **붙여넣을 수 있는 한 문장 단위**로 잘라 `declaration`
|
||||
테이블에 넣는다 (`index/db.py`).
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| kind | data / types / constants / tables / field-symbol / parameter / select-option / ranges / type-pool / controls / class / macro |
|
||||
| scope | `global`(TOP·공용 인클루드) / `unit`(FORM·METHOD 머리의 로컬 선언) |
|
||||
| code | **선언 원문** — 체인 항목이면 헤드를 다시 붙이고 `,` 를 `.` 로 닫아 유효한 문장으로 만든다 |
|
||||
| depends | 이 선언이 참조하는 다른 이름 (`LIKE GT_DATA` → GT_DATA, `TYPE TABLE OF TY_X` → TY_X) |
|
||||
|
||||
세 가지가 핵심이다.
|
||||
|
||||
1. **`BEGIN OF … END OF` 는 한 덩어리다.** 업무 프로그램의 내부테이블은 거의 전부 이 형태다
|
||||
(`DATA : BEGIN OF GT_UPLOAD OCCURS 0, … END OF GT_UPLOAD.`). `parser/dataflow.py` 의 심볼
|
||||
추출은 이걸 건너뛰지만(추적에는 이름만 필요하다), 정의부에는 반드시 통째로 있어야 한다.
|
||||
2. **체인 항목도 혼자 설 수 있어야 한다.** `DATA: a TYPE i,\n b TYPE i.` 의 `b` 만 잘라내면
|
||||
`b TYPE i.` 라 붙여넣을 수 없다 → `DATA:\n b TYPE i.` 로 헤드를 복원한다.
|
||||
이를 위해 `parser/statements.py` 가 체인 전개 시 **항목별 줄 범위**를 기록한다.
|
||||
3. **로컬 클래스·매크로도 정의부다.** `CLASS lcl_x DEFINITION … ENDCLASS`, `DEFINE … END-OF-DEFINITION`
|
||||
은 unit 통째를 한 건으로 담는다.
|
||||
|
||||
## 조각 → 정의부 조립 (`index/decls.py`)
|
||||
|
||||
1. 조각 코드의 식별자를 모은다 (파서 토크나이저 · 문장 단위)
|
||||
2. `declaration` 에서 찾는다 — **unit 로컬 먼저, 없으면 전역**
|
||||
3. 찾은 선언의 `depends` 를 **재귀로** 끌어온다 (`LT_DATA LIKE GT_DATA` → GT_DATA)
|
||||
4. 의존이 먼저 오도록(post-order) 정렬해 `declaration_code` 한 덩어리로 만든다
|
||||
|
||||
결과는 네 갈래로 분류해 넘긴다.
|
||||
|
||||
| 필드 | 뜻 | 붙여넣는 쪽이 할 일 |
|
||||
|---|---|---|
|
||||
| `declarations` / `declaration_code` | 함께 가야 하는 선언 원문 | 있으면 버리거나, 이름을 바꿔 쓰거나, 그대로 넣는다 |
|
||||
| `declaration_external_refs` | DDIC 사전 객체 (BKPF, LVC_T_FCAT …) | 가져갈 게 없다. 대상 시스템에 있는지만 본다 |
|
||||
| `declaration_params` | 이 조각이 쓰는 FORM 파라미터 | 호출 측에서 넘어오는 값이다 |
|
||||
| `declaration_unresolved` | 변수처럼 생겼는데 선언을 못 찾은 이름 | 수집 안 된 인클루드·함수 인터페이스일 수 있다 |
|
||||
|
||||
### 판정에서 가른 두 가지
|
||||
|
||||
- **`LIKE BSEG-WRBTR` 은 작업영역이 아니다.** 사전에서 타입만 빌려 쓰는 것이라
|
||||
TOP 의 `TABLES: BSEG.` 를 딸려 보내면 안 된다. 반면 `bseg-wrbtr = 100.` 처럼 **작업영역을
|
||||
실제로 쓰면** 그 선언이 필요하다. 그래서 참조를 컴포넌트까지 보존해 둘을 구분한다.
|
||||
- **호출문의 형식 파라미터는 변수가 아니다.** `CALL FUNCTION 'X' EXPORTING i_bukrs = gv_bukrs`
|
||||
의 `i_bukrs` 와 `EXCEPTIONS no_rate_found` 는 호출 대상의 인터페이스다. 이름만 보면 변수와
|
||||
구분되지 않아(둘 다 `I_`/`NO_`) 문장 구조로 걸러낸다.
|
||||
|
||||
### 저장하지 않고 읽을 때 계산한다
|
||||
|
||||
조각↔선언 대응은 `logic_chunk` 에 캐시하지 않는다. `declaration` 과 조각 코드만 있으면
|
||||
결정론적으로 다시 나오므로, 캐시하면 조각 재추출·선언 변경과 엇갈려 따로 늙기만 한다.
|
||||
조각 하나당 수십 줄을 토큰화하는 비용이라 조회 경로에서 충분히 싸다.
|
||||
|
||||
## 실측 (샘플 55본 / 조각 3,212건, 2026-09-21)
|
||||
|
||||
- 선언 18,049건 수집 (data 11.7k · constants 1.7k · field-symbol 1.0k · tables 1.0k · type-pool 488 · macro 295 · class 91 …)
|
||||
- 조각의 **79%** 가 정의부를 갖는다 (평균 3.5건, 최대 21건).
|
||||
나머지 21%는 호출·제어·메시지처럼 프로그램 선언을 쓰지 않는 조각이다.
|
||||
- 선언을 못 찾은 이름이 남은 조각 **3%** — DDIC 테이블(J_1BBRANCH), 함수 인터페이스 파라미터,
|
||||
화면 필드가 대부분이다.
|
||||
|
||||
### 곁가지로 고친 파서 버그 3건
|
||||
|
||||
정의부를 조립하면서 "선언이 없다"고 새어 나온 이름들을 좇아가 드러난 것들이다.
|
||||
|
||||
1. `CLASS lcl_x DEFINITION DEFERRED.` 를 블록으로 열어 **그 뒤 인클루드 전체를 CLASS_DEF 하나로
|
||||
삼켰다** (ENDCLASS 가 없다). ZCO_ALV 의 선언 수십 건이 통째로 사라져 있었다.
|
||||
2. FORM 시그니처의 파라미터를 **첫 개만** 잡았다 (`USING p_date LIKE x p_days LIKE y` 에서
|
||||
TYPE/LIKE 를 만나면 멈췄다).
|
||||
3. `PARAMETER`(단수형)를 선언 키워드로 몰랐다 — 미인식 문장 123건 → 3건.
|
||||
|
||||
## API
|
||||
|
||||
| Method | Path | 정의부 관련 |
|
||||
|---|---|---|
|
||||
| GET | `/chunks/{chunk_id}` | `declarations`, `declaration_code`, `declaration_external_refs`, `declaration_params`, `declaration_unresolved` (끄려면 `?decls=false`) |
|
||||
| GET | `/programs/{name}/units/{unit}/code` | 같은 필드 (unit 통째로 가져갈 때) |
|
||||
| GET | `/programs/{name}/declarations?scope=global\|unit` | 프로그램의 선언 카탈로그 |
|
||||
|
||||
## 위키에서의 표시
|
||||
|
||||
원문은 프로그램 문서의 `## 정의부` 섹션에 **한 번만** 싣는다 (인클루드별, 전역 선언 전체 +
|
||||
조각이 실제로 쓰는 unit 로컬 선언). 조각 쪽에는 이름만 두고 그 항목으로 가는 **문서 내 링크**를 건다.
|
||||
|
||||
```markdown
|
||||
- 정의부(복사 시 함께 필요): [`GT_LOAD`](#decl-zfic0020top-6-gt_load), [`C_X`](#decl-zficom-213-c_x)
|
||||
…
|
||||
#### GT_LOAD — data · L6-L56 {#decl-zfic0020top-6-gt_load}
|
||||
```
|
||||
|
||||
`{#id}` 는 제목에 앵커를 다는 표기이고, 뷰어(`wiki_out/viewer.html`)가 이걸 `id` 로 바꿔
|
||||
눌러서 이동하게 만든다(이동 후 1.4초 강조). **뷰어 전용 기능이 아니다** — 마크다운으로 읽으면
|
||||
링크 텍스트가 곧 선언 이름이라 LLM 에게는 지금까지와 똑같이 읽히고, 앵커는 무시된다.
|
||||
|
||||
링크와 앵커가 어긋나면 조용히 죽으므로 짝을 테스트로 묶어 뒀다
|
||||
(`tests/test_wiki_out.py::test_chunk_links_to_declaration`). 실측 11,051개 링크 / 깨진 링크 0개.
|
||||
Reference in New Issue
Block a user