Delphi-Konzept: Reale tree-sitter-pascal AST-Typen, Integration Guide, erweiterte Fixtures
- Alle Docs auf verifizierte AST-Knotentypen aktualisiert (declClass, declIntf, declProc, etc.) - Scaffold/ entfernt und durch konkreten Integration Guide ersetzt (06-Integration-Guide.md) - Neue Docs: AST-Referenz (03), NodeKind-Mapping (05), Integration Guide (06) - Checklist um bekannte Einschränkungen erweitert (with-Statements, .dfm/.fmx, Generics) - UAuth.pas erweitert: Sichtbarkeitsbereiche, Felder, Properties, Constructor/Destructor - UTypes.pas neu: Enums, Records, Type-Aliase, Konstanten, Class Methods, verschachtelte Typen - UnitResolution.md mit konkreten AST-Strukturen aktualisiert - README.md an neue Architektur-Beschreibung angepasst
This commit is contained in:
@@ -2,53 +2,96 @@
|
||||
|
||||
## 0) Prämissen
|
||||
|
||||
- CodeGraph extrahiert **Nodes** und **Edges** über **tree-sitter** und language-spezifische **Queries (`.scm`)**.
|
||||
- CodeGraph extrahiert **Nodes** und **Edges** über **tree-sitter** und eine `LanguageExtractor`-Konfiguration pro Sprache in `src/extraction/tree-sitter.ts`.
|
||||
- Sprachen werden zentral in drei Dateien registriert: `grammars.ts`, `types.ts`, und `tree-sitter.ts`.
|
||||
- Danach folgt **Reference Resolution** (Calls → Definitionen, imports → Dateien, inheritance, framework patterns).
|
||||
- **Grammar:** [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) (unterstützt Delphi, FreePascal und Standard-Pascal).
|
||||
|
||||
## 1) Parser / Grammar
|
||||
|
||||
Empfohlen: `tree-sitter-pascal` (unterstützt Delphi + FreePascal).
|
||||
npm-Paket: `tree-sitter-pascal`
|
||||
|
||||
**Konkrete AST-Knoten (Auswahl):**
|
||||
|
||||
| tree-sitter-pascal Typ | Beschreibung |
|
||||
|---|---|
|
||||
| `unit`, `program`, `library` | Top-Level-Module |
|
||||
| `moduleName` | Name des Moduls (Kind-Knoten von `unit`/`program`/`library`) |
|
||||
| `declClass` | Klassen, Records, Objects (enthält `kClass`/`kRecord`/`kObject`) |
|
||||
| `declIntf` | Interfaces (`kInterface`/`kDispInterface`) |
|
||||
| `declProc` | Procedures, Functions, Methods, Constructors, Destructors |
|
||||
| `declProp` | Properties |
|
||||
| `declField` | Felder in Klassen/Records |
|
||||
| `declEnum` | Aufzählungstypen |
|
||||
| `declEnumValue` | Einzelne Enum-Werte |
|
||||
| `declType` | Type-Deklarationen (enthält `declClass`/`declIntf`/`declEnum`/etc.) |
|
||||
| `declUses` | `uses`-Klauseln (imports) |
|
||||
| `declConst` | Konstanten-Deklarationen |
|
||||
| `declSection` | Sichtbarkeitsbereiche (`kPrivate`/`kPublic`/`kProtected`/`kPublished`) |
|
||||
| `declHelper` | Class/Record Helpers |
|
||||
| `declExports` | exports-Klauseln |
|
||||
| `defProc` | Implementations-Body einer Prozedur/Funktion |
|
||||
| `exprCall` | Funktionsaufrufe |
|
||||
| `exprDot` | Qualifizierte Zugriffe (`Obj.Method`) |
|
||||
| `declArgs` | Parameterliste |
|
||||
| `declArg` | Einzelner Parameter |
|
||||
|
||||
**Tasks:**
|
||||
- Dependency hinzufügen (wie bei bestehenden Sprachen).
|
||||
- Language-Loader/Registry erweitern: `languageId = "pascal"` (oder `"delphi"`, aber konsistent).
|
||||
- `tree-sitter-pascal` als npm-Dependency hinzufügen
|
||||
- Grammar-Loader in `src/extraction/grammars.ts` registrieren: `pascal: () => require('tree-sitter-pascal')`
|
||||
- Extension-Mapping und Language-Typ anlegen (siehe Integration Guide)
|
||||
|
||||
## 2) File-Erkennung
|
||||
|
||||
Extensions (MVP):
|
||||
- `.pas` (Units)
|
||||
- `.dpr` (Program)
|
||||
- `.dpk` (Package)
|
||||
- `.dpr` (Delphi Program)
|
||||
- `.dpk` (Delphi Package)
|
||||
- `.lpr` (Lazarus Program)
|
||||
|
||||
Optional:
|
||||
- `.inc` (Include-Fragmente) – erst später oder nur, wenn Parser tolerant genug ist.
|
||||
Optional (Phase 2):
|
||||
- `.inc` (Include-Fragmente) – erst später, da Parser möglicherweise keinen vollständigen AST liefert.
|
||||
|
||||
## 3) Queries (MVP)
|
||||
## 3) Extraktion (MVP)
|
||||
|
||||
### 3.1 Nodes
|
||||
Extrahiere mindestens:
|
||||
- Unit/Program/Package Name
|
||||
- Klassen / Interfaces / Records
|
||||
- Procedures/Functions
|
||||
- Methoden (class/instance)
|
||||
|
||||
| Delphi-Konzept | tree-sitter Typ | → CodeGraph `NodeKind` |
|
||||
|---|---|---|
|
||||
| Unit / Program / Library | `unit` / `program` / `library` | `module` |
|
||||
| Klasse / Record / Object | `declClass` | `class` |
|
||||
| Interface | `declIntf` | `interface` |
|
||||
| Procedure / Function (top-level) | `declProc` (mit `kProcedure`/`kFunction`) | `function` |
|
||||
| Methode / Constructor / Destructor | `declProc` (mit `kConstructor`/`kDestructor`, oder innerhalb `declClass`) | `method` |
|
||||
| Property | `declProp` | `property` |
|
||||
| Feld | `declField` | `field` |
|
||||
| Enum | `declEnum` | `enum` |
|
||||
| Enum-Wert | `declEnumValue` | `enum_member` |
|
||||
| Type-Alias | `declType` (einfache Alias-Formen) | `type_alias` |
|
||||
| Konstante | `declConst` | `constant` |
|
||||
| Uses-Klausel | `declUses` | `import` |
|
||||
|
||||
### 3.2 Edges
|
||||
Extrahiere mindestens:
|
||||
- `uses` (import/dependency)
|
||||
- `extends` (Basisklasse)
|
||||
- `implements` (Interface-Implementierungen)
|
||||
- `calls` (Identifier + optional Qualifier)
|
||||
|
||||
| Beziehung | Erkennung | → CodeGraph `EdgeKind` |
|
||||
|---|---|---|
|
||||
| `uses X, Y;` | `declUses` → Kind-Knoten `identifier` | `imports` |
|
||||
| Klassen-Vererbung | `declClass` → `parent` Feld → erster `typeref` | `extends` |
|
||||
| Interface-Implementierung | `declClass` → `parent` Feld → weitere `typeref`s | `implements` |
|
||||
| Funktionsaufruf | `exprCall` | `calls` |
|
||||
| Qualifizierter Aufruf | `exprDot` als Elternteil von `exprCall` | `calls` |
|
||||
| Enthaltensein | Parent-Child im AST (z.B. `declClass` → `declProc`) | `contains` |
|
||||
| Instanziierung | `exprCall` mit `TClassName.Create` | `instantiates` |
|
||||
|
||||
## 4) Reference Resolution (MVP)
|
||||
|
||||
### 4.1 Unit-Auflösung (`uses`)
|
||||
- Index-Phase: Map `UnitName -> fileId` (aus Units/Programs/Packages)
|
||||
- Index-Phase: Map `unitName → fileId` (aus `moduleName` Knoten in `unit`/`program`/`library`)
|
||||
- Resolution: Bei `uses Foo, Bar;` Edges auf Ziel-Units setzen.
|
||||
- Spezialfälle (später): `Foo in 'path\Foo.pas'` (DPR/DPK), Namespaces (`System.SysUtils`).
|
||||
- Spezialfälle (Phase 2): `Foo in 'path\Foo.pas'` (DPR/DPK), Namespaces (`System.SysUtils`).
|
||||
|
||||
### 4.2 Call-Auflösung (best effort)
|
||||
- Extraktion: `callName`, optional `qualifier` (`Obj.DoIt`, `TMyClass.DoIt`)
|
||||
- Extraktion: Name aus `exprCall`, Qualifier aus `exprDot`
|
||||
- Heuristik-Reihenfolge:
|
||||
1. lokale Procs/Funcs in der gleichen Unit
|
||||
2. Methoden in der gleichen Klasse
|
||||
@@ -59,14 +102,17 @@ Extrahiere mindestens:
|
||||
|
||||
- Fixtures aus `fixtures/` in die Test-Suite aufnehmen
|
||||
- Assertions:
|
||||
- Node-Anzahl + zentrale Namen
|
||||
- `uses`-Edges
|
||||
- Node-Anzahl + zentrale Namen prüfen
|
||||
- `uses`-Edges (imports)
|
||||
- `extends`/`implements`-Edges
|
||||
- einfache Call-Edges
|
||||
- Sichtbarkeiten (public/private/protected)
|
||||
- Properties, Felder, Enums
|
||||
|
||||
## 6) Definition of Done
|
||||
|
||||
- `codegraph index` indexiert Delphi/Pascal-Dateien ohne Crash
|
||||
- `codegraph_search` findet Klassen/Methoden
|
||||
- `codegraph_search` findet Klassen/Methoden/Properties
|
||||
- `codegraph_callers/callees` liefert sinnvolle Ergebnisse für einfache Fälle
|
||||
- `uses`-Graph stimmt (Unit-Abhängigkeiten)
|
||||
- Sichtbarkeiten werden korrekt extrahiert
|
||||
|
||||
@@ -1,53 +1,97 @@
|
||||
# Capture Convention (wie die `.scm` Queries an CodeGraph andocken)
|
||||
# Capture Convention: Wie Pascal/Delphi an CodeGraph andockt
|
||||
|
||||
Da wir hier nur ein ZIP-Konzept liefern (ohne den CodeGraph-Source lokal auszulesen), ist das wichtigste: **du musst die Captures so benennen, wie CodeGraph sie in den bestehenden Sprachen bereits erwartet**.
|
||||
## Architektur-Übersicht
|
||||
|
||||
## Vorgehen
|
||||
CodeGraph verwendet **keine** `.scm`-Query-Dateien für die Extraktion. Stattdessen wird die Logik über ein `LanguageExtractor`-Objekt in `src/extraction/tree-sitter.ts` definiert. Dieses Objekt mappt tree-sitter AST-Knotentypen auf die interne Verarbeitungslogik.
|
||||
|
||||
1. Öffne im CodeGraph-Repo eine bestehende Sprache (z.B. Go/Rust/Java).
|
||||
2. Suche nach `.scm` Dateien und schaue:
|
||||
- welche Capture-Namen verwendet werden
|
||||
- wie Node-Kind/Edge-Kind gesetzt werden (oft via `#set!`)
|
||||
3. Übertrage exakt dieses Schema auf Pascal.
|
||||
### Zentrale Dateien für die Integration
|
||||
|
||||
## Empfohlene semantische Einteilung
|
||||
| Datei | Änderung |
|
||||
|---|---|
|
||||
| `src/extraction/grammars.ts` | Grammar-Loader, Extension-Mapping, Display-Name |
|
||||
| `src/types.ts` | `Language`-Union-Type, `DEFAULT_CONFIG.include` Patterns |
|
||||
| `src/extraction/tree-sitter.ts` | `EXTRACTORS`-Map mit `LanguageExtractor`-Konfiguration |
|
||||
|
||||
Unabhängig von den konkreten Capture-Namen sollte jedes Query-Match folgende Informationen liefern:
|
||||
## LanguageExtractor-Interface
|
||||
|
||||
### Für Nodes
|
||||
- **kind**: `unit | program | package | class | interface | record | function | procedure | method | property | field | type | enum | variable`
|
||||
- **name**: Symbolname
|
||||
- **container** (optional): z.B. Klassenname für Methoden
|
||||
- **signature** (optional): Parameter/Returntype, falls leicht verfügbar
|
||||
- **range**: Start/Ende im File (Line/Column)
|
||||
Die Konfiguration definiert, welche AST-Knotentypen welcher Semantik entsprechen:
|
||||
|
||||
### Für Edges
|
||||
- **kind**: `imports | calls | extends | implements | contains | returns_type`
|
||||
- **from**: Node-ID der Quelle (oder "current scope")
|
||||
- **to**: Zielsymbol (Name) oder Ziel-Node-ID, wenn schon auflösbar
|
||||
- **raw**: Originaltext (hilft Resolvern)
|
||||
```typescript
|
||||
interface LanguageExtractor {
|
||||
functionTypes: string[]; // Top-Level Funktionen/Prozeduren
|
||||
classTypes: string[]; // Klassen, Records, Objects
|
||||
methodTypes: string[]; // Methoden, Konstruktoren, Destruktoren
|
||||
interfaceTypes: string[]; // Interfaces
|
||||
structTypes: string[]; // Structs (in Delphi: leer)
|
||||
enumTypes: string[]; // Enums
|
||||
typeAliasTypes: string[]; // Type-Aliase
|
||||
importTypes: string[]; // uses-Klauseln
|
||||
callTypes: string[]; // Funktionsaufrufe
|
||||
variableTypes: string[]; // Variable/Feld-Deklarationen
|
||||
nameField: string; // AST-Feld für den Namen
|
||||
bodyField: string; // AST-Feld für den Body
|
||||
paramsField: string; // AST-Feld für Parameter
|
||||
returnField?: string; // AST-Feld für Rückgabetyp
|
||||
getSignature?: (node, source) => string | undefined;
|
||||
getVisibility?: (node) => 'public' | 'private' | 'protected' | 'internal' | undefined;
|
||||
isExported?: (node, source) => boolean;
|
||||
isStatic?: (node) => boolean;
|
||||
isConst?: (node) => boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Minimaler Capture-Satz (Pseudo)
|
||||
## Konkrete Zuordnung für Pascal/Delphi
|
||||
|
||||
> **Wichtig:** Diese Namen sind **Platzhalter**. Bitte auf das CodeGraph-interne Schema mappen.
|
||||
| LanguageExtractor-Feld | tree-sitter-pascal Typ(en) | Anmerkung |
|
||||
|---|---|---|
|
||||
| `functionTypes` | `['declProc']` | Top-Level, gefiltert nach Kontext (kein Parent `declClass`) |
|
||||
| `classTypes` | `['declClass']` | Enthält class, record, object |
|
||||
| `methodTypes` | `['declProc']` | Innerhalb `declClass`, inkl. constructor/destructor |
|
||||
| `interfaceTypes` | `['declIntf']` | interface, dispinterface |
|
||||
| `structTypes` | `[]` | Records werden über `declClass` (mit `kRecord`) abgedeckt |
|
||||
| `enumTypes` | `['declEnum']` | Innerhalb von `declType` |
|
||||
| `typeAliasTypes` | `['declType']` | Einfache `type X = Y` Deklarationen |
|
||||
| `importTypes` | `['declUses']` | `uses`-Klauseln |
|
||||
| `callTypes` | `['exprCall']` | Funktions-/Methodenaufrufe |
|
||||
| `variableTypes` | `['declField', 'declConst']` | Felder und Konstanten |
|
||||
| `nameField` | `'name'` | `declProc`, `declConst`, `declField`, `declEnumValue` haben `name`-Feld |
|
||||
| `bodyField` | `'body'` | Nur bei `defProc` (Implementierungen) |
|
||||
| `paramsField` | `'args'` | `declProc` hat `args`-Feld (`declArgs`) |
|
||||
| `returnField` | `'type'` | `declProc` hat optionales `type`-Feld |
|
||||
|
||||
- `@cg.node` mit Properties:
|
||||
- `(#set! cg.kind "class")`
|
||||
- `(#set! cg.name @nameCapture)` oder per separate captures
|
||||
## Delphi-spezifische Herausforderungen
|
||||
|
||||
- `@cg.edge` mit Properties:
|
||||
- `(#set! cg.kind "calls")`
|
||||
- Zielsymbol als Capture: `@cg.target`
|
||||
### declProc: Funktion oder Methode?
|
||||
|
||||
Wenn CodeGraph statt `#set!` eine feste Capture-Nomenklatur nutzt (z.B. `@definition.function`), dann übernimm exakt diese.
|
||||
`declProc` wird sowohl für Top-Level-Funktionen als auch für Methoden verwendet. Die Unterscheidung erfolgt über den Kontext:
|
||||
- **Top-Level**: Parent ist `interface` (der Unit-interface-Abschnitt) oder `implementation`
|
||||
- **Methode**: Parent ist `declClass` oder `declIntf`
|
||||
- **Kind-Unterscheidung**: Enthält `kProcedure`, `kFunction`, `kConstructor`, oder `kDestructor`
|
||||
|
||||
## Delphi-spezifische Stolperfallen
|
||||
### Sichtbarkeit über declSection
|
||||
|
||||
- `procedure Foo;` vs `function Foo: Integer;`
|
||||
- `class procedure` / `class function`
|
||||
- `constructor` / `destructor`
|
||||
- `property` (kann wie Feld wirken, ist aber call-ähnlich)
|
||||
- `with`-Statements (Call-Qualifier wird verschleiert)
|
||||
- Namespaces: `System.SysUtils`
|
||||
Delphi gruppiert Mitglieder in Sichtbarkeitsbereichen:
|
||||
```
|
||||
declClass → declSection (kPublic) → declProc, declField, declProp
|
||||
→ declSection (kPrivate) → declField
|
||||
```
|
||||
Die `getVisibility`-Funktion muss den nächsten `declSection`-Vorfahren inspizieren.
|
||||
|
||||
Für MVP: **nicht perfekt auflösen**, aber **zuverlässig extrahieren**.
|
||||
### Vererbung und Interface-Implementierung
|
||||
|
||||
`declClass` hat ein `parent`-Feld mit einer Liste von `typeref`-Knoten:
|
||||
- Der **erste** `typeref` ist typischerweise die Basisklasse (`extends`)
|
||||
- Weitere `typeref`s sind implementierte Interfaces (`implements`)
|
||||
|
||||
### `with`-Statements (Phase 2)
|
||||
|
||||
`with`-Statements erzeugen einen impliziten Scope, der Call-Qualifier verschleiert:
|
||||
```delphi
|
||||
with MyObj do
|
||||
DoSomething; // ist eigentlich MyObj.DoSomething
|
||||
```
|
||||
Im MVP wird dies **nicht** aufgelöst – Calls innerhalb von `with` werden ohne Qualifier extrahiert.
|
||||
|
||||
### Namespaces
|
||||
|
||||
Delphi-Unit-Namen können Punkte enthalten (`System.SysUtils`). Der `moduleName`-Knoten enthält den vollständigen Namen. Die Resolution muss sowohl exakte Matches als auch Suffix-Matches unterstützen.
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# AST-Referenz: tree-sitter-pascal (verifiziert)
|
||||
|
||||
Dieses Dokument zeigt die **tatsächlichen** AST-Knotentypen aus der [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) Grammar, verifiziert anhand der `node-types.json` des Pakets.
|
||||
|
||||
> **Hinweis:** CodeGraph nutzt keine `.scm`-Dateien zur Extraktion, sondern ein `LanguageExtractor`-Objekt in TypeScript. Die hier dokumentierten AST-Strukturen zeigen, wie der Parser Delphi-Code als Baum darstellt.
|
||||
|
||||
## A) Top-Level Module
|
||||
|
||||
### unit
|
||||
```
|
||||
(unit (kUnit) (moduleName (identifier)) (interface ...) (implementation ...) (kEndDot))
|
||||
```
|
||||
|
||||
### program
|
||||
```
|
||||
(program (kProgram) (moduleName (identifier)) ...body...)
|
||||
```
|
||||
|
||||
### library
|
||||
```
|
||||
(library (kLibrary) (moduleName (identifier)) ...body...)
|
||||
```
|
||||
|
||||
## B) Deklarationen
|
||||
|
||||
### declClass (Klassen, Records, Objects)
|
||||
```
|
||||
(declType
|
||||
name: (identifier) ← "TMyClass"
|
||||
value: (type (declClass
|
||||
(kClass) ← oder (kRecord), (kObject)
|
||||
parent: ((typeref) (typeref) ...) ← Basisklasse + Interfaces
|
||||
(declSection (kPublic) ← Sichtbarkeit
|
||||
(declProc ...) ← Methoden
|
||||
(declField ...) ← Felder
|
||||
(declProp ...) ← Properties
|
||||
)
|
||||
(kEnd))))
|
||||
```
|
||||
|
||||
### declIntf (Interfaces)
|
||||
```
|
||||
(declType
|
||||
name: (identifier) ← "IMyInterface"
|
||||
value: (type (declIntf
|
||||
(kInterface)
|
||||
parent: ((typeref) ...) ← Basis-Interface
|
||||
guid: (guid ...) ← optionale GUID
|
||||
(declProc ...) (declProp ...)
|
||||
(kEnd))))
|
||||
```
|
||||
|
||||
### declProc (Procedures, Functions, Methods, Constructors, Destructors)
|
||||
```
|
||||
(declProc
|
||||
(kFunction) ← oder kProcedure, kConstructor, kDestructor
|
||||
name: (identifier) ← "MyMethod"
|
||||
args: (declArgs (declArg name: (identifier) type: (type ...)))
|
||||
type: (type ...) ← Rückgabetyp (nur bei functions)
|
||||
attribute: (procAttribute ...)) ← z.B. override, virtual, static
|
||||
```
|
||||
|
||||
**Unterscheidung Funktion vs. Methode:**
|
||||
- Top-Level: `declProc` als Kind von `interface`/`implementation`
|
||||
- Methode: `declProc` als Kind von `declClass` oder `declIntf`
|
||||
- Statisch: enthält `kClass` Kind-Knoten (`class procedure`)
|
||||
|
||||
### declProp (Properties)
|
||||
```
|
||||
(declProp (kProperty) name: (identifier) type: (type ...) (kRead) (identifier) (kWrite) (identifier))
|
||||
```
|
||||
|
||||
### declField (Felder)
|
||||
```
|
||||
(declField name: (identifier) type: (type ...))
|
||||
```
|
||||
|
||||
### declEnum (Enums)
|
||||
```
|
||||
(declEnum (declEnumValue name: (identifier)) (declEnumValue name: (identifier)) ...)
|
||||
```
|
||||
|
||||
### declUses (uses-Klauseln)
|
||||
```
|
||||
(declUses (kUses) (identifier) (identifier) ...)
|
||||
```
|
||||
Optional mit Pfadangabe: `(kIn) (literalString)` für `uses Foo in 'path/Foo.pas'`
|
||||
|
||||
### declConst (Konstanten)
|
||||
```
|
||||
(declConst name: (identifier) type: (type ...) defaultValue: (defaultValue ...))
|
||||
```
|
||||
|
||||
## C) Ausdrücke
|
||||
|
||||
### exprCall (Funktionsaufruf)
|
||||
```
|
||||
(exprCall (identifier) ...args...) ← WriteLn(...)
|
||||
```
|
||||
|
||||
### exprDot (qualifizierter Zugriff)
|
||||
```
|
||||
(exprDot lhs: (identifier) operator: (kDot) rhs: (exprCall (identifier) ...)) ← Svc.Login(...)
|
||||
(exprDot lhs: (identifier) operator: (kDot) rhs: (identifier)) ← TAuthService.Create
|
||||
```
|
||||
|
||||
## D) Sichtbarkeit (declSection)
|
||||
```
|
||||
(declSection (kPublic)) ← oder kPrivate, kProtected, kPublished, kStrict
|
||||
```
|
||||
Gilt für alle nachfolgenden Deklarationen bis zum nächsten `declSection` oder `kEnd`.
|
||||
|
||||
## E) Vererbung (parent-Feld von declClass)
|
||||
|
||||
Das `parent`-Feld enthält eine Liste von `typeref`-Knoten:
|
||||
- **Erster** `typeref` = Basisklasse → EdgeKind `extends`
|
||||
- **Weitere** `typeref`s = Interfaces → EdgeKind `implements`
|
||||
|
||||
```
|
||||
(declClass (kClass) parent: ((typeref (identifier "TInterfacedObject")) (typeref (identifier "ITokenValidator"))) ...)
|
||||
```
|
||||
|
||||
## F) Zusammenfassung: Relevante Knoten für CodeGraph
|
||||
|
||||
| AST-Knotentyp | Feld `name` | Weitere wichtige Felder |
|
||||
|---|---|---|
|
||||
| `unit`/`program`/`library` | Kind: `moduleName` | `interface`, `implementation` |
|
||||
| `declClass` | via Parent `declType` | `parent` (Vererbung), `declSection` |
|
||||
| `declIntf` | via Parent `declType` | `parent`, `guid` |
|
||||
| `declProc` | `name` | `args`, `type`, `attribute` |
|
||||
| `declProp` | `name` | `type` |
|
||||
| `declField` | `name` | `type` |
|
||||
| `declEnum` | — | Kind: `declEnumValue` |
|
||||
| `declEnumValue` | `name` | `value` |
|
||||
| `declConst` | `name` | `type`, `defaultValue` |
|
||||
| `declUses` | — | Kind: `identifier` |
|
||||
| `exprCall` | — | Kind: `identifier` (callee) |
|
||||
| `exprDot` | — | `lhs`, `rhs` |
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
# Tree-sitter Query Skizzen (Pascal/Delphi)
|
||||
|
||||
Diese Skizzen sind bewusst so geschrieben, dass du sie schnell an die **konkreten Node-Namen** von `tree-sitter-pascal` und die **Capture-Konvention** von CodeGraph anpassen kannst.
|
||||
|
||||
> Praxis-Tipp: Nutze `tree-sitter parse` bzw. Editor-Tools (z.B. Neovim `:InspectTree`) um die exakten Node-Typen im AST zu sehen.
|
||||
|
||||
## A) Nodes
|
||||
|
||||
### 1) Unit / Program / Package
|
||||
|
||||
- Match: `unit`/`program`/`library`/`package` header
|
||||
- Extrahiere Name
|
||||
|
||||
Skizze:
|
||||
|
||||
```scm
|
||||
; (unit_header name: (identifier) @NAME) @NODE
|
||||
; (#set! "kind" "unit")
|
||||
|
||||
; (program_header name: (identifier) @NAME) @NODE
|
||||
; (#set! "kind" "program")
|
||||
```
|
||||
|
||||
### 2) Klassen / Interfaces / Records
|
||||
|
||||
Skizze:
|
||||
|
||||
```scm
|
||||
; (type_declaration
|
||||
; (type_definition
|
||||
; name: (identifier) @TYPE_NAME
|
||||
; value: (class_type) @CLASS
|
||||
; )
|
||||
; )
|
||||
|
||||
; (type_definition name: (identifier) @TYPE_NAME value: (interface_type) @IFACE)
|
||||
; (type_definition name: (identifier) @TYPE_NAME value: (record_type) @REC)
|
||||
```
|
||||
|
||||
### 3) Procs/Funcs (top-level)
|
||||
|
||||
```scm
|
||||
; (procedure_declaration name: (identifier) @NAME) @NODE
|
||||
; (#set! "kind" "procedure")
|
||||
|
||||
; (function_declaration name: (identifier) @NAME) @NODE
|
||||
; (#set! "kind" "function")
|
||||
```
|
||||
|
||||
### 4) Methoden
|
||||
|
||||
Je nach Grammar sind Methoden oft als procedure/function nodes innerhalb eines `class_body` oder `visibility_section` enthalten.
|
||||
|
||||
```scm
|
||||
; (method_declaration name: (identifier) @NAME) @NODE
|
||||
; (#set! "kind" "method")
|
||||
; optional: capture class name via ancestor match (wenn CodeGraph das unterstützt)
|
||||
```
|
||||
|
||||
## B) Edges
|
||||
|
||||
### 1) uses (imports/dependencies)
|
||||
|
||||
```scm
|
||||
; (uses_clause (qualified_identifier) @UNIT_NAME) @EDGE
|
||||
; (#set! "kind" "imports")
|
||||
```
|
||||
|
||||
### 2) extends (Basisklasse)
|
||||
|
||||
```scm
|
||||
; (class_type
|
||||
; base_class: (qualified_identifier) @BASE
|
||||
; ) @EDGE
|
||||
; (#set! "kind" "extends")
|
||||
```
|
||||
|
||||
### 3) implements (Interfaces)
|
||||
|
||||
```scm
|
||||
; (class_type
|
||||
; implements: (interface_list (qualified_identifier) @IFACE)
|
||||
; ) @EDGE
|
||||
; (#set! "kind" "implements")
|
||||
```
|
||||
|
||||
### 4) calls (Call-Graph)
|
||||
|
||||
Du willst in Delphi mindestens folgende Formen sehen:
|
||||
- `Foo()`
|
||||
- `Obj.Foo()`
|
||||
- `TMyClass.Foo()`
|
||||
|
||||
Skizze:
|
||||
|
||||
```scm
|
||||
; (call_expression
|
||||
; function: (identifier) @CALLEE
|
||||
; ) @EDGE
|
||||
; (#set! "kind" "calls")
|
||||
|
||||
; (call_expression
|
||||
; function: (qualified_identifier
|
||||
; qualifier: (identifier) @QUAL
|
||||
; name: (identifier) @CALLEE)
|
||||
; ) @EDGE
|
||||
; (#set! "kind" "calls")
|
||||
```
|
||||
|
||||
## C) MVP: „Contains“-Edges
|
||||
|
||||
Optional, aber oft sehr nützlich: Datei/Unit enthält Klassen/Procs.
|
||||
|
||||
```scm
|
||||
; (type_definition name: (identifier) @CHILD) @EDGE
|
||||
; (#set! "kind" "contains")
|
||||
```
|
||||
|
||||
Das ist abhängig davon, wie CodeGraph containment modelliert.
|
||||
+55
-26
@@ -1,32 +1,61 @@
|
||||
# Checklist: Pascal/Delphi Support in CodeGraph
|
||||
|
||||
## A) Plumbing
|
||||
- [ ] tree-sitter grammar dependency (Pascal)
|
||||
- [ ] Language registry: `pascal` hinzufügen
|
||||
- [ ] Extension mapping: `.pas .dpr .dpk .lpr` (optional `.inc`)
|
||||
- [ ] Queries laden (nodes + edges)
|
||||
## A) Plumbing (Infrastruktur)
|
||||
- [ ] `npm install tree-sitter-pascal` als Dependency
|
||||
- [ ] `src/types.ts`: `'pascal'` zum `Language` Union-Type hinzufügen
|
||||
- [ ] `src/types.ts`: `DEFAULT_CONFIG.include` um `'**/*.pas'`, `'**/*.dpr'`, `'**/*.dpk'`, `'**/*.lpr'` erweitern
|
||||
- [ ] `src/extraction/grammars.ts`: Grammar-Loader `pascal: () => require('tree-sitter-pascal')`
|
||||
- [ ] `src/extraction/grammars.ts`: Extension-Mapping `.pas`, `.dpr`, `.dpk`, `.lpr` → `'pascal'`
|
||||
- [ ] `src/extraction/grammars.ts`: Display-Name `pascal: 'Pascal / Delphi'`
|
||||
- [ ] `src/extraction/tree-sitter.ts`: `LanguageExtractor` für `pascal` in `EXTRACTORS` Map
|
||||
|
||||
## B) Extraction
|
||||
- [ ] Unit/Program/Package Nodes
|
||||
- [ ] Class/Interface/Record Nodes
|
||||
- [ ] Procedure/Function Nodes
|
||||
- [ ] Method Nodes
|
||||
- [ ] uses -> imports edges
|
||||
- [ ] extends edges
|
||||
- [ ] implements edges
|
||||
- [ ] calls edges (best-effort)
|
||||
## B) Node-Extraktion
|
||||
- [ ] `unit` / `program` / `library` → NodeKind `module`
|
||||
- [ ] `declClass` → NodeKind `class` (inkl. record, object)
|
||||
- [ ] `declIntf` → NodeKind `interface`
|
||||
- [ ] `declProc` (top-level) → NodeKind `function`
|
||||
- [ ] `declProc` (in Klasse) → NodeKind `method` (inkl. constructor/destructor)
|
||||
- [ ] `declProp` → NodeKind `property`
|
||||
- [ ] `declField` → NodeKind `field`
|
||||
- [ ] `declEnum` → NodeKind `enum`
|
||||
- [ ] `declEnumValue` → NodeKind `enum_member`
|
||||
- [ ] `declConst` → NodeKind `constant`
|
||||
- [ ] `declType` (einfach) → NodeKind `type_alias`
|
||||
|
||||
## C) Resolution
|
||||
- [ ] unitName -> file/node mapping
|
||||
- [ ] resolve `uses X;` to unit node
|
||||
- [ ] resolve `X in 'path'` (DPR/DPK) (Phase 2)
|
||||
- [ ] call resolution baseline (same file + class)
|
||||
## C) Edge-Extraktion
|
||||
- [ ] `declUses` → EdgeKind `imports`
|
||||
- [ ] `declClass.parent[0]` → EdgeKind `extends`
|
||||
- [ ] `declClass.parent[1..]` → EdgeKind `implements`
|
||||
- [ ] `exprCall` → EdgeKind `calls`
|
||||
- [ ] `exprDot` + `exprCall` → EdgeKind `calls` (qualifiziert)
|
||||
- [ ] `TClass.Create` → EdgeKind `instantiates`
|
||||
- [ ] Parent-Child Containment → EdgeKind `contains`
|
||||
|
||||
## D) Testing
|
||||
- [ ] add fixtures to tests
|
||||
- [ ] assert extracted nodes
|
||||
- [ ] assert imports/extends/implements/calls
|
||||
## D) Sichtbarkeit & Attribute
|
||||
- [ ] `getVisibility`: `declSection` → public/private/protected
|
||||
- [ ] `isStatic`: `kClass` in `declProc` → true
|
||||
- [ ] `getSignature`: Parameter + Rückgabetyp extrahieren
|
||||
|
||||
## E) Polishing
|
||||
- [ ] expose language in `Supported Languages` docs
|
||||
- [ ] update CLI help / config validation
|
||||
## E) Resolution
|
||||
- [ ] `moduleName` → `unitName → fileId` Mapping
|
||||
- [ ] `uses X;` → Unit-Node auflösen
|
||||
- [ ] `uses X in 'path'` → Datei direkt auflösen (Phase 2)
|
||||
- [ ] Call-Resolution: gleiche Unit → gleiche Klasse → uses-Units → global
|
||||
|
||||
## F) Testing
|
||||
- [ ] Fixtures in Test-Suite aufnehmen (`UAuth.pas`, `App.dpr`, `UTypes.pas`)
|
||||
- [ ] Nodes: Anzahl + Namen verifizieren
|
||||
- [ ] Edges: imports/extends/implements/calls prüfen
|
||||
- [ ] Sichtbarkeiten prüfen
|
||||
- [ ] Properties, Enums, Konstanten prüfen
|
||||
|
||||
## G) Polishing
|
||||
- [ ] Language in Supported-Languages-Dokumentation aufnehmen
|
||||
- [ ] CLI-Hilfe / Config-Validierung aktualisieren
|
||||
|
||||
## H) Bekannte Einschränkungen (für spätere Phasen)
|
||||
- [ ] **`with`-Statements** (Phase 2): Verschleiern den Qualifier bei Aufrufen. Im MVP werden Calls innerhalb von `with`-Blöcken ohne Qualifier extrahiert.
|
||||
- [ ] **`.dfm`/`.fmx` Form-Dateien** (Zukunft): Enthalten Komponenten-Deklarationen und Event-Verknüpfungen. Aktuell nicht unterstützt.
|
||||
- [ ] **`.inc` Include-Dateien** (Optional): Enthalten Code-Fragmente, die per `{$I filename}` eingebunden werden. Parser liefert möglicherweise keinen vollständigen AST.
|
||||
- [ ] **Class Helpers / Record Helpers** (Phase 2): `declHelper`-Knoten erweitern existierende Typen. Erfordert spezielle Resolution-Logik.
|
||||
- [ ] **Generics** (Phase 2): `typerefTpl` und `genericTpl` für generische Typen.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# NodeKind Mapping: Delphi → CodeGraph
|
||||
|
||||
Dieses Dokument zeigt die explizite Zuordnung von Delphi/Pascal-Konzepten zu den bestehenden CodeGraph `NodeKind`- und `EdgeKind`-Werten.
|
||||
|
||||
> **Ergebnis:** Alle Delphi-Konzepte lassen sich auf existierende `NodeKind`-Werte abbilden. Es werden **keine neuen** Kinds benötigt.
|
||||
|
||||
## Node-Mapping
|
||||
|
||||
| Delphi-Konzept | tree-sitter Typ | CodeGraph `NodeKind` | Anmerkungen |
|
||||
|---|---|---|---|
|
||||
| Unit | `unit` + `moduleName` | `module` | Kompilierungseinheit, vergleichbar mit Modulen |
|
||||
| Program | `program` + `moduleName` | `module` | Ausführbarer Einstiegspunkt |
|
||||
| Library | `library` + `moduleName` | `module` | DLL/shared library |
|
||||
| Klasse | `declClass` (mit `kClass`) | `class` | Standard-Klasse |
|
||||
| Record | `declClass` (mit `kRecord`) | `class` | Value-Type, aber strukturell wie Klasse |
|
||||
| Object | `declClass` (mit `kObject`) | `class` | Legacy, wie Klasse behandeln |
|
||||
| Interface | `declIntf` (mit `kInterface`) | `interface` | — |
|
||||
| DispInterface | `declIntf` (mit `kDispInterface`) | `interface` | COM-spezifisch |
|
||||
| Procedure (top-level) | `declProc` (mit `kProcedure`) | `function` | Ohne Rückgabetyp |
|
||||
| Function (top-level) | `declProc` (mit `kFunction`) | `function` | Mit Rückgabetyp |
|
||||
| Methode | `declProc` in `declClass` | `method` | Instance-Methode |
|
||||
| Constructor | `declProc` (mit `kConstructor`) | `method` | Spezielle Methode |
|
||||
| Destructor | `declProc` (mit `kDestructor`) | `method` | Spezielle Methode |
|
||||
| Class Method | `declProc` (mit `kClass`) | `method` | Statisch, `isStatic = true` |
|
||||
| Property | `declProp` | `property` | Mit Getter/Setter |
|
||||
| Feld | `declField` | `field` | — |
|
||||
| Enum | `declEnum` | `enum` | — |
|
||||
| Enum-Wert | `declEnumValue` | `enum_member` | — |
|
||||
| Type-Alias | `declType` (einfach) | `type_alias` | `type TMyInt = Integer;` |
|
||||
| Konstante | `declConst` | `constant` | — |
|
||||
| Uses-Eintrag | `declUses` → `identifier` | `import` | Pro Unit-Name ein Import-Node |
|
||||
|
||||
## Edge-Mapping
|
||||
|
||||
| Delphi-Beziehung | Erkennung | CodeGraph `EdgeKind` |
|
||||
|---|---|---|
|
||||
| `uses X;` | `declUses` → Kind `identifier` | `imports` |
|
||||
| Basisklasse | `declClass.parent[0]` (erster `typeref`) | `extends` |
|
||||
| Interface-Impl. | `declClass.parent[1..]` (weitere `typeref`s) | `implements` |
|
||||
| Funktionsaufruf | `exprCall` | `calls` |
|
||||
| Qualifizierter Aufruf | `exprDot` → `exprCall` | `calls` |
|
||||
| `TClass.Create` | `exprDot` mit `.Create` Suffix | `instantiates` |
|
||||
| Unit enthält Klasse | AST Parent-Child | `contains` |
|
||||
| Klasse enthält Methode | AST Parent-Child | `contains` |
|
||||
|
||||
## Nicht abgebildete Konzepte (Phase 2+)
|
||||
|
||||
| Konzept | Grund |
|
||||
|---|---|
|
||||
| `with`-Statement | Kein eigener EdgeKind nötig; erschwert nur die Call-Qualifizierung |
|
||||
| Class Helper | Kein eigener NodeKind; als `class` mit speziellem Bezug zum erweiterten Typ |
|
||||
| `.dfm`/`.fmx` Komponenten | Kein Code im Pascal-Sinne; erfordert eigenen Parser |
|
||||
| `exports`-Klauseln | `declExports` → könnte als `export` NodeKind abgebildet werden |
|
||||
| Generics | `typerefTpl` → beeinflusst Typ-Referenzen, nicht den NodeKind |
|
||||
|
||||
## Sichtbarkeit
|
||||
|
||||
| Delphi-Keyword | CodeGraph Visibility |
|
||||
|---|---|
|
||||
| `published` | `public` (CodeGraph kennt kein `published`) |
|
||||
| `public` | `public` |
|
||||
| `protected` | `protected` |
|
||||
| `private` | `private` |
|
||||
| `strict private` | `private` |
|
||||
| `strict protected` | `protected` |
|
||||
| (keine Angabe) | `public` (Default in Delphi-Klassen) |
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# Integration Guide: Konkrete Code-Änderungen
|
||||
|
||||
Dieses Dokument zeigt die **exakten Änderungen** an den CodeGraph-Quelldateien, die für Pascal/Delphi-Support nötig sind.
|
||||
|
||||
## 1) Dependency installieren
|
||||
|
||||
```bash
|
||||
npm install tree-sitter-pascal
|
||||
```
|
||||
|
||||
## 2) `src/types.ts` — Language-Typ erweitern
|
||||
|
||||
```typescript
|
||||
// Zum Language Union-Type hinzufügen (nach 'liquid'):
|
||||
export type Language =
|
||||
| 'typescript'
|
||||
// ...bestehende Sprachen...
|
||||
| 'liquid'
|
||||
| 'pascal' // ← NEU
|
||||
| 'unknown';
|
||||
```
|
||||
|
||||
**DEFAULT_CONFIG.include** erweitern:
|
||||
|
||||
```typescript
|
||||
include: [
|
||||
// ...bestehende Patterns...
|
||||
// Liquid (Shopify themes)
|
||||
'**/*.liquid',
|
||||
// Pascal / Delphi ← NEU
|
||||
'**/*.pas',
|
||||
'**/*.dpr',
|
||||
'**/*.dpk',
|
||||
'**/*.lpr',
|
||||
],
|
||||
```
|
||||
|
||||
## 3) `src/extraction/grammars.ts` — Grammar registrieren
|
||||
|
||||
### grammarLoaders Map:
|
||||
|
||||
```typescript
|
||||
const grammarLoaders: Record<GrammarLanguage, GrammarLoader> = {
|
||||
// ...bestehende Loader...
|
||||
dart: () => {
|
||||
return require('@sengac/tree-sitter-dart');
|
||||
},
|
||||
pascal: () => { // ← NEU
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
return require('tree-sitter-pascal');
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### EXTENSION_MAP:
|
||||
|
||||
```typescript
|
||||
export const EXTENSION_MAP: Record<string, Language> = {
|
||||
// ...bestehende Mappings...
|
||||
'.liquid': 'liquid',
|
||||
'.svelte': 'svelte',
|
||||
'.pas': 'pascal', // ← NEU
|
||||
'.dpr': 'pascal', // ← NEU
|
||||
'.dpk': 'pascal', // ← NEU
|
||||
'.lpr': 'pascal', // ← NEU
|
||||
};
|
||||
```
|
||||
|
||||
### getLanguageDisplayName():
|
||||
|
||||
```typescript
|
||||
const names: Record<Language, string> = {
|
||||
// ...bestehende Namen...
|
||||
liquid: 'Liquid',
|
||||
pascal: 'Pascal / Delphi', // ← NEU
|
||||
unknown: 'Unknown',
|
||||
};
|
||||
```
|
||||
|
||||
## 4) `src/extraction/tree-sitter.ts` — LanguageExtractor
|
||||
|
||||
```typescript
|
||||
pascal: {
|
||||
functionTypes: ['declProc'], // Top-Level Funktionen/Prozeduren
|
||||
classTypes: ['declClass'], // Klassen, Records, Objects
|
||||
methodTypes: ['declProc'], // Methoden (in Klasse), Konstruktoren, Destruktoren
|
||||
interfaceTypes: ['declIntf'], // Interfaces
|
||||
structTypes: [], // Records über declClass abgedeckt
|
||||
enumTypes: ['declEnum'], // Aufzählungen
|
||||
typeAliasTypes: ['declType'], // Type-Aliase
|
||||
importTypes: ['declUses'], // uses-Klauseln
|
||||
callTypes: ['exprCall'], // Funktionsaufrufe
|
||||
variableTypes: ['declField', 'declConst'], // Felder und Konstanten
|
||||
nameField: 'name',
|
||||
bodyField: 'body', // nur bei defProc (Implementierung)
|
||||
paramsField: 'args', // declArgs
|
||||
returnField: 'type',
|
||||
getSignature: (node, source) => {
|
||||
const args = getChildByField(node, 'args');
|
||||
const returnType = getChildByField(node, 'type');
|
||||
if (!args) return undefined;
|
||||
const argsText = getNodeText(args, source);
|
||||
return returnType
|
||||
? getNodeText(returnType, source) + ' ' + argsText
|
||||
: argsText;
|
||||
},
|
||||
getVisibility: (node) => {
|
||||
// Suche den nächsten declSection-Vorfahren
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
if (current.type === 'declSection') {
|
||||
for (let i = 0; i < current.childCount; i++) {
|
||||
const child = current.child(i);
|
||||
if (child?.type === 'kPublic' || child?.type === 'kPublished')
|
||||
return 'public';
|
||||
if (child?.type === 'kPrivate') return 'private';
|
||||
if (child?.type === 'kProtected') return 'protected';
|
||||
}
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
isStatic: (node) => {
|
||||
// class procedure / class function
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
if (node.child(i)?.type === 'kClass') return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isConst: (node) => {
|
||||
return node.type === 'declConst';
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
## Hinweise
|
||||
|
||||
- `declProc` wird sowohl für `functionTypes` als auch `methodTypes` verwendet. CodeGraph unterscheidet anhand des AST-Kontexts (Parent ist `declClass` → method, sonst → function).
|
||||
- Die `getVisibility`-Funktion traversiert aufwärts zum `declSection`-Knoten.
|
||||
- Records werden als `class` behandelt, da `declClass` mit `kRecord` Kind-Knoten beides abdeckt.
|
||||
|
||||
+31
-16
@@ -1,25 +1,40 @@
|
||||
# CodeGraph Pascal/Delphi Support (Fork-Konzept)
|
||||
# CodeGraph Pascal/Delphi Support — Konzept & Bauplan
|
||||
|
||||
Dieses ZIP ist **kein fertiger CodeGraph-Fork**, sondern ein **umsetzbarer Bauplan** inklusive Query-Skizzen, Scaffold-Dateistruktur, Test-Fixtures und einer Checkliste.
|
||||
Dieses Verzeichnis enthält einen **umsetzbaren Bauplan** für Pascal/Delphi-Support in CodeGraph. Es basiert auf der tatsächlichen CodeGraph-Architektur und den verifizierten AST-Knotentypen von [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal).
|
||||
|
||||
Ziel: CodeGraph soll **Pascal/Delphi**-Dateien indexieren und daraus **Nodes** (Units, Klassen, Methoden, …) sowie **Edges** (uses/imports, calls, extends, implements, …) extrahieren.
|
||||
**Ziel:** CodeGraph soll **Pascal/Delphi**-Dateien indexieren und daraus **Nodes** (Units, Klassen, Methoden, Properties, Enums, …) sowie **Edges** (uses/imports, calls, extends, implements, …) extrahieren.
|
||||
|
||||
## Inhalt
|
||||
|
||||
- `Docs/` – Konzept, Capture-Konventionen, Implementations-Schritte
|
||||
- `Scaffold/` – vorgeschlagene Ordner/Dateien für `src/languages/pascal/` inkl. `.scm` Queries
|
||||
- `fixtures/` – kleine Delphi-Beispieldateien zum Testen der Extraktion
|
||||
- `resolution/` – Resolver-Heuristiken (Unit-Mapping, Call-Resolution)
|
||||
| Verzeichnis / Datei | Beschreibung |
|
||||
|---|---|
|
||||
| `Docs/01-Implementation-Plan.md` | Implementierungs-Roadmap mit konkreten AST-Typen |
|
||||
| `Docs/02-Capture-Convention.md` | Wie Pascal an CodeGraphs `LanguageExtractor`-Interface andockt |
|
||||
| `Docs/03-AST-Referenz.md` | Verifizierte AST-Knotentypen aus tree-sitter-pascal |
|
||||
| `Docs/04-Checklist.md` | Umsetzungs-Checkliste mit bekannten Einschränkungen |
|
||||
| `Docs/05-NodeKind-Mapping.md` | Explizite Zuordnung Delphi → CodeGraph NodeKind/EdgeKind |
|
||||
| `Docs/06-Integration-Guide.md` | Konkrete Code-Diffs für `grammars.ts`, `types.ts`, `tree-sitter.ts` |
|
||||
| `fixtures/` | Delphi-Beispieldateien zum Testen der Extraktion |
|
||||
| `resolution/` | Resolver-Heuristiken (Unit-Mapping, Call-Resolution) |
|
||||
|
||||
## Architektur-Übersicht
|
||||
|
||||
CodeGraph verwendet **keine** per-Sprache Plugin-Verzeichnisse. Die Integration erfolgt zentral in drei Dateien:
|
||||
|
||||
1. **`src/types.ts`** — `Language` Union-Type und `DEFAULT_CONFIG.include` Patterns
|
||||
2. **`src/extraction/grammars.ts`** — Grammar-Loader, Extension-Mapping, Display-Name
|
||||
3. **`src/extraction/tree-sitter.ts`** — `LanguageExtractor`-Konfiguration in der `EXTRACTORS` Map
|
||||
|
||||
## Wie du damit arbeitest
|
||||
|
||||
1. **Im CodeGraph-Repo** die bestehende Struktur für eine Sprache ansehen (z.B. `src/languages/go` oder `src/languages/java`).
|
||||
2. Dieses ZIP als Vorlage nehmen und **1:1** in deinen Fork übertragen:
|
||||
- `src/languages/pascal/**`
|
||||
- Extension-Mapping (`.pas`, `.dpr`, `.dpk`, `.lpr`, optional `.inc`)
|
||||
- Language-Registry erweitern
|
||||
3. Queries (`.scm`) auf die **Capture-Namen** mappen, die CodeGraph intern erwartet.
|
||||
4. Resolver erweitern (mindestens `uses` → Unit-File, einfache Call-Auflösung).
|
||||
5. Fixtures in die Tests aufnehmen.
|
||||
1. **Integration Guide lesen** (`Docs/06-Integration-Guide.md`) — enthält die exakten Code-Änderungen
|
||||
2. **`npm install tree-sitter-pascal`** als Dependency hinzufügen
|
||||
3. **Änderungen in den drei Dateien** vornehmen (types.ts, grammars.ts, tree-sitter.ts)
|
||||
4. **Resolver erweitern** (mindestens `uses` → Unit-File, einfache Call-Auflösung)
|
||||
5. **Fixtures in die Tests aufnehmen** und Assertions hinzufügen
|
||||
|
||||
> Hinweis: Tree-sitter ist die Grundlage. Für Pascal/Delphi bietet sich `tree-sitter-pascal` an (Delphi + FreePascal).
|
||||
## Grammar
|
||||
|
||||
Verwendet wird: [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) (npm: `tree-sitter-pascal`)
|
||||
- Unterstützt: Delphi, FreePascal, Standard-Pascal
|
||||
- Dateierweiterungen: `.pas`, `.dpr`, `.dpk`, `.lpr`
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Pascal / Delphi Language Module (Scaffold)
|
||||
|
||||
Diese Dateien sind als **Startpunkt** für einen CodeGraph-Fork gedacht.
|
||||
|
||||
## Strukturvorschlag
|
||||
|
||||
- `language.ts` – registriert Parser, Extensions, Query-Files
|
||||
- `queries/nodes.scm` – Node-Extraktion
|
||||
- `queries/edges.scm` – Edge-Extraktion
|
||||
- `queries/comments.scm` (optional) – Docstrings/Kommentare
|
||||
|
||||
## Wichtig
|
||||
|
||||
Die Capture-Namen in den `.scm` Dateien sind **Platzhalter**.
|
||||
Passe sie so an, dass sie zum internen Schema von CodeGraph passen (siehe `Docs/02-Capture-Convention.md`).
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Pascal / Delphi language registration scaffold for CodeGraph.
|
||||
*
|
||||
* NOTE: This is intentionally pseudo-code / a template.
|
||||
* You must adapt it to the actual CodeGraph codebase structure.
|
||||
*/
|
||||
|
||||
export const pascalLanguage = {
|
||||
id: 'pascal',
|
||||
displayName: 'Pascal / Delphi',
|
||||
|
||||
// MVP extensions
|
||||
extensions: ['.pas', '.dpr', '.dpk', '.lpr'],
|
||||
|
||||
// Suggested tree-sitter grammar package name (verify in your fork)
|
||||
// parser: require('tree-sitter-pascal'),
|
||||
|
||||
// Queries used by the extractor
|
||||
queries: {
|
||||
nodes: __dirname + '/queries/nodes.scm',
|
||||
edges: __dirname + '/queries/edges.scm',
|
||||
// comments: __dirname + '/queries/comments.scm',
|
||||
},
|
||||
|
||||
// Optional language-specific settings
|
||||
options: {
|
||||
// includeFiles: ['**/*.inc'] // only if you decide to support includes
|
||||
}
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
; Pascal/Delphi edge extraction queries (SCAFFOLD)
|
||||
; -----------------------------------------------
|
||||
; IMPORTANT: Replace capture names with the exact ones CodeGraph expects.
|
||||
|
||||
; === uses (imports) ===
|
||||
|
||||
; (uses_clause (qualified_identifier) @cg.target) @cg.edge
|
||||
; (#set! cg.kind "imports")
|
||||
|
||||
|
||||
; === class inheritance ===
|
||||
|
||||
; (class_type
|
||||
; base_class: (qualified_identifier) @cg.target
|
||||
; ) @cg.edge
|
||||
; (#set! cg.kind "extends")
|
||||
|
||||
|
||||
; === implements interfaces ===
|
||||
|
||||
; (class_type
|
||||
; implements: (interface_list (qualified_identifier) @cg.target)
|
||||
; ) @cg.edge
|
||||
; (#set! cg.kind "implements")
|
||||
|
||||
|
||||
; === call graph (best-effort) ===
|
||||
|
||||
; (call_expression function: (identifier) @cg.target) @cg.edge
|
||||
; (#set! cg.kind "calls")
|
||||
|
||||
; Qualified calls: Obj.Foo(), TMyClass.Bar()
|
||||
; (call_expression
|
||||
; function: (qualified_identifier
|
||||
; qualifier: (identifier) @cg.qualifier
|
||||
; name: (identifier) @cg.target)
|
||||
; ) @cg.edge
|
||||
; (#set! cg.kind "calls")
|
||||
@@ -1,61 +0,0 @@
|
||||
; Pascal/Delphi node extraction queries (SCAFFOLD)
|
||||
; -------------------------------------------------
|
||||
; IMPORTANT: Replace capture names with the exact ones CodeGraph expects.
|
||||
; Use existing languages as reference.
|
||||
|
||||
; === Unit / Program / Library / Package ===
|
||||
|
||||
; (unit_header name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "unit")
|
||||
|
||||
; (program_header name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "program")
|
||||
|
||||
; (library_header name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "library")
|
||||
|
||||
; (package_header name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "package")
|
||||
|
||||
|
||||
; === Types: class / interface / record ===
|
||||
|
||||
; (type_definition
|
||||
; name: (identifier) @cg.name
|
||||
; value: (class_type)
|
||||
; ) @cg.node
|
||||
; (#set! cg.kind "class")
|
||||
|
||||
; (type_definition
|
||||
; name: (identifier) @cg.name
|
||||
; value: (interface_type)
|
||||
; ) @cg.node
|
||||
; (#set! cg.kind "interface")
|
||||
|
||||
; (type_definition
|
||||
; name: (identifier) @cg.name
|
||||
; value: (record_type)
|
||||
; ) @cg.node
|
||||
; (#set! cg.kind "record")
|
||||
|
||||
|
||||
; === Top-level procedures/functions ===
|
||||
|
||||
; (procedure_declaration name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "procedure")
|
||||
|
||||
; (function_declaration name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "function")
|
||||
|
||||
|
||||
; === Methods (depending on grammar) ===
|
||||
|
||||
; (method_declaration name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "method")
|
||||
|
||||
; Constructors / Destructors (optional)
|
||||
; (constructor_declaration name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "constructor")
|
||||
;
|
||||
; (destructor_declaration name: (identifier) @cg.name) @cg.node
|
||||
; (#set! cg.kind "destructor")
|
||||
@@ -1,3 +1,7 @@
|
||||
/// Unit UAuth: Authentication-Service mit Interface-Implementierung.
|
||||
/// Testet: uses, interface (mit GUID), Klasse mit Vererbung + Interface,
|
||||
/// Sichtbarkeitsbereiche (private/public), Felder, Properties,
|
||||
/// Methoden, Constructor, Destructor, Funktionsaufrufe.
|
||||
unit UAuth;
|
||||
|
||||
interface
|
||||
@@ -7,19 +11,56 @@ uses
|
||||
System.Classes;
|
||||
|
||||
type
|
||||
/// Interface für Token-Validierung
|
||||
ITokenValidator = interface
|
||||
['{11111111-1111-1111-1111-111111111111}']
|
||||
function Validate(const AToken: string): Boolean;
|
||||
end;
|
||||
|
||||
/// Auth-Service mit Token-Validierung
|
||||
TAuthService = class(TInterfacedObject, ITokenValidator)
|
||||
private
|
||||
FToken: string;
|
||||
FLoginCount: Integer;
|
||||
procedure IncLoginCount;
|
||||
protected
|
||||
function GetToken: string;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
function Validate(const AToken: string): Boolean;
|
||||
function Login(const AUser, APass: string): string;
|
||||
property Token: string read GetToken;
|
||||
property LoginCount: Integer read FLoginCount;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TAuthService }
|
||||
|
||||
constructor TAuthService.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FToken := '';
|
||||
FLoginCount := 0;
|
||||
end;
|
||||
|
||||
destructor TAuthService.Destroy;
|
||||
begin
|
||||
FToken := '';
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TAuthService.IncLoginCount;
|
||||
begin
|
||||
Inc(FLoginCount);
|
||||
end;
|
||||
|
||||
function TAuthService.GetToken: string;
|
||||
begin
|
||||
Result := FToken;
|
||||
end;
|
||||
|
||||
function TAuthService.Validate(const AToken: string): Boolean;
|
||||
begin
|
||||
Result := AToken <> '';
|
||||
@@ -27,8 +68,12 @@ end;
|
||||
|
||||
function TAuthService.Login(const AUser, APass: string): string;
|
||||
begin
|
||||
IncLoginCount;
|
||||
if Validate(AUser + ':' + APass) then
|
||||
Result := 'ok'
|
||||
begin
|
||||
FToken := AUser;
|
||||
Result := 'ok';
|
||||
end
|
||||
else
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/// Unit UTypes: Delphi-Typsystem-Fixture.
|
||||
/// Testet: Enums, Records (mit Feldern), Type-Aliase,
|
||||
/// Konstanten, verschachtelte Typen, Class-Methoden (static).
|
||||
unit UTypes;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils;
|
||||
|
||||
const
|
||||
C_MAX_RETRIES = 3;
|
||||
C_DEFAULT_NAME = 'Guest';
|
||||
|
||||
type
|
||||
/// Enum: Benutzerrolle
|
||||
TUserRole = (urAdmin, urEditor, urViewer);
|
||||
|
||||
/// Record: Punkt mit X/Y-Koordinaten
|
||||
TPoint2D = record
|
||||
X: Double;
|
||||
Y: Double;
|
||||
end;
|
||||
|
||||
/// Type-Alias
|
||||
TUserName = string;
|
||||
|
||||
/// Klasse mit class method (static), verschachteltem Typ und Enum-Verwendung
|
||||
TUserInfo = class
|
||||
public
|
||||
type
|
||||
/// Verschachtelter Record innerhalb der Klasse
|
||||
TAddress = record
|
||||
Street: string;
|
||||
City: string;
|
||||
Zip: string;
|
||||
end;
|
||||
private
|
||||
FName: TUserName;
|
||||
FRole: TUserRole;
|
||||
FAddress: TAddress;
|
||||
public
|
||||
constructor Create(const AName: TUserName; ARole: TUserRole);
|
||||
function GetDisplayName: string;
|
||||
class function CreateAdmin(const AName: TUserName): TUserInfo; static;
|
||||
property Name: TUserName read FName write FName;
|
||||
property Role: TUserRole read FRole;
|
||||
property Address: TAddress read FAddress write FAddress;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TUserInfo }
|
||||
|
||||
constructor TUserInfo.Create(const AName: TUserName; ARole: TUserRole);
|
||||
begin
|
||||
FName := AName;
|
||||
FRole := ARole;
|
||||
end;
|
||||
|
||||
function TUserInfo.GetDisplayName: string;
|
||||
begin
|
||||
if FRole = urAdmin then
|
||||
Result := '[Admin] ' + FName
|
||||
else
|
||||
Result := FName;
|
||||
end;
|
||||
|
||||
class function TUserInfo.CreateAdmin(const AName: TUserName): TUserInfo;
|
||||
begin
|
||||
Result := TUserInfo.Create(AName, urAdmin);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -2,29 +2,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
- Extracted edges of kind `imports` from `uses` clauses.
|
||||
- Extracted nodes of kind `unit|program|package|library` with their names.
|
||||
- Extrahierte Edges vom Typ `imports` aus `declUses`-Knoten.
|
||||
- Extrahierte Nodes vom Typ `module` aus `unit`/`program`/`library` (Name aus `moduleName`-Kind-Knoten).
|
||||
|
||||
## AST-Struktur
|
||||
|
||||
```
|
||||
(declUses
|
||||
(kUses)
|
||||
(identifier "System.SysUtils") ← Unit-Name
|
||||
(identifier "UAuth") ← Unit-Name
|
||||
(kIn) (literalString "'UAuth.pas'") ← optionaler Pfad
|
||||
)
|
||||
```
|
||||
|
||||
## MVP Algorithm
|
||||
|
||||
1. Build `unitName -> nodeId` map for all indexed files.
|
||||
2. For each `imports` edge with target name `X`:
|
||||
- try exact match `X`
|
||||
- try case-insensitive match
|
||||
- try namespace normalization:
|
||||
- `System.SysUtils` may correspond to unit `SysUtils` (optional heuristic)
|
||||
3. If resolved: store `edge.to_node_id = targetNodeId`.
|
||||
4. If not resolved: keep `edge.to_symbol = X` so search still works.
|
||||
1. Build `unitName → nodeId` Map für alle indexierten Dateien (aus `moduleName`-Knoten).
|
||||
2. Für jeden `imports`-Edge mit Zielname `X`:
|
||||
- Exakter Match auf `X`
|
||||
- Case-insensitiver Match (Delphi ist case-insensitive)
|
||||
- Namespace-Normalisierung:
|
||||
- `System.SysUtils` → versuche auch `SysUtils` (letzter Segment)
|
||||
- Dateiname-basierter Fallback: `X` → `X.pas` in den indexierten Dateien suchen
|
||||
3. Falls aufgelöst: `edge.to_node_id = targetNodeId`
|
||||
4. Falls nicht aufgelöst: `edge.to_symbol = X` beibehalten (Suche funktioniert trotzdem)
|
||||
|
||||
## DPR/DPK "in 'path'" cases (Phase 2)
|
||||
## DPR/DPK `in 'path'` (Phase 2)
|
||||
|
||||
In Delphi project files you can have:
|
||||
In Delphi-Projektdateien kann ein expliziter Pfad angegeben werden:
|
||||
|
||||
```
|
||||
```delphi
|
||||
uses
|
||||
Foo in 'src/Foo.pas',
|
||||
Bar in 'Bar.pas';
|
||||
```
|
||||
|
||||
Enhancement:
|
||||
- Parse optional path literal and resolve directly to file.
|
||||
Im AST erkennbar durch `(kIn) (literalString)` nach dem `(identifier)`.
|
||||
|
||||
**Enhancement:**
|
||||
- `literalString` extrahieren und direkt zur Datei auflösen
|
||||
- Pfad relativ zur DPR/DPK-Datei interpretieren
|
||||
|
||||
Reference in New Issue
Block a user