code2llm 0.5.115__tar.gz → 0.5.117__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code2llm-0.5.115 → code2llm-0.5.117}/PKG-INFO +2 -2
- {code2llm-0.5.115 → code2llm-0.5.117}/README.md +1 -1
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/__init__.py +1 -1
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/call_graph.py +2 -7
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/cfg.py +2 -7
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/dfg.py +2 -7
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/formats.py +17 -20
- code2llm-0.5.117/code2llm/core/lang/__init__.py +173 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ruby.py +22 -1
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/base.py +16 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/PKG-INFO +2 -2
- {code2llm-0.5.115 → code2llm-0.5.117}/pyproject.toml +1 -1
- {code2llm-0.5.115 → code2llm-0.5.117}/setup.py +1 -1
- code2llm-0.5.115/code2llm/core/lang/__init__.py +0 -11
- {code2llm-0.5.115 → code2llm-0.5.117}/LICENSE +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/__main__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/api.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_analysis.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/orchestrator.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/analyzer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/config.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/models.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/persistent_cache.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/setup.cfg +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.115 → code2llm-0.5.117}/tests/test_toon_v2.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.117
|
|
4
4
|
Summary: High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries
|
|
5
5
|
Home-page: https://github.com/wronai/stts
|
|
6
6
|
Author: STTS Project
|
|
@@ -67,7 +67,7 @@ Dynamic: requires-python
|
|
|
67
67
|
|
|
68
68
|
## AI Cost Tracking
|
|
69
69
|
|
|
70
|
-
    
|
|
71
71
|
  
|
|
72
72
|
|
|
73
73
|
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
7
|
  
|
|
8
8
|
|
|
9
9
|
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
@@ -8,7 +8,7 @@ Includes NLP Processing Pipeline for query normalization, intent matching,
|
|
|
8
8
|
and entity resolution with multilingual support.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
__version__ = "0.5.
|
|
11
|
+
__version__ = "0.5.117"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -5,6 +5,7 @@ from typing import Optional, Set, List, Dict
|
|
|
5
5
|
|
|
6
6
|
from code2llm.core.config import Config
|
|
7
7
|
from code2llm.core.models import AnalysisResult, FlowEdge
|
|
8
|
+
from code2llm.analysis.utils import ast_unparse
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class CallGraphExtractor(ast.NodeVisitor):
|
|
@@ -202,10 +203,4 @@ class CallGraphExtractor(ast.NodeVisitor):
|
|
|
202
203
|
return None
|
|
203
204
|
|
|
204
205
|
def _expr_to_str(self, node: ast.AST) -> str:
|
|
205
|
-
|
|
206
|
-
if node is None:
|
|
207
|
-
return ""
|
|
208
|
-
try:
|
|
209
|
-
return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
|
|
210
|
-
except:
|
|
211
|
-
return str(node)
|
|
206
|
+
return ast_unparse(node, default_none="")
|
|
@@ -6,6 +6,7 @@ from typing import Optional
|
|
|
6
6
|
|
|
7
7
|
from code2llm.core.config import Config
|
|
8
8
|
from code2llm.core.models import AnalysisResult, FlowNode, FlowEdge, FunctionInfo
|
|
9
|
+
from code2llm.analysis.utils import ast_unparse
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class CFGExtractor(ast.NodeVisitor):
|
|
@@ -275,13 +276,7 @@ class CFGExtractor(ast.NodeVisitor):
|
|
|
275
276
|
return str(node)[:50]
|
|
276
277
|
|
|
277
278
|
def _expr_to_str(self, node: ast.AST) -> str:
|
|
278
|
-
|
|
279
|
-
if node is None:
|
|
280
|
-
return "None"
|
|
281
|
-
try:
|
|
282
|
-
return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
|
|
283
|
-
except:
|
|
284
|
-
return str(node)
|
|
279
|
+
return ast_unparse(node)
|
|
285
280
|
|
|
286
281
|
def _format_except(self, handler: ast.ExceptHandler) -> str:
|
|
287
282
|
"""Format except handler."""
|
|
@@ -6,6 +6,7 @@ from typing import Set, Dict, List
|
|
|
6
6
|
|
|
7
7
|
from code2llm.core.config import Config
|
|
8
8
|
from code2llm.core.models import AnalysisResult, FlowEdge, DataFlow, Mutation
|
|
9
|
+
from code2llm.analysis.utils import ast_unparse
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class DFGExtractor(ast.NodeVisitor):
|
|
@@ -207,13 +208,7 @@ class DFGExtractor(ast.NodeVisitor):
|
|
|
207
208
|
return names
|
|
208
209
|
|
|
209
210
|
def _expr_to_str(self, node: ast.AST) -> str:
|
|
210
|
-
|
|
211
|
-
if node is None:
|
|
212
|
-
return "None"
|
|
213
|
-
try:
|
|
214
|
-
return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
|
|
215
|
-
except:
|
|
216
|
-
return str(node)
|
|
211
|
+
return ast_unparse(node)
|
|
217
212
|
|
|
218
213
|
def _build_data_flow_edges(self):
|
|
219
214
|
"""Build DFG edges from data flow records."""
|
|
@@ -215,30 +215,27 @@ def _export_mermaid_pngs(args, output_dir: Path) -> None:
|
|
|
215
215
|
print(f" - PNG: Skipped (install with: make install-mermaid)")
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
def
|
|
219
|
-
"""
|
|
220
|
-
|
|
221
|
-
Generates calls.yaml with structured call graph data:
|
|
222
|
-
- nodes: functions with metadata (CC, calls_in/out)
|
|
223
|
-
- edges: caller -> callee relationships
|
|
224
|
-
- modules: grouping by module
|
|
225
|
-
- stats: summary statistics
|
|
226
|
-
"""
|
|
218
|
+
def _export_calls_format(args, result, output_dir: Path, toon: bool = False) -> None:
|
|
219
|
+
"""Shared helper: export call graph in YAML or toon format."""
|
|
227
220
|
yaml_exporter = YAMLExporter()
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
221
|
+
if toon:
|
|
222
|
+
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
223
|
+
if args.verbose:
|
|
224
|
+
print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
|
|
225
|
+
else:
|
|
226
|
+
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
227
|
+
if args.verbose:
|
|
228
|
+
print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
|
|
231
229
|
|
|
232
230
|
|
|
233
|
-
def
|
|
234
|
-
"""Export calls.
|
|
231
|
+
def _export_calls(args, result, output_dir: Path):
|
|
232
|
+
"""Export standalone calls.yaml (structured call graph YAML)."""
|
|
233
|
+
_export_calls_format(args, result, output_dir, toon=False)
|
|
235
234
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if args.verbose:
|
|
241
|
-
print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
|
|
235
|
+
|
|
236
|
+
def _export_calls_toon(args, result, output_dir: Path):
|
|
237
|
+
"""Export calls.toon.yaml (call graph in human-readable toon format)."""
|
|
238
|
+
_export_calls_format(args, result, output_dir, toon=True)
|
|
242
239
|
|
|
243
240
|
|
|
244
241
|
def _export_mermaid(args, result, output_dir: Path):
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Language-specific analyzers for non-Python source files.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- LanguageParser ABC — abstract base class for all language parsers
|
|
5
|
+
- LANGUAGE_REGISTRY — dict mapping extensions to parser functions
|
|
6
|
+
- register_language — decorator for auto-registration
|
|
7
|
+
- get_parser(extension) — lookup parser by file extension
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from typing import Dict, Any, Callable, Set, Optional
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from code2llm.core.models import ModuleInfo, FunctionInfo, ClassInfo
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Type alias for parser results
|
|
18
|
+
ParserResult = Dict[str, Any]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LanguageParser(ABC):
|
|
22
|
+
"""Abstract base class for language-specific parsers.
|
|
23
|
+
|
|
24
|
+
All language parsers must inherit from this class and implement:
|
|
25
|
+
- analyze() method to parse file content
|
|
26
|
+
- supported_extensions class attribute
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
@register_language('.go', '.golang')
|
|
30
|
+
class GoParser(LanguageParser):
|
|
31
|
+
supported_extensions = ('.go', '.golang')
|
|
32
|
+
|
|
33
|
+
def analyze(self, content, file_path, module_name, stats):
|
|
34
|
+
# Parse Go code
|
|
35
|
+
return {'module': ..., 'functions': ..., 'classes': ...}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
supported_extensions: tuple = ()
|
|
39
|
+
language_name: str = ""
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def analyze(
|
|
43
|
+
self,
|
|
44
|
+
content: str,
|
|
45
|
+
file_path: str,
|
|
46
|
+
module_name: str,
|
|
47
|
+
stats: Dict[str, Any]
|
|
48
|
+
) -> ParserResult:
|
|
49
|
+
"""Analyze file content and return parsed structure.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
content: File content as string
|
|
53
|
+
file_path: Absolute path to file
|
|
54
|
+
module_name: Logical module name
|
|
55
|
+
stats: Statistics dict to update
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dict with 'module', 'functions', 'classes', 'nodes', 'edges'
|
|
59
|
+
"""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
def can_parse(self, file_path: str) -> bool:
|
|
63
|
+
"""Check if this parser can handle the given file."""
|
|
64
|
+
ext = Path(file_path).suffix.lower()
|
|
65
|
+
return ext in self.supported_extensions
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Legacy parser function type
|
|
69
|
+
LegacyParser = Callable[[str, str, str, str, Dict[str, Any]], ParserResult]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Registry: extension -> parser instance or function
|
|
73
|
+
LANGUAGE_REGISTRY: Dict[str, Any] = {}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def register_language(*extensions: str, name: str = ""):
|
|
77
|
+
"""Decorator to register a language parser.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
*extensions: File extensions this parser handles (e.g., '.go', '.rs')
|
|
81
|
+
name: Human-readable language name
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
@register_language('.go', '.golang', name='Go')
|
|
85
|
+
class GoParser(LanguageParser):
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
@register_language('.rb', name='Ruby')
|
|
89
|
+
def analyze_ruby(content, file_path, module_name, ext, stats):
|
|
90
|
+
...
|
|
91
|
+
"""
|
|
92
|
+
def decorator(cls_or_func):
|
|
93
|
+
if isinstance(cls_or_func, type) and issubclass(cls_or_func, LanguageParser):
|
|
94
|
+
# It's a class
|
|
95
|
+
parser = cls_or_func()
|
|
96
|
+
parser.language_name = name or cls_or_func.__name__.replace('Parser', '')
|
|
97
|
+
for ext in extensions:
|
|
98
|
+
LANGUAGE_REGISTRY[ext.lower()] = parser
|
|
99
|
+
else:
|
|
100
|
+
# It's a function
|
|
101
|
+
for ext in extensions:
|
|
102
|
+
LANGUAGE_REGISTRY[ext.lower()] = cls_or_func
|
|
103
|
+
return cls_or_func
|
|
104
|
+
return decorator
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_parser(extension: str) -> Optional[Any]:
|
|
108
|
+
"""Get parser for a file extension.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
extension: File extension (e.g., '.go', '.rs')
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
LanguageParser instance or legacy function, or None if not found
|
|
115
|
+
"""
|
|
116
|
+
return LANGUAGE_REGISTRY.get(extension.lower())
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def list_parsers() -> Dict[str, Any]:
|
|
120
|
+
"""List all registered parsers."""
|
|
121
|
+
return dict(LANGUAGE_REGISTRY)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# Legacy function imports (for backward compatibility)
|
|
125
|
+
from .typescript import analyze_typescript_js
|
|
126
|
+
from .go_lang import analyze_go
|
|
127
|
+
from .rust import analyze_rust
|
|
128
|
+
from .java import analyze_java
|
|
129
|
+
from .cpp import analyze_cpp
|
|
130
|
+
from .csharp import analyze_csharp
|
|
131
|
+
from .php import analyze_php
|
|
132
|
+
from .ruby import analyze_ruby
|
|
133
|
+
from .generic import analyze_generic
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# Register legacy parsers
|
|
137
|
+
register_language('.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs')(analyze_typescript_js)
|
|
138
|
+
register_language('.go')(analyze_go)
|
|
139
|
+
register_language('.rs')(analyze_rust)
|
|
140
|
+
register_language('.java')(analyze_java)
|
|
141
|
+
register_language('.cpp', '.cc', '.cxx', '.hpp', '.h', '.c')(analyze_cpp)
|
|
142
|
+
register_language('.cs')(analyze_csharp)
|
|
143
|
+
register_language('.php')(analyze_php)
|
|
144
|
+
|
|
145
|
+
# Import and register RubyParser class (demonstrating new ABC pattern)
|
|
146
|
+
from .ruby import RubyParser
|
|
147
|
+
LANGUAGE_REGISTRY['.rb'] = RubyParser()
|
|
148
|
+
LANGUAGE_REGISTRY['.rbw'] = RubyParser()
|
|
149
|
+
|
|
150
|
+
# Generic parser as fallback for unknown extensions
|
|
151
|
+
LANGUAGE_REGISTRY['*'] = analyze_generic
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
__all__ = [
|
|
155
|
+
'LanguageParser',
|
|
156
|
+
'ParserResult',
|
|
157
|
+
'register_language',
|
|
158
|
+
'LANGUAGE_REGISTRY',
|
|
159
|
+
'get_parser',
|
|
160
|
+
'list_parsers',
|
|
161
|
+
# New class-based parsers
|
|
162
|
+
'RubyParser',
|
|
163
|
+
# Legacy exports
|
|
164
|
+
'analyze_typescript_js',
|
|
165
|
+
'analyze_go',
|
|
166
|
+
'analyze_rust',
|
|
167
|
+
'analyze_java',
|
|
168
|
+
'analyze_cpp',
|
|
169
|
+
'analyze_csharp',
|
|
170
|
+
'analyze_php',
|
|
171
|
+
'analyze_ruby',
|
|
172
|
+
'analyze_generic',
|
|
173
|
+
]
|
|
@@ -138,6 +138,27 @@ def analyze_ruby(content: str, file_path: str, module_name: str,
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
extract_calls_regex(content, module_name, result)
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
stats['files_processed'] += 1
|
|
143
143
|
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# New LanguageParser ABC implementation (demonstrating the new pattern)
|
|
147
|
+
# Note: Imports at the bottom to avoid circular imports
|
|
148
|
+
# The __init__.py handles the actual registration
|
|
149
|
+
class RubyParser:
|
|
150
|
+
"""Ruby language parser - registered via @register_language in __init__.py."""
|
|
151
|
+
|
|
152
|
+
supported_extensions = ('.rb', '.rbw')
|
|
153
|
+
language_name = 'Ruby'
|
|
154
|
+
|
|
155
|
+
def analyze(
|
|
156
|
+
self,
|
|
157
|
+
content: str,
|
|
158
|
+
file_path: str,
|
|
159
|
+
module_name: str,
|
|
160
|
+
stats: Dict
|
|
161
|
+
) -> Dict:
|
|
162
|
+
"""Analyze Ruby file content."""
|
|
163
|
+
# Delegate to the existing legacy function for now
|
|
164
|
+
return analyze_ruby(content, file_path, module_name, '.rb', stats)
|
|
@@ -94,6 +94,22 @@ class BaseExporter(ABC):
|
|
|
94
94
|
Exporter = BaseExporter
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
class ViewGeneratorMixin:
|
|
98
|
+
"""Mixin providing the shared ``generate`` implementation for view generators.
|
|
99
|
+
|
|
100
|
+
Eliminates the identical ``generate`` method duplicated across
|
|
101
|
+
``ArticleViewGenerator``, ``ContextViewGenerator``, and ``ToonViewGenerator``.
|
|
102
|
+
|
|
103
|
+
Subclasses must implement ``_render(data) -> List[str]``.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def generate(self, data: Dict[str, Any], output_path: str) -> None:
|
|
107
|
+
lines = self._render(data)
|
|
108
|
+
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
110
|
+
f.write("\n".join(lines) + "\n")
|
|
111
|
+
|
|
112
|
+
|
|
97
113
|
# Export registry: format_name -> exporter_class
|
|
98
114
|
EXPORT_REGISTRY: Dict[str, Type[BaseExporter]] = {}
|
|
99
115
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.117
|
|
4
4
|
Summary: High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries
|
|
5
5
|
Home-page: https://github.com/wronai/stts
|
|
6
6
|
Author: STTS Project
|
|
@@ -67,7 +67,7 @@ Dynamic: requires-python
|
|
|
67
67
|
|
|
68
68
|
## AI Cost Tracking
|
|
69
69
|
|
|
70
|
-
    
|
|
71
71
|
  
|
|
72
72
|
|
|
73
73
|
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code2llm"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.117"
|
|
8
8
|
description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"""Language-specific analyzers for non-Python source files."""
|
|
2
|
-
|
|
3
|
-
from .typescript import analyze_typescript_js
|
|
4
|
-
from .go_lang import analyze_go
|
|
5
|
-
from .rust import analyze_rust
|
|
6
|
-
from .java import analyze_java
|
|
7
|
-
from .cpp import analyze_cpp
|
|
8
|
-
from .csharp import analyze_csharp
|
|
9
|
-
from .php import analyze_php
|
|
10
|
-
from .ruby import analyze_ruby
|
|
11
|
-
from .generic import analyze_generic
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|