code2logic 1.0.26__tar.gz → 1.0.27__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.
- {code2logic-1.0.26 → code2logic-1.0.27}/PKG-INFO +1 -1
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/__init__.py +1 -1
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/analyzer.py +1 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/common.py +21 -11
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/runner.py +192 -37
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/logicml.py +10 -6
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/parsers.py +364 -1
- {code2logic-1.0.26 → code2logic-1.0.27}/pyproject.toml +2 -2
- {code2logic-1.0.26 → code2logic-1.0.27}/LICENSE +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/README.md +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/__main__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/adaptive.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/base.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmark.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/results.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/chunked_reproduction.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/cli.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/code_review.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/config.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/core/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/dependency.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/errors.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/file_formats.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/formats/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/function_logic.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/generators.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/gherkin.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/integrations/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/intent.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_clients.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_clients_new.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_profiler.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/markdown_format.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/mcp_server.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/metrics.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/models.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/project_reproducer.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/prompts.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/py.typed +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/quality.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/refactor.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/reproducer.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/reproduction.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/json_schema.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/logicml_schema.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/markdown_schema.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/yaml_schema.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/shared_utils.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/similarity.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/terminal.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/tools/__init__.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/toon_format.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/universal.py +0 -0
- {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: code2logic
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.27
|
|
4
4
|
Summary: Convert source code to logical representation for LLM analysis
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: code-analysis,llm,ast,static-analysis,tree-sitter,code-understanding,documentation,dependency-graph,nlp
|
|
@@ -174,19 +174,16 @@ Name the test class Test<ClassName> or TestFunctions."""
|
|
|
174
174
|
return prompt
|
|
175
175
|
|
|
176
176
|
|
|
177
|
-
def get_token_reproduction_prompt(spec: str, fmt: str, file_name: str) -> str:
|
|
177
|
+
def get_token_reproduction_prompt(spec: str, fmt: str, file_name: str, language: str = "python") -> str:
|
|
178
178
|
format_hints = {
|
|
179
179
|
"json": "Parse the JSON structure and implement all classes and functions.",
|
|
180
180
|
"json_compact": "Parse the compact JSON and implement all elements.",
|
|
181
181
|
"yaml": "Parse the YAML structure and implement all classes and functions with exact signatures.",
|
|
182
|
-
"gherkin": "Implement scenarios as SIMPLE, MINIMAL
|
|
182
|
+
"gherkin": "Implement scenarios as SIMPLE, MINIMAL code. NO over-engineering. Keep code short and direct.",
|
|
183
183
|
"markdown": "Parse embedded Gherkin (behaviors) and YAML (structures).",
|
|
184
|
-
"logicml": """Parse LogicML and generate VALID
|
|
185
|
-
- 'sig:
|
|
186
|
-
- '
|
|
187
|
-
- 'sig: @property (self)' = @property decorator
|
|
188
|
-
- 'bases: [BaseModel]' = class X(BaseModel) with Field()
|
|
189
|
-
- 'type: re-export' = from .module import X
|
|
184
|
+
"logicml": """Parse LogicML and generate VALID code:
|
|
185
|
+
- 'sig:' lines describe function signatures (translate to the target language)
|
|
186
|
+
- 'type: re-export' means this module primarily re-exports symbols
|
|
190
187
|
CRITICAL: Ensure valid syntax - balanced brackets, proper indentation, no undefined variables.""",
|
|
191
188
|
"toon": """Parse TOON (Token-Oriented Object Notation) format carefully:
|
|
192
189
|
|
|
@@ -211,17 +208,30 @@ CRITICAL: Use imports[], function_docs, and exact signatures to reproduce code a
|
|
|
211
208
|
max_spec = 5000
|
|
212
209
|
spec_truncated = spec[:max_spec] if len(spec) > max_spec else spec
|
|
213
210
|
|
|
214
|
-
|
|
211
|
+
language_norm = (language or "python").strip().lower()
|
|
212
|
+
lang_label_map = {
|
|
213
|
+
"python": "Python",
|
|
214
|
+
"javascript": "JavaScript",
|
|
215
|
+
"typescript": "TypeScript",
|
|
216
|
+
"go": "Go",
|
|
217
|
+
"rust": "Rust",
|
|
218
|
+
"java": "Java",
|
|
219
|
+
"csharp": "C#",
|
|
220
|
+
"sql": "SQL",
|
|
221
|
+
}
|
|
222
|
+
lang_label = lang_label_map.get(language_norm, language_norm)
|
|
223
|
+
|
|
224
|
+
prompt = f"""Generate {lang_label} code from this {fmt.upper()} specification.
|
|
215
225
|
{format_hints.get(fmt, '')}
|
|
216
226
|
|
|
217
227
|
{spec_truncated}
|
|
218
228
|
|
|
219
229
|
Requirements:
|
|
220
|
-
- Complete, working
|
|
230
|
+
- Complete, working {lang_label} code for {file_name}
|
|
221
231
|
- Include imports and type hints
|
|
222
232
|
- Implement all functions with actual logic
|
|
223
233
|
|
|
224
|
-
```
|
|
234
|
+
```{language_norm}
|
|
225
235
|
"""
|
|
226
236
|
return prompt
|
|
227
237
|
|
|
@@ -17,6 +17,7 @@ Usage:
|
|
|
17
17
|
|
|
18
18
|
import sys
|
|
19
19
|
import time
|
|
20
|
+
import difflib
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import List, Optional
|
|
22
23
|
|
|
@@ -56,6 +57,98 @@ def _test_python_runs(code: str, timeout: int = 5) -> bool:
|
|
|
56
57
|
return True # Timeout might mean waiting for input
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
def _basic_syntax_ok(code: str, language: str) -> bool:
|
|
61
|
+
"""Heuristic syntax check for non-Python languages."""
|
|
62
|
+
s = (code or "").strip()
|
|
63
|
+
if not s:
|
|
64
|
+
return False
|
|
65
|
+
if language in ("javascript", "typescript"):
|
|
66
|
+
# Basic bracket/brace balance to catch the most common truncations
|
|
67
|
+
pairs = {')': '(', ']': '[', '}': '{'}
|
|
68
|
+
stack: List[str] = []
|
|
69
|
+
for ch in s:
|
|
70
|
+
if ch in "([{":
|
|
71
|
+
stack.append(ch)
|
|
72
|
+
elif ch in ")]}":
|
|
73
|
+
if not stack or stack[-1] != pairs.get(ch):
|
|
74
|
+
return False
|
|
75
|
+
stack.pop()
|
|
76
|
+
return not stack
|
|
77
|
+
if language == "go":
|
|
78
|
+
return "package" in s and "func" in s
|
|
79
|
+
if language == "rust":
|
|
80
|
+
return "fn" in s or "struct" in s or "enum" in s
|
|
81
|
+
if language == "java":
|
|
82
|
+
return "class" in s or "interface" in s
|
|
83
|
+
if language == "csharp":
|
|
84
|
+
return "class" in s or "interface" in s or "record" in s
|
|
85
|
+
if language == "sql":
|
|
86
|
+
upper = s.upper()
|
|
87
|
+
return "CREATE" in upper or "SELECT" in upper or "INSERT" in upper
|
|
88
|
+
return len(s) > 10
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _count_structural_elements(code: str, language: str) -> dict:
|
|
92
|
+
s = code or ""
|
|
93
|
+
if language == "python":
|
|
94
|
+
return {
|
|
95
|
+
'classes': len(re.findall(r'^class\s+\w+', s, re.MULTILINE)),
|
|
96
|
+
'functions': len(re.findall(r'^def\s+\w+', s, re.MULTILINE)),
|
|
97
|
+
'imports': len(re.findall(r'^(?:from|import)\s+', s, re.MULTILINE)),
|
|
98
|
+
}
|
|
99
|
+
if language in ("javascript", "typescript"):
|
|
100
|
+
return {
|
|
101
|
+
'classes': len(re.findall(r'\bclass\s+\w+', s)),
|
|
102
|
+
'functions': len(re.findall(r'\bfunction\s+\w+\s*\(', s)) + len(re.findall(r'\bconst\s+\w+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>', s)),
|
|
103
|
+
'types': len(re.findall(r'\binterface\s+\w+', s)) + len(re.findall(r'\btype\s+\w+\s*=', s)) + len(re.findall(r'\benum\s+\w+', s)),
|
|
104
|
+
'imports': len(re.findall(r'^import\s+', s, re.MULTILINE)) + len(re.findall(r'\brequire\(', s)),
|
|
105
|
+
}
|
|
106
|
+
if language == "go":
|
|
107
|
+
return {
|
|
108
|
+
'types': len(re.findall(r'^type\s+\w+\s+(?:struct|interface)\b', s, re.MULTILINE)),
|
|
109
|
+
'functions': len(re.findall(r'^func\s+(?:\([^)]*\)\s*)?\w+\s*\(', s, re.MULTILINE)),
|
|
110
|
+
'imports': len(re.findall(r'^import\b', s, re.MULTILINE)),
|
|
111
|
+
}
|
|
112
|
+
if language == "rust":
|
|
113
|
+
return {
|
|
114
|
+
'types': len(re.findall(r'\bstruct\s+\w+', s)) + len(re.findall(r'\benum\s+\w+', s)) + len(re.findall(r'\btrait\s+\w+', s)),
|
|
115
|
+
'functions': len(re.findall(r'\bfn\s+\w+\s*\(', s)),
|
|
116
|
+
'imports': len(re.findall(r'^use\s+', s, re.MULTILINE)),
|
|
117
|
+
}
|
|
118
|
+
if language == "java":
|
|
119
|
+
return {
|
|
120
|
+
'types': len(re.findall(r'\bclass\s+\w+', s)) + len(re.findall(r'\binterface\s+\w+', s)) + len(re.findall(r'\benum\s+\w+', s)) + len(re.findall(r'\brecord\s+\w+', s)),
|
|
121
|
+
'functions': len(re.findall(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{', s)),
|
|
122
|
+
'imports': len(re.findall(r'^import\s+', s, re.MULTILINE)),
|
|
123
|
+
}
|
|
124
|
+
if language == "csharp":
|
|
125
|
+
return {
|
|
126
|
+
'types': len(re.findall(r'\bclass\s+\w+', s)) + len(re.findall(r'\binterface\s+\w+', s)) + len(re.findall(r'\brecord\s+\w+', s)),
|
|
127
|
+
'functions': len(re.findall(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{', s)),
|
|
128
|
+
'imports': len(re.findall(r'^using\s+', s, re.MULTILINE)),
|
|
129
|
+
}
|
|
130
|
+
if language == "sql":
|
|
131
|
+
upper = s.upper()
|
|
132
|
+
return {
|
|
133
|
+
'types': len(re.findall(r'\bCREATE\s+TABLE\s+\w+', upper)) + len(re.findall(r'\bCREATE\s+(?:OR\s+REPLACE\s+)?VIEW\s+\w+', upper)),
|
|
134
|
+
'functions': len(re.findall(r'\bCREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+\w+', upper)),
|
|
135
|
+
}
|
|
136
|
+
return {}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _structural_score(original: str, generated: str, language: str) -> float:
|
|
140
|
+
o = _count_structural_elements(original, language)
|
|
141
|
+
g = _count_structural_elements(generated, language)
|
|
142
|
+
if not o:
|
|
143
|
+
return 0.0
|
|
144
|
+
keys = list(o.keys())
|
|
145
|
+
matches = 0
|
|
146
|
+
for k in keys:
|
|
147
|
+
if o.get(k, 0) == g.get(k, 0):
|
|
148
|
+
matches += 1
|
|
149
|
+
return matches / max(len(keys), 1) * 100
|
|
150
|
+
|
|
151
|
+
|
|
59
152
|
def _extract_code(response: str) -> str:
|
|
60
153
|
"""Extract code from LLM response."""
|
|
61
154
|
if not response:
|
|
@@ -111,8 +204,8 @@ class BenchmarkRunner:
|
|
|
111
204
|
self.client = get_client()
|
|
112
205
|
return self.client
|
|
113
206
|
|
|
114
|
-
def _template_generate_code(self, spec: str, fmt: str, file_name: str) -> str:
|
|
115
|
-
"""Generate minimal
|
|
207
|
+
def _template_generate_code(self, spec: str, fmt: str, file_name: str, language: str = "python") -> str:
|
|
208
|
+
"""Generate minimal code without an LLM (fallback mode)."""
|
|
116
209
|
import re
|
|
117
210
|
|
|
118
211
|
# Try to infer class/function names from spec
|
|
@@ -139,11 +232,35 @@ class BenchmarkRunner:
|
|
|
139
232
|
classes = [c for c in uniq(classes) if c.isidentifier()][:5]
|
|
140
233
|
functions = [f for f in uniq(functions) if f.isidentifier() and f not in classes][:10]
|
|
141
234
|
|
|
142
|
-
|
|
235
|
+
language_norm = (language or "python").strip().lower()
|
|
236
|
+
if language_norm == "python":
|
|
237
|
+
code = """from __future__ import annotations
|
|
143
238
|
|
|
144
239
|
from dataclasses import dataclass
|
|
145
240
|
from typing import Any, Optional, List, Dict
|
|
146
241
|
|
|
242
|
+
"""
|
|
243
|
+
elif language_norm in ("javascript", "typescript"):
|
|
244
|
+
code = """// Auto-generated placeholder
|
|
245
|
+
"""
|
|
246
|
+
elif language_norm == "go":
|
|
247
|
+
code = """package main
|
|
248
|
+
|
|
249
|
+
"""
|
|
250
|
+
elif language_norm == "rust":
|
|
251
|
+
code = """// Auto-generated placeholder
|
|
252
|
+
"""
|
|
253
|
+
elif language_norm == "java":
|
|
254
|
+
code = """// Auto-generated placeholder
|
|
255
|
+
"""
|
|
256
|
+
elif language_norm == "csharp":
|
|
257
|
+
code = """// Auto-generated placeholder
|
|
258
|
+
"""
|
|
259
|
+
elif language_norm == "sql":
|
|
260
|
+
code = """-- Auto-generated placeholder
|
|
261
|
+
"""
|
|
262
|
+
else:
|
|
263
|
+
code = """// Auto-generated placeholder
|
|
147
264
|
"""
|
|
148
265
|
|
|
149
266
|
if not classes and not functions:
|
|
@@ -153,20 +270,56 @@ from typing import Any, Optional, List, Dict
|
|
|
153
270
|
classes = ["GeneratedClass"]
|
|
154
271
|
functions = ["generated_function"]
|
|
155
272
|
|
|
156
|
-
|
|
157
|
-
|
|
273
|
+
if language_norm == "python":
|
|
274
|
+
for cls in classes:
|
|
275
|
+
code += f"""@dataclass
|
|
158
276
|
class {cls}:
|
|
159
277
|
\"\"\"Generated placeholder for {file_name} ({fmt}).\"\"\"
|
|
160
278
|
value: Any = None
|
|
161
279
|
|
|
162
280
|
"""
|
|
163
281
|
|
|
164
|
-
|
|
165
|
-
|
|
282
|
+
for fn in functions:
|
|
283
|
+
code += f"""def {fn}(*args: Any, **kwargs: Any) -> Any:
|
|
166
284
|
\"\"\"Generated placeholder for {file_name} ({fmt}).\"\"\"
|
|
167
285
|
return None
|
|
168
286
|
|
|
169
287
|
"""
|
|
288
|
+
elif language_norm in ("javascript", "typescript"):
|
|
289
|
+
for fn in functions:
|
|
290
|
+
export_kw = "export " if language_norm == "typescript" else ""
|
|
291
|
+
code += f"{export_kw}function {fn}(...args) {{\n return null;\n}}\n\n"
|
|
292
|
+
for cls in classes:
|
|
293
|
+
export_kw = "export " if language_norm == "typescript" else ""
|
|
294
|
+
code += f"{export_kw}class {cls} {{\n constructor() {{}}\n}}\n\n"
|
|
295
|
+
elif language_norm == "go":
|
|
296
|
+
for fn in functions:
|
|
297
|
+
code += f"func {fn}() {{\n}}\n\n"
|
|
298
|
+
for cls in classes:
|
|
299
|
+
code += f"type {cls} struct {{\n}}\n\n"
|
|
300
|
+
elif language_norm == "rust":
|
|
301
|
+
for fn in functions:
|
|
302
|
+
code += f"pub fn {fn}() {{\n}}\n\n"
|
|
303
|
+
for cls in classes:
|
|
304
|
+
code += f"pub struct {cls} {{\n}}\n\n"
|
|
305
|
+
elif language_norm == "java":
|
|
306
|
+
safe_class = classes[0] if classes else "Generated"
|
|
307
|
+
code += f"public class {safe_class} {{\n"
|
|
308
|
+
for fn in functions:
|
|
309
|
+
code += f" public static void {fn}() {{ }}\n"
|
|
310
|
+
code += "}\n"
|
|
311
|
+
elif language_norm == "csharp":
|
|
312
|
+
safe_class = classes[0] if classes else "Generated"
|
|
313
|
+
code += f"public class {safe_class} {{\n"
|
|
314
|
+
for fn in functions:
|
|
315
|
+
code += f" public static void {fn}() {{ }}\n"
|
|
316
|
+
code += "}\n"
|
|
317
|
+
elif language_norm == "sql":
|
|
318
|
+
for cls in classes:
|
|
319
|
+
code += f"CREATE TABLE {cls} (id INT);\n"
|
|
320
|
+
else:
|
|
321
|
+
for fn in functions:
|
|
322
|
+
code += f"function {fn}() {{}}\n"
|
|
170
323
|
|
|
171
324
|
return code
|
|
172
325
|
|
|
@@ -213,45 +366,35 @@ class {cls}:
|
|
|
213
366
|
|
|
214
367
|
# Analyze project
|
|
215
368
|
path = Path(folder)
|
|
216
|
-
|
|
217
|
-
if limit
|
|
218
|
-
py_files = py_files[:limit]
|
|
369
|
+
project = analyze_project(str(path), use_treesitter=False)
|
|
370
|
+
modules = project.modules[:limit] if limit else project.modules
|
|
219
371
|
|
|
220
|
-
result.total_files = len(
|
|
372
|
+
result.total_files = len(modules)
|
|
221
373
|
|
|
222
374
|
if verbose:
|
|
223
375
|
render.heading(2, "Format Benchmark")
|
|
224
376
|
render.codeblock("yaml", f"folder: {folder}\nfiles: {len(py_files)}\nformats: [{', '.join(formats)}]")
|
|
225
377
|
|
|
226
|
-
project = analyze_project(str(path), use_treesitter=False)
|
|
227
|
-
|
|
228
378
|
start_time = time.time()
|
|
229
379
|
|
|
230
380
|
# Process each file with each format
|
|
231
|
-
for
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# Find module info
|
|
235
|
-
module_info = None
|
|
236
|
-
for m in project.modules:
|
|
237
|
-
if Path(m.path).name == py_file.name:
|
|
238
|
-
module_info = m
|
|
239
|
-
break
|
|
240
|
-
|
|
241
|
-
if not module_info:
|
|
381
|
+
for module_info in modules:
|
|
382
|
+
abs_file = path / module_info.path
|
|
383
|
+
if not abs_file.exists():
|
|
242
384
|
continue
|
|
385
|
+
original = abs_file.read_text(encoding='utf-8', errors='ignore')
|
|
243
386
|
|
|
244
|
-
single_project = create_single_project(module_info,
|
|
387
|
+
single_project = create_single_project(module_info, abs_file)
|
|
245
388
|
|
|
246
389
|
file_result = FileResult(
|
|
247
|
-
file_path=str(
|
|
248
|
-
language=
|
|
390
|
+
file_path=str(abs_file),
|
|
391
|
+
language=module_info.language,
|
|
249
392
|
original_size=len(original),
|
|
250
393
|
)
|
|
251
394
|
|
|
252
395
|
for fmt in formats:
|
|
253
396
|
fmt_result = self._test_format(
|
|
254
|
-
single_project, original, fmt,
|
|
397
|
+
single_project, original, fmt, abs_file.name, client, verbose, language=module_info.language
|
|
255
398
|
)
|
|
256
399
|
file_result.format_results[fmt] = fmt_result
|
|
257
400
|
|
|
@@ -288,6 +431,7 @@ class {cls}:
|
|
|
288
431
|
file_name: str,
|
|
289
432
|
client: Optional[BaseLLMClient],
|
|
290
433
|
verbose: bool = False,
|
|
434
|
+
language: str = "python",
|
|
291
435
|
) -> FormatResult:
|
|
292
436
|
"""Test a single format."""
|
|
293
437
|
result = FormatResult(format_name=fmt)
|
|
@@ -299,12 +443,12 @@ class {cls}:
|
|
|
299
443
|
result.spec_tokens = estimate_tokens(spec)
|
|
300
444
|
|
|
301
445
|
# Generate prompt
|
|
302
|
-
prompt = get_token_reproduction_prompt(spec, fmt, file_name)
|
|
446
|
+
prompt = get_token_reproduction_prompt(spec, fmt, file_name, language=language)
|
|
303
447
|
|
|
304
448
|
# Reproduce
|
|
305
449
|
start = time.time()
|
|
306
450
|
if client is None:
|
|
307
|
-
generated = self._template_generate_code(spec, fmt, file_name)
|
|
451
|
+
generated = self._template_generate_code(spec, fmt, file_name, language=language)
|
|
308
452
|
result.gen_time = 0.0
|
|
309
453
|
else:
|
|
310
454
|
response = client.generate(prompt, max_tokens=self.config.max_tokens)
|
|
@@ -313,15 +457,26 @@ class {cls}:
|
|
|
313
457
|
result.generated_size = len(generated)
|
|
314
458
|
|
|
315
459
|
# Test quality
|
|
316
|
-
|
|
317
|
-
if
|
|
318
|
-
result.
|
|
460
|
+
language_norm = (language or "python").strip().lower()
|
|
461
|
+
if language_norm == 'python':
|
|
462
|
+
result.syntax_ok = _test_python_syntax(generated)
|
|
463
|
+
if result.syntax_ok:
|
|
464
|
+
result.runs_ok = _test_python_runs(generated)
|
|
465
|
+
else:
|
|
466
|
+
result.syntax_ok = _basic_syntax_ok(generated, language_norm)
|
|
467
|
+
result.runs_ok = False
|
|
319
468
|
|
|
320
469
|
# Calculate metrics
|
|
321
470
|
if original and generated:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
471
|
+
if language_norm == 'python':
|
|
472
|
+
analysis = self._metrics.analyze(original, generated, spec, format_name=fmt)
|
|
473
|
+
result.score = analysis.overall_score
|
|
474
|
+
result.similarity = analysis.text.char_similarity
|
|
475
|
+
else:
|
|
476
|
+
sim = difflib.SequenceMatcher(None, ' '.join(original.split()), ' '.join(generated.split())).ratio() * 100
|
|
477
|
+
struct = _structural_score(original, generated, language_norm)
|
|
478
|
+
result.similarity = sim
|
|
479
|
+
result.score = (sim * 0.7) + (struct * 0.3)
|
|
325
480
|
|
|
326
481
|
# Efficiency
|
|
327
482
|
if result.spec_size and len(original) > 0:
|
|
@@ -753,7 +908,7 @@ Requirements:
|
|
|
753
908
|
|
|
754
909
|
# Test format
|
|
755
910
|
fmt_result = self._test_format(
|
|
756
|
-
single_project, original, fmt, abs_path.name, client, verbose=False
|
|
911
|
+
single_project, original, fmt, abs_path.name, client, verbose=False, language=module_info.language
|
|
757
912
|
)
|
|
758
913
|
|
|
759
914
|
file_result.format_results[fmt] = fmt_result
|
|
@@ -129,12 +129,16 @@ class LogicMLGenerator:
|
|
|
129
129
|
lines.append("# Re-export module")
|
|
130
130
|
lines.append("type: re-export")
|
|
131
131
|
lines.append("exports:")
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
export_items: List[str] = []
|
|
133
|
+
if getattr(module, "exports", None):
|
|
134
|
+
export_items = [e for e in (module.exports or []) if e]
|
|
135
|
+
else:
|
|
136
|
+
export_items = [i for i in (module.imports or []) if i]
|
|
137
|
+
|
|
138
|
+
for item in export_items[:20]:
|
|
139
|
+
export_name = item.strip()
|
|
140
|
+
if export_name.endswith(".*"):
|
|
141
|
+
export_name = "*"
|
|
138
142
|
lines.append(f" - {export_name}")
|
|
139
143
|
return '\n'.join(lines)
|
|
140
144
|
|
|
@@ -1424,8 +1424,18 @@ class UniversalParser:
|
|
|
1424
1424
|
|
|
1425
1425
|
if language == 'python':
|
|
1426
1426
|
return self._parse_python(filepath, content)
|
|
1427
|
-
|
|
1427
|
+
if language in ('javascript', 'typescript'):
|
|
1428
1428
|
return self._parse_js_ts(filepath, content, language)
|
|
1429
|
+
if language == 'go':
|
|
1430
|
+
return self._parse_go(filepath, content)
|
|
1431
|
+
if language == 'rust':
|
|
1432
|
+
return self._parse_rust(filepath, content)
|
|
1433
|
+
if language == 'java':
|
|
1434
|
+
return self._parse_java(filepath, content)
|
|
1435
|
+
if language == 'csharp':
|
|
1436
|
+
return self._parse_csharp(filepath, content)
|
|
1437
|
+
if language == 'sql':
|
|
1438
|
+
return self._parse_sql(filepath, content)
|
|
1429
1439
|
return None
|
|
1430
1440
|
|
|
1431
1441
|
def _parse_python(self, filepath: str, content: str) -> Optional[ModuleInfo]:
|
|
@@ -1948,6 +1958,359 @@ class UniversalParser:
|
|
|
1948
1958
|
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('//')])
|
|
1949
1959
|
)
|
|
1950
1960
|
|
|
1961
|
+
def _parse_go(self, filepath: str, content: str) -> ModuleInfo:
|
|
1962
|
+
"""Parse Go using regex patterns."""
|
|
1963
|
+
imports: List[str] = []
|
|
1964
|
+
classes: List[ClassInfo] = []
|
|
1965
|
+
functions: List[FunctionInfo] = []
|
|
1966
|
+
types: List[TypeInfo] = []
|
|
1967
|
+
constants: List[str] = []
|
|
1968
|
+
exports: List[str] = []
|
|
1969
|
+
|
|
1970
|
+
for m in re.finditer(r'^package\s+(\w+)', content, re.MULTILINE):
|
|
1971
|
+
_ = m.group(1)
|
|
1972
|
+
|
|
1973
|
+
for m in re.finditer(r'^import\s+\(?\s*"([^"]+)"', content, re.MULTILINE):
|
|
1974
|
+
imports.append(m.group(1))
|
|
1975
|
+
|
|
1976
|
+
for m in re.finditer(r'^type\s+(\w+)\s+struct\b', content, re.MULTILINE):
|
|
1977
|
+
name = m.group(1)
|
|
1978
|
+
classes.append(ClassInfo(name=name))
|
|
1979
|
+
exports.append(name)
|
|
1980
|
+
types.append(TypeInfo(name=name, kind='struct', definition=''))
|
|
1981
|
+
|
|
1982
|
+
for m in re.finditer(r'^type\s+(\w+)\s+interface\b', content, re.MULTILINE):
|
|
1983
|
+
name = m.group(1)
|
|
1984
|
+
classes.append(ClassInfo(name=name, is_interface=True))
|
|
1985
|
+
exports.append(name)
|
|
1986
|
+
types.append(TypeInfo(name=name, kind='interface', definition=''))
|
|
1987
|
+
|
|
1988
|
+
for m in re.finditer(r'^func\s+(?:\([^)]*\)\s*)?(\w+)\s*\(([^)]*)\)\s*(\([^)]*\)|\w+)?', content, re.MULTILINE):
|
|
1989
|
+
name = m.group(1)
|
|
1990
|
+
params = [p.strip() for p in (m.group(2) or '').split(',') if p.strip()][:8]
|
|
1991
|
+
ret = (m.group(3) or '').strip()
|
|
1992
|
+
functions.append(FunctionInfo(
|
|
1993
|
+
name=name,
|
|
1994
|
+
params=params,
|
|
1995
|
+
return_type=ret,
|
|
1996
|
+
docstring=None,
|
|
1997
|
+
docstring_full=None,
|
|
1998
|
+
calls=[],
|
|
1999
|
+
raises=[],
|
|
2000
|
+
decorators=[],
|
|
2001
|
+
complexity=1,
|
|
2002
|
+
lines=1,
|
|
2003
|
+
is_async=False,
|
|
2004
|
+
is_static=False,
|
|
2005
|
+
is_classmethod=False,
|
|
2006
|
+
is_property=False,
|
|
2007
|
+
intent=self.intent_gen.generate(name),
|
|
2008
|
+
start_line=0,
|
|
2009
|
+
end_line=0,
|
|
2010
|
+
is_private=False,
|
|
2011
|
+
))
|
|
2012
|
+
exports.append(name)
|
|
2013
|
+
|
|
2014
|
+
for m in re.finditer(r'^const\s+([A-Z][A-Za-z0-9_]*)\b', content, re.MULTILINE):
|
|
2015
|
+
constants.append(m.group(1))
|
|
2016
|
+
|
|
2017
|
+
lines = content.split('\n')
|
|
2018
|
+
return ModuleInfo(
|
|
2019
|
+
path=filepath,
|
|
2020
|
+
language='go',
|
|
2021
|
+
imports=imports[:20],
|
|
2022
|
+
exports=list(dict.fromkeys(exports))[:50],
|
|
2023
|
+
classes=classes,
|
|
2024
|
+
functions=functions,
|
|
2025
|
+
types=types,
|
|
2026
|
+
constants=constants[:10],
|
|
2027
|
+
docstring=None,
|
|
2028
|
+
lines_total=len(lines),
|
|
2029
|
+
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('//')])
|
|
2030
|
+
)
|
|
2031
|
+
|
|
2032
|
+
def _parse_rust(self, filepath: str, content: str) -> ModuleInfo:
|
|
2033
|
+
"""Parse Rust using regex patterns."""
|
|
2034
|
+
imports: List[str] = []
|
|
2035
|
+
classes: List[ClassInfo] = []
|
|
2036
|
+
functions: List[FunctionInfo] = []
|
|
2037
|
+
types: List[TypeInfo] = []
|
|
2038
|
+
constants: List[str] = []
|
|
2039
|
+
exports: List[str] = []
|
|
2040
|
+
|
|
2041
|
+
for m in re.finditer(r'^use\s+([^;]+);', content, re.MULTILINE):
|
|
2042
|
+
imports.append(m.group(1).strip())
|
|
2043
|
+
|
|
2044
|
+
for m in re.finditer(r'^(?:pub\s+)?struct\s+(\w+)', content, re.MULTILINE):
|
|
2045
|
+
name = m.group(1)
|
|
2046
|
+
classes.append(ClassInfo(name=name))
|
|
2047
|
+
types.append(TypeInfo(name=name, kind='struct', definition=''))
|
|
2048
|
+
exports.append(name)
|
|
2049
|
+
|
|
2050
|
+
for m in re.finditer(r'^(?:pub\s+)?enum\s+(\w+)', content, re.MULTILINE):
|
|
2051
|
+
name = m.group(1)
|
|
2052
|
+
types.append(TypeInfo(name=name, kind='enum', definition=''))
|
|
2053
|
+
exports.append(name)
|
|
2054
|
+
|
|
2055
|
+
for m in re.finditer(r'^(?:pub\s+)?trait\s+(\w+)', content, re.MULTILINE):
|
|
2056
|
+
name = m.group(1)
|
|
2057
|
+
types.append(TypeInfo(name=name, kind='trait', definition=''))
|
|
2058
|
+
exports.append(name)
|
|
2059
|
+
|
|
2060
|
+
for m in re.finditer(r'^(?:pub\s+)?fn\s+(\w+)\s*\(([^)]*)\)\s*(?:->\s*([^\s{]+))?', content, re.MULTILINE):
|
|
2061
|
+
name = m.group(1)
|
|
2062
|
+
params = [p.strip() for p in (m.group(2) or '').split(',') if p.strip()][:8]
|
|
2063
|
+
ret = (m.group(3) or '').strip()
|
|
2064
|
+
functions.append(FunctionInfo(
|
|
2065
|
+
name=name,
|
|
2066
|
+
params=params,
|
|
2067
|
+
return_type=ret,
|
|
2068
|
+
docstring=None,
|
|
2069
|
+
docstring_full=None,
|
|
2070
|
+
calls=[],
|
|
2071
|
+
raises=[],
|
|
2072
|
+
decorators=[],
|
|
2073
|
+
complexity=1,
|
|
2074
|
+
lines=1,
|
|
2075
|
+
is_async='async' in m.group(0),
|
|
2076
|
+
is_static=False,
|
|
2077
|
+
is_classmethod=False,
|
|
2078
|
+
is_property=False,
|
|
2079
|
+
intent=self.intent_gen.generate(name),
|
|
2080
|
+
start_line=0,
|
|
2081
|
+
end_line=0,
|
|
2082
|
+
is_private=False,
|
|
2083
|
+
))
|
|
2084
|
+
exports.append(name)
|
|
2085
|
+
|
|
2086
|
+
for m in re.finditer(r'^(?:pub\s+)?const\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
|
|
2087
|
+
constants.append(m.group(1))
|
|
2088
|
+
|
|
2089
|
+
lines = content.split('\n')
|
|
2090
|
+
return ModuleInfo(
|
|
2091
|
+
path=filepath,
|
|
2092
|
+
language='rust',
|
|
2093
|
+
imports=imports[:20],
|
|
2094
|
+
exports=list(dict.fromkeys(exports))[:50],
|
|
2095
|
+
classes=classes,
|
|
2096
|
+
functions=functions,
|
|
2097
|
+
types=types,
|
|
2098
|
+
constants=constants[:10],
|
|
2099
|
+
docstring=None,
|
|
2100
|
+
lines_total=len(lines),
|
|
2101
|
+
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('//')])
|
|
2102
|
+
)
|
|
2103
|
+
|
|
2104
|
+
def _parse_java(self, filepath: str, content: str) -> ModuleInfo:
|
|
2105
|
+
"""Parse Java using regex patterns."""
|
|
2106
|
+
imports: List[str] = []
|
|
2107
|
+
classes: List[ClassInfo] = []
|
|
2108
|
+
functions: List[FunctionInfo] = []
|
|
2109
|
+
types: List[TypeInfo] = []
|
|
2110
|
+
constants: List[str] = []
|
|
2111
|
+
exports: List[str] = []
|
|
2112
|
+
|
|
2113
|
+
for m in re.finditer(r'^import\s+([^;]+);', content, re.MULTILINE):
|
|
2114
|
+
imports.append(m.group(1).strip())
|
|
2115
|
+
|
|
2116
|
+
for m in re.finditer(r'^(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
|
|
2117
|
+
name = m.group(1)
|
|
2118
|
+
classes.append(ClassInfo(name=name, is_abstract='abstract' in m.group(0)))
|
|
2119
|
+
exports.append(name)
|
|
2120
|
+
|
|
2121
|
+
for m in re.finditer(r'^(?:public\s+)?interface\s+(\w+)', content, re.MULTILINE):
|
|
2122
|
+
name = m.group(1)
|
|
2123
|
+
classes.append(ClassInfo(name=name, is_interface=True))
|
|
2124
|
+
exports.append(name)
|
|
2125
|
+
types.append(TypeInfo(name=name, kind='interface', definition=''))
|
|
2126
|
+
|
|
2127
|
+
for m in re.finditer(r'^(?:public\s+)?enum\s+(\w+)', content, re.MULTILINE):
|
|
2128
|
+
name = m.group(1)
|
|
2129
|
+
types.append(TypeInfo(name=name, kind='enum', definition=''))
|
|
2130
|
+
exports.append(name)
|
|
2131
|
+
|
|
2132
|
+
for m in re.finditer(r'^(?:public\s+)?record\s+(\w+)\s*\(', content, re.MULTILINE):
|
|
2133
|
+
name = m.group(1)
|
|
2134
|
+
classes.append(ClassInfo(name=name))
|
|
2135
|
+
types.append(TypeInfo(name=name, kind='record', definition=''))
|
|
2136
|
+
exports.append(name)
|
|
2137
|
+
|
|
2138
|
+
# Very rough method detection (only top-level class members are not tracked here)
|
|
2139
|
+
for m in re.finditer(r'^(?:public|protected|private)\s+(?:static\s+)?([\w<>\[\]]+)\s+(\w+)\s*\(([^)]*)\)\s*\{', content, re.MULTILINE):
|
|
2140
|
+
ret_type = m.group(1)
|
|
2141
|
+
name = m.group(2)
|
|
2142
|
+
params = [p.strip() for p in (m.group(3) or '').split(',') if p.strip()][:8]
|
|
2143
|
+
functions.append(FunctionInfo(
|
|
2144
|
+
name=name,
|
|
2145
|
+
params=params,
|
|
2146
|
+
return_type=ret_type,
|
|
2147
|
+
docstring=None,
|
|
2148
|
+
docstring_full=None,
|
|
2149
|
+
calls=[],
|
|
2150
|
+
raises=[],
|
|
2151
|
+
decorators=[],
|
|
2152
|
+
complexity=1,
|
|
2153
|
+
lines=1,
|
|
2154
|
+
is_async=False,
|
|
2155
|
+
is_static='static' in m.group(0),
|
|
2156
|
+
is_classmethod=False,
|
|
2157
|
+
is_property=False,
|
|
2158
|
+
intent=self.intent_gen.generate(name),
|
|
2159
|
+
start_line=0,
|
|
2160
|
+
end_line=0,
|
|
2161
|
+
is_private=False,
|
|
2162
|
+
))
|
|
2163
|
+
|
|
2164
|
+
for m in re.finditer(r'^(?:public\s+)?static\s+final\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
|
|
2165
|
+
constants.append(m.group(1))
|
|
2166
|
+
|
|
2167
|
+
lines = content.split('\n')
|
|
2168
|
+
return ModuleInfo(
|
|
2169
|
+
path=filepath,
|
|
2170
|
+
language='java',
|
|
2171
|
+
imports=imports[:20],
|
|
2172
|
+
exports=list(dict.fromkeys(exports))[:50],
|
|
2173
|
+
classes=classes,
|
|
2174
|
+
functions=functions,
|
|
2175
|
+
types=types,
|
|
2176
|
+
constants=constants[:10],
|
|
2177
|
+
docstring=None,
|
|
2178
|
+
lines_total=len(lines),
|
|
2179
|
+
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('//')])
|
|
2180
|
+
)
|
|
2181
|
+
|
|
2182
|
+
def _parse_csharp(self, filepath: str, content: str) -> ModuleInfo:
|
|
2183
|
+
"""Parse C# using regex patterns."""
|
|
2184
|
+
imports: List[str] = []
|
|
2185
|
+
classes: List[ClassInfo] = []
|
|
2186
|
+
functions: List[FunctionInfo] = []
|
|
2187
|
+
types: List[TypeInfo] = []
|
|
2188
|
+
constants: List[str] = []
|
|
2189
|
+
exports: List[str] = []
|
|
2190
|
+
|
|
2191
|
+
for m in re.finditer(r'^using\s+([^;]+);', content, re.MULTILINE):
|
|
2192
|
+
imports.append(m.group(1).strip())
|
|
2193
|
+
|
|
2194
|
+
for m in re.finditer(r'^(?:public\s+)?interface\s+(I\w+)', content, re.MULTILINE):
|
|
2195
|
+
name = m.group(1)
|
|
2196
|
+
classes.append(ClassInfo(name=name, is_interface=True))
|
|
2197
|
+
exports.append(name)
|
|
2198
|
+
types.append(TypeInfo(name=name, kind='interface', definition=''))
|
|
2199
|
+
|
|
2200
|
+
for m in re.finditer(r'^(?:public\s+)?(?:abstract\s+)?class\s+(\w+)', content, re.MULTILINE):
|
|
2201
|
+
name = m.group(1)
|
|
2202
|
+
classes.append(ClassInfo(name=name, is_abstract='abstract' in m.group(0)))
|
|
2203
|
+
exports.append(name)
|
|
2204
|
+
|
|
2205
|
+
for m in re.finditer(r'^(?:public\s+)?record\s+(\w+)', content, re.MULTILINE):
|
|
2206
|
+
name = m.group(1)
|
|
2207
|
+
classes.append(ClassInfo(name=name))
|
|
2208
|
+
exports.append(name)
|
|
2209
|
+
types.append(TypeInfo(name=name, kind='record', definition=''))
|
|
2210
|
+
|
|
2211
|
+
for m in re.finditer(r'^(?:public|private|protected|internal)\s+(?:static\s+)?([\w<>\[\]?]+)\s+(\w+)\s*\(([^)]*)\)\s*\{', content, re.MULTILINE):
|
|
2212
|
+
ret_type = m.group(1)
|
|
2213
|
+
name = m.group(2)
|
|
2214
|
+
params = [p.strip() for p in (m.group(3) or '').split(',') if p.strip()][:8]
|
|
2215
|
+
functions.append(FunctionInfo(
|
|
2216
|
+
name=name,
|
|
2217
|
+
params=params,
|
|
2218
|
+
return_type=ret_type,
|
|
2219
|
+
docstring=None,
|
|
2220
|
+
docstring_full=None,
|
|
2221
|
+
calls=[],
|
|
2222
|
+
raises=[],
|
|
2223
|
+
decorators=[],
|
|
2224
|
+
complexity=1,
|
|
2225
|
+
lines=1,
|
|
2226
|
+
is_async='async' in m.group(0),
|
|
2227
|
+
is_static='static' in m.group(0),
|
|
2228
|
+
is_classmethod=False,
|
|
2229
|
+
is_property=False,
|
|
2230
|
+
intent=self.intent_gen.generate(name),
|
|
2231
|
+
start_line=0,
|
|
2232
|
+
end_line=0,
|
|
2233
|
+
is_private=False,
|
|
2234
|
+
))
|
|
2235
|
+
|
|
2236
|
+
for m in re.finditer(r'^(?:public\s+)?const\s+[\w<>\[\]]+\s+([A-Z][A-Z0-9_]*)\b', content, re.MULTILINE):
|
|
2237
|
+
constants.append(m.group(1))
|
|
2238
|
+
|
|
2239
|
+
lines = content.split('\n')
|
|
2240
|
+
return ModuleInfo(
|
|
2241
|
+
path=filepath,
|
|
2242
|
+
language='csharp',
|
|
2243
|
+
imports=imports[:20],
|
|
2244
|
+
exports=list(dict.fromkeys(exports))[:50],
|
|
2245
|
+
classes=classes,
|
|
2246
|
+
functions=functions,
|
|
2247
|
+
types=types,
|
|
2248
|
+
constants=constants[:10],
|
|
2249
|
+
docstring=None,
|
|
2250
|
+
lines_total=len(lines),
|
|
2251
|
+
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('//')])
|
|
2252
|
+
)
|
|
2253
|
+
|
|
2254
|
+
def _parse_sql(self, filepath: str, content: str) -> ModuleInfo:
|
|
2255
|
+
"""Parse SQL using regex patterns."""
|
|
2256
|
+
imports: List[str] = []
|
|
2257
|
+
classes: List[ClassInfo] = []
|
|
2258
|
+
functions: List[FunctionInfo] = []
|
|
2259
|
+
types: List[TypeInfo] = []
|
|
2260
|
+
constants: List[str] = []
|
|
2261
|
+
exports: List[str] = []
|
|
2262
|
+
|
|
2263
|
+
for m in re.finditer(r'CREATE\s+TABLE\s+(\w+)', content, re.IGNORECASE):
|
|
2264
|
+
name = m.group(1)
|
|
2265
|
+
classes.append(ClassInfo(name=name))
|
|
2266
|
+
exports.append(name)
|
|
2267
|
+
types.append(TypeInfo(name=name, kind='table', definition=''))
|
|
2268
|
+
|
|
2269
|
+
for m in re.finditer(r'CREATE\s+(?:OR\s+REPLACE\s+)?VIEW\s+(\w+)', content, re.IGNORECASE):
|
|
2270
|
+
name = m.group(1)
|
|
2271
|
+
classes.append(ClassInfo(name=name))
|
|
2272
|
+
exports.append(name)
|
|
2273
|
+
types.append(TypeInfo(name=name, kind='view', definition=''))
|
|
2274
|
+
|
|
2275
|
+
for m in re.finditer(r'CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(\w+)', content, re.IGNORECASE):
|
|
2276
|
+
name = m.group(1)
|
|
2277
|
+
functions.append(FunctionInfo(
|
|
2278
|
+
name=name,
|
|
2279
|
+
params=[],
|
|
2280
|
+
return_type='',
|
|
2281
|
+
docstring=None,
|
|
2282
|
+
docstring_full=None,
|
|
2283
|
+
calls=[],
|
|
2284
|
+
raises=[],
|
|
2285
|
+
decorators=[],
|
|
2286
|
+
complexity=1,
|
|
2287
|
+
lines=1,
|
|
2288
|
+
is_async=False,
|
|
2289
|
+
is_static=False,
|
|
2290
|
+
is_classmethod=False,
|
|
2291
|
+
is_property=False,
|
|
2292
|
+
intent=self.intent_gen.generate(name),
|
|
2293
|
+
start_line=0,
|
|
2294
|
+
end_line=0,
|
|
2295
|
+
is_private=False,
|
|
2296
|
+
))
|
|
2297
|
+
exports.append(name)
|
|
2298
|
+
|
|
2299
|
+
lines = content.split('\n')
|
|
2300
|
+
return ModuleInfo(
|
|
2301
|
+
path=filepath,
|
|
2302
|
+
language='sql',
|
|
2303
|
+
imports=imports,
|
|
2304
|
+
exports=list(dict.fromkeys(exports))[:50],
|
|
2305
|
+
classes=classes,
|
|
2306
|
+
functions=functions,
|
|
2307
|
+
types=types,
|
|
2308
|
+
constants=constants,
|
|
2309
|
+
docstring=None,
|
|
2310
|
+
lines_total=len(lines),
|
|
2311
|
+
lines_code=len([l for l in lines if l.strip() and not l.strip().startswith('--')])
|
|
2312
|
+
)
|
|
2313
|
+
|
|
1951
2314
|
|
|
1952
2315
|
def is_tree_sitter_available() -> bool:
|
|
1953
2316
|
"""Check if Tree-sitter is available."""
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "code2logic"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.27"
|
|
8
8
|
description = "Convert source code to logical representation for LLM analysis"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -183,7 +183,7 @@ module = [
|
|
|
183
183
|
ignore_missing_imports = true
|
|
184
184
|
|
|
185
185
|
[tool.bumpver]
|
|
186
|
-
current_version = "1.0.
|
|
186
|
+
current_version = "1.0.27"
|
|
187
187
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
188
188
|
commit = false
|
|
189
189
|
tag = false
|
|
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
|