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,98 @@
|
||||
"""ABAP 라인 → (코드, 주석) 분리와 토큰화.
|
||||
|
||||
목표는 완전한 ABAP 렉서가 아니라 (계획서 §11.3) 문장 패턴 매칭에 충분한 토큰열이다.
|
||||
- 줄 첫 문자 '*' → 전체 주석 줄
|
||||
- 코드 중 '"' 부터 줄 끝 → 인라인 주석 (단, '...' / `...` / |...| 리터럴 내부 제외)
|
||||
- 식별자는 -, ->, =>, / 를 포함해 한 토큰으로 취급한다
|
||||
(gs_head-belnr, zcl_x=>meth, lo_obj->meth, TEXT-004, /bic/xxx 등)
|
||||
- 필드심볼도 컴포넌트까지 한 토큰이다 (<ls_fcat>-fieldname). 이걸 쪼개면 일반 대입
|
||||
판정(up[1] == "=")이 깨져 쓰기 지점이 누락되고 미인식 문장으로 잡힌다 — ALV 필드카탈로그를
|
||||
채우는 관용구라 프로그램에 따라 미인식률을 12% 까지 끌어올렸다.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Line:
|
||||
no: int # 1-based
|
||||
code: str # 주석 제거된 코드 부분
|
||||
comment: str # 이 줄의 주석 텍스트 (전체주석/인라인 모두)
|
||||
is_full_comment: bool
|
||||
|
||||
|
||||
_TOKEN_RE = re.compile(
|
||||
r"""
|
||||
'(?:[^']|'')*' # '문자열' ('' 이스케이프)
|
||||
| `[^`]*` # `문자열`
|
||||
| \|[^|]*\| # |템플릿|
|
||||
| <[A-Za-z_][A-Za-z0-9_]*>(?:->|=>|[A-Za-z0-9_~/-])* # <필드심볼>[-컴포넌트 | ->메서드]
|
||||
| &[0-9]+ # 매크로(DEFINE) 치환 파라미터 &1 &2 …
|
||||
| [A-Za-z_/](?:->|=>|[A-Za-z0-9_~/-])*\(? # 식별자(-, ->, =>, /, ~ 포함; 단독 = 는 제외), 직결 여는괄호 허용
|
||||
| [0-9]+
|
||||
| ##[A-Za-z_]+ # pragma
|
||||
| . # 그 외 1문자 ( ) . , : = 등
|
||||
""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def split_comment(line: str) -> tuple[str, str, bool]:
|
||||
"""한 줄을 (코드, 주석, 전체주석여부)로 분리."""
|
||||
if line.startswith("*"):
|
||||
return "", line[1:].strip(), True
|
||||
code_chars: list[str] = []
|
||||
i, n = 0, len(line)
|
||||
while i < n:
|
||||
ch = line[i]
|
||||
if ch == "'":
|
||||
j = i + 1
|
||||
while j < n:
|
||||
if line[j] == "'":
|
||||
if j + 1 < n and line[j + 1] == "'":
|
||||
j += 2
|
||||
continue
|
||||
break
|
||||
j += 1
|
||||
code_chars.append(line[i : min(j + 1, n)])
|
||||
i = j + 1
|
||||
elif ch == "`":
|
||||
j = line.find("`", i + 1)
|
||||
j = n - 1 if j < 0 else j
|
||||
code_chars.append(line[i : j + 1])
|
||||
i = j + 1
|
||||
elif ch == "|":
|
||||
j = line.find("|", i + 1)
|
||||
j = n - 1 if j < 0 else j
|
||||
code_chars.append(line[i : j + 1])
|
||||
i = j + 1
|
||||
elif ch == '"':
|
||||
return "".join(code_chars), line[i + 1 :].strip(), False
|
||||
else:
|
||||
code_chars.append(ch)
|
||||
i += 1
|
||||
return "".join(code_chars), "", False
|
||||
|
||||
|
||||
def tokenize_code(code: str) -> list[str]:
|
||||
tokens = []
|
||||
for m in _TOKEN_RE.finditer(code):
|
||||
t = m.group(0)
|
||||
if t.strip():
|
||||
# 식별자에 붙은 여는 괄호는 분리한다: "meth(" → "meth", "("
|
||||
if len(t) > 1 and t.endswith("(") and not t.startswith(("'", "`", "|")):
|
||||
tokens.append(t[:-1])
|
||||
tokens.append("(")
|
||||
else:
|
||||
tokens.append(t)
|
||||
return tokens
|
||||
|
||||
|
||||
def read_lines(text: str) -> list[Line]:
|
||||
out: list[Line] = []
|
||||
for i, raw in enumerate(text.split("\n"), start=1):
|
||||
code, comment, full = split_comment(raw.rstrip("\r"))
|
||||
out.append(Line(no=i, code=code, comment=comment, is_full_comment=full))
|
||||
return out
|
||||
Reference in New Issue
Block a user