From 645fd71e45631906fe12cca7b23377a1c45d6410 Mon Sep 17 00:00:00 2001 From: Olaf Monien Date: Tue, 10 Feb 2026 23:55:42 +0100 Subject: [PATCH] Delphi concept --- Delphi/Docs/01-Implementation-Plan.md | 72 +++++++++++ Delphi/Docs/02-Capture-Convention.md | 53 ++++++++ Delphi/Docs/03-TreeSitter-Query-Sketches.md | 119 ++++++++++++++++++ Delphi/Docs/04-Checklist.md | 32 +++++ Delphi/README.md | 25 ++++ .../Scaffold/src/languages/pascal/README.md | 15 +++ .../Scaffold/src/languages/pascal/language.ts | 29 +++++ .../src/languages/pascal/queries/edges.scm | 38 ++++++ .../src/languages/pascal/queries/nodes.scm | 61 +++++++++ Delphi/fixtures/App.dpr | 19 +++ Delphi/fixtures/UAuth.pas | 36 ++++++ Delphi/resolution/UnitResolution.md | 30 +++++ 12 files changed, 529 insertions(+) create mode 100644 Delphi/Docs/01-Implementation-Plan.md create mode 100644 Delphi/Docs/02-Capture-Convention.md create mode 100644 Delphi/Docs/03-TreeSitter-Query-Sketches.md create mode 100644 Delphi/Docs/04-Checklist.md create mode 100644 Delphi/README.md create mode 100644 Delphi/Scaffold/src/languages/pascal/README.md create mode 100644 Delphi/Scaffold/src/languages/pascal/language.ts create mode 100644 Delphi/Scaffold/src/languages/pascal/queries/edges.scm create mode 100644 Delphi/Scaffold/src/languages/pascal/queries/nodes.scm create mode 100644 Delphi/fixtures/App.dpr create mode 100644 Delphi/fixtures/UAuth.pas create mode 100644 Delphi/resolution/UnitResolution.md diff --git a/Delphi/Docs/01-Implementation-Plan.md b/Delphi/Docs/01-Implementation-Plan.md new file mode 100644 index 0000000..e8a985d --- /dev/null +++ b/Delphi/Docs/01-Implementation-Plan.md @@ -0,0 +1,72 @@ +# Implementation Plan: Pascal/Delphi in CodeGraph + +## 0) Prämissen + +- CodeGraph extrahiert **Nodes** und **Edges** über **tree-sitter** und language-spezifische **Queries (`.scm`)**. +- Danach folgt **Reference Resolution** (Calls → Definitionen, imports → Dateien, inheritance, framework patterns). + +## 1) Parser / Grammar + +Empfohlen: `tree-sitter-pascal` (unterstützt Delphi + FreePascal). + +**Tasks:** +- Dependency hinzufügen (wie bei bestehenden Sprachen). +- Language-Loader/Registry erweitern: `languageId = "pascal"` (oder `"delphi"`, aber konsistent). + +## 2) File-Erkennung + +Extensions (MVP): +- `.pas` (Units) +- `.dpr` (Program) +- `.dpk` (Package) +- `.lpr` (Lazarus Program) + +Optional: +- `.inc` (Include-Fragmente) – erst später oder nur, wenn Parser tolerant genug ist. + +## 3) Queries (MVP) + +### 3.1 Nodes +Extrahiere mindestens: +- Unit/Program/Package Name +- Klassen / Interfaces / Records +- Procedures/Functions +- Methoden (class/instance) + +### 3.2 Edges +Extrahiere mindestens: +- `uses` (import/dependency) +- `extends` (Basisklasse) +- `implements` (Interface-Implementierungen) +- `calls` (Identifier + optional Qualifier) + +## 4) Reference Resolution (MVP) + +### 4.1 Unit-Auflösung (`uses`) +- Index-Phase: Map `UnitName -> fileId` (aus Units/Programs/Packages) +- Resolution: Bei `uses Foo, Bar;` Edges auf Ziel-Units setzen. +- Spezialfälle (später): `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`) +- Heuristik-Reihenfolge: + 1. lokale Procs/Funcs in der gleichen Unit + 2. Methoden in der gleichen Klasse + 3. Units aus `uses` + 4. globaler Fallback nach Name + +## 5) Tests + +- Fixtures aus `fixtures/` in die Test-Suite aufnehmen +- Assertions: + - Node-Anzahl + zentrale Namen + - `uses`-Edges + - `extends`/`implements`-Edges + - einfache Call-Edges + +## 6) Definition of Done + +- `codegraph index` indexiert Delphi/ Pascal-Dateien ohne Crash +- `codegraph_search` findet Klassen/Methoden +- `codegraph_callers/callees` liefert sinnvolle Ergebnisse für einfache Fälle +- `uses`-Graph stimmt (Unit-Abhängigkeiten) diff --git a/Delphi/Docs/02-Capture-Convention.md b/Delphi/Docs/02-Capture-Convention.md new file mode 100644 index 0000000..4538748 --- /dev/null +++ b/Delphi/Docs/02-Capture-Convention.md @@ -0,0 +1,53 @@ +# Capture Convention (wie die `.scm` Queries an CodeGraph andocken) + +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**. + +## Vorgehen + +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. + +## Empfohlene semantische Einteilung + +Unabhängig von den konkreten Capture-Namen sollte jedes Query-Match folgende Informationen liefern: + +### 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) + +### 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) + +## Minimaler Capture-Satz (Pseudo) + +> **Wichtig:** Diese Namen sind **Platzhalter**. Bitte auf das CodeGraph-interne Schema mappen. + +- `@cg.node` mit Properties: + - `(#set! cg.kind "class")` + - `(#set! cg.name @nameCapture)` oder per separate captures + +- `@cg.edge` mit Properties: + - `(#set! cg.kind "calls")` + - Zielsymbol als Capture: `@cg.target` + +Wenn CodeGraph statt `#set!` eine feste Capture-Nomenklatur nutzt (z.B. `@definition.function`), dann übernimm exakt diese. + +## Delphi-spezifische Stolperfallen + +- `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` + +Für MVP: **nicht perfekt auflösen**, aber **zuverlässig extrahieren**. diff --git a/Delphi/Docs/03-TreeSitter-Query-Sketches.md b/Delphi/Docs/03-TreeSitter-Query-Sketches.md new file mode 100644 index 0000000..e73021e --- /dev/null +++ b/Delphi/Docs/03-TreeSitter-Query-Sketches.md @@ -0,0 +1,119 @@ +# 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. diff --git a/Delphi/Docs/04-Checklist.md b/Delphi/Docs/04-Checklist.md new file mode 100644 index 0000000..ff8a5cb --- /dev/null +++ b/Delphi/Docs/04-Checklist.md @@ -0,0 +1,32 @@ +# 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) + +## 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) + +## 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) + +## D) Testing +- [ ] add fixtures to tests +- [ ] assert extracted nodes +- [ ] assert imports/extends/implements/calls + +## E) Polishing +- [ ] expose language in `Supported Languages` docs +- [ ] update CLI help / config validation diff --git a/Delphi/README.md b/Delphi/README.md new file mode 100644 index 0000000..a6e2057 --- /dev/null +++ b/Delphi/README.md @@ -0,0 +1,25 @@ +# CodeGraph Pascal/Delphi Support (Fork-Konzept) + +Dieses ZIP ist **kein fertiger CodeGraph-Fork**, sondern ein **umsetzbarer Bauplan** inklusive Query-Skizzen, Scaffold-Dateistruktur, Test-Fixtures und einer Checkliste. + +Ziel: CodeGraph soll **Pascal/Delphi**-Dateien indexieren und daraus **Nodes** (Units, Klassen, Methoden, …) 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) + +## 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. + +> Hinweis: Tree-sitter ist die Grundlage. Für Pascal/Delphi bietet sich `tree-sitter-pascal` an (Delphi + FreePascal). diff --git a/Delphi/Scaffold/src/languages/pascal/README.md b/Delphi/Scaffold/src/languages/pascal/README.md new file mode 100644 index 0000000..a6e966f --- /dev/null +++ b/Delphi/Scaffold/src/languages/pascal/README.md @@ -0,0 +1,15 @@ +# 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`). diff --git a/Delphi/Scaffold/src/languages/pascal/language.ts b/Delphi/Scaffold/src/languages/pascal/language.ts new file mode 100644 index 0000000..47a7574 --- /dev/null +++ b/Delphi/Scaffold/src/languages/pascal/language.ts @@ -0,0 +1,29 @@ +/** + * 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 + } +}; diff --git a/Delphi/Scaffold/src/languages/pascal/queries/edges.scm b/Delphi/Scaffold/src/languages/pascal/queries/edges.scm new file mode 100644 index 0000000..ac14ffe --- /dev/null +++ b/Delphi/Scaffold/src/languages/pascal/queries/edges.scm @@ -0,0 +1,38 @@ +; 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") diff --git a/Delphi/Scaffold/src/languages/pascal/queries/nodes.scm b/Delphi/Scaffold/src/languages/pascal/queries/nodes.scm new file mode 100644 index 0000000..76db826 --- /dev/null +++ b/Delphi/Scaffold/src/languages/pascal/queries/nodes.scm @@ -0,0 +1,61 @@ +; 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") diff --git a/Delphi/fixtures/App.dpr b/Delphi/fixtures/App.dpr new file mode 100644 index 0000000..14ac145 --- /dev/null +++ b/Delphi/fixtures/App.dpr @@ -0,0 +1,19 @@ +program App; + +{$APPTYPE CONSOLE} + +uses + System.SysUtils, + UAuth in 'UAuth.pas'; + +var + Svc: TAuthService; + +begin + Svc := TAuthService.Create; + try + Writeln(Svc.Login('olaf', 'secret')); + finally + Svc.Free; + end; +end. diff --git a/Delphi/fixtures/UAuth.pas b/Delphi/fixtures/UAuth.pas new file mode 100644 index 0000000..6d89521 --- /dev/null +++ b/Delphi/fixtures/UAuth.pas @@ -0,0 +1,36 @@ +unit UAuth; + +interface + +uses + System.SysUtils, + System.Classes; + +type + ITokenValidator = interface + ['{11111111-1111-1111-1111-111111111111}'] + function Validate(const AToken: string): Boolean; + end; + + TAuthService = class(TInterfacedObject, ITokenValidator) + public + function Validate(const AToken: string): Boolean; + function Login(const AUser, APass: string): string; + end; + +implementation + +function TAuthService.Validate(const AToken: string): Boolean; +begin + Result := AToken <> ''; +end; + +function TAuthService.Login(const AUser, APass: string): string; +begin + if Validate(AUser + ':' + APass) then + Result := 'ok' + else + Result := ''; +end; + +end. diff --git a/Delphi/resolution/UnitResolution.md b/Delphi/resolution/UnitResolution.md new file mode 100644 index 0000000..d960eab --- /dev/null +++ b/Delphi/resolution/UnitResolution.md @@ -0,0 +1,30 @@ +# Unit Resolution Heuristics (uses → File) + +## Input + +- Extracted edges of kind `imports` from `uses` clauses. +- Extracted nodes of kind `unit|program|package|library` with their names. + +## 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. + +## DPR/DPK "in 'path'" cases (Phase 2) + +In Delphi project files you can have: + +``` +uses + Foo in 'src/Foo.pas', + Bar in 'Bar.pas'; +``` + +Enhancement: +- Parse optional path literal and resolve directly to file.