code2logic 1.0.26__tar.gz → 1.0.28__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.28}/PKG-INFO +1 -1
  2. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/__init__.py +1 -1
  3. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/analyzer.py +1 -0
  4. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/benchmarks/common.py +21 -11
  5. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/benchmarks/runner.py +194 -38
  6. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/logicml.py +10 -6
  7. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/parsers.py +375 -11
  8. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/universal.py +162 -1
  9. {code2logic-1.0.26 → code2logic-1.0.28}/pyproject.toml +2 -2
  10. {code2logic-1.0.26 → code2logic-1.0.28}/LICENSE +0 -0
  11. {code2logic-1.0.26 → code2logic-1.0.28}/README.md +0 -0
  12. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/__main__.py +0 -0
  13. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/adaptive.py +0 -0
  14. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/base.py +0 -0
  15. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/benchmark.py +0 -0
  16. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/benchmarks/__init__.py +0 -0
  17. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/benchmarks/results.py +0 -0
  18. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/chunked_reproduction.py +0 -0
  19. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/cli.py +0 -0
  20. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/code_review.py +0 -0
  21. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/config.py +0 -0
  22. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/core/__init__.py +0 -0
  23. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/dependency.py +0 -0
  24. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/errors.py +0 -0
  25. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/file_formats.py +0 -0
  26. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/formats/__init__.py +0 -0
  27. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/function_logic.py +0 -0
  28. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/generators.py +0 -0
  29. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/gherkin.py +0 -0
  30. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/integrations/__init__.py +0 -0
  31. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/intent.py +0 -0
  32. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/llm/__init__.py +0 -0
  33. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/llm.py +0 -0
  34. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/llm_clients.py +0 -0
  35. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/llm_clients_new.py +0 -0
  36. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/llm_profiler.py +0 -0
  37. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/markdown_format.py +0 -0
  38. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/mcp_server.py +0 -0
  39. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/metrics.py +0 -0
  40. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/models.py +0 -0
  41. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/project_reproducer.py +0 -0
  42. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/prompts.py +0 -0
  43. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/py.typed +0 -0
  44. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/quality.py +0 -0
  45. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/refactor.py +0 -0
  46. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/reproducer.py +0 -0
  47. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/reproduction.py +0 -0
  48. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/schemas/__init__.py +0 -0
  49. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/schemas/json_schema.py +0 -0
  50. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/schemas/logicml_schema.py +0 -0
  51. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/schemas/markdown_schema.py +0 -0
  52. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/schemas/yaml_schema.py +0 -0
  53. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/shared_utils.py +0 -0
  54. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/similarity.py +0 -0
  55. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/terminal.py +0 -0
  56. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/tools/__init__.py +0 -0
  57. {code2logic-1.0.26 → code2logic-1.0.28}/code2logic/toon_format.py +0 -0
  58. {code2logic-1.0.26 → code2logic-1.0.28}/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.28
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.28"
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,8 @@ Usage:
17
17
 
18
18
  import sys
19
19
  import time
20
+ import difflib
21
+ import re
20
22
  from pathlib import Path
21
23
  from typing import List, Optional
22
24
 
@@ -56,6 +58,98 @@ def _test_python_runs(code: str, timeout: int = 5) -> bool:
56
58
  return True # Timeout might mean waiting for input
57
59
 
58
60
 
61
+ def _basic_syntax_ok(code: str, language: str) -> bool:
62
+ """Heuristic syntax check for non-Python languages."""
63
+ s = (code or "").strip()
64
+ if not s:
65
+ return False
66
+ if language in ("javascript", "typescript"):
67
+ # Basic bracket/brace balance to catch the most common truncations
68
+ pairs = {')': '(', ']': '[', '}': '{'}
69
+ stack: List[str] = []
70
+ for ch in s:
71
+ if ch in "([{":
72
+ stack.append(ch)
73
+ elif ch in ")]}":
74
+ if not stack or stack[-1] != pairs.get(ch):
75
+ return False
76
+ stack.pop()
77
+ return not stack
78
+ if language == "go":
79
+ return "package" in s and "func" in s
80
+ if language == "rust":
81
+ return "fn" in s or "struct" in s or "enum" in s
82
+ if language == "java":
83
+ return "class" in s or "interface" in s
84
+ if language == "csharp":
85
+ return "class" in s or "interface" in s or "record" in s
86
+ if language == "sql":
87
+ upper = s.upper()
88
+ return "CREATE" in upper or "SELECT" in upper or "INSERT" in upper
89
+ return len(s) > 10
90
+
91
+
92
+ def _count_structural_elements(code: str, language: str) -> dict:
93
+ s = code or ""
94
+ if language == "python":
95
+ return {
96
+ 'classes': len(re.findall(r'^class\s+\w+', s, re.MULTILINE)),
97
+ 'functions': len(re.findall(r'^def\s+\w+', s, re.MULTILINE)),
98
+ 'imports': len(re.findall(r'^(?:from|import)\s+', s, re.MULTILINE)),
99
+ }
100
+ if language in ("javascript", "typescript"):
101
+ return {
102
+ 'classes': len(re.findall(r'\bclass\s+\w+', s)),
103
+ 'functions': len(re.findall(r'\bfunction\s+\w+\s*\(', s)) + len(re.findall(r'\bconst\s+\w+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>', s)),
104
+ '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)),
105
+ 'imports': len(re.findall(r'^import\s+', s, re.MULTILINE)) + len(re.findall(r'\brequire\(', s)),
106
+ }
107
+ if language == "go":
108
+ return {
109
+ 'types': len(re.findall(r'^type\s+\w+\s+(?:struct|interface)\b', s, re.MULTILINE)),
110
+ 'functions': len(re.findall(r'^func\s+(?:\([^)]*\)\s*)?\w+\s*\(', s, re.MULTILINE)),
111
+ 'imports': len(re.findall(r'^import\b', s, re.MULTILINE)),
112
+ }
113
+ if language == "rust":
114
+ return {
115
+ '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)),
116
+ 'functions': len(re.findall(r'\bfn\s+\w+\s*\(', s)),
117
+ 'imports': len(re.findall(r'^use\s+', s, re.MULTILINE)),
118
+ }
119
+ if language == "java":
120
+ return {
121
+ '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)),
122
+ 'functions': len(re.findall(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{', s)),
123
+ 'imports': len(re.findall(r'^import\s+', s, re.MULTILINE)),
124
+ }
125
+ if language == "csharp":
126
+ return {
127
+ '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)),
128
+ 'functions': len(re.findall(r'\b\w+\s+\w+\s*\([^)]*\)\s*\{', s)),
129
+ 'imports': len(re.findall(r'^using\s+', s, re.MULTILINE)),
130
+ }
131
+ if language == "sql":
132
+ upper = s.upper()
133
+ return {
134
+ '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)),
135
+ 'functions': len(re.findall(r'\bCREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+\w+', upper)),
136
+ }
137
+ return {}
138
+
139
+
140
+ def _structural_score(original: str, generated: str, language: str) -> float:
141
+ o = _count_structural_elements(original, language)
142
+ g = _count_structural_elements(generated, language)
143
+ if not o:
144
+ return 0.0
145
+ keys = list(o.keys())
146
+ matches = 0
147
+ for k in keys:
148
+ if o.get(k, 0) == g.get(k, 0):
149
+ matches += 1
150
+ return matches / max(len(keys), 1) * 100
151
+
152
+
59
153
  def _extract_code(response: str) -> str:
60
154
  """Extract code from LLM response."""
61
155
  if not response:
@@ -111,8 +205,8 @@ class BenchmarkRunner:
111
205
  self.client = get_client()
112
206
  return self.client
113
207
 
114
- def _template_generate_code(self, spec: str, fmt: str, file_name: str) -> str:
115
- """Generate minimal Python code without an LLM (fallback mode)."""
208
+ def _template_generate_code(self, spec: str, fmt: str, file_name: str, language: str = "python") -> str:
209
+ """Generate minimal code without an LLM (fallback mode)."""
116
210
  import re
117
211
 
118
212
  # Try to infer class/function names from spec
@@ -139,11 +233,35 @@ class BenchmarkRunner:
139
233
  classes = [c for c in uniq(classes) if c.isidentifier()][:5]
140
234
  functions = [f for f in uniq(functions) if f.isidentifier() and f not in classes][:10]
141
235
 
142
- code = """from __future__ import annotations
236
+ language_norm = (language or "python").strip().lower()
237
+ if language_norm == "python":
238
+ code = """from __future__ import annotations
143
239
 
144
240
  from dataclasses import dataclass
145
241
  from typing import Any, Optional, List, Dict
146
242
 
243
+ """
244
+ elif language_norm in ("javascript", "typescript"):
245
+ code = """// Auto-generated placeholder
246
+ """
247
+ elif language_norm == "go":
248
+ code = """package main
249
+
250
+ """
251
+ elif language_norm == "rust":
252
+ code = """// Auto-generated placeholder
253
+ """
254
+ elif language_norm == "java":
255
+ code = """// Auto-generated placeholder
256
+ """
257
+ elif language_norm == "csharp":
258
+ code = """// Auto-generated placeholder
259
+ """
260
+ elif language_norm == "sql":
261
+ code = """-- Auto-generated placeholder
262
+ """
263
+ else:
264
+ code = """// Auto-generated placeholder
147
265
  """
148
266
 
149
267
  if not classes and not functions:
@@ -153,20 +271,56 @@ from typing import Any, Optional, List, Dict
153
271
  classes = ["GeneratedClass"]
154
272
  functions = ["generated_function"]
155
273
 
156
- for cls in classes:
157
- code += f"""@dataclass
274
+ if language_norm == "python":
275
+ for cls in classes:
276
+ code += f"""@dataclass
158
277
  class {cls}:
159
278
  \"\"\"Generated placeholder for {file_name} ({fmt}).\"\"\"
160
279
  value: Any = None
161
280
 
162
281
  """
163
282
 
164
- for fn in functions:
165
- code += f"""def {fn}(*args: Any, **kwargs: Any) -> Any:
283
+ for fn in functions:
284
+ code += f"""def {fn}(*args: Any, **kwargs: Any) -> Any:
166
285
  \"\"\"Generated placeholder for {file_name} ({fmt}).\"\"\"
167
286
  return None
168
287
 
169
288
  """
289
+ elif language_norm in ("javascript", "typescript"):
290
+ for fn in functions:
291
+ export_kw = "export " if language_norm == "typescript" else ""
292
+ code += f"{export_kw}function {fn}(...args) {{\n return null;\n}}\n\n"
293
+ for cls in classes:
294
+ export_kw = "export " if language_norm == "typescript" else ""
295
+ code += f"{export_kw}class {cls} {{\n constructor() {{}}\n}}\n\n"
296
+ elif language_norm == "go":
297
+ for fn in functions:
298
+ code += f"func {fn}() {{\n}}\n\n"
299
+ for cls in classes:
300
+ code += f"type {cls} struct {{\n}}\n\n"
301
+ elif language_norm == "rust":
302
+ for fn in functions:
303
+ code += f"pub fn {fn}() {{\n}}\n\n"
304
+ for cls in classes:
305
+ code += f"pub struct {cls} {{\n}}\n\n"
306
+ elif language_norm == "java":
307
+ safe_class = classes[0] if classes else "Generated"
308
+ code += f"public class {safe_class} {{\n"
309
+ for fn in functions:
310
+ code += f" public static void {fn}() {{ }}\n"
311
+ code += "}\n"
312
+ elif language_norm == "csharp":
313
+ safe_class = classes[0] if classes else "Generated"
314
+ code += f"public class {safe_class} {{\n"
315
+ for fn in functions:
316
+ code += f" public static void {fn}() {{ }}\n"
317
+ code += "}\n"
318
+ elif language_norm == "sql":
319
+ for cls in classes:
320
+ code += f"CREATE TABLE {cls} (id INT);\n"
321
+ else:
322
+ for fn in functions:
323
+ code += f"function {fn}() {{}}\n"
170
324
 
171
325
  return code
172
326
 
@@ -213,45 +367,35 @@ class {cls}:
213
367
 
214
368
  # Analyze project
215
369
  path = Path(folder)
216
- py_files = list(path.glob('*.py'))
217
- if limit:
218
- py_files = py_files[:limit]
370
+ project = analyze_project(str(path), use_treesitter=False)
371
+ modules = project.modules[:limit] if limit else project.modules
219
372
 
220
- result.total_files = len(py_files)
373
+ result.total_files = len(modules)
221
374
 
222
375
  if verbose:
223
376
  render.heading(2, "Format Benchmark")
224
- render.codeblock("yaml", f"folder: {folder}\nfiles: {len(py_files)}\nformats: [{', '.join(formats)}]")
225
-
226
- project = analyze_project(str(path), use_treesitter=False)
377
+ render.codeblock("yaml", f"folder: {folder}\nfiles: {len(modules)}\nformats: [{', '.join(formats)}]")
227
378
 
228
379
  start_time = time.time()
229
380
 
230
381
  # 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:
382
+ for module_info in modules:
383
+ abs_file = path / module_info.path
384
+ if not abs_file.exists():
242
385
  continue
386
+ original = abs_file.read_text(encoding='utf-8', errors='ignore')
243
387
 
244
- single_project = create_single_project(module_info, py_file)
388
+ single_project = create_single_project(module_info, abs_file)
245
389
 
246
390
  file_result = FileResult(
247
- file_path=str(py_file),
248
- language='python',
391
+ file_path=str(abs_file),
392
+ language=module_info.language,
249
393
  original_size=len(original),
250
394
  )
251
395
 
252
396
  for fmt in formats:
253
397
  fmt_result = self._test_format(
254
- single_project, original, fmt, py_file.name, client, verbose
398
+ single_project, original, fmt, abs_file.name, client, verbose, language=module_info.language
255
399
  )
256
400
  file_result.format_results[fmt] = fmt_result
257
401
 
@@ -288,6 +432,7 @@ class {cls}:
288
432
  file_name: str,
289
433
  client: Optional[BaseLLMClient],
290
434
  verbose: bool = False,
435
+ language: str = "python",
291
436
  ) -> FormatResult:
292
437
  """Test a single format."""
293
438
  result = FormatResult(format_name=fmt)
@@ -299,12 +444,12 @@ class {cls}:
299
444
  result.spec_tokens = estimate_tokens(spec)
300
445
 
301
446
  # Generate prompt
302
- prompt = get_token_reproduction_prompt(spec, fmt, file_name)
447
+ prompt = get_token_reproduction_prompt(spec, fmt, file_name, language=language)
303
448
 
304
449
  # Reproduce
305
450
  start = time.time()
306
451
  if client is None:
307
- generated = self._template_generate_code(spec, fmt, file_name)
452
+ generated = self._template_generate_code(spec, fmt, file_name, language=language)
308
453
  result.gen_time = 0.0
309
454
  else:
310
455
  response = client.generate(prompt, max_tokens=self.config.max_tokens)
@@ -313,15 +458,26 @@ class {cls}:
313
458
  result.generated_size = len(generated)
314
459
 
315
460
  # Test quality
316
- result.syntax_ok = _test_python_syntax(generated)
317
- if result.syntax_ok:
318
- result.runs_ok = _test_python_runs(generated)
461
+ language_norm = (language or "python").strip().lower()
462
+ if language_norm == 'python':
463
+ result.syntax_ok = _test_python_syntax(generated)
464
+ if result.syntax_ok:
465
+ result.runs_ok = _test_python_runs(generated)
466
+ else:
467
+ result.syntax_ok = _basic_syntax_ok(generated, language_norm)
468
+ result.runs_ok = False
319
469
 
320
470
  # Calculate metrics
321
471
  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
472
+ if language_norm == 'python':
473
+ analysis = self._metrics.analyze(original, generated, spec, format_name=fmt)
474
+ result.score = analysis.overall_score
475
+ result.similarity = analysis.text.char_similarity
476
+ else:
477
+ sim = difflib.SequenceMatcher(None, ' '.join(original.split()), ' '.join(generated.split())).ratio() * 100
478
+ struct = _structural_score(original, generated, language_norm)
479
+ result.similarity = sim
480
+ result.score = (sim * 0.7) + (struct * 0.3)
325
481
 
326
482
  # Efficiency
327
483
  if result.spec_size and len(original) > 0:
@@ -753,7 +909,7 @@ Requirements:
753
909
 
754
910
  # Test format
755
911
  fmt_result = self._test_format(
756
- single_project, original, fmt, abs_path.name, client, verbose=False
912
+ single_project, original, fmt, abs_path.name, client, verbose=False, language=module_info.language
757
913
  )
758
914
 
759
915
  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