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.
Files changed (58) hide show
  1. {code2logic-1.0.26 → code2logic-1.0.27}/PKG-INFO +1 -1
  2. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/__init__.py +1 -1
  3. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/analyzer.py +1 -0
  4. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/common.py +21 -11
  5. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/runner.py +192 -37
  6. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/logicml.py +10 -6
  7. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/parsers.py +364 -1
  8. {code2logic-1.0.26 → code2logic-1.0.27}/pyproject.toml +2 -2
  9. {code2logic-1.0.26 → code2logic-1.0.27}/LICENSE +0 -0
  10. {code2logic-1.0.26 → code2logic-1.0.27}/README.md +0 -0
  11. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/__main__.py +0 -0
  12. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/adaptive.py +0 -0
  13. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/base.py +0 -0
  14. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmark.py +0 -0
  15. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/__init__.py +0 -0
  16. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/benchmarks/results.py +0 -0
  17. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/chunked_reproduction.py +0 -0
  18. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/cli.py +0 -0
  19. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/code_review.py +0 -0
  20. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/config.py +0 -0
  21. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/core/__init__.py +0 -0
  22. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/dependency.py +0 -0
  23. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/errors.py +0 -0
  24. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/file_formats.py +0 -0
  25. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/formats/__init__.py +0 -0
  26. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/function_logic.py +0 -0
  27. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/generators.py +0 -0
  28. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/gherkin.py +0 -0
  29. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/integrations/__init__.py +0 -0
  30. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/intent.py +0 -0
  31. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm/__init__.py +0 -0
  32. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm.py +0 -0
  33. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_clients.py +0 -0
  34. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_clients_new.py +0 -0
  35. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/llm_profiler.py +0 -0
  36. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/markdown_format.py +0 -0
  37. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/mcp_server.py +0 -0
  38. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/metrics.py +0 -0
  39. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/models.py +0 -0
  40. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/project_reproducer.py +0 -0
  41. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/prompts.py +0 -0
  42. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/py.typed +0 -0
  43. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/quality.py +0 -0
  44. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/refactor.py +0 -0
  45. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/reproducer.py +0 -0
  46. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/reproduction.py +0 -0
  47. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/__init__.py +0 -0
  48. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/json_schema.py +0 -0
  49. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/logicml_schema.py +0 -0
  50. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/markdown_schema.py +0 -0
  51. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/schemas/yaml_schema.py +0 -0
  52. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/shared_utils.py +0 -0
  53. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/similarity.py +0 -0
  54. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/terminal.py +0 -0
  55. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/tools/__init__.py +0 -0
  56. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/toon_format.py +0 -0
  57. {code2logic-1.0.26 → code2logic-1.0.27}/code2logic/universal.py +0 -0
  58. {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.26
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
@@ -18,7 +18,7 @@ Example:
18
18
  >>> print(output)
19
19
  """
20
20
 
21
- __version__ = "1.0.26"
21
+ __version__ = "1.0.27"
22
22
  __author__ = "Softreck"
23
23
  __email__ = "info@softreck.dev"
24
24
  __license__ = "MIT"
@@ -52,6 +52,7 @@ class ProjectAnalyzer:
52
52
  '.java': 'java',
53
53
  '.go': 'go',
54
54
  '.rs': 'rust',
55
+ '.cs': 'csharp',
55
56
  '.c': 'cpp',
56
57
  '.cpp': 'cpp',
57
58
  '.cc': 'cpp',
@@ -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 Python code. NO extra error classes, NO over-engineering. Keep code short and direct.",
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 Python code:
185
- - 'sig: (params) -> Type' = def func(params) -> Type
186
- - 'sig: async (params)' = async def func(params)
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
- prompt = f"""Generate Python code from this {fmt.upper()} specification.
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 Python code for {file_name}
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
- ```python
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 Python code without an LLM (fallback mode)."""
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
- code = """from __future__ import annotations
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
- for cls in classes:
157
- code += f"""@dataclass
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
- for fn in functions:
165
- code += f"""def {fn}(*args: Any, **kwargs: Any) -> Any:
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
- py_files = list(path.glob('*.py'))
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(py_files)
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 py_file in py_files:
232
- original = py_file.read_text()
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, py_file)
387
+ single_project = create_single_project(module_info, abs_file)
245
388
 
246
389
  file_result = FileResult(
247
- file_path=str(py_file),
248
- language='python',
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, py_file.name, client, verbose
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
- result.syntax_ok = _test_python_syntax(generated)
317
- if result.syntax_ok:
318
- result.runs_ok = _test_python_runs(generated)
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
- analysis = self._metrics.analyze(original, generated, spec, format_name=fmt)
323
- result.score = analysis.overall_score
324
- result.similarity = analysis.overall_score
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
- for imp in module.imports[:20]:
133
- # Extract export name from import
134
- if '.' in imp:
135
- export_name = imp.split('.')[-1]
136
- else:
137
- export_name = imp
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
- elif language in ('javascript', 'typescript'):
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.26"
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.26"
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