fix: Add GDScript-specific dependency extraction to eliminate syntax errors
PROBLEM:
- 265+ "Syntax error in *.gd" warnings during analysis
- GDScript files were routed to Python AST parser (_extract_python_imports)
- Python AST failed because GDScript syntax differs (extends, signal, @export)
SOLUTION:
- Created dedicated _extract_gdscript_imports() method using regex
- Parses GDScript-specific patterns:
* const/var = preload("res://path")
* const/var = load("res://path")
* extends "res://path/to/base.gd"
* extends MyBaseClass (with built-in Godot class filtering)
- Converts res:// paths to relative paths
- Routes GDScript files to new extractor instead of Python AST
CHANGES:
- dependency_analyzer.py (line 114-116): Route GDScript to new extractor
- dependency_analyzer.py (line 201-318): Add _extract_gdscript_imports()
- Updated module docstring: 9 → 10 languages + Godot ecosystem
- Updated analyze_file() docstring with GDScript support
IMPACT:
- Eliminates all 265+ syntax error warnings
- Correctly extracts GDScript dependencies (preload/load/extends)
- Completes C3.10 Signal Flow Analysis integration
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
Dependency Graph Analyzer (C2.6)
|
Dependency Graph Analyzer (C2.6)
|
||||||
|
|
||||||
Analyzes import/require/include/use statements to build dependency graphs.
|
Analyzes import/require/include/use statements to build dependency graphs.
|
||||||
Supports 9 programming languages with language-specific extraction.
|
Supports 10 programming languages + Godot ecosystem with language-specific extraction.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Multi-language import extraction (Python AST, others regex-based)
|
- Multi-language import extraction (Python AST, others regex-based)
|
||||||
@@ -14,6 +14,8 @@ Features:
|
|||||||
|
|
||||||
Supported Languages:
|
Supported Languages:
|
||||||
- Python: import, from...import, relative imports (AST-based)
|
- Python: import, from...import, relative imports (AST-based)
|
||||||
|
- GDScript: preload(), load(), extends (regex-based, Godot game engine)
|
||||||
|
- Godot Files: .tscn, .tres, .gdshader ext_resource parsing
|
||||||
- JavaScript/TypeScript: ES6 import, CommonJS require (regex-based)
|
- JavaScript/TypeScript: ES6 import, CommonJS require (regex-based)
|
||||||
- C/C++: #include directives (regex-based)
|
- C/C++: #include directives (regex-based)
|
||||||
- C#: using statements (regex, based on MS C# spec)
|
- C#: using statements (regex, based on MS C# spec)
|
||||||
@@ -101,7 +103,8 @@ class DependencyAnalyzer:
|
|||||||
Args:
|
Args:
|
||||||
file_path: Path to source file
|
file_path: Path to source file
|
||||||
content: File content
|
content: File content
|
||||||
language: Programming language (Python, JavaScript, TypeScript, C, C++, C#, Go, Rust, Java, Ruby, PHP)
|
language: Programming language (Python, GDScript, GodotScene, GodotResource, GodotShader,
|
||||||
|
JavaScript, TypeScript, C, C++, C#, Go, Rust, Java, Ruby, PHP)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of DependencyInfo objects
|
List of DependencyInfo objects
|
||||||
@@ -109,8 +112,8 @@ class DependencyAnalyzer:
|
|||||||
if language == "Python":
|
if language == "Python":
|
||||||
deps = self._extract_python_imports(content, file_path)
|
deps = self._extract_python_imports(content, file_path)
|
||||||
elif language == "GDScript":
|
elif language == "GDScript":
|
||||||
# GDScript is Python-like, uses similar import syntax
|
# GDScript uses preload/load, not Python imports
|
||||||
deps = self._extract_python_imports(content, file_path)
|
deps = self._extract_gdscript_imports(content, file_path)
|
||||||
elif language in ("GodotScene", "GodotResource", "GodotShader"):
|
elif language in ("GodotScene", "GodotResource", "GodotShader"):
|
||||||
# Godot resource files use ext_resource references
|
# Godot resource files use ext_resource references
|
||||||
deps = self._extract_godot_resources(content, file_path)
|
deps = self._extract_godot_resources(content, file_path)
|
||||||
@@ -195,6 +198,125 @@ class DependencyAnalyzer:
|
|||||||
|
|
||||||
return deps
|
return deps
|
||||||
|
|
||||||
|
def _extract_gdscript_imports(self, content: str, file_path: str) -> list[DependencyInfo]:
|
||||||
|
"""
|
||||||
|
Extract GDScript import/preload/load statements.
|
||||||
|
|
||||||
|
Handles:
|
||||||
|
- const MyClass = preload("res://path/to/file.gd")
|
||||||
|
- var scene = load("res://path/to/scene.tscn")
|
||||||
|
- extends "res://path/to/base.gd"
|
||||||
|
- extends MyBaseClass (implicit dependency)
|
||||||
|
|
||||||
|
Note: GDScript uses res:// paths which are converted to relative paths.
|
||||||
|
"""
|
||||||
|
deps = []
|
||||||
|
|
||||||
|
# Extract preload() calls: const/var NAME = preload("path")
|
||||||
|
preload_pattern = r'(?:const|var)\s+\w+\s*=\s*preload\("(.+?)"\)'
|
||||||
|
for match in re.finditer(preload_pattern, content):
|
||||||
|
resource_path = match.group(1)
|
||||||
|
line_num = content[: match.start()].count("\n") + 1
|
||||||
|
|
||||||
|
# Convert res:// paths to relative
|
||||||
|
if resource_path.startswith("res://"):
|
||||||
|
resource_path = resource_path[6:]
|
||||||
|
|
||||||
|
deps.append(
|
||||||
|
DependencyInfo(
|
||||||
|
source_file=file_path,
|
||||||
|
imported_module=resource_path,
|
||||||
|
import_type="preload",
|
||||||
|
is_relative=True,
|
||||||
|
line_number=line_num,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract load() calls: var/const NAME = load("path")
|
||||||
|
load_pattern = r'(?:const|var)\s+\w+\s*=\s*load\("(.+?)"\)'
|
||||||
|
for match in re.finditer(load_pattern, content):
|
||||||
|
resource_path = match.group(1)
|
||||||
|
line_num = content[: match.start()].count("\n") + 1
|
||||||
|
|
||||||
|
if resource_path.startswith("res://"):
|
||||||
|
resource_path = resource_path[6:]
|
||||||
|
|
||||||
|
deps.append(
|
||||||
|
DependencyInfo(
|
||||||
|
source_file=file_path,
|
||||||
|
imported_module=resource_path,
|
||||||
|
import_type="load",
|
||||||
|
is_relative=True,
|
||||||
|
line_number=line_num,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract extends with string path: extends "res://path/to/base.gd"
|
||||||
|
extends_path_pattern = r'extends\s+"(.+?)"'
|
||||||
|
for match in re.finditer(extends_path_pattern, content):
|
||||||
|
resource_path = match.group(1)
|
||||||
|
line_num = content[: match.start()].count("\n") + 1
|
||||||
|
|
||||||
|
if resource_path.startswith("res://"):
|
||||||
|
resource_path = resource_path[6:]
|
||||||
|
|
||||||
|
deps.append(
|
||||||
|
DependencyInfo(
|
||||||
|
source_file=file_path,
|
||||||
|
imported_module=resource_path,
|
||||||
|
import_type="extends",
|
||||||
|
is_relative=True,
|
||||||
|
line_number=line_num,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract extends with class name: extends MyBaseClass
|
||||||
|
# Note: This creates a symbolic dependency that may not resolve to a file
|
||||||
|
extends_class_pattern = r'extends\s+([A-Z]\w+)'
|
||||||
|
for match in re.finditer(extends_class_pattern, content):
|
||||||
|
class_name = match.group(1)
|
||||||
|
line_num = content[: match.start()].count("\n") + 1
|
||||||
|
|
||||||
|
# Skip built-in Godot classes (Node, Resource, etc.)
|
||||||
|
if class_name not in (
|
||||||
|
"Node",
|
||||||
|
"Node2D",
|
||||||
|
"Node3D",
|
||||||
|
"Resource",
|
||||||
|
"RefCounted",
|
||||||
|
"Object",
|
||||||
|
"Control",
|
||||||
|
"Area2D",
|
||||||
|
"Area3D",
|
||||||
|
"CharacterBody2D",
|
||||||
|
"CharacterBody3D",
|
||||||
|
"RigidBody2D",
|
||||||
|
"RigidBody3D",
|
||||||
|
"StaticBody2D",
|
||||||
|
"StaticBody3D",
|
||||||
|
"Camera2D",
|
||||||
|
"Camera3D",
|
||||||
|
"Sprite2D",
|
||||||
|
"Sprite3D",
|
||||||
|
"Label",
|
||||||
|
"Button",
|
||||||
|
"Panel",
|
||||||
|
"Container",
|
||||||
|
"VBoxContainer",
|
||||||
|
"HBoxContainer",
|
||||||
|
):
|
||||||
|
deps.append(
|
||||||
|
DependencyInfo(
|
||||||
|
source_file=file_path,
|
||||||
|
imported_module=class_name,
|
||||||
|
import_type="extends",
|
||||||
|
is_relative=False,
|
||||||
|
line_number=line_num,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return deps
|
||||||
|
|
||||||
def _extract_js_imports(self, content: str, file_path: str) -> list[DependencyInfo]:
|
def _extract_js_imports(self, content: str, file_path: str) -> list[DependencyInfo]:
|
||||||
"""
|
"""
|
||||||
Extract JavaScript/TypeScript import statements.
|
Extract JavaScript/TypeScript import statements.
|
||||||
|
|||||||
Reference in New Issue
Block a user