41 lines
2.2 KiB
Python
Executable File
41 lines
2.2 KiB
Python
Executable File
"""Lexical dependency hints, explicitly not a substitute for Android compilation."""
|
|
from __future__ import annotations
|
|
import re
|
|
from pathlib import Path
|
|
from common import sha256
|
|
|
|
RISKS={
|
|
'desktop_ui':r'\b(?:Forms|Controls|Graphics|Dialogs|LCLIntf|LCLType|Interfaces)\b',
|
|
'windows_api':r'\b(?:Windows|Winapi|ShellAPI|MMSystem|Messages)\b',
|
|
'legacy_gl':r'\b(?:dglOpenGL|glBegin|glEnd|glMatrixMode|glVertexPointer|glOrtho)\b',
|
|
'native_audio':r'\b(?:BASS\w*|OpenAL|DirectSound)\b',
|
|
'native_files':r'\b(?:TFileStream|FileExists|FindFirst|FindNext|GetCurrentDir|ParamStr)\b',
|
|
'layout_or_assembler':r'\b(?:Extended|NativeUInt|NativeInt|Pointer|assembler|asm|packed\s+record)\b',
|
|
'initialization':r'^\s*initialization\b'}
|
|
|
|
def inventory(source:Path)->dict:
|
|
units={};risks=[];edges=[];files=[]
|
|
for p in sorted(source.rglob('*')):
|
|
if not p.is_file() or p.suffix.lower() not in ('.pas','.pp'):continue
|
|
text=p.read_text(encoding='utf-8-sig',errors='replace');rel=p.relative_to(source).as_posix()
|
|
m=re.search(r'^\s*unit\s+([\w.]+)\s*;',text,re.I|re.M)
|
|
if not m:continue
|
|
units.setdefault(m[1].casefold(),[]).append(rel)
|
|
files.append({'path':rel,'sha256':sha256(p)})
|
|
clean=re.sub(r'\{.*?\}|\(\*.*?\*\)|//[^\n]*',' ',text,flags=re.S)
|
|
for use in re.finditer(r'\buses\s+(.+?);',clean,re.I|re.S):
|
|
for term in use[1].split(','):
|
|
dep=re.match(r'\s*([\w.]+)',term)
|
|
if dep:edges.append({'from':m[1],'to':dep[1]})
|
|
for kind,pattern in RISKS.items():
|
|
for hit in list(re.finditer(pattern,text,re.I|re.M))[:8]:
|
|
risks.append({'path':rel,'line':text[:hit.start()].count('\n')+1,'kind':kind,'token':hit[0]})
|
|
return {'scope':'Lexical, both conditional branches included; compilation decides active blockers.',
|
|
'units':units,'duplicates':{k:v for k,v in units.items() if len(v)>1},'edges':edges,'risks':risks,'files':files}
|
|
|
|
def search_paths(source:Path)->list[Path]:
|
|
dirs={source}
|
|
for p in source.rglob('*'):
|
|
if p.is_file() and p.suffix.lower() in ('.pas','.pp','.inc','.inl'):dirs.add(p.parent)
|
|
return sorted(dirs,key=lambda p:('ext' in p.relative_to(source).parts,len(p.parts),str(p)))
|