IncludeCPP 3.3.11__py3-none-any.whl → 3.4.2__py3-none-any.whl
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.
- includecpp/__init__.py +4 -3
- includecpp/cli/commands.py +665 -62
- includecpp/core/ai_integration.py +69 -34
- includecpp/core/cppy_converter.py +570 -31
- includecpp/core/cssl/__init__.py +40 -0
- includecpp/core/cssl/cssl_builtins.py +1693 -0
- includecpp/core/cssl/cssl_events.py +621 -0
- includecpp/core/cssl/cssl_modules.py +2803 -0
- includecpp/core/cssl/cssl_parser.py +1493 -0
- includecpp/core/cssl/cssl_runtime.py +1549 -0
- includecpp/core/cssl/cssl_syntax.py +488 -0
- includecpp/core/cssl/cssl_types.py +390 -0
- includecpp/core/cssl_bridge.py +132 -0
- includecpp/core/error_formatter.py +50 -19
- includecpp/core/project_ui.py +3370 -0
- includecpp/core/settings_ui.py +127 -48
- includecpp/generator/parser.cpp +81 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/METADATA +160 -18
- includecpp-3.4.2.dist-info/RECORD +40 -0
- includecpp-3.3.11.dist-info/RECORD +0 -30
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/WHEEL +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/entry_points.txt +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.4.2.dist-info}/top_level.txt +0 -0
includecpp/cli/commands.py
CHANGED
|
@@ -10,6 +10,22 @@ import urllib.error
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from .config_parser import CppProjectConfig
|
|
12
12
|
|
|
13
|
+
|
|
14
|
+
def _is_experimental_enabled() -> bool:
|
|
15
|
+
"""Check if experimental features (cppy, ai) are enabled."""
|
|
16
|
+
config_path = Path.home() / '.includecpp' / '.secret'
|
|
17
|
+
if config_path.exists():
|
|
18
|
+
try:
|
|
19
|
+
config = json.loads(config_path.read_text())
|
|
20
|
+
return config.get('experimental_features', False)
|
|
21
|
+
except:
|
|
22
|
+
pass
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Check once at module load time
|
|
27
|
+
_EXPERIMENTAL_ENABLED = _is_experimental_enabled()
|
|
28
|
+
|
|
13
29
|
# Unicode fallback for Windows terminals with limited encoding
|
|
14
30
|
def _supports_unicode():
|
|
15
31
|
"""Check if terminal supports Unicode output."""
|
|
@@ -49,7 +65,7 @@ def _safe_echo(text, **kwargs):
|
|
|
49
65
|
('╔', '+'), ('╗', '+'), ('╚', '+'), ('╝', '+'),
|
|
50
66
|
('┌', '+'), ('┐', '+'), ('└', '+'), ('┘', '+'),
|
|
51
67
|
('═', '-'), ('─', '-'), ('║', '|'), ('│', '|'),
|
|
52
|
-
('✗', '[X]'), ('•', '*'),
|
|
68
|
+
('✗', '[X]'), ('✓', '[OK]'), ('❌', '[X]'), ('•', '*'),
|
|
53
69
|
('→', '->'), ('▶', '>'), ('◆', '*'),
|
|
54
70
|
('\u2011', '-'), ('\u2010', '-'),
|
|
55
71
|
('\u2013', '-'), ('\u2014', '--'),
|
|
@@ -133,13 +149,13 @@ def _render_readme_with_colors(readme_text):
|
|
|
133
149
|
|
|
134
150
|
def _show_changelog():
|
|
135
151
|
"""Extract and display changelog from README."""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
152
|
+
_safe_echo("=" * 70)
|
|
153
|
+
_safe_echo("IncludeCPP Changelog", fg='cyan', bold=True)
|
|
154
|
+
_safe_echo("=" * 70)
|
|
155
|
+
_safe_echo("")
|
|
140
156
|
|
|
141
157
|
try:
|
|
142
|
-
|
|
158
|
+
_safe_echo(" Fetching changelog from PyPI...", nl=False)
|
|
143
159
|
req = urllib.request.Request(
|
|
144
160
|
"https://pypi.org/pypi/IncludeCPP/json",
|
|
145
161
|
headers={"User-Agent": "IncludeCPP-CLI"}
|
|
@@ -150,12 +166,12 @@ def _show_changelog():
|
|
|
150
166
|
data = json.loads(raw_data)
|
|
151
167
|
description = data.get('info', {}).get('description', '')
|
|
152
168
|
version = data.get('info', {}).get('version', 'unknown')
|
|
153
|
-
|
|
154
|
-
|
|
169
|
+
_safe_echo(" OK", fg='green')
|
|
170
|
+
_safe_echo("")
|
|
155
171
|
|
|
156
172
|
if description:
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
_safe_echo(f"Current Version: {version}", fg='green', bold=True)
|
|
174
|
+
_safe_echo("")
|
|
159
175
|
|
|
160
176
|
lines = description.split('\n')
|
|
161
177
|
in_changelog = False
|
|
@@ -174,23 +190,23 @@ def _show_changelog():
|
|
|
174
190
|
for line in changelog_lines:
|
|
175
191
|
stripped = line.strip()
|
|
176
192
|
if stripped.startswith('## '):
|
|
177
|
-
|
|
193
|
+
_safe_echo(stripped[3:], fg='yellow', bold=True)
|
|
178
194
|
elif stripped.startswith('- '):
|
|
179
|
-
|
|
195
|
+
_safe_echo(" " + stripped)
|
|
180
196
|
elif stripped:
|
|
181
|
-
|
|
197
|
+
_safe_echo(" " + stripped)
|
|
182
198
|
else:
|
|
183
|
-
|
|
199
|
+
_safe_echo("No changelog found in README.", fg='yellow')
|
|
184
200
|
else:
|
|
185
|
-
|
|
201
|
+
_safe_echo("No documentation available.", fg='yellow')
|
|
186
202
|
|
|
187
203
|
except Exception as e:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
_safe_echo(" FAILED", fg='red')
|
|
205
|
+
_safe_echo("")
|
|
206
|
+
_safe_echo("Could not fetch changelog: " + str(e), fg='red', err=True)
|
|
191
207
|
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
_safe_echo("")
|
|
209
|
+
_safe_echo("=" * 70)
|
|
194
210
|
|
|
195
211
|
|
|
196
212
|
@click.group(invoke_without_command=True)
|
|
@@ -4420,8 +4436,10 @@ def fix(module_name, all_modules, exclude, undo, auto_fix, verbose, use_ai):
|
|
|
4420
4436
|
if var_decl:
|
|
4421
4437
|
var_name = var_decl.group(1)
|
|
4422
4438
|
# Check if variable appears again in rest of content
|
|
4439
|
+
# v3.4.1: Use word boundaries to avoid false positives from substring matches
|
|
4423
4440
|
rest_content = '\n'.join(lines[i:])
|
|
4424
|
-
|
|
4441
|
+
uses = len(re.findall(rf'\b{re.escape(var_name)}\b', rest_content))
|
|
4442
|
+
if uses <= 1:
|
|
4425
4443
|
add_issue('info', 'Mistake', file_path, i, 'C003',
|
|
4426
4444
|
f"Variable '{var_name}' may be unused",
|
|
4427
4445
|
"Remove unused variable or mark as [[maybe_unused]]")
|
|
@@ -4816,7 +4834,8 @@ def fix(module_name, all_modules, exclude, undo, auto_fix, verbose, use_ai):
|
|
|
4816
4834
|
click.echo(f"{'='*60}")
|
|
4817
4835
|
|
|
4818
4836
|
|
|
4819
|
-
|
|
4837
|
+
# AI group - conditionally registered based on experimental_features setting
|
|
4838
|
+
@click.group(invoke_without_command=True)
|
|
4820
4839
|
@click.option('--info', is_flag=True, help='Show AI status and usage')
|
|
4821
4840
|
@click.pass_context
|
|
4822
4841
|
def ai(ctx, info):
|
|
@@ -5306,7 +5325,7 @@ def ai_edit(task, module_name, files, all_modules, exclude, think_mode, think_tw
|
|
|
5306
5325
|
question = None # Continuation doesn't ask more questions
|
|
5307
5326
|
|
|
5308
5327
|
if not changes:
|
|
5309
|
-
verbose.status("No changes needed",
|
|
5328
|
+
verbose.status("No changes needed", phase='complete')
|
|
5310
5329
|
verbose.end(success=True, message="No changes required")
|
|
5311
5330
|
return
|
|
5312
5331
|
|
|
@@ -5520,7 +5539,7 @@ def ai_generate(task, files, think_mode, think_twice, think_three, use_websearch
|
|
|
5520
5539
|
verbose.api_call(endpoint='chat/completions', tokens_out=len(str(changes)) // 4 if changes else len(response) // 4)
|
|
5521
5540
|
|
|
5522
5541
|
if not changes:
|
|
5523
|
-
verbose.status("No file changes needed",
|
|
5542
|
+
verbose.status("No file changes needed", phase='complete')
|
|
5524
5543
|
# Show response if any
|
|
5525
5544
|
if response:
|
|
5526
5545
|
_safe_echo(f"\n{response[:2000]}")
|
|
@@ -5598,7 +5617,7 @@ def ai_generate(task, files, think_mode, think_twice, think_three, use_websearch
|
|
|
5598
5617
|
shell=True, capture_output=True, text=True, cwd=project_root
|
|
5599
5618
|
)
|
|
5600
5619
|
if result.returncode == 0:
|
|
5601
|
-
verbose.status("Plugin generated",
|
|
5620
|
+
verbose.status("Plugin generated", phase='complete')
|
|
5602
5621
|
else:
|
|
5603
5622
|
verbose.error(f"Plugin error: {result.stderr[:100]}")
|
|
5604
5623
|
verbose.end(success=False, message="Plugin generation failed")
|
|
@@ -5611,7 +5630,7 @@ def ai_generate(task, files, think_mode, think_twice, think_three, use_websearch
|
|
|
5611
5630
|
shell=True, capture_output=True, text=True, cwd=project_root
|
|
5612
5631
|
)
|
|
5613
5632
|
if result.returncode == 0:
|
|
5614
|
-
verbose.status("Build successful",
|
|
5633
|
+
verbose.status("Build successful", phase='complete')
|
|
5615
5634
|
else:
|
|
5616
5635
|
verbose.error(f"Build error: {result.stderr[:100]}")
|
|
5617
5636
|
|
|
@@ -5719,7 +5738,7 @@ def ai_optimize(module_name, files, agent_task, auto_confirm, no_verbose):
|
|
|
5719
5738
|
|
|
5720
5739
|
verbose.phase('analyzing', 'Analyzing code for optimization opportunities')
|
|
5721
5740
|
for fp in files_to_optimize.keys():
|
|
5722
|
-
verbose.status(f"Scanning {Path(fp).name}",
|
|
5741
|
+
verbose.status(f"Scanning {Path(fp).name}", phase='analyzing')
|
|
5723
5742
|
|
|
5724
5743
|
verbose.phase('thinking', 'AI is analyzing and planning optimizations')
|
|
5725
5744
|
verbose.api_call(endpoint='chat/completions', tokens_in=total_lines * 4)
|
|
@@ -5734,7 +5753,7 @@ def ai_optimize(module_name, files, agent_task, auto_confirm, no_verbose):
|
|
|
5734
5753
|
verbose.api_call(endpoint='chat/completions', tokens_out=len(str(changes)) // 4 if changes else 0)
|
|
5735
5754
|
|
|
5736
5755
|
if not changes:
|
|
5737
|
-
verbose.status("No optimizations needed",
|
|
5756
|
+
verbose.status("No optimizations needed", phase='complete')
|
|
5738
5757
|
verbose.end(success=True, message="Code is already optimal")
|
|
5739
5758
|
return
|
|
5740
5759
|
|
|
@@ -5787,7 +5806,7 @@ def ai_optimize(module_name, files, agent_task, auto_confirm, no_verbose):
|
|
|
5787
5806
|
click.secho("CONFIRM REQUIRED:", fg='yellow', bold=True)
|
|
5788
5807
|
click.echo(f"\n{confirm}\n")
|
|
5789
5808
|
if not click.confirm("Apply this change?"):
|
|
5790
|
-
verbose.status(f"Skipped: {change['file']}",
|
|
5809
|
+
verbose.status(f"Skipped: {change['file']}", phase='warning')
|
|
5791
5810
|
changes = [c for c in changes if c != change]
|
|
5792
5811
|
if not click.confirm("Apply all changes?"):
|
|
5793
5812
|
verbose.warning("Changes aborted by user")
|
|
@@ -5848,11 +5867,59 @@ def settings():
|
|
|
5848
5867
|
click.echo(" includecpp ai key <YOUR_KEY>")
|
|
5849
5868
|
|
|
5850
5869
|
|
|
5870
|
+
@click.command()
|
|
5871
|
+
@click.option('--path', '-p', type=click.Path(), default=None,
|
|
5872
|
+
help='Path to project directory (default: current directory)')
|
|
5873
|
+
def project(path):
|
|
5874
|
+
"""Open the Project Interface with CodeMaker.
|
|
5875
|
+
|
|
5876
|
+
Opens a professional visual mindmap tool for planning and designing
|
|
5877
|
+
your C++ project structure.
|
|
5878
|
+
|
|
5879
|
+
Features:
|
|
5880
|
+
- Visual node-based system design
|
|
5881
|
+
- Class, Function, Object, and Definition nodes
|
|
5882
|
+
- Connectable nodes with bezier curves
|
|
5883
|
+
- Pan/zoom navigation (middle mouse button)
|
|
5884
|
+
- Auto-save functionality
|
|
5885
|
+
- .ma map file management
|
|
5886
|
+
|
|
5887
|
+
Controls:
|
|
5888
|
+
- Right-click canvas: Create new nodes
|
|
5889
|
+
- Right-click node: Edit/Connect/Delete
|
|
5890
|
+
- Middle mouse + drag: Pan view
|
|
5891
|
+
- Middle mouse + scroll: Zoom in/out
|
|
5892
|
+
- ESC: Cancel connection mode
|
|
5893
|
+
- DELETE: Remove selected nodes
|
|
5894
|
+
|
|
5895
|
+
Requires: pip install PyQt6
|
|
5896
|
+
"""
|
|
5897
|
+
from pathlib import Path as PathLib
|
|
5898
|
+
project_path = PathLib(path) if path else PathLib.cwd()
|
|
5899
|
+
|
|
5900
|
+
if not project_path.exists():
|
|
5901
|
+
click.secho(f"Path does not exist: {project_path}", fg='red', err=True)
|
|
5902
|
+
return
|
|
5903
|
+
|
|
5904
|
+
try:
|
|
5905
|
+
from ..core.project_ui import show_project, PYQT_AVAILABLE
|
|
5906
|
+
if not PYQT_AVAILABLE:
|
|
5907
|
+
click.secho("PyQt6 not installed.", fg='red', err=True)
|
|
5908
|
+
click.echo("Install with: pip install PyQt6")
|
|
5909
|
+
return
|
|
5910
|
+
success, msg = show_project(str(project_path))
|
|
5911
|
+
if not success:
|
|
5912
|
+
click.secho(msg, fg='red', err=True)
|
|
5913
|
+
except Exception as e:
|
|
5914
|
+
click.secho(f"Error opening project interface: {e}", fg='red', err=True)
|
|
5915
|
+
|
|
5916
|
+
|
|
5851
5917
|
# ============================================================================
|
|
5852
|
-
# CPPY - Code Conversion Tools
|
|
5918
|
+
# CPPY - Code Conversion Tools (Experimental)
|
|
5853
5919
|
# ============================================================================
|
|
5854
5920
|
|
|
5855
|
-
|
|
5921
|
+
# CPPY group - conditionally registered based on experimental_features setting
|
|
5922
|
+
@click.group(invoke_without_command=True)
|
|
5856
5923
|
@click.pass_context
|
|
5857
5924
|
def cppy(ctx):
|
|
5858
5925
|
"""Code conversion tools for Python <-> C++.
|
|
@@ -6358,6 +6425,38 @@ def _convert_with_ai(content: str, module_name: str, source_file,
|
|
|
6358
6425
|
return _convert_to_python(content, module_name, source_file, output_dir, verbose)
|
|
6359
6426
|
|
|
6360
6427
|
ai_verbose.api_call(endpoint='chat/completions', tokens_out=len(response) // 4)
|
|
6428
|
+
|
|
6429
|
+
# Check if AI needs clarification on unconvertible modules
|
|
6430
|
+
if 'CLARIFICATION_NEEDED:' in response:
|
|
6431
|
+
ai_verbose.phase('clarification', 'AI needs clarification')
|
|
6432
|
+
clarification_match = re.search(r'CLARIFICATION_NEEDED:\n((?:- .+\n?)+)', response)
|
|
6433
|
+
if clarification_match:
|
|
6434
|
+
questions = clarification_match.group(1).strip()
|
|
6435
|
+
click.echo("\n" + "=" * 60)
|
|
6436
|
+
click.secho("AI NEEDS CLARIFICATION:", fg='yellow', bold=True)
|
|
6437
|
+
click.echo(questions)
|
|
6438
|
+
click.echo("=" * 60)
|
|
6439
|
+
user_input = click.prompt("Your response (or 'skip' to use defaults)")
|
|
6440
|
+
if user_input.lower() != 'skip':
|
|
6441
|
+
# Re-run AI with user's clarification
|
|
6442
|
+
clarified_prompt = ai_prompt + f"\n\nUSER CLARIFICATION:\n{user_input}\n\nNow convert with this clarification:"
|
|
6443
|
+
success, response, _ = ai_manager.generate(
|
|
6444
|
+
task=clarified_prompt,
|
|
6445
|
+
files={str(source_file): content},
|
|
6446
|
+
project_root=project_root,
|
|
6447
|
+
think=think,
|
|
6448
|
+
think_twice=think_twice,
|
|
6449
|
+
think_three=think_three,
|
|
6450
|
+
use_websearch=use_websearch,
|
|
6451
|
+
skip_tool_execution=True
|
|
6452
|
+
)
|
|
6453
|
+
if not success:
|
|
6454
|
+
ai_verbose.warning("Clarified conversion failed, using standard converter")
|
|
6455
|
+
if to_cpp:
|
|
6456
|
+
return _convert_to_cpp(content, module_name, source_file, output_dir, no_header, namespace, verbose)
|
|
6457
|
+
else:
|
|
6458
|
+
return _convert_to_python(content, module_name, source_file, output_dir, verbose)
|
|
6459
|
+
|
|
6361
6460
|
ai_verbose.phase('parsing', 'Extracting converted code from AI response')
|
|
6362
6461
|
|
|
6363
6462
|
# Parse AI response
|
|
@@ -6367,7 +6466,7 @@ def _convert_with_ai(content: str, module_name: str, source_file,
|
|
|
6367
6466
|
converted_code = _extract_ai_converted_code(response, to_cpp, module_name, namespace)
|
|
6368
6467
|
|
|
6369
6468
|
if not converted_code:
|
|
6370
|
-
ai_verbose.status("Using hybrid conversion (AI + standard)",
|
|
6469
|
+
ai_verbose.status("Using hybrid conversion (AI + standard)", phase='complete')
|
|
6371
6470
|
# Fall back to standard conversion with AI enhancements
|
|
6372
6471
|
if to_cpp:
|
|
6373
6472
|
converter = PythonToCppConverter()
|
|
@@ -6460,22 +6559,95 @@ def _convert_with_ai(content: str, module_name: str, source_file,
|
|
|
6460
6559
|
return result
|
|
6461
6560
|
|
|
6462
6561
|
|
|
6562
|
+
def _get_includecpp_readme() -> str:
|
|
6563
|
+
"""Load IncludeCPP README.md for AI context."""
|
|
6564
|
+
try:
|
|
6565
|
+
import importlib.resources
|
|
6566
|
+
import includecpp
|
|
6567
|
+
readme_path = Path(includecpp.__file__).parent.parent / 'README.md'
|
|
6568
|
+
if readme_path.exists():
|
|
6569
|
+
readme = readme_path.read_text(encoding='utf-8')
|
|
6570
|
+
# Truncate to first sections (keep it focused)
|
|
6571
|
+
sections = readme.split('# Changelog')[0] # Everything before changelog
|
|
6572
|
+
return sections[:8000] # Limit to ~8k chars
|
|
6573
|
+
except Exception:
|
|
6574
|
+
pass
|
|
6575
|
+
return ""
|
|
6576
|
+
|
|
6463
6577
|
def _build_cppy_ai_prompt(source: str, mode: str, module_name: str, namespace: str, analysis: dict, no_header: bool = False) -> str:
|
|
6464
6578
|
"""Build AI prompt with IncludeCPP-specific instructions."""
|
|
6465
6579
|
|
|
6580
|
+
# Load README for AI context (dynamically, not hardcoded)
|
|
6581
|
+
readme_context = _get_includecpp_readme()
|
|
6582
|
+
includecpp_docs = ""
|
|
6583
|
+
if readme_context:
|
|
6584
|
+
includecpp_docs = f'''
|
|
6585
|
+
=== INCLUDECPP DOCUMENTATION (from README.md) ===
|
|
6586
|
+
{readme_context}
|
|
6587
|
+
=== END DOCUMENTATION ===
|
|
6588
|
+
'''
|
|
6589
|
+
|
|
6590
|
+
# Critical file extension rules
|
|
6591
|
+
file_rules = f'''
|
|
6592
|
+
CRITICAL FILE EXTENSION RULES:
|
|
6593
|
+
- C++ implementation file MUST be: {module_name}.cpp (NOT .cp, NOT .cc, NOT .cxx)
|
|
6594
|
+
- C++ header file MUST be: {module_name}.h (NOT .hpp, NOT .hh)
|
|
6595
|
+
- Python file MUST be: {module_name}.py
|
|
6596
|
+
FOLLOW THESE EXTENSIONS EXACTLY.
|
|
6597
|
+
'''
|
|
6598
|
+
|
|
6599
|
+
# Unconvertible module handling
|
|
6600
|
+
unconvertible_info = ""
|
|
6601
|
+
for item, reason, line in getattr(analysis.get('converter', None), 'unconvertible', []) if isinstance(analysis, dict) else []:
|
|
6602
|
+
unconvertible_info += f"- Line {line}: {item} ({reason})\n"
|
|
6603
|
+
|
|
6604
|
+
unconvertible_guidance = ""
|
|
6605
|
+
if unconvertible_info:
|
|
6606
|
+
unconvertible_guidance = f'''
|
|
6607
|
+
UNCONVERTIBLE MODULES DETECTED:
|
|
6608
|
+
{unconvertible_info}
|
|
6609
|
+
|
|
6610
|
+
For unconvertible modules (tkinter, pygame, ursina, etc.):
|
|
6611
|
+
1. COMMENT OUT: Add /* UNCONVERTIBLE: tkinter */ comment
|
|
6612
|
+
2. ALTERNATIVE: Suggest C++ alternatives (Qt for GUI, SDL2 for games, etc.)
|
|
6613
|
+
3. SKIP: Remove the code with explanation comment
|
|
6614
|
+
|
|
6615
|
+
If you need clarification, output:
|
|
6616
|
+
CLARIFICATION_NEEDED:
|
|
6617
|
+
- <what you need clarified>
|
|
6618
|
+
'''
|
|
6619
|
+
|
|
6466
6620
|
if mode == 'py_to_cpp':
|
|
6467
6621
|
direction = "Python to C++"
|
|
6468
6622
|
source_lang = "python"
|
|
6469
6623
|
target_lang = "cpp"
|
|
6470
6624
|
|
|
6625
|
+
# CRITICAL: No pybind11 - IncludeCPP handles bindings automatically
|
|
6626
|
+
includecpp_note = '''
|
|
6627
|
+
**CRITICAL - IncludeCPP HANDLES BINDINGS AUTOMATICALLY:**
|
|
6628
|
+
- DO NOT include pybind11 headers
|
|
6629
|
+
- DO NOT write PYBIND11_MODULE macros
|
|
6630
|
+
- DO NOT use py:: namespace or py::object
|
|
6631
|
+
- Just write CLEAN C++ code in namespace includecpp
|
|
6632
|
+
- IncludeCPP will auto-generate Python bindings via: includecpp plugin <name> <file.cpp>
|
|
6633
|
+
|
|
6634
|
+
The user runs:
|
|
6635
|
+
1. includecpp cppy convert file.py --cpp (generates .cpp)
|
|
6636
|
+
2. includecpp plugin mymod file.cpp (auto-generates pybind11 bindings)
|
|
6637
|
+
3. includecpp rebuild (compiles to Python module)
|
|
6638
|
+
'''
|
|
6639
|
+
|
|
6471
6640
|
if no_header:
|
|
6472
6641
|
# No header mode - put everything in .cpp
|
|
6473
6642
|
specific_rules = f'''
|
|
6474
6643
|
INCLUDECPP-SPECIFIC REQUIREMENTS:
|
|
6475
6644
|
1. ALL code MUST be wrapped in: namespace {namespace} {{ ... }}
|
|
6476
|
-
2. DO NOT create a header file -
|
|
6477
|
-
3.
|
|
6478
|
-
4.
|
|
6645
|
+
2. **CRITICAL: DO NOT create a header file** - --no-h flag is set
|
|
6646
|
+
3. Put ALL declarations AND implementations in the .cpp file only
|
|
6647
|
+
4. NO separate .h file should be created
|
|
6648
|
+
{includecpp_note}
|
|
6649
|
+
|
|
6650
|
+
{file_rules}
|
|
6479
6651
|
|
|
6480
6652
|
TYPE CONVERSIONS:
|
|
6481
6653
|
- int -> int
|
|
@@ -6487,8 +6659,14 @@ TYPE CONVERSIONS:
|
|
|
6487
6659
|
- Set[T] -> std::unordered_set<T>
|
|
6488
6660
|
- Optional[T] -> std::optional<T>
|
|
6489
6661
|
- Callable -> std::function<R(Args...)>
|
|
6490
|
-
-
|
|
6491
|
-
|
|
6662
|
+
- bytes -> std::vector<uint8_t>
|
|
6663
|
+
|
|
6664
|
+
GENERIC/TEMPLATE FUNCTIONS:
|
|
6665
|
+
- For functions that accept any type (like choices), use C++ templates:
|
|
6666
|
+
template<typename T> T getRandomChoice(const std::vector<T>& choices);
|
|
6667
|
+
- NOT: auto getRandomChoice(const std::vector<auto>& choices) // INVALID!
|
|
6668
|
+
|
|
6669
|
+
{unconvertible_guidance}'''
|
|
6492
6670
|
else:
|
|
6493
6671
|
# Normal mode with header
|
|
6494
6672
|
specific_rules = f'''
|
|
@@ -6497,22 +6675,30 @@ INCLUDECPP-SPECIFIC REQUIREMENTS:
|
|
|
6497
6675
|
2. Create BOTH .cpp implementation AND .h header file
|
|
6498
6676
|
3. Use #include "{module_name}.h" at top of .cpp file
|
|
6499
6677
|
4. Header guard: #ifndef {module_name.upper()}_H / #define / #endif
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
- Context managers: Use RAII pattern
|
|
6504
|
-
- Duck typing: Use templates with concepts
|
|
6678
|
+
{includecpp_note}
|
|
6679
|
+
|
|
6680
|
+
{file_rules}
|
|
6505
6681
|
|
|
6506
|
-
|
|
6682
|
+
GENERIC/TEMPLATE FUNCTIONS:
|
|
6683
|
+
- For functions that accept any type, use C++ templates:
|
|
6684
|
+
template<typename T> T getRandomChoice(const std::vector<T>& choices);
|
|
6685
|
+
- NOT: auto getRandomChoice(const std::vector<auto>& choices) // INVALID!
|
|
6686
|
+
|
|
6687
|
+
TEMPLATE EXAMPLE (for generic functions):
|
|
6507
6688
|
```cpp
|
|
6508
|
-
|
|
6509
|
-
|
|
6689
|
+
// In header (.h):
|
|
6690
|
+
template<typename T>
|
|
6691
|
+
T getRandomChoice(const std::vector<T>& choices);
|
|
6510
6692
|
|
|
6511
|
-
//
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6693
|
+
// In implementation (.cpp):
|
|
6694
|
+
template<typename T>
|
|
6695
|
+
T Manager::getRandomChoice(const std::vector<T>& choices) {{
|
|
6696
|
+
// implementation
|
|
6515
6697
|
}}
|
|
6698
|
+
// Explicit instantiations for common types:
|
|
6699
|
+
template int Manager::getRandomChoice<int>(const std::vector<int>&);
|
|
6700
|
+
template double Manager::getRandomChoice<double>(const std::vector<double>&);
|
|
6701
|
+
template std::string Manager::getRandomChoice<std::string>(const std::vector<std::string>&);
|
|
6516
6702
|
```
|
|
6517
6703
|
|
|
6518
6704
|
TYPE CONVERSIONS:
|
|
@@ -6520,13 +6706,14 @@ TYPE CONVERSIONS:
|
|
|
6520
6706
|
- float -> double
|
|
6521
6707
|
- str -> std::string
|
|
6522
6708
|
- bool -> bool
|
|
6709
|
+
- bytes -> std::vector<uint8_t>
|
|
6523
6710
|
- List[T] -> std::vector<T>
|
|
6524
6711
|
- Dict[K,V] -> std::unordered_map<K,V>
|
|
6525
6712
|
- Set[T] -> std::unordered_set<T>
|
|
6526
6713
|
- Optional[T] -> std::optional<T>
|
|
6527
6714
|
- Callable -> std::function<R(Args...)>
|
|
6528
|
-
|
|
6529
|
-
'''
|
|
6715
|
+
|
|
6716
|
+
{unconvertible_guidance}'''
|
|
6530
6717
|
else:
|
|
6531
6718
|
direction = "C++ to Python"
|
|
6532
6719
|
source_lang = "cpp"
|
|
@@ -6559,37 +6746,60 @@ TYPE CONVERSIONS:
|
|
|
6559
6746
|
# Build output format based on mode and no_header flag
|
|
6560
6747
|
if mode == 'py_to_cpp':
|
|
6561
6748
|
if no_header:
|
|
6562
|
-
output_format = f'''OUTPUT FORMAT (NO HEADER
|
|
6563
|
-
|
|
6749
|
+
output_format = f'''OUTPUT FORMAT (--no-h flag is set, NO HEADER FILE):
|
|
6750
|
+
**CRITICAL**: Output ONLY a single .cpp file. NO .h file allowed.
|
|
6751
|
+
|
|
6752
|
+
Output the implementation file (extension MUST be .cpp):
|
|
6564
6753
|
```cpp
|
|
6565
6754
|
// {module_name}.cpp
|
|
6566
|
-
<
|
|
6755
|
+
#include <string>
|
|
6756
|
+
#include <vector>
|
|
6757
|
+
// ... other includes ...
|
|
6758
|
+
|
|
6759
|
+
namespace {namespace} {{
|
|
6760
|
+
|
|
6761
|
+
// All declarations AND implementations go here
|
|
6762
|
+
// No forward declarations needed since everything is in one file
|
|
6763
|
+
|
|
6764
|
+
}} // namespace {namespace}
|
|
6567
6765
|
```
|
|
6568
6766
|
|
|
6569
|
-
|
|
6767
|
+
REMEMBER:
|
|
6768
|
+
- File extension is .cpp (NOT .cp, NOT .cc)
|
|
6769
|
+
- DO NOT create a .h header file
|
|
6770
|
+
- Put ALL code in the single .cpp file'''
|
|
6570
6771
|
else:
|
|
6571
6772
|
output_format = f'''OUTPUT FORMAT (with header):
|
|
6572
|
-
1. First output the
|
|
6773
|
+
1. First output the implementation file (extension MUST be .cpp):
|
|
6573
6774
|
```cpp
|
|
6574
6775
|
// {module_name}.cpp
|
|
6575
|
-
|
|
6776
|
+
#include "{module_name}.h"
|
|
6777
|
+
// ... implementations ...
|
|
6576
6778
|
```
|
|
6577
6779
|
|
|
6578
|
-
2. Then the header file (
|
|
6780
|
+
2. Then output the header file (extension MUST be .h):
|
|
6579
6781
|
```cpp
|
|
6580
6782
|
// {module_name}.h
|
|
6581
|
-
|
|
6582
|
-
|
|
6783
|
+
#ifndef {module_name.upper()}_H
|
|
6784
|
+
#define {module_name.upper()}_H
|
|
6785
|
+
// ... declarations ...
|
|
6786
|
+
#endif // {module_name.upper()}_H
|
|
6787
|
+
```
|
|
6788
|
+
|
|
6789
|
+
REMEMBER:
|
|
6790
|
+
- Implementation file extension is .cpp (NOT .cp, NOT .cc)
|
|
6791
|
+
- Header file extension is .h (NOT .hpp, NOT .hh)'''
|
|
6583
6792
|
else:
|
|
6584
6793
|
output_format = f'''OUTPUT FORMAT:
|
|
6585
|
-
Output the Python file:
|
|
6794
|
+
Output the Python file (extension MUST be .py):
|
|
6586
6795
|
```python
|
|
6587
6796
|
# {module_name}.py
|
|
6588
6797
|
<full python content>
|
|
6589
6798
|
```'''
|
|
6590
6799
|
|
|
6591
6800
|
prompt = f'''Convert the following {direction}.
|
|
6592
|
-
|
|
6801
|
+
You are IncludeCPP's AI code converter. Follow all requirements precisely.
|
|
6802
|
+
{includecpp_docs}
|
|
6593
6803
|
MODULE NAME: {module_name}
|
|
6594
6804
|
NAMESPACE: {namespace}
|
|
6595
6805
|
{specific_rules}
|
|
@@ -6602,6 +6812,10 @@ SOURCE CODE ({source_lang}):
|
|
|
6602
6812
|
|
|
6603
6813
|
{output_format}
|
|
6604
6814
|
|
|
6815
|
+
If you need clarification about how to convert specific modules or patterns:
|
|
6816
|
+
CLARIFICATION_NEEDED:
|
|
6817
|
+
- <specific question about conversion approach>
|
|
6818
|
+
|
|
6605
6819
|
At the end, list any API changes:
|
|
6606
6820
|
API_CHANGES:
|
|
6607
6821
|
- <change description>
|
|
@@ -6921,5 +7135,394 @@ def cppy_types():
|
|
|
6921
7135
|
click.echo("=" * 60)
|
|
6922
7136
|
|
|
6923
7137
|
|
|
7138
|
+
# ============================================================================
|
|
7139
|
+
# EXEC - Interactive Code Execution
|
|
7140
|
+
# ============================================================================
|
|
7141
|
+
|
|
7142
|
+
@cli.command()
|
|
7143
|
+
@click.argument('lang', type=click.Choice(['py', 'cpp', 'python', 'c++']))
|
|
7144
|
+
@click.argument('path', required=False, type=click.Path())
|
|
7145
|
+
@click.option('--all', 'import_all', is_flag=True, help='Import all available modules')
|
|
7146
|
+
def exec(lang, path, import_all):
|
|
7147
|
+
"""Execute code interactively for quick testing.
|
|
7148
|
+
|
|
7149
|
+
Run Python or C++ code snippets without creating files.
|
|
7150
|
+
Perfect for testing your IncludeCPP modules quickly.
|
|
7151
|
+
|
|
7152
|
+
\b
|
|
7153
|
+
Usage:
|
|
7154
|
+
includecpp exec py # Interactive Python
|
|
7155
|
+
includecpp exec cpp # Interactive C++
|
|
7156
|
+
includecpp exec py mymodule # Auto-import mymodule
|
|
7157
|
+
includecpp exec py plugins/x.cp # Auto-import from plugin
|
|
7158
|
+
includecpp exec py --all # Import all modules
|
|
7159
|
+
|
|
7160
|
+
\b
|
|
7161
|
+
Controls:
|
|
7162
|
+
ENTER = Add new line
|
|
7163
|
+
Empty line = Execute code (press ENTER twice)
|
|
7164
|
+
CTRL+C = Cancel
|
|
7165
|
+
|
|
7166
|
+
\b
|
|
7167
|
+
Examples:
|
|
7168
|
+
$ includecpp exec py fast_math
|
|
7169
|
+
>>> x = fast_math.add(1, 2)
|
|
7170
|
+
>>> print(x)
|
|
7171
|
+
>>>
|
|
7172
|
+
3
|
|
7173
|
+
"""
|
|
7174
|
+
import sys
|
|
7175
|
+
import subprocess
|
|
7176
|
+
import tempfile
|
|
7177
|
+
from pathlib import Path as PathLib
|
|
7178
|
+
|
|
7179
|
+
# Normalize language
|
|
7180
|
+
is_python = lang in ('py', 'python')
|
|
7181
|
+
lang_name = 'Python' if is_python else 'C++'
|
|
7182
|
+
|
|
7183
|
+
# Build imports/includes
|
|
7184
|
+
imports = []
|
|
7185
|
+
includes = []
|
|
7186
|
+
|
|
7187
|
+
if import_all:
|
|
7188
|
+
# Import all available modules
|
|
7189
|
+
try:
|
|
7190
|
+
from ..core.cpp_api import CppApi
|
|
7191
|
+
api = CppApi()
|
|
7192
|
+
modules = list(api.registry.keys())
|
|
7193
|
+
if is_python:
|
|
7194
|
+
for mod in modules:
|
|
7195
|
+
imports.append(f'from includecpp import {mod}')
|
|
7196
|
+
if modules:
|
|
7197
|
+
click.secho(f"Auto-importing {len(modules)} modules: {', '.join(modules)}", fg='cyan')
|
|
7198
|
+
else:
|
|
7199
|
+
for mod in modules:
|
|
7200
|
+
includes.append(f'#include "{mod}.h"')
|
|
7201
|
+
if modules:
|
|
7202
|
+
click.secho(f"Auto-including {len(modules)} headers", fg='cyan')
|
|
7203
|
+
except Exception:
|
|
7204
|
+
click.secho("Warning: Could not load module registry", fg='yellow')
|
|
7205
|
+
|
|
7206
|
+
elif path:
|
|
7207
|
+
path_obj = PathLib(path)
|
|
7208
|
+
module_name = None
|
|
7209
|
+
|
|
7210
|
+
# Check if it's a .cp plugin file
|
|
7211
|
+
if path.endswith('.cp') or '/plugins/' in path or '\\plugins\\' in path:
|
|
7212
|
+
# Extract module name from plugin path
|
|
7213
|
+
module_name = path_obj.stem
|
|
7214
|
+
elif path_obj.exists():
|
|
7215
|
+
# It's a file path
|
|
7216
|
+
module_name = path_obj.stem
|
|
7217
|
+
else:
|
|
7218
|
+
# Assume it's a module name directly
|
|
7219
|
+
module_name = path
|
|
7220
|
+
|
|
7221
|
+
if module_name:
|
|
7222
|
+
if is_python:
|
|
7223
|
+
imports.append(f'from includecpp import {module_name}')
|
|
7224
|
+
click.secho(f"Auto-importing: {module_name}", fg='cyan')
|
|
7225
|
+
else:
|
|
7226
|
+
includes.append(f'#include "{module_name}.h"')
|
|
7227
|
+
click.secho(f"Auto-including: {module_name}.h", fg='cyan')
|
|
7228
|
+
|
|
7229
|
+
# Show header
|
|
7230
|
+
click.echo()
|
|
7231
|
+
click.secho(f"=== IncludeCPP {lang_name} REPL ===", fg='cyan', bold=True)
|
|
7232
|
+
click.echo("Enter code line by line. Press ENTER on empty line to execute.")
|
|
7233
|
+
click.echo("Press CTRL+C to cancel.")
|
|
7234
|
+
click.echo()
|
|
7235
|
+
|
|
7236
|
+
# Show pre-loaded imports
|
|
7237
|
+
if imports:
|
|
7238
|
+
click.secho("Pre-loaded:", fg='green')
|
|
7239
|
+
for imp in imports:
|
|
7240
|
+
click.echo(f" {imp}")
|
|
7241
|
+
click.echo()
|
|
7242
|
+
|
|
7243
|
+
if includes:
|
|
7244
|
+
click.secho("Pre-loaded:", fg='green')
|
|
7245
|
+
for inc in includes:
|
|
7246
|
+
click.echo(f" {inc}")
|
|
7247
|
+
click.echo()
|
|
7248
|
+
|
|
7249
|
+
# Collect code lines
|
|
7250
|
+
lines = []
|
|
7251
|
+
prompt = '>>> ' if is_python else 'cpp> '
|
|
7252
|
+
continuation = '... ' if is_python else ' > '
|
|
7253
|
+
|
|
7254
|
+
try:
|
|
7255
|
+
while True:
|
|
7256
|
+
try:
|
|
7257
|
+
# Determine prompt
|
|
7258
|
+
current_prompt = prompt if not lines else continuation
|
|
7259
|
+
line = input(current_prompt)
|
|
7260
|
+
|
|
7261
|
+
# Empty line = execute
|
|
7262
|
+
if not line.strip():
|
|
7263
|
+
if lines:
|
|
7264
|
+
break
|
|
7265
|
+
continue
|
|
7266
|
+
|
|
7267
|
+
lines.append(line)
|
|
7268
|
+
|
|
7269
|
+
except EOFError:
|
|
7270
|
+
break
|
|
7271
|
+
|
|
7272
|
+
except KeyboardInterrupt:
|
|
7273
|
+
click.echo()
|
|
7274
|
+
click.secho("Cancelled.", fg='yellow')
|
|
7275
|
+
return
|
|
7276
|
+
|
|
7277
|
+
if not lines:
|
|
7278
|
+
click.secho("No code entered.", fg='yellow')
|
|
7279
|
+
return
|
|
7280
|
+
|
|
7281
|
+
# Build full code
|
|
7282
|
+
code_lines = imports + [''] + lines if imports else lines
|
|
7283
|
+
|
|
7284
|
+
click.echo()
|
|
7285
|
+
click.secho("--- Output ---", fg='green')
|
|
7286
|
+
|
|
7287
|
+
if is_python:
|
|
7288
|
+
# Execute Python code
|
|
7289
|
+
full_code = '\n'.join(code_lines)
|
|
7290
|
+
try:
|
|
7291
|
+
# Use exec with captured output
|
|
7292
|
+
import io
|
|
7293
|
+
from contextlib import redirect_stdout, redirect_stderr
|
|
7294
|
+
|
|
7295
|
+
stdout_capture = io.StringIO()
|
|
7296
|
+
stderr_capture = io.StringIO()
|
|
7297
|
+
|
|
7298
|
+
# Create execution context
|
|
7299
|
+
exec_globals = {'__name__': '__main__'}
|
|
7300
|
+
|
|
7301
|
+
with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
|
|
7302
|
+
exec(full_code, exec_globals)
|
|
7303
|
+
|
|
7304
|
+
stdout_val = stdout_capture.getvalue()
|
|
7305
|
+
stderr_val = stderr_capture.getvalue()
|
|
7306
|
+
|
|
7307
|
+
if stdout_val:
|
|
7308
|
+
click.echo(stdout_val, nl=False)
|
|
7309
|
+
if stderr_val:
|
|
7310
|
+
click.secho(stderr_val, fg='red', nl=False)
|
|
7311
|
+
|
|
7312
|
+
if not stdout_val and not stderr_val:
|
|
7313
|
+
click.secho("(no output)", fg='bright_black')
|
|
7314
|
+
|
|
7315
|
+
except Exception as e:
|
|
7316
|
+
click.secho(f"Error: {e}", fg='red')
|
|
7317
|
+
|
|
7318
|
+
else:
|
|
7319
|
+
# Execute C++ code
|
|
7320
|
+
# Build a complete C++ program
|
|
7321
|
+
cpp_code_lines = [
|
|
7322
|
+
'#include <iostream>',
|
|
7323
|
+
'#include <vector>',
|
|
7324
|
+
'#include <string>',
|
|
7325
|
+
'#include <algorithm>',
|
|
7326
|
+
'#include <cmath>',
|
|
7327
|
+
'using namespace std;',
|
|
7328
|
+
''
|
|
7329
|
+
]
|
|
7330
|
+
cpp_code_lines.extend(includes)
|
|
7331
|
+
cpp_code_lines.append('')
|
|
7332
|
+
cpp_code_lines.append('int main() {')
|
|
7333
|
+
|
|
7334
|
+
# Indent user code
|
|
7335
|
+
for line in lines:
|
|
7336
|
+
cpp_code_lines.append(' ' + line)
|
|
7337
|
+
|
|
7338
|
+
cpp_code_lines.append(' return 0;')
|
|
7339
|
+
cpp_code_lines.append('}')
|
|
7340
|
+
|
|
7341
|
+
full_code = '\n'.join(cpp_code_lines)
|
|
7342
|
+
|
|
7343
|
+
# Write to temp file and compile
|
|
7344
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.cpp', delete=False, encoding='utf-8') as f:
|
|
7345
|
+
f.write(full_code)
|
|
7346
|
+
cpp_file = f.name
|
|
7347
|
+
|
|
7348
|
+
exe_file = cpp_file.replace('.cpp', '.exe' if sys.platform == 'win32' else '')
|
|
7349
|
+
|
|
7350
|
+
try:
|
|
7351
|
+
# Compile
|
|
7352
|
+
compile_cmd = ['g++', '-std=c++17', '-o', exe_file, cpp_file]
|
|
7353
|
+
result = subprocess.run(compile_cmd, capture_output=True, text=True)
|
|
7354
|
+
|
|
7355
|
+
if result.returncode != 0:
|
|
7356
|
+
click.secho("Compilation Error:", fg='red')
|
|
7357
|
+
click.echo(result.stderr)
|
|
7358
|
+
else:
|
|
7359
|
+
# Run
|
|
7360
|
+
run_result = subprocess.run([exe_file], capture_output=True, text=True, timeout=10)
|
|
7361
|
+
if run_result.stdout:
|
|
7362
|
+
click.echo(run_result.stdout, nl=False)
|
|
7363
|
+
if run_result.stderr:
|
|
7364
|
+
click.secho(run_result.stderr, fg='red', nl=False)
|
|
7365
|
+
if not run_result.stdout and not run_result.stderr:
|
|
7366
|
+
click.secho("(no output)", fg='bright_black')
|
|
7367
|
+
|
|
7368
|
+
except subprocess.TimeoutExpired:
|
|
7369
|
+
click.secho("Execution timed out (10s limit)", fg='red')
|
|
7370
|
+
except FileNotFoundError:
|
|
7371
|
+
click.secho("Error: g++ not found. Install a C++ compiler.", fg='red')
|
|
7372
|
+
except Exception as e:
|
|
7373
|
+
click.secho(f"Error: {e}", fg='red')
|
|
7374
|
+
finally:
|
|
7375
|
+
# Cleanup temp files
|
|
7376
|
+
import os
|
|
7377
|
+
try:
|
|
7378
|
+
os.unlink(cpp_file)
|
|
7379
|
+
if os.path.exists(exe_file):
|
|
7380
|
+
os.unlink(exe_file)
|
|
7381
|
+
except Exception:
|
|
7382
|
+
pass
|
|
7383
|
+
|
|
7384
|
+
click.echo()
|
|
7385
|
+
click.secho("--------------", fg='green')
|
|
7386
|
+
|
|
7387
|
+
|
|
7388
|
+
# ============================================================================
|
|
7389
|
+
# CSSL - Hidden Command Group
|
|
7390
|
+
# ============================================================================
|
|
7391
|
+
|
|
7392
|
+
@click.group(hidden=True)
|
|
7393
|
+
def cssl():
|
|
7394
|
+
"""CSSL scripting commands."""
|
|
7395
|
+
pass
|
|
7396
|
+
|
|
7397
|
+
|
|
7398
|
+
@cssl.command(name='exec')
|
|
7399
|
+
@click.argument('path', required=False, type=click.Path())
|
|
7400
|
+
@click.option('--code', '-c', type=str, help='Execute code directly')
|
|
7401
|
+
def cssl_exec(path, code):
|
|
7402
|
+
"""Execute CSSL code or file."""
|
|
7403
|
+
from pathlib import Path as PathLib
|
|
7404
|
+
|
|
7405
|
+
try:
|
|
7406
|
+
from ..core.cssl_bridge import CsslLang
|
|
7407
|
+
except ImportError as e:
|
|
7408
|
+
click.secho(f"CSSL runtime not available: {e}", fg='red')
|
|
7409
|
+
return
|
|
7410
|
+
|
|
7411
|
+
cssl_lang = CsslLang()
|
|
7412
|
+
|
|
7413
|
+
# Determine source
|
|
7414
|
+
if code:
|
|
7415
|
+
source = code
|
|
7416
|
+
elif path:
|
|
7417
|
+
path_obj = PathLib(path)
|
|
7418
|
+
if not path_obj.exists():
|
|
7419
|
+
click.secho(f"File not found: {path}", fg='red')
|
|
7420
|
+
return
|
|
7421
|
+
source = path_obj.read_text(encoding='utf-8')
|
|
7422
|
+
else:
|
|
7423
|
+
# Interactive mode
|
|
7424
|
+
click.secho("=== CSSL REPL ===", fg='magenta', bold=True)
|
|
7425
|
+
click.echo("Enter CSSL code. Empty line to execute. CTRL+C to cancel.")
|
|
7426
|
+
click.echo()
|
|
7427
|
+
|
|
7428
|
+
lines = []
|
|
7429
|
+
prompt = 'cssl> '
|
|
7430
|
+
|
|
7431
|
+
try:
|
|
7432
|
+
while True:
|
|
7433
|
+
try:
|
|
7434
|
+
line = input(prompt)
|
|
7435
|
+
if not line.strip():
|
|
7436
|
+
if lines:
|
|
7437
|
+
break
|
|
7438
|
+
continue
|
|
7439
|
+
lines.append(line)
|
|
7440
|
+
except EOFError:
|
|
7441
|
+
break
|
|
7442
|
+
except KeyboardInterrupt:
|
|
7443
|
+
click.echo()
|
|
7444
|
+
click.secho("Cancelled.", fg='yellow')
|
|
7445
|
+
return
|
|
7446
|
+
|
|
7447
|
+
if not lines:
|
|
7448
|
+
click.secho("No code entered.", fg='yellow')
|
|
7449
|
+
return
|
|
7450
|
+
|
|
7451
|
+
source = '\n'.join(lines)
|
|
7452
|
+
|
|
7453
|
+
# Execute
|
|
7454
|
+
click.secho("--- Output ---", fg='green')
|
|
7455
|
+
try:
|
|
7456
|
+
result = cssl_lang.exec(source)
|
|
7457
|
+
|
|
7458
|
+
# Print output buffer
|
|
7459
|
+
for line in cssl_lang.get_output():
|
|
7460
|
+
click.echo(line)
|
|
7461
|
+
|
|
7462
|
+
if result is not None:
|
|
7463
|
+
click.echo(f"Result: {result}")
|
|
7464
|
+
|
|
7465
|
+
except Exception as e:
|
|
7466
|
+
click.secho(f"CSSL Error: {e}", fg='red')
|
|
7467
|
+
|
|
7468
|
+
click.secho("--------------", fg='green')
|
|
7469
|
+
|
|
7470
|
+
|
|
7471
|
+
@cssl.command(name='makemodule')
|
|
7472
|
+
@click.argument('path', type=click.Path(exists=True))
|
|
7473
|
+
@click.option('--output', '-o', type=click.Path(), help='Output path for .cssl-mod')
|
|
7474
|
+
def cssl_makemodule(path, output):
|
|
7475
|
+
"""Create a .cssl-mod module from Python/C++ file."""
|
|
7476
|
+
from pathlib import Path as PathLib
|
|
7477
|
+
import pickle
|
|
7478
|
+
import base64
|
|
7479
|
+
|
|
7480
|
+
path_obj = PathLib(path)
|
|
7481
|
+
suffix = path_obj.suffix.lower()
|
|
7482
|
+
|
|
7483
|
+
if suffix not in ('.py', '.cpp', '.cp', '.h'):
|
|
7484
|
+
click.secho(f"Unsupported file type: {suffix}", fg='red')
|
|
7485
|
+
click.echo("Supported: .py, .cpp, .cp, .h")
|
|
7486
|
+
return
|
|
7487
|
+
|
|
7488
|
+
# Read source
|
|
7489
|
+
source = path_obj.read_text(encoding='utf-8')
|
|
7490
|
+
|
|
7491
|
+
# Create module data
|
|
7492
|
+
module_data = {
|
|
7493
|
+
'name': path_obj.stem,
|
|
7494
|
+
'type': 'python' if suffix == '.py' else 'cpp',
|
|
7495
|
+
'source': source,
|
|
7496
|
+
'version': '1.0',
|
|
7497
|
+
}
|
|
7498
|
+
|
|
7499
|
+
# Encode as base64 pickle
|
|
7500
|
+
encoded = base64.b64encode(pickle.dumps(module_data)).decode('utf-8')
|
|
7501
|
+
|
|
7502
|
+
# Write .cssl-mod file
|
|
7503
|
+
out_path = PathLib(output) if output else path_obj.with_suffix('.cssl-mod')
|
|
7504
|
+
out_path.write_text(f"CSSLMOD1\n{encoded}", encoding='utf-8')
|
|
7505
|
+
|
|
7506
|
+
click.secho(f"Created: {out_path}", fg='green')
|
|
7507
|
+
click.echo(f" Name: {module_data['name']}")
|
|
7508
|
+
click.echo(f" Type: {module_data['type']}")
|
|
7509
|
+
|
|
7510
|
+
|
|
7511
|
+
# Register hidden cssl command group
|
|
7512
|
+
cli.add_command(cssl)
|
|
7513
|
+
|
|
7514
|
+
|
|
7515
|
+
# ============================================================================
|
|
7516
|
+
# Conditional Registration of Experimental Commands
|
|
7517
|
+
# ============================================================================
|
|
7518
|
+
# AI and CPPY commands are only available when experimental features are enabled
|
|
7519
|
+
# Enable via: includecpp settings -> "Enable Experimental Features" checkbox
|
|
7520
|
+
|
|
7521
|
+
if _EXPERIMENTAL_ENABLED:
|
|
7522
|
+
cli.add_command(ai)
|
|
7523
|
+
cli.add_command(cppy)
|
|
7524
|
+
cli.add_command(project)
|
|
7525
|
+
|
|
7526
|
+
|
|
6924
7527
|
if __name__ == '__main__':
|
|
6925
7528
|
cli()
|