IncludeCPP 4.0.2__py3-none-any.whl → 4.2.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/CHANGELOG.md +175 -0
- includecpp/DOCUMENTATION.md +593 -0
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +4 -1
- includecpp/cli/commands.py +698 -84
- includecpp/core/ai_integration.py +46 -13
- includecpp/core/cpp_api_extensions.pyi +350 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +186 -5
- includecpp/core/cssl/cssl_builtins.py +101 -4
- includecpp/core/cssl/cssl_languages.py +1757 -0
- includecpp/core/cssl/cssl_parser.py +429 -98
- includecpp/core/cssl/cssl_runtime.py +666 -51
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +172 -1
- includecpp/core/cssl_bridge.py +194 -8
- includecpp/core/cssl_bridge.pyi +148 -10
- includecpp/generator/parser.cpp +121 -4
- includecpp/generator/parser.h +6 -0
- includecpp/vscode/cssl/package.json +43 -1
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +140 -17
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/METADATA +101 -1
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/RECORD +26 -22
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/WHEEL +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1757 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CSSL Multi-Language Support Module
|
|
3
|
+
|
|
4
|
+
Provides language definitions, syntax transformers, and cross-language instance access
|
|
5
|
+
for Python, Java, C#, C++, and JavaScript.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
@py = libinclude("python")
|
|
9
|
+
cpp = libinclude("c++")
|
|
10
|
+
|
|
11
|
+
define my_func() : supports @py {
|
|
12
|
+
# Python syntax here
|
|
13
|
+
for i in range(10):
|
|
14
|
+
print(i)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class MyClass : extends cpp$BaseClass {
|
|
18
|
+
// C++ style
|
|
19
|
+
}
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import Dict, List, Any, Optional, Callable
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from enum import Enum
|
|
25
|
+
import re
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SupportedLanguage(Enum):
|
|
29
|
+
"""Enumeration of supported programming languages"""
|
|
30
|
+
PYTHON = "python"
|
|
31
|
+
JAVA = "java"
|
|
32
|
+
CSHARP = "c#"
|
|
33
|
+
CPP = "c++"
|
|
34
|
+
JAVASCRIPT = "javascript"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class LanguageSyntax:
|
|
39
|
+
"""Defines syntax rules for a programming language"""
|
|
40
|
+
name: str
|
|
41
|
+
statement_terminator: str # ";" or "\n"
|
|
42
|
+
uses_braces: bool # True for {}, False for indentation (Python)
|
|
43
|
+
boolean_true: str # "True", "true"
|
|
44
|
+
boolean_false: str # "False", "false"
|
|
45
|
+
null_keyword: str # "None", "null", "nullptr"
|
|
46
|
+
variable_keywords: List[str] # ["let", "const", "var"] for JS
|
|
47
|
+
function_keywords: List[str] # ["def"] for Python, ["function"] for JS
|
|
48
|
+
class_keywords: List[str] # ["class"]
|
|
49
|
+
constructor_name: str # "__init__" for Python, "constructor" for JS
|
|
50
|
+
print_function: str # "print", "console.log", "System.out.println"
|
|
51
|
+
comment_single: str # "#" or "//"
|
|
52
|
+
comment_multi_start: str # "/*" or '"""'
|
|
53
|
+
comment_multi_end: str # "*/" or '"""'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class LanguageSupport:
|
|
58
|
+
"""
|
|
59
|
+
Language support object returned by libinclude().
|
|
60
|
+
|
|
61
|
+
Provides syntax transformation and cross-language instance sharing.
|
|
62
|
+
Now with real runtime bridges for C++, Java, and JavaScript (v4.1.1).
|
|
63
|
+
"""
|
|
64
|
+
language: SupportedLanguage
|
|
65
|
+
syntax: LanguageSyntax
|
|
66
|
+
name: str
|
|
67
|
+
_instances: Dict[str, Any] = field(default_factory=dict)
|
|
68
|
+
_transformer: Optional['LanguageTransformer'] = field(default=None, repr=False)
|
|
69
|
+
_bridge: Optional['RuntimeBridge'] = field(default=None, repr=False)
|
|
70
|
+
|
|
71
|
+
def _get_bridge(self) -> Optional['RuntimeBridge']:
|
|
72
|
+
"""Get the runtime bridge for this language."""
|
|
73
|
+
if self._bridge is not None:
|
|
74
|
+
return self._bridge
|
|
75
|
+
|
|
76
|
+
# Lazy-load the appropriate bridge
|
|
77
|
+
if self.language == SupportedLanguage.CPP:
|
|
78
|
+
self._bridge = get_cpp_bridge()
|
|
79
|
+
elif self.language == SupportedLanguage.JAVA:
|
|
80
|
+
self._bridge = get_java_bridge()
|
|
81
|
+
elif self.language == SupportedLanguage.JAVASCRIPT:
|
|
82
|
+
self._bridge = get_js_bridge()
|
|
83
|
+
|
|
84
|
+
return self._bridge
|
|
85
|
+
|
|
86
|
+
def share(self, name: str, instance: Any) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Share an instance for cross-language access.
|
|
89
|
+
|
|
90
|
+
Usage in CSSL:
|
|
91
|
+
cpp.share("Engine", myEngine)
|
|
92
|
+
|
|
93
|
+
Then accessible via:
|
|
94
|
+
cpp$Engine
|
|
95
|
+
"""
|
|
96
|
+
self._instances[name] = instance
|
|
97
|
+
# Also share with the runtime bridge if available
|
|
98
|
+
bridge = self._get_bridge()
|
|
99
|
+
if bridge:
|
|
100
|
+
bridge.share(name, instance)
|
|
101
|
+
|
|
102
|
+
def get_instance(self, name: str) -> Any:
|
|
103
|
+
"""
|
|
104
|
+
Get a shared instance by name with full bidirectional bridge support.
|
|
105
|
+
|
|
106
|
+
v4.1.1: Enhanced to access instances from runtime bridges.
|
|
107
|
+
|
|
108
|
+
Usage in CSSL:
|
|
109
|
+
engine = cpp.get("Engine")
|
|
110
|
+
// Or via $ syntax:
|
|
111
|
+
engine = cpp$Engine
|
|
112
|
+
|
|
113
|
+
For C++: Also accesses classes from IncludeCPP modules.
|
|
114
|
+
For Java: Accesses instances from JVM via JPype.
|
|
115
|
+
For JavaScript: Accesses instances from Node.js runtime.
|
|
116
|
+
"""
|
|
117
|
+
# First check local instances
|
|
118
|
+
if name in self._instances:
|
|
119
|
+
return self._instances[name]
|
|
120
|
+
|
|
121
|
+
# Check the runtime bridge
|
|
122
|
+
bridge = self._get_bridge()
|
|
123
|
+
if bridge:
|
|
124
|
+
# Try to get from bridge's shared instances
|
|
125
|
+
instance = bridge.get_instance(name)
|
|
126
|
+
if instance is not None:
|
|
127
|
+
return instance
|
|
128
|
+
|
|
129
|
+
# C++ specific: Also check loaded modules for classes
|
|
130
|
+
if self.language == SupportedLanguage.CPP and hasattr(bridge, '_modules'):
|
|
131
|
+
for mod in bridge._modules.values():
|
|
132
|
+
if hasattr(mod, name):
|
|
133
|
+
cls_or_instance = getattr(mod, name)
|
|
134
|
+
# Cache for future access
|
|
135
|
+
self._instances[name] = cls_or_instance
|
|
136
|
+
return cls_or_instance
|
|
137
|
+
|
|
138
|
+
# Java specific: Try to load class by name
|
|
139
|
+
if self.language == SupportedLanguage.JAVA and hasattr(bridge, 'load_class'):
|
|
140
|
+
try:
|
|
141
|
+
cls = bridge.load_class(name)
|
|
142
|
+
if cls is not None:
|
|
143
|
+
self._instances[name] = cls
|
|
144
|
+
return cls
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
# JavaScript specific: Try to get from JS context
|
|
149
|
+
if self.language == SupportedLanguage.JAVASCRIPT and hasattr(bridge, 'eval'):
|
|
150
|
+
try:
|
|
151
|
+
result = bridge.eval(f"typeof {name} !== 'undefined' ? {name} : null")
|
|
152
|
+
if result is not None:
|
|
153
|
+
self._instances[name] = result
|
|
154
|
+
return result
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
def has_instance(self, name: str) -> bool:
|
|
161
|
+
"""Check if an instance is shared (includes bridge instances)."""
|
|
162
|
+
if name in self._instances:
|
|
163
|
+
return True
|
|
164
|
+
bridge = self._get_bridge()
|
|
165
|
+
if bridge:
|
|
166
|
+
if bridge.get_instance(name) is not None:
|
|
167
|
+
return True
|
|
168
|
+
# For C++, also check modules
|
|
169
|
+
if self.language == SupportedLanguage.CPP and hasattr(bridge, '_modules'):
|
|
170
|
+
for mod in bridge._modules.values():
|
|
171
|
+
if hasattr(mod, name):
|
|
172
|
+
return True
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
def list_instances(self) -> List[str]:
|
|
176
|
+
"""List all shared instance names (includes bridge instances and C++ classes)."""
|
|
177
|
+
return self.list_available()
|
|
178
|
+
|
|
179
|
+
def list_available(self) -> List[str]:
|
|
180
|
+
"""
|
|
181
|
+
List all available items accessible via $ syntax.
|
|
182
|
+
|
|
183
|
+
v4.1.1: Enhanced to show all accessible items from runtime bridges.
|
|
184
|
+
|
|
185
|
+
Returns a list of names that can be used with lang$Name syntax.
|
|
186
|
+
"""
|
|
187
|
+
available = set(self._instances.keys())
|
|
188
|
+
bridge = self._get_bridge()
|
|
189
|
+
if bridge:
|
|
190
|
+
# Get from bridge's list_available if it has one
|
|
191
|
+
if hasattr(bridge, 'list_available'):
|
|
192
|
+
try:
|
|
193
|
+
available.update(bridge.list_available())
|
|
194
|
+
except:
|
|
195
|
+
pass
|
|
196
|
+
# Also check bridge's _instances directly
|
|
197
|
+
if hasattr(bridge, '_instances'):
|
|
198
|
+
available.update(bridge._instances.keys())
|
|
199
|
+
# For C++, also list module exports
|
|
200
|
+
if self.language == SupportedLanguage.CPP and hasattr(bridge, '_modules'):
|
|
201
|
+
for mod in bridge._modules.values():
|
|
202
|
+
for attr in dir(mod):
|
|
203
|
+
if not attr.startswith('_'):
|
|
204
|
+
available.add(attr)
|
|
205
|
+
return sorted(available)
|
|
206
|
+
|
|
207
|
+
def remove_instance(self, name: str) -> bool:
|
|
208
|
+
"""Remove a shared instance"""
|
|
209
|
+
if name in self._instances:
|
|
210
|
+
del self._instances[name]
|
|
211
|
+
return True
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
def get_transformer(self) -> 'LanguageTransformer':
|
|
215
|
+
"""Get the syntax transformer for this language"""
|
|
216
|
+
if self._transformer is None:
|
|
217
|
+
self._transformer = create_transformer(self)
|
|
218
|
+
return self._transformer
|
|
219
|
+
|
|
220
|
+
# === Runtime Bridge Methods (v4.1.1) ===
|
|
221
|
+
|
|
222
|
+
def load_module(self, module_name: str) -> Any:
|
|
223
|
+
"""Load a module from the target language (C++ only)."""
|
|
224
|
+
bridge = self._get_bridge()
|
|
225
|
+
if bridge and hasattr(bridge, 'load_module'):
|
|
226
|
+
return bridge.load_module(module_name)
|
|
227
|
+
raise NotImplementedError(f"load_module not supported for {self.name}")
|
|
228
|
+
|
|
229
|
+
def create(self, class_name: str, *args) -> Any:
|
|
230
|
+
"""Create an instance of a class in the target language."""
|
|
231
|
+
bridge = self._get_bridge()
|
|
232
|
+
if bridge and hasattr(bridge, 'create'):
|
|
233
|
+
return bridge.create(class_name, *args)
|
|
234
|
+
raise NotImplementedError(f"create not supported for {self.name}")
|
|
235
|
+
|
|
236
|
+
def load_class(self, class_name: str) -> Any:
|
|
237
|
+
"""Load a class from the target language (Java only)."""
|
|
238
|
+
bridge = self._get_bridge()
|
|
239
|
+
if bridge and hasattr(bridge, 'load_class'):
|
|
240
|
+
return bridge.load_class(class_name)
|
|
241
|
+
raise NotImplementedError(f"load_class not supported for {self.name}")
|
|
242
|
+
|
|
243
|
+
def add_classpath(self, path: str) -> None:
|
|
244
|
+
"""Add to classpath (Java only)."""
|
|
245
|
+
bridge = self._get_bridge()
|
|
246
|
+
if bridge and hasattr(bridge, 'add_classpath'):
|
|
247
|
+
bridge.add_classpath(path)
|
|
248
|
+
else:
|
|
249
|
+
raise NotImplementedError(f"add_classpath not supported for {self.name}")
|
|
250
|
+
|
|
251
|
+
def eval(self, code: str) -> Any:
|
|
252
|
+
"""Evaluate code in the target language (JavaScript only)."""
|
|
253
|
+
bridge = self._get_bridge()
|
|
254
|
+
if bridge and hasattr(bridge, 'eval'):
|
|
255
|
+
return bridge.eval(code)
|
|
256
|
+
raise NotImplementedError(f"eval not supported for {self.name}")
|
|
257
|
+
|
|
258
|
+
def call(self, func_name: str, *args) -> Any:
|
|
259
|
+
"""Call a function in the target language (JavaScript only)."""
|
|
260
|
+
bridge = self._get_bridge()
|
|
261
|
+
if bridge and hasattr(bridge, 'call'):
|
|
262
|
+
return bridge.call(func_name, *args)
|
|
263
|
+
raise NotImplementedError(f"call not supported for {self.name}")
|
|
264
|
+
|
|
265
|
+
def is_available(self) -> bool:
|
|
266
|
+
"""Check if the runtime for this language is available."""
|
|
267
|
+
bridge = self._get_bridge()
|
|
268
|
+
if bridge:
|
|
269
|
+
return bridge.is_available()
|
|
270
|
+
return True # Python/C# don't need external runtimes
|
|
271
|
+
|
|
272
|
+
def __getattr__(self, name: str) -> Any:
|
|
273
|
+
"""Allow method-like access for convenience"""
|
|
274
|
+
if name == 'get':
|
|
275
|
+
return self.get_instance
|
|
276
|
+
raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class LanguageTransformer:
|
|
280
|
+
"""
|
|
281
|
+
Base class for transforming language-specific syntax to CSSL.
|
|
282
|
+
|
|
283
|
+
Each language has a specific transformer that handles its unique syntax.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(self, lang_support: LanguageSupport):
|
|
287
|
+
self.lang = lang_support
|
|
288
|
+
self.syntax = lang_support.syntax
|
|
289
|
+
|
|
290
|
+
def transform_source(self, source: str) -> str:
|
|
291
|
+
"""Transform source code from target language to CSSL"""
|
|
292
|
+
raise NotImplementedError("Subclasses must implement transform_source")
|
|
293
|
+
|
|
294
|
+
def _common_replacements(self, stmt: str) -> str:
|
|
295
|
+
"""Apply common replacements across all languages"""
|
|
296
|
+
return stmt
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class PythonTransformer(LanguageTransformer):
|
|
300
|
+
"""
|
|
301
|
+
Transforms Python syntax to CSSL.
|
|
302
|
+
|
|
303
|
+
Handles:
|
|
304
|
+
- Indentation-based blocks -> brace-based blocks
|
|
305
|
+
- def -> define
|
|
306
|
+
- print() -> printl()
|
|
307
|
+
- self. -> this->
|
|
308
|
+
- None -> null
|
|
309
|
+
- Python-style for loops -> CSSL for loops
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
def transform_source(self, source: str) -> str:
|
|
313
|
+
lines = source.split('\n')
|
|
314
|
+
result = []
|
|
315
|
+
indent_stack = [0] # Stack of indentation levels
|
|
316
|
+
|
|
317
|
+
for i, line in enumerate(lines):
|
|
318
|
+
stripped = line.lstrip()
|
|
319
|
+
|
|
320
|
+
# Handle empty lines and comments
|
|
321
|
+
if not stripped:
|
|
322
|
+
continue
|
|
323
|
+
if stripped.startswith('#'):
|
|
324
|
+
# Convert Python comment to CSSL comment
|
|
325
|
+
result.append('// ' + stripped[1:].lstrip())
|
|
326
|
+
continue
|
|
327
|
+
|
|
328
|
+
current_indent = len(line) - len(stripped)
|
|
329
|
+
|
|
330
|
+
# Handle dedent - close blocks
|
|
331
|
+
while len(indent_stack) > 1 and current_indent < indent_stack[-1]:
|
|
332
|
+
indent_stack.pop()
|
|
333
|
+
result.append(' ' * indent_stack[-1] + '}')
|
|
334
|
+
|
|
335
|
+
# Transform the statement
|
|
336
|
+
transformed = self._transform_statement(stripped)
|
|
337
|
+
|
|
338
|
+
# Check if line opens a new block (ends with :)
|
|
339
|
+
if stripped.rstrip().endswith(':'):
|
|
340
|
+
# Remove trailing colon, add opening brace
|
|
341
|
+
transformed = transformed.rstrip(':').rstrip() + ' {'
|
|
342
|
+
# Get next line's indentation
|
|
343
|
+
next_indent = self._get_next_indent(lines, i)
|
|
344
|
+
if next_indent > current_indent:
|
|
345
|
+
indent_stack.append(next_indent)
|
|
346
|
+
elif not transformed.endswith(('{', '}', ';')):
|
|
347
|
+
# Add semicolon if not a block statement
|
|
348
|
+
transformed += ';'
|
|
349
|
+
|
|
350
|
+
result.append(' ' * current_indent + transformed)
|
|
351
|
+
|
|
352
|
+
# Close remaining open blocks
|
|
353
|
+
while len(indent_stack) > 1:
|
|
354
|
+
indent_stack.pop()
|
|
355
|
+
result.append(' ' * indent_stack[-1] + '}')
|
|
356
|
+
|
|
357
|
+
return '\n'.join(result)
|
|
358
|
+
|
|
359
|
+
def _transform_statement(self, stmt: str) -> str:
|
|
360
|
+
"""Transform a single Python statement to CSSL"""
|
|
361
|
+
|
|
362
|
+
# def func(args): -> define func(args)
|
|
363
|
+
# Handles: def func(self, param: type = default) -> ReturnType:
|
|
364
|
+
if stmt.startswith('def '):
|
|
365
|
+
# Match function with optional return type annotation
|
|
366
|
+
match = re.match(r'def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*\w+)?\s*:', stmt)
|
|
367
|
+
if match:
|
|
368
|
+
func_name = match.group(1)
|
|
369
|
+
params = match.group(2)
|
|
370
|
+
# Strip type annotations from parameters
|
|
371
|
+
params = self._strip_type_annotations(params)
|
|
372
|
+
# Remove 'self' parameter (first param in methods)
|
|
373
|
+
params = self._strip_self_param(params)
|
|
374
|
+
return f"define {func_name}({params})"
|
|
375
|
+
|
|
376
|
+
# class ClassName(Parent): -> class ClassName : extends Parent
|
|
377
|
+
if stmt.startswith('class '):
|
|
378
|
+
match = re.match(r'class\s+(\w+)(?:\s*\((.*?)\))?\s*:', stmt)
|
|
379
|
+
if match:
|
|
380
|
+
class_name = match.group(1)
|
|
381
|
+
parent = match.group(2)
|
|
382
|
+
if parent and parent.strip():
|
|
383
|
+
return f"class {class_name} : extends {parent}"
|
|
384
|
+
return f"class {class_name}"
|
|
385
|
+
|
|
386
|
+
# if condition: -> if (condition)
|
|
387
|
+
if stmt.startswith('if '):
|
|
388
|
+
match = re.match(r'if\s+(.+?):', stmt)
|
|
389
|
+
if match:
|
|
390
|
+
condition = match.group(1)
|
|
391
|
+
return f"if ({condition})"
|
|
392
|
+
|
|
393
|
+
# elif condition: -> elif (condition)
|
|
394
|
+
if stmt.startswith('elif '):
|
|
395
|
+
match = re.match(r'elif\s+(.+?):', stmt)
|
|
396
|
+
if match:
|
|
397
|
+
condition = match.group(1)
|
|
398
|
+
return f"elif ({condition})"
|
|
399
|
+
|
|
400
|
+
# else: -> else
|
|
401
|
+
if stmt.strip() == 'else:':
|
|
402
|
+
return 'else'
|
|
403
|
+
|
|
404
|
+
# while condition: -> while (condition)
|
|
405
|
+
if stmt.startswith('while '):
|
|
406
|
+
match = re.match(r'while\s+(.+?):', stmt)
|
|
407
|
+
if match:
|
|
408
|
+
condition = match.group(1)
|
|
409
|
+
return f"while ({condition})"
|
|
410
|
+
|
|
411
|
+
# for i in range(n): -> for (i in range(0, n))
|
|
412
|
+
# for i in iterable: -> for (i in iterable)
|
|
413
|
+
if stmt.startswith('for '):
|
|
414
|
+
match = re.match(r'for\s+(\w+)\s+in\s+(.+?):', stmt)
|
|
415
|
+
if match:
|
|
416
|
+
var = match.group(1)
|
|
417
|
+
iterable = match.group(2)
|
|
418
|
+
# Handle range with single argument
|
|
419
|
+
range_match = re.match(r'range\s*\(\s*(\d+)\s*\)', iterable)
|
|
420
|
+
if range_match:
|
|
421
|
+
return f"for ({var} in range(0, {range_match.group(1)}))"
|
|
422
|
+
return f"for ({var} in {iterable})"
|
|
423
|
+
|
|
424
|
+
# try: -> try
|
|
425
|
+
if stmt.strip() == 'try:':
|
|
426
|
+
return 'try'
|
|
427
|
+
|
|
428
|
+
# except Exception as e: -> catch (e)
|
|
429
|
+
if stmt.startswith('except'):
|
|
430
|
+
match = re.match(r'except\s*(?:\w+\s+)?(?:as\s+(\w+))?\s*:', stmt)
|
|
431
|
+
if match:
|
|
432
|
+
var = match.group(1) or 'e'
|
|
433
|
+
return f"catch ({var})"
|
|
434
|
+
|
|
435
|
+
# finally: -> finally
|
|
436
|
+
if stmt.strip() == 'finally:':
|
|
437
|
+
return 'finally'
|
|
438
|
+
|
|
439
|
+
# return value -> return value
|
|
440
|
+
# (no change needed, just ensure semicolon is added)
|
|
441
|
+
|
|
442
|
+
# Common replacements
|
|
443
|
+
stmt = self._apply_replacements(stmt)
|
|
444
|
+
|
|
445
|
+
return stmt
|
|
446
|
+
|
|
447
|
+
def _apply_replacements(self, stmt: str) -> str:
|
|
448
|
+
"""Apply common Python to CSSL replacements"""
|
|
449
|
+
# print() -> printl()
|
|
450
|
+
stmt = re.sub(r'\bprint\s*\(', 'printl(', stmt)
|
|
451
|
+
|
|
452
|
+
# self. -> this->
|
|
453
|
+
stmt = stmt.replace('self.', 'this->')
|
|
454
|
+
|
|
455
|
+
# None -> null
|
|
456
|
+
stmt = re.sub(r'\bNone\b', 'null', stmt)
|
|
457
|
+
|
|
458
|
+
# True/False stay the same (CSSL supports both cases)
|
|
459
|
+
|
|
460
|
+
# v4.2.0: Transform compound assignment operators (CSSL doesn't have +=, -=, etc.)
|
|
461
|
+
# var -= expr -> var = var - expr
|
|
462
|
+
# var += expr -> var = var + expr
|
|
463
|
+
# var *= expr -> var = var * expr
|
|
464
|
+
# var /= expr -> var = var / expr
|
|
465
|
+
# Handles: this->health -= damage, x += 1, etc.
|
|
466
|
+
compound_ops = [
|
|
467
|
+
(r'(\S+)\s*-=\s*(.+)', r'\1 = \1 - \2'), # -= to = -
|
|
468
|
+
(r'(\S+)\s*\+=\s*(.+)', r'\1 = \1 + \2'), # += to = +
|
|
469
|
+
(r'(\S+)\s*\*=\s*(.+)', r'\1 = \1 * \2'), # *= to = *
|
|
470
|
+
(r'(\S+)\s*/=\s*(.+)', r'\1 = \1 / \2'), # /= to = /
|
|
471
|
+
]
|
|
472
|
+
for pattern, replacement in compound_ops:
|
|
473
|
+
stmt = re.sub(pattern, replacement, stmt)
|
|
474
|
+
|
|
475
|
+
# __init__ -> constructor handling would be done at class level
|
|
476
|
+
|
|
477
|
+
return stmt
|
|
478
|
+
|
|
479
|
+
def _get_next_indent(self, lines: List[str], current_idx: int) -> int:
|
|
480
|
+
"""Get indentation of next non-empty, non-comment line"""
|
|
481
|
+
for i in range(current_idx + 1, len(lines)):
|
|
482
|
+
line = lines[i]
|
|
483
|
+
stripped = line.lstrip()
|
|
484
|
+
if stripped and not stripped.startswith('#'):
|
|
485
|
+
return len(line) - len(stripped)
|
|
486
|
+
return 0
|
|
487
|
+
|
|
488
|
+
def _strip_type_annotations(self, params: str) -> str:
|
|
489
|
+
"""Strip Python type annotations from parameter list.
|
|
490
|
+
|
|
491
|
+
Examples:
|
|
492
|
+
'self, x: int, y: str = "default"' -> 'self, x, y = "default"'
|
|
493
|
+
'a: List[int], b: Dict[str, int]' -> 'a, b'
|
|
494
|
+
"""
|
|
495
|
+
if not params.strip():
|
|
496
|
+
return params
|
|
497
|
+
|
|
498
|
+
result = []
|
|
499
|
+
# Split by comma, but be careful with nested brackets
|
|
500
|
+
depth = 0
|
|
501
|
+
current = []
|
|
502
|
+
for char in params + ',':
|
|
503
|
+
if char in '([{':
|
|
504
|
+
depth += 1
|
|
505
|
+
current.append(char)
|
|
506
|
+
elif char in ')]}':
|
|
507
|
+
depth -= 1
|
|
508
|
+
current.append(char)
|
|
509
|
+
elif char == ',' and depth == 0:
|
|
510
|
+
param = ''.join(current).strip()
|
|
511
|
+
if param:
|
|
512
|
+
# Strip type annotation: "name: type = default" -> "name = default"
|
|
513
|
+
# or "name: type" -> "name"
|
|
514
|
+
colon_match = re.match(r'^(\w+)\s*:', param)
|
|
515
|
+
if colon_match:
|
|
516
|
+
param_name = colon_match.group(1)
|
|
517
|
+
# Check for default value after type annotation
|
|
518
|
+
default_match = re.search(r'=\s*(.+)$', param)
|
|
519
|
+
if default_match:
|
|
520
|
+
param = f"{param_name} = {default_match.group(1)}"
|
|
521
|
+
else:
|
|
522
|
+
param = param_name
|
|
523
|
+
result.append(param)
|
|
524
|
+
current = []
|
|
525
|
+
else:
|
|
526
|
+
current.append(char)
|
|
527
|
+
|
|
528
|
+
return ', '.join(result)
|
|
529
|
+
|
|
530
|
+
def _strip_self_param(self, params: str) -> str:
|
|
531
|
+
"""Remove 'self' parameter from method parameters.
|
|
532
|
+
|
|
533
|
+
Examples:
|
|
534
|
+
'self, x, y' -> 'x, y'
|
|
535
|
+
'self' -> ''
|
|
536
|
+
'x, y' -> 'x, y'
|
|
537
|
+
"""
|
|
538
|
+
if not params.strip():
|
|
539
|
+
return params
|
|
540
|
+
|
|
541
|
+
parts = [p.strip() for p in params.split(',')]
|
|
542
|
+
if parts and parts[0] == 'self':
|
|
543
|
+
parts = parts[1:]
|
|
544
|
+
return ', '.join(parts)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
class JavaScriptTransformer(LanguageTransformer):
|
|
548
|
+
"""
|
|
549
|
+
Transforms JavaScript syntax to CSSL.
|
|
550
|
+
|
|
551
|
+
Handles:
|
|
552
|
+
- let/const/var -> dynamic
|
|
553
|
+
- function name() -> define name()
|
|
554
|
+
- console.log() -> printl()
|
|
555
|
+
- null/undefined -> null
|
|
556
|
+
- Arrow functions (basic support)
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
def transform_source(self, source: str) -> str:
|
|
560
|
+
lines = source.split('\n')
|
|
561
|
+
result = []
|
|
562
|
+
|
|
563
|
+
for line in lines:
|
|
564
|
+
stripped = line.strip()
|
|
565
|
+
|
|
566
|
+
# Skip empty lines
|
|
567
|
+
if not stripped:
|
|
568
|
+
continue
|
|
569
|
+
|
|
570
|
+
# Convert comments
|
|
571
|
+
if stripped.startswith('//'):
|
|
572
|
+
result.append(stripped)
|
|
573
|
+
continue
|
|
574
|
+
|
|
575
|
+
# Transform the line
|
|
576
|
+
transformed = self._transform_line(stripped)
|
|
577
|
+
result.append(transformed)
|
|
578
|
+
|
|
579
|
+
return '\n'.join(result)
|
|
580
|
+
|
|
581
|
+
def _transform_line(self, line: str) -> str:
|
|
582
|
+
"""Transform a single JavaScript line to CSSL"""
|
|
583
|
+
|
|
584
|
+
# function name(args) { -> define name(args) {
|
|
585
|
+
match = re.match(r'function\s+(\w+)\s*\((.*?)\)\s*\{?', line)
|
|
586
|
+
if match:
|
|
587
|
+
func_name = match.group(1)
|
|
588
|
+
params = match.group(2)
|
|
589
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
590
|
+
return f"define {func_name}({params}){suffix}"
|
|
591
|
+
|
|
592
|
+
# const/let/var name = value; -> dynamic name = value;
|
|
593
|
+
match = re.match(r'(const|let|var)\s+(\w+)\s*=\s*(.+)', line)
|
|
594
|
+
if match:
|
|
595
|
+
var_name = match.group(2)
|
|
596
|
+
value = match.group(3)
|
|
597
|
+
return f"dynamic {var_name} = {value}"
|
|
598
|
+
|
|
599
|
+
# const/let/var name; -> dynamic name;
|
|
600
|
+
match = re.match(r'(const|let|var)\s+(\w+)\s*;', line)
|
|
601
|
+
if match:
|
|
602
|
+
var_name = match.group(2)
|
|
603
|
+
return f"dynamic {var_name};"
|
|
604
|
+
|
|
605
|
+
# class Name { or class Name extends Parent {
|
|
606
|
+
match = re.match(r'class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{?', line)
|
|
607
|
+
if match:
|
|
608
|
+
class_name = match.group(1)
|
|
609
|
+
parent = match.group(2)
|
|
610
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
611
|
+
if parent:
|
|
612
|
+
return f"class {class_name} : extends {parent}{suffix}"
|
|
613
|
+
return f"class {class_name}{suffix}"
|
|
614
|
+
|
|
615
|
+
# constructor(args) { -> constr ClassName(args) {
|
|
616
|
+
if line.strip().startswith('constructor'):
|
|
617
|
+
match = re.match(r'constructor\s*\((.*?)\)\s*\{?', line)
|
|
618
|
+
if match:
|
|
619
|
+
params = match.group(1)
|
|
620
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
621
|
+
return f"constr __init__({params}){suffix}"
|
|
622
|
+
|
|
623
|
+
# Common replacements
|
|
624
|
+
line = self._apply_replacements(line)
|
|
625
|
+
|
|
626
|
+
return line
|
|
627
|
+
|
|
628
|
+
def _apply_replacements(self, line: str) -> str:
|
|
629
|
+
"""Apply common JavaScript to CSSL replacements"""
|
|
630
|
+
# console.log() -> printl()
|
|
631
|
+
line = re.sub(r'console\.log\s*\(', 'printl(', line)
|
|
632
|
+
|
|
633
|
+
# console.error() -> error()
|
|
634
|
+
line = re.sub(r'console\.error\s*\(', 'error(', line)
|
|
635
|
+
|
|
636
|
+
# console.warn() -> warn()
|
|
637
|
+
line = re.sub(r'console\.warn\s*\(', 'warn(', line)
|
|
638
|
+
|
|
639
|
+
# true/false -> True/False
|
|
640
|
+
line = re.sub(r'\btrue\b', 'True', line)
|
|
641
|
+
line = re.sub(r'\bfalse\b', 'False', line)
|
|
642
|
+
|
|
643
|
+
# undefined -> null
|
|
644
|
+
line = re.sub(r'\bundefined\b', 'null', line)
|
|
645
|
+
|
|
646
|
+
# this. stays as this. (CSSL uses this-> but also supports this.)
|
|
647
|
+
|
|
648
|
+
return line
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
class JavaTransformer(LanguageTransformer):
|
|
652
|
+
"""
|
|
653
|
+
Transforms Java syntax to CSSL.
|
|
654
|
+
|
|
655
|
+
Handles:
|
|
656
|
+
- System.out.println() -> printl()
|
|
657
|
+
- true/false -> True/False
|
|
658
|
+
- String -> string (optional lowercase)
|
|
659
|
+
"""
|
|
660
|
+
|
|
661
|
+
def transform_source(self, source: str) -> str:
|
|
662
|
+
lines = source.split('\n')
|
|
663
|
+
result = []
|
|
664
|
+
|
|
665
|
+
for line in lines:
|
|
666
|
+
stripped = line.strip()
|
|
667
|
+
|
|
668
|
+
if not stripped:
|
|
669
|
+
continue
|
|
670
|
+
|
|
671
|
+
if stripped.startswith('//'):
|
|
672
|
+
result.append(stripped)
|
|
673
|
+
continue
|
|
674
|
+
|
|
675
|
+
transformed = self._transform_line(stripped)
|
|
676
|
+
result.append(transformed)
|
|
677
|
+
|
|
678
|
+
return '\n'.join(result)
|
|
679
|
+
|
|
680
|
+
def _transform_line(self, line: str) -> str:
|
|
681
|
+
"""Transform a single Java line to CSSL"""
|
|
682
|
+
|
|
683
|
+
# public/private/protected static void main(String[] args)
|
|
684
|
+
# -> define main(args)
|
|
685
|
+
match = re.match(r'(?:public|private|protected)?\s*(?:static)?\s*(?:void|int|String|boolean|float|double)\s+(\w+)\s*\((.*?)\)\s*\{?', line)
|
|
686
|
+
if match:
|
|
687
|
+
func_name = match.group(1)
|
|
688
|
+
params = match.group(2)
|
|
689
|
+
# Simplify Java params: String[] args -> args
|
|
690
|
+
params = re.sub(r'\w+(?:\[\])?\s+(\w+)', r'\1', params)
|
|
691
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
692
|
+
return f"define {func_name}({params}){suffix}"
|
|
693
|
+
|
|
694
|
+
# class Name extends Parent implements Interface {
|
|
695
|
+
match = re.match(r'(?:public\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+\w+(?:,\s*\w+)*)?\s*\{?', line)
|
|
696
|
+
if match:
|
|
697
|
+
class_name = match.group(1)
|
|
698
|
+
parent = match.group(2)
|
|
699
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
700
|
+
if parent:
|
|
701
|
+
return f"class {class_name} : extends {parent}{suffix}"
|
|
702
|
+
return f"class {class_name}{suffix}"
|
|
703
|
+
|
|
704
|
+
# Common replacements
|
|
705
|
+
line = self._apply_replacements(line)
|
|
706
|
+
|
|
707
|
+
return line
|
|
708
|
+
|
|
709
|
+
def _apply_replacements(self, line: str) -> str:
|
|
710
|
+
"""Apply common Java to CSSL replacements"""
|
|
711
|
+
# System.out.println() -> printl()
|
|
712
|
+
line = re.sub(r'System\.out\.println\s*\(', 'printl(', line)
|
|
713
|
+
line = re.sub(r'System\.out\.print\s*\(', 'print(', line)
|
|
714
|
+
|
|
715
|
+
# true/false -> True/False
|
|
716
|
+
line = re.sub(r'\btrue\b', 'True', line)
|
|
717
|
+
line = re.sub(r'\bfalse\b', 'False', line)
|
|
718
|
+
|
|
719
|
+
# String -> string (CSSL convention)
|
|
720
|
+
line = re.sub(r'\bString\b', 'string', line)
|
|
721
|
+
|
|
722
|
+
return line
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
class CSharpTransformer(LanguageTransformer):
|
|
726
|
+
"""
|
|
727
|
+
Transforms C# syntax to CSSL.
|
|
728
|
+
|
|
729
|
+
Handles:
|
|
730
|
+
- Console.WriteLine() -> printl()
|
|
731
|
+
- true/false -> True/False
|
|
732
|
+
- var -> dynamic
|
|
733
|
+
"""
|
|
734
|
+
|
|
735
|
+
def transform_source(self, source: str) -> str:
|
|
736
|
+
lines = source.split('\n')
|
|
737
|
+
result = []
|
|
738
|
+
|
|
739
|
+
for line in lines:
|
|
740
|
+
stripped = line.strip()
|
|
741
|
+
|
|
742
|
+
if not stripped:
|
|
743
|
+
continue
|
|
744
|
+
|
|
745
|
+
if stripped.startswith('//'):
|
|
746
|
+
result.append(stripped)
|
|
747
|
+
continue
|
|
748
|
+
|
|
749
|
+
transformed = self._transform_line(stripped)
|
|
750
|
+
result.append(transformed)
|
|
751
|
+
|
|
752
|
+
return '\n'.join(result)
|
|
753
|
+
|
|
754
|
+
def _transform_line(self, line: str) -> str:
|
|
755
|
+
"""Transform a single C# line to CSSL"""
|
|
756
|
+
|
|
757
|
+
# public/private void MethodName(params) {
|
|
758
|
+
match = re.match(r'(?:public|private|protected|internal)?\s*(?:static)?\s*(?:async)?\s*(?:void|int|string|bool|float|double|var|dynamic|\w+)\s+(\w+)\s*\((.*?)\)\s*\{?', line)
|
|
759
|
+
if match and not line.strip().startswith('class'):
|
|
760
|
+
func_name = match.group(1)
|
|
761
|
+
params = match.group(2)
|
|
762
|
+
# Simplify C# params: string name -> name
|
|
763
|
+
params = re.sub(r'\w+\s+(\w+)', r'\1', params)
|
|
764
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
765
|
+
return f"define {func_name}({params}){suffix}"
|
|
766
|
+
|
|
767
|
+
# class Name : Parent {
|
|
768
|
+
match = re.match(r'(?:public\s+)?(?:partial\s+)?class\s+(\w+)(?:\s*:\s*(\w+))?\s*\{?', line)
|
|
769
|
+
if match:
|
|
770
|
+
class_name = match.group(1)
|
|
771
|
+
parent = match.group(2)
|
|
772
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
773
|
+
if parent:
|
|
774
|
+
return f"class {class_name} : extends {parent}{suffix}"
|
|
775
|
+
return f"class {class_name}{suffix}"
|
|
776
|
+
|
|
777
|
+
# var name = value; -> dynamic name = value;
|
|
778
|
+
match = re.match(r'var\s+(\w+)\s*=\s*(.+)', line)
|
|
779
|
+
if match:
|
|
780
|
+
var_name = match.group(1)
|
|
781
|
+
value = match.group(2)
|
|
782
|
+
return f"dynamic {var_name} = {value}"
|
|
783
|
+
|
|
784
|
+
# Common replacements
|
|
785
|
+
line = self._apply_replacements(line)
|
|
786
|
+
|
|
787
|
+
return line
|
|
788
|
+
|
|
789
|
+
def _apply_replacements(self, line: str) -> str:
|
|
790
|
+
"""Apply common C# to CSSL replacements"""
|
|
791
|
+
# Console.WriteLine() -> printl()
|
|
792
|
+
line = re.sub(r'Console\.WriteLine\s*\(', 'printl(', line)
|
|
793
|
+
line = re.sub(r'Console\.Write\s*\(', 'print(', line)
|
|
794
|
+
|
|
795
|
+
# true/false -> True/False
|
|
796
|
+
line = re.sub(r'\btrue\b', 'True', line)
|
|
797
|
+
line = re.sub(r'\bfalse\b', 'False', line)
|
|
798
|
+
|
|
799
|
+
return line
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
class CppTransformer(LanguageTransformer):
|
|
803
|
+
"""
|
|
804
|
+
Transforms C++ syntax to CSSL.
|
|
805
|
+
|
|
806
|
+
Handles:
|
|
807
|
+
- std::cout << x << std::endl; -> printl(x);
|
|
808
|
+
- nullptr -> null
|
|
809
|
+
- auto -> dynamic
|
|
810
|
+
- true/false -> True/False
|
|
811
|
+
"""
|
|
812
|
+
|
|
813
|
+
def transform_source(self, source: str) -> str:
|
|
814
|
+
lines = source.split('\n')
|
|
815
|
+
result = []
|
|
816
|
+
|
|
817
|
+
for line in lines:
|
|
818
|
+
stripped = line.strip()
|
|
819
|
+
|
|
820
|
+
if not stripped:
|
|
821
|
+
continue
|
|
822
|
+
|
|
823
|
+
if stripped.startswith('//'):
|
|
824
|
+
result.append(stripped)
|
|
825
|
+
continue
|
|
826
|
+
|
|
827
|
+
transformed = self._transform_line(stripped)
|
|
828
|
+
result.append(transformed)
|
|
829
|
+
|
|
830
|
+
return '\n'.join(result)
|
|
831
|
+
|
|
832
|
+
def _transform_line(self, line: str) -> str:
|
|
833
|
+
"""Transform a single C++ line to CSSL"""
|
|
834
|
+
|
|
835
|
+
# void/int/etc functionName(params) {
|
|
836
|
+
match = re.match(r'(?:virtual\s+)?(?:static\s+)?(?:inline\s+)?(?:void|int|string|bool|float|double|auto|\w+)\s+(\w+)\s*\((.*?)\)\s*(?:const)?\s*(?:override)?\s*\{?', line)
|
|
837
|
+
if match and not any(kw in line for kw in ['class ', 'struct ', 'namespace ']):
|
|
838
|
+
func_name = match.group(1)
|
|
839
|
+
params = match.group(2)
|
|
840
|
+
# Simplify C++ params: const std::string& name -> name
|
|
841
|
+
params = re.sub(r'(?:const\s+)?(?:std::)?(?:\w+)(?:&|\*)?\s+(\w+)', r'\1', params)
|
|
842
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
843
|
+
return f"define {func_name}({params}){suffix}"
|
|
844
|
+
|
|
845
|
+
# class Name : public Parent {
|
|
846
|
+
match = re.match(r'class\s+(\w+)(?:\s*:\s*(?:public|protected|private)\s+(\w+))?\s*\{?', line)
|
|
847
|
+
if match:
|
|
848
|
+
class_name = match.group(1)
|
|
849
|
+
parent = match.group(2)
|
|
850
|
+
suffix = ' {' if line.rstrip().endswith('{') else ''
|
|
851
|
+
if parent:
|
|
852
|
+
return f"class {class_name} : extends {parent}{suffix}"
|
|
853
|
+
return f"class {class_name}{suffix}"
|
|
854
|
+
|
|
855
|
+
# auto name = value; -> dynamic name = value;
|
|
856
|
+
match = re.match(r'auto\s+(\w+)\s*=\s*(.+)', line)
|
|
857
|
+
if match:
|
|
858
|
+
var_name = match.group(1)
|
|
859
|
+
value = match.group(2)
|
|
860
|
+
return f"dynamic {var_name} = {value}"
|
|
861
|
+
|
|
862
|
+
# Common replacements
|
|
863
|
+
line = self._apply_replacements(line)
|
|
864
|
+
|
|
865
|
+
return line
|
|
866
|
+
|
|
867
|
+
def _apply_replacements(self, line: str) -> str:
|
|
868
|
+
"""Apply common C++ to CSSL replacements"""
|
|
869
|
+
# std::cout << x << std::endl; -> printl(x);
|
|
870
|
+
match = re.match(r'std::cout\s*<<\s*(.*?)\s*<<\s*std::endl\s*;', line)
|
|
871
|
+
if match:
|
|
872
|
+
content = match.group(1)
|
|
873
|
+
return f'printl({content});'
|
|
874
|
+
|
|
875
|
+
match = re.match(r'std::cout\s*<<\s*(.*?)\s*;', line)
|
|
876
|
+
if match:
|
|
877
|
+
content = match.group(1)
|
|
878
|
+
return f'print({content});'
|
|
879
|
+
|
|
880
|
+
# true/false -> True/False
|
|
881
|
+
line = re.sub(r'\btrue\b', 'True', line)
|
|
882
|
+
line = re.sub(r'\bfalse\b', 'False', line)
|
|
883
|
+
|
|
884
|
+
# nullptr -> null
|
|
885
|
+
line = re.sub(r'\bnullptr\b', 'null', line)
|
|
886
|
+
|
|
887
|
+
# auto -> dynamic (for standalone declarations)
|
|
888
|
+
line = re.sub(r'\bauto\b', 'dynamic', line)
|
|
889
|
+
|
|
890
|
+
return line
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
# =============================================================================
|
|
894
|
+
# CROSS-LANGUAGE RUNTIME BRIDGES (v4.1.1)
|
|
895
|
+
# =============================================================================
|
|
896
|
+
|
|
897
|
+
class RuntimeBridge:
|
|
898
|
+
"""
|
|
899
|
+
Base class for cross-language runtime bridges.
|
|
900
|
+
|
|
901
|
+
v4.1.1: Provides bidirectional instance sharing and access between
|
|
902
|
+
CSSL and target languages (C++, Java, JavaScript).
|
|
903
|
+
"""
|
|
904
|
+
|
|
905
|
+
def __init__(self):
|
|
906
|
+
self._instances: Dict[str, Any] = {}
|
|
907
|
+
self._initialized = False
|
|
908
|
+
|
|
909
|
+
def initialize(self) -> bool:
|
|
910
|
+
"""Initialize the runtime bridge. Returns True if successful."""
|
|
911
|
+
raise NotImplementedError
|
|
912
|
+
|
|
913
|
+
def is_available(self) -> bool:
|
|
914
|
+
"""Check if this runtime is available on the system."""
|
|
915
|
+
raise NotImplementedError
|
|
916
|
+
|
|
917
|
+
def share(self, name: str, instance: Any) -> None:
|
|
918
|
+
"""Share an instance for cross-language access."""
|
|
919
|
+
self._instances[name] = instance
|
|
920
|
+
|
|
921
|
+
def get_instance(self, name: str) -> Any:
|
|
922
|
+
"""Get a shared instance by name."""
|
|
923
|
+
return self._instances.get(name)
|
|
924
|
+
|
|
925
|
+
def has_instance(self, name: str) -> bool:
|
|
926
|
+
"""Check if an instance exists."""
|
|
927
|
+
return name in self._instances
|
|
928
|
+
|
|
929
|
+
def list_available(self) -> List[str]:
|
|
930
|
+
"""List all available instances and classes."""
|
|
931
|
+
return sorted(self._instances.keys())
|
|
932
|
+
|
|
933
|
+
def call_method(self, instance: Any, method: str, *args) -> Any:
|
|
934
|
+
"""Call a method on a foreign language object."""
|
|
935
|
+
raise NotImplementedError
|
|
936
|
+
|
|
937
|
+
def get_field(self, instance: Any, field: str) -> Any:
|
|
938
|
+
"""Get a field value from a foreign language object."""
|
|
939
|
+
raise NotImplementedError
|
|
940
|
+
|
|
941
|
+
def set_field(self, instance: Any, field: str, value: Any) -> None:
|
|
942
|
+
"""Set a field value on a foreign language object."""
|
|
943
|
+
raise NotImplementedError
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
class CppRuntimeBridge(RuntimeBridge):
|
|
947
|
+
"""
|
|
948
|
+
C++ Runtime Bridge using IncludeCPP's pybind11 modules.
|
|
949
|
+
|
|
950
|
+
Accesses C++ classes and objects via the generated Python bindings.
|
|
951
|
+
v4.1.1: Full bidirectional support with automatic module discovery.
|
|
952
|
+
|
|
953
|
+
Usage in CSSL:
|
|
954
|
+
cpp = libinclude("c++");
|
|
955
|
+
|
|
956
|
+
// List available C++ classes/modules
|
|
957
|
+
printl(cpp.list_available());
|
|
958
|
+
|
|
959
|
+
// Access a C++ module built with IncludeCPP
|
|
960
|
+
cpp.load_module("mymodule");
|
|
961
|
+
|
|
962
|
+
// Create C++ instance
|
|
963
|
+
obj = cpp.create("MyClass", 10, 20);
|
|
964
|
+
cpp.share("MyInstance", obj);
|
|
965
|
+
|
|
966
|
+
// Access via $ syntax
|
|
967
|
+
instance = cpp$MyInstance;
|
|
968
|
+
|
|
969
|
+
// Direct class access (if module is loaded)
|
|
970
|
+
MyClass = cpp$MyClass;
|
|
971
|
+
obj2 = new MyClass(5, 10);
|
|
972
|
+
"""
|
|
973
|
+
|
|
974
|
+
def __init__(self):
|
|
975
|
+
super().__init__()
|
|
976
|
+
self._modules: Dict[str, Any] = {}
|
|
977
|
+
self._api = None
|
|
978
|
+
self._class_cache: Dict[str, Any] = {} # Cache class lookups
|
|
979
|
+
|
|
980
|
+
def initialize(self) -> bool:
|
|
981
|
+
"""Initialize by connecting to IncludeCPP's CppApi."""
|
|
982
|
+
if self._initialized:
|
|
983
|
+
return True
|
|
984
|
+
try:
|
|
985
|
+
from ...cpp_api import CppApi
|
|
986
|
+
self._api = CppApi()
|
|
987
|
+
self._initialized = True
|
|
988
|
+
# Auto-load all registered modules
|
|
989
|
+
for name in self._api.registry.keys():
|
|
990
|
+
try:
|
|
991
|
+
module = self._api.include(name)
|
|
992
|
+
self._modules[name] = module
|
|
993
|
+
# Cache all classes from the module
|
|
994
|
+
self._cache_module_classes(name, module)
|
|
995
|
+
except Exception:
|
|
996
|
+
pass # Module might not be built yet
|
|
997
|
+
return True
|
|
998
|
+
except Exception as e:
|
|
999
|
+
# CppApi not available, but bridge still works for sharing
|
|
1000
|
+
self._initialized = True
|
|
1001
|
+
return True
|
|
1002
|
+
|
|
1003
|
+
def _cache_module_classes(self, module_name: str, module: Any) -> None:
|
|
1004
|
+
"""Cache all classes and functions from a module for quick access."""
|
|
1005
|
+
for attr_name in dir(module):
|
|
1006
|
+
if not attr_name.startswith('_'):
|
|
1007
|
+
attr = getattr(module, attr_name, None)
|
|
1008
|
+
if attr is not None:
|
|
1009
|
+
self._class_cache[attr_name] = attr
|
|
1010
|
+
|
|
1011
|
+
def is_available(self) -> bool:
|
|
1012
|
+
"""C++ is always available via pybind11."""
|
|
1013
|
+
return True
|
|
1014
|
+
|
|
1015
|
+
def load_module(self, module_name: str) -> Any:
|
|
1016
|
+
"""Load a C++ module built with IncludeCPP."""
|
|
1017
|
+
if not self._initialized:
|
|
1018
|
+
self.initialize()
|
|
1019
|
+
|
|
1020
|
+
# Check if already loaded
|
|
1021
|
+
if module_name in self._modules:
|
|
1022
|
+
return self._modules[module_name]
|
|
1023
|
+
|
|
1024
|
+
if self._api and module_name in self._api.registry:
|
|
1025
|
+
module = self._api.include(module_name)
|
|
1026
|
+
self._modules[module_name] = module
|
|
1027
|
+
self._cache_module_classes(module_name, module)
|
|
1028
|
+
return module
|
|
1029
|
+
|
|
1030
|
+
# Try to import as a regular Python module (for external pybind11 modules)
|
|
1031
|
+
try:
|
|
1032
|
+
import importlib
|
|
1033
|
+
module = importlib.import_module(module_name)
|
|
1034
|
+
self._modules[module_name] = module
|
|
1035
|
+
self._cache_module_classes(module_name, module)
|
|
1036
|
+
return module
|
|
1037
|
+
except ImportError:
|
|
1038
|
+
pass
|
|
1039
|
+
|
|
1040
|
+
return None
|
|
1041
|
+
|
|
1042
|
+
def get_class(self, class_name: str) -> Any:
|
|
1043
|
+
"""Get a C++ class by name (from any loaded module)."""
|
|
1044
|
+
if not self._initialized:
|
|
1045
|
+
self.initialize()
|
|
1046
|
+
|
|
1047
|
+
# Check cache first
|
|
1048
|
+
if class_name in self._class_cache:
|
|
1049
|
+
return self._class_cache[class_name]
|
|
1050
|
+
|
|
1051
|
+
# Search through modules
|
|
1052
|
+
for mod in self._modules.values():
|
|
1053
|
+
if hasattr(mod, class_name):
|
|
1054
|
+
cls = getattr(mod, class_name)
|
|
1055
|
+
self._class_cache[class_name] = cls
|
|
1056
|
+
return cls
|
|
1057
|
+
|
|
1058
|
+
return None
|
|
1059
|
+
|
|
1060
|
+
def create(self, class_name: str, *args, module: str = None) -> Any:
|
|
1061
|
+
"""Create an instance of a C++ class."""
|
|
1062
|
+
if not self._initialized:
|
|
1063
|
+
self.initialize()
|
|
1064
|
+
|
|
1065
|
+
# Try cache first
|
|
1066
|
+
if class_name in self._class_cache:
|
|
1067
|
+
return self._class_cache[class_name](*args)
|
|
1068
|
+
|
|
1069
|
+
# Search through loaded modules for the class
|
|
1070
|
+
for mod_name, mod in self._modules.items():
|
|
1071
|
+
if module and mod_name != module:
|
|
1072
|
+
continue
|
|
1073
|
+
if hasattr(mod, class_name):
|
|
1074
|
+
cls = getattr(mod, class_name)
|
|
1075
|
+
self._class_cache[class_name] = cls
|
|
1076
|
+
return cls(*args)
|
|
1077
|
+
|
|
1078
|
+
raise ValueError(f"C++ class '{class_name}' not found. Available: {self.list_available()}")
|
|
1079
|
+
|
|
1080
|
+
def get_instance(self, name: str) -> Any:
|
|
1081
|
+
"""Get a shared instance OR a class by name."""
|
|
1082
|
+
# First check shared instances
|
|
1083
|
+
instance = self._instances.get(name)
|
|
1084
|
+
if instance is not None:
|
|
1085
|
+
return instance
|
|
1086
|
+
|
|
1087
|
+
# Then check class cache
|
|
1088
|
+
if name in self._class_cache:
|
|
1089
|
+
return self._class_cache[name]
|
|
1090
|
+
|
|
1091
|
+
# Finally search modules
|
|
1092
|
+
for mod in self._modules.values():
|
|
1093
|
+
if hasattr(mod, name):
|
|
1094
|
+
attr = getattr(mod, name)
|
|
1095
|
+
self._class_cache[name] = attr
|
|
1096
|
+
return attr
|
|
1097
|
+
|
|
1098
|
+
return None
|
|
1099
|
+
|
|
1100
|
+
def list_available(self) -> List[str]:
|
|
1101
|
+
"""List all available C++ classes, functions, and shared instances."""
|
|
1102
|
+
if not self._initialized:
|
|
1103
|
+
self.initialize()
|
|
1104
|
+
|
|
1105
|
+
available = set()
|
|
1106
|
+
|
|
1107
|
+
# Add shared instances
|
|
1108
|
+
available.update(self._instances.keys())
|
|
1109
|
+
|
|
1110
|
+
# Add cached classes
|
|
1111
|
+
available.update(self._class_cache.keys())
|
|
1112
|
+
|
|
1113
|
+
# Add module exports
|
|
1114
|
+
for mod in self._modules.values():
|
|
1115
|
+
for attr in dir(mod):
|
|
1116
|
+
if not attr.startswith('_'):
|
|
1117
|
+
available.add(attr)
|
|
1118
|
+
|
|
1119
|
+
return sorted(available)
|
|
1120
|
+
|
|
1121
|
+
def list_modules(self) -> List[str]:
|
|
1122
|
+
"""List all loaded C++ module names."""
|
|
1123
|
+
if not self._initialized:
|
|
1124
|
+
self.initialize()
|
|
1125
|
+
return list(self._modules.keys())
|
|
1126
|
+
|
|
1127
|
+
def call_method(self, instance: Any, method: str, *args) -> Any:
|
|
1128
|
+
"""Call a method on a C++ object."""
|
|
1129
|
+
if hasattr(instance, method):
|
|
1130
|
+
return getattr(instance, method)(*args)
|
|
1131
|
+
raise AttributeError(f"C++ object has no method '{method}'")
|
|
1132
|
+
|
|
1133
|
+
def get_field(self, instance: Any, field: str) -> Any:
|
|
1134
|
+
"""Get a field from a C++ object."""
|
|
1135
|
+
return getattr(instance, field)
|
|
1136
|
+
|
|
1137
|
+
def set_field(self, instance: Any, field: str, value: Any) -> None:
|
|
1138
|
+
"""Set a field on a C++ object."""
|
|
1139
|
+
setattr(instance, field, value)
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
class JavaRuntimeBridge(RuntimeBridge):
|
|
1143
|
+
"""
|
|
1144
|
+
Java Runtime Bridge using JPype.
|
|
1145
|
+
|
|
1146
|
+
Connects to JVM and allows access to Java classes and objects.
|
|
1147
|
+
v4.1.1: Full bidirectional support with class caching.
|
|
1148
|
+
|
|
1149
|
+
Requirements:
|
|
1150
|
+
pip install jpype1
|
|
1151
|
+
|
|
1152
|
+
Usage in CSSL:
|
|
1153
|
+
java = libinclude("java");
|
|
1154
|
+
|
|
1155
|
+
// Add JAR to classpath BEFORE first use
|
|
1156
|
+
java.add_classpath("/path/to/mylib.jar");
|
|
1157
|
+
|
|
1158
|
+
// Load a Java class
|
|
1159
|
+
MyClass = java.load_class("com.example.MyClass");
|
|
1160
|
+
|
|
1161
|
+
// Create instance
|
|
1162
|
+
obj = java.create("com.example.MyClass", 10, "hello");
|
|
1163
|
+
java.share("MyService", obj);
|
|
1164
|
+
|
|
1165
|
+
// Access via $ syntax
|
|
1166
|
+
service = java$MyService;
|
|
1167
|
+
|
|
1168
|
+
// List what's available
|
|
1169
|
+
printl(java.list_available());
|
|
1170
|
+
"""
|
|
1171
|
+
|
|
1172
|
+
def __init__(self):
|
|
1173
|
+
super().__init__()
|
|
1174
|
+
self._jpype = None
|
|
1175
|
+
self._jvm_started = False
|
|
1176
|
+
self._classpaths: List[str] = []
|
|
1177
|
+
self._loaded_classes: Dict[str, Any] = {} # Cache loaded classes
|
|
1178
|
+
|
|
1179
|
+
def initialize(self) -> bool:
|
|
1180
|
+
"""Initialize JPype and start JVM if not already running."""
|
|
1181
|
+
if self._initialized:
|
|
1182
|
+
return True
|
|
1183
|
+
try:
|
|
1184
|
+
import jpype
|
|
1185
|
+
import jpype.imports
|
|
1186
|
+
self._jpype = jpype
|
|
1187
|
+
|
|
1188
|
+
if not jpype.isJVMStarted():
|
|
1189
|
+
# Build classpath from all added paths
|
|
1190
|
+
import os
|
|
1191
|
+
sep = ";" if os.name == 'nt' else ":"
|
|
1192
|
+
classpath = sep.join(self._classpaths) if self._classpaths else None
|
|
1193
|
+
jpype.startJVM(classpath=classpath)
|
|
1194
|
+
self._jvm_started = True
|
|
1195
|
+
|
|
1196
|
+
self._initialized = True
|
|
1197
|
+
return True
|
|
1198
|
+
except ImportError:
|
|
1199
|
+
# JPype not installed
|
|
1200
|
+
self._initialized = True # Mark as initialized but limited
|
|
1201
|
+
return False
|
|
1202
|
+
except Exception as e:
|
|
1203
|
+
self._initialized = True
|
|
1204
|
+
return False
|
|
1205
|
+
|
|
1206
|
+
def is_available(self) -> bool:
|
|
1207
|
+
"""Check if JPype is installed."""
|
|
1208
|
+
try:
|
|
1209
|
+
import jpype
|
|
1210
|
+
return True
|
|
1211
|
+
except ImportError:
|
|
1212
|
+
return False
|
|
1213
|
+
|
|
1214
|
+
def add_classpath(self, path: str) -> None:
|
|
1215
|
+
"""Add a JAR or directory to the classpath (must be called before first use)."""
|
|
1216
|
+
import os
|
|
1217
|
+
# Normalize path for current OS
|
|
1218
|
+
path = os.path.abspath(path)
|
|
1219
|
+
if path not in self._classpaths:
|
|
1220
|
+
self._classpaths.append(path)
|
|
1221
|
+
|
|
1222
|
+
def load_class(self, class_name: str) -> Any:
|
|
1223
|
+
"""Load a Java class by fully qualified name."""
|
|
1224
|
+
if not self._initialized:
|
|
1225
|
+
self.initialize()
|
|
1226
|
+
if not self._jpype:
|
|
1227
|
+
raise RuntimeError("JPype not available. Install with: pip install jpype1")
|
|
1228
|
+
|
|
1229
|
+
# Check cache
|
|
1230
|
+
if class_name in self._loaded_classes:
|
|
1231
|
+
return self._loaded_classes[class_name]
|
|
1232
|
+
|
|
1233
|
+
# Load the Java class
|
|
1234
|
+
try:
|
|
1235
|
+
cls = self._jpype.JClass(class_name)
|
|
1236
|
+
self._loaded_classes[class_name] = cls
|
|
1237
|
+
# Also cache by simple name
|
|
1238
|
+
simple_name = class_name.rsplit('.', 1)[-1]
|
|
1239
|
+
self._loaded_classes[simple_name] = cls
|
|
1240
|
+
return cls
|
|
1241
|
+
except Exception as e:
|
|
1242
|
+
raise RuntimeError(f"Failed to load Java class '{class_name}': {e}")
|
|
1243
|
+
|
|
1244
|
+
def create(self, class_name: str, *args) -> Any:
|
|
1245
|
+
"""Create an instance of a Java class."""
|
|
1246
|
+
cls = self.load_class(class_name)
|
|
1247
|
+
return cls(*args)
|
|
1248
|
+
|
|
1249
|
+
def get_instance(self, name: str) -> Any:
|
|
1250
|
+
"""Get a shared instance OR a loaded class by name."""
|
|
1251
|
+
# First check shared instances
|
|
1252
|
+
if name in self._instances:
|
|
1253
|
+
return self._instances[name]
|
|
1254
|
+
# Then check loaded classes (by simple name)
|
|
1255
|
+
if name in self._loaded_classes:
|
|
1256
|
+
return self._loaded_classes[name]
|
|
1257
|
+
return None
|
|
1258
|
+
|
|
1259
|
+
def list_available(self) -> List[str]:
|
|
1260
|
+
"""List all available Java classes and shared instances."""
|
|
1261
|
+
available = set()
|
|
1262
|
+
available.update(self._instances.keys())
|
|
1263
|
+
# Add loaded classes (simple names only for readability)
|
|
1264
|
+
for name in self._loaded_classes.keys():
|
|
1265
|
+
if '.' not in name: # Only simple names
|
|
1266
|
+
available.add(name)
|
|
1267
|
+
return sorted(available)
|
|
1268
|
+
|
|
1269
|
+
def list_classpaths(self) -> List[str]:
|
|
1270
|
+
"""List all added classpaths."""
|
|
1271
|
+
return list(self._classpaths)
|
|
1272
|
+
|
|
1273
|
+
def call_method(self, instance: Any, method: str, *args) -> Any:
|
|
1274
|
+
"""Call a method on a Java object."""
|
|
1275
|
+
return getattr(instance, method)(*args)
|
|
1276
|
+
|
|
1277
|
+
def get_field(self, instance: Any, field: str) -> Any:
|
|
1278
|
+
"""Get a field from a Java object."""
|
|
1279
|
+
return getattr(instance, field)
|
|
1280
|
+
|
|
1281
|
+
def set_field(self, instance: Any, field: str, value: Any) -> None:
|
|
1282
|
+
"""Set a field on a Java object."""
|
|
1283
|
+
setattr(instance, field, value)
|
|
1284
|
+
|
|
1285
|
+
def call_static(self, class_name: str, method: str, *args) -> Any:
|
|
1286
|
+
"""Call a static method on a Java class."""
|
|
1287
|
+
cls = self.load_class(class_name)
|
|
1288
|
+
return getattr(cls, method)(*args)
|
|
1289
|
+
|
|
1290
|
+
def shutdown(self) -> None:
|
|
1291
|
+
"""Shutdown the JVM (only if we started it)."""
|
|
1292
|
+
if self._jvm_started and self._jpype and self._jpype.isJVMStarted():
|
|
1293
|
+
self._jpype.shutdownJVM()
|
|
1294
|
+
self._jvm_started = False
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
class JavaScriptRuntimeBridge(RuntimeBridge):
|
|
1298
|
+
"""
|
|
1299
|
+
JavaScript Runtime Bridge using Node.js subprocess.
|
|
1300
|
+
|
|
1301
|
+
Runs JavaScript code via Node.js and communicates via JSON IPC.
|
|
1302
|
+
v4.1.1: Full bidirectional support with persistent JS context.
|
|
1303
|
+
|
|
1304
|
+
Requirements:
|
|
1305
|
+
Node.js must be installed and in PATH
|
|
1306
|
+
|
|
1307
|
+
Usage in CSSL:
|
|
1308
|
+
js = libinclude("javascript");
|
|
1309
|
+
|
|
1310
|
+
// Execute JavaScript code
|
|
1311
|
+
result = js.eval("2 + 2");
|
|
1312
|
+
|
|
1313
|
+
// Define a function in JS context
|
|
1314
|
+
js.eval('''
|
|
1315
|
+
function greet(name) {
|
|
1316
|
+
return "Hello, " + name + "!";
|
|
1317
|
+
}
|
|
1318
|
+
''');
|
|
1319
|
+
|
|
1320
|
+
// Call it
|
|
1321
|
+
result = js.call("greet", "World");
|
|
1322
|
+
|
|
1323
|
+
// Store value in JS context for $ access
|
|
1324
|
+
js.set("myValue", 42);
|
|
1325
|
+
|
|
1326
|
+
// Access via $ syntax
|
|
1327
|
+
value = js$myValue;
|
|
1328
|
+
|
|
1329
|
+
// List what's available
|
|
1330
|
+
printl(js.list_available());
|
|
1331
|
+
"""
|
|
1332
|
+
|
|
1333
|
+
def __init__(self):
|
|
1334
|
+
super().__init__()
|
|
1335
|
+
self._process = None
|
|
1336
|
+
self._node_path = "node"
|
|
1337
|
+
self._js_context_vars: List[str] = [] # Track defined variables/functions
|
|
1338
|
+
|
|
1339
|
+
def initialize(self) -> bool:
|
|
1340
|
+
"""Start Node.js subprocess if not already running."""
|
|
1341
|
+
if self._initialized:
|
|
1342
|
+
return True
|
|
1343
|
+
try:
|
|
1344
|
+
import subprocess
|
|
1345
|
+
import json
|
|
1346
|
+
|
|
1347
|
+
# Check if Node.js is available
|
|
1348
|
+
result = subprocess.run([self._node_path, "--version"],
|
|
1349
|
+
capture_output=True, text=True,
|
|
1350
|
+
timeout=5)
|
|
1351
|
+
if result.returncode != 0:
|
|
1352
|
+
self._initialized = True
|
|
1353
|
+
return False
|
|
1354
|
+
|
|
1355
|
+
# Start persistent Node.js process with IPC
|
|
1356
|
+
self._process = subprocess.Popen(
|
|
1357
|
+
[self._node_path, "-e", self._get_ipc_server_code()],
|
|
1358
|
+
stdin=subprocess.PIPE,
|
|
1359
|
+
stdout=subprocess.PIPE,
|
|
1360
|
+
stderr=subprocess.PIPE,
|
|
1361
|
+
text=True,
|
|
1362
|
+
bufsize=1
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
self._initialized = True
|
|
1366
|
+
return True
|
|
1367
|
+
except Exception as e:
|
|
1368
|
+
self._initialized = True
|
|
1369
|
+
return False
|
|
1370
|
+
|
|
1371
|
+
def _get_ipc_server_code(self) -> str:
|
|
1372
|
+
"""Get the Node.js IPC server code."""
|
|
1373
|
+
return '''
|
|
1374
|
+
const readline = require('readline');
|
|
1375
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
1376
|
+
|
|
1377
|
+
// Global context for storing variables accessible via $
|
|
1378
|
+
const __cssl_context = {};
|
|
1379
|
+
|
|
1380
|
+
rl.on('line', (line) => {
|
|
1381
|
+
try {
|
|
1382
|
+
const cmd = JSON.parse(line);
|
|
1383
|
+
let result;
|
|
1384
|
+
|
|
1385
|
+
if (cmd.type === 'eval') {
|
|
1386
|
+
result = eval(cmd.code);
|
|
1387
|
+
} else if (cmd.type === 'call') {
|
|
1388
|
+
const fn = eval(cmd.func);
|
|
1389
|
+
result = fn(...(cmd.args || []));
|
|
1390
|
+
} else if (cmd.type === 'get') {
|
|
1391
|
+
// First check context, then global
|
|
1392
|
+
result = __cssl_context[cmd.name] !== undefined
|
|
1393
|
+
? __cssl_context[cmd.name]
|
|
1394
|
+
: (typeof global[cmd.name] !== 'undefined' ? global[cmd.name] : null);
|
|
1395
|
+
} else if (cmd.type === 'set') {
|
|
1396
|
+
__cssl_context[cmd.name] = cmd.value;
|
|
1397
|
+
global[cmd.name] = cmd.value;
|
|
1398
|
+
result = true;
|
|
1399
|
+
} else if (cmd.type === 'has') {
|
|
1400
|
+
result = cmd.name in __cssl_context || typeof global[cmd.name] !== 'undefined';
|
|
1401
|
+
} else if (cmd.type === 'list') {
|
|
1402
|
+
result = Object.keys(__cssl_context);
|
|
1403
|
+
} else if (cmd.type === 'define') {
|
|
1404
|
+
// Define a function that can be called later
|
|
1405
|
+
eval(cmd.code);
|
|
1406
|
+
if (cmd.name) {
|
|
1407
|
+
__cssl_context[cmd.name] = eval(cmd.name);
|
|
1408
|
+
}
|
|
1409
|
+
result = true;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
console.log(JSON.stringify({success: true, result: result}));
|
|
1413
|
+
} catch (e) {
|
|
1414
|
+
console.log(JSON.stringify({success: false, error: e.message}));
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
'''
|
|
1418
|
+
|
|
1419
|
+
def is_available(self) -> bool:
|
|
1420
|
+
"""Check if Node.js is available."""
|
|
1421
|
+
try:
|
|
1422
|
+
import subprocess
|
|
1423
|
+
result = subprocess.run([self._node_path, "--version"],
|
|
1424
|
+
capture_output=True, text=True,
|
|
1425
|
+
timeout=5)
|
|
1426
|
+
return result.returncode == 0
|
|
1427
|
+
except:
|
|
1428
|
+
return False
|
|
1429
|
+
|
|
1430
|
+
def eval(self, code: str) -> Any:
|
|
1431
|
+
"""Evaluate JavaScript code and return the result."""
|
|
1432
|
+
return self._send_command({"type": "eval", "code": code})
|
|
1433
|
+
|
|
1434
|
+
def call(self, func_name: str, *args) -> Any:
|
|
1435
|
+
"""Call a JavaScript function."""
|
|
1436
|
+
return self._send_command({"type": "call", "func": func_name, "args": list(args)})
|
|
1437
|
+
|
|
1438
|
+
def set(self, name: str, value: Any) -> None:
|
|
1439
|
+
"""Set a variable in the JS context for $ access."""
|
|
1440
|
+
self._send_command({"type": "set", "name": name, "value": value})
|
|
1441
|
+
self._js_context_vars.append(name)
|
|
1442
|
+
# Also store locally for Python-side access
|
|
1443
|
+
self._instances[name] = value
|
|
1444
|
+
|
|
1445
|
+
def get(self, name: str) -> Any:
|
|
1446
|
+
"""Get a variable from the JS context."""
|
|
1447
|
+
return self._send_command({"type": "get", "name": name})
|
|
1448
|
+
|
|
1449
|
+
def define(self, name: str, code: str) -> None:
|
|
1450
|
+
"""Define a function or class in the JS context."""
|
|
1451
|
+
self._send_command({"type": "define", "name": name, "code": code})
|
|
1452
|
+
self._js_context_vars.append(name)
|
|
1453
|
+
|
|
1454
|
+
def get_instance(self, name: str) -> Any:
|
|
1455
|
+
"""Get a shared instance from local cache or JS context."""
|
|
1456
|
+
# First check local Python instances
|
|
1457
|
+
if name in self._instances:
|
|
1458
|
+
return self._instances[name]
|
|
1459
|
+
|
|
1460
|
+
# Then try to get from JS context
|
|
1461
|
+
try:
|
|
1462
|
+
has_it = self._send_command({"type": "has", "name": name})
|
|
1463
|
+
if has_it:
|
|
1464
|
+
result = self._send_command({"type": "get", "name": name})
|
|
1465
|
+
if result is not None:
|
|
1466
|
+
self._instances[name] = result # Cache it
|
|
1467
|
+
return result
|
|
1468
|
+
except:
|
|
1469
|
+
pass
|
|
1470
|
+
|
|
1471
|
+
return None
|
|
1472
|
+
|
|
1473
|
+
def list_available(self) -> List[str]:
|
|
1474
|
+
"""List all available variables in JS context and local instances."""
|
|
1475
|
+
available = set(self._instances.keys())
|
|
1476
|
+
available.update(self._js_context_vars)
|
|
1477
|
+
|
|
1478
|
+
# Also get from JS context
|
|
1479
|
+
try:
|
|
1480
|
+
js_vars = self._send_command({"type": "list"})
|
|
1481
|
+
if js_vars:
|
|
1482
|
+
available.update(js_vars)
|
|
1483
|
+
except:
|
|
1484
|
+
pass
|
|
1485
|
+
|
|
1486
|
+
return sorted(available)
|
|
1487
|
+
|
|
1488
|
+
def _send_command(self, cmd: dict) -> Any:
|
|
1489
|
+
"""Send a command to Node.js and get the result."""
|
|
1490
|
+
if not self._initialized:
|
|
1491
|
+
self.initialize()
|
|
1492
|
+
if not self._process:
|
|
1493
|
+
raise RuntimeError("Node.js not available. Make sure Node.js is installed and in PATH.")
|
|
1494
|
+
|
|
1495
|
+
import json
|
|
1496
|
+
|
|
1497
|
+
try:
|
|
1498
|
+
self._process.stdin.write(json.dumps(cmd) + "\n")
|
|
1499
|
+
self._process.stdin.flush()
|
|
1500
|
+
|
|
1501
|
+
response_line = self._process.stdout.readline()
|
|
1502
|
+
if not response_line:
|
|
1503
|
+
raise RuntimeError("No response from Node.js process")
|
|
1504
|
+
|
|
1505
|
+
response = json.loads(response_line)
|
|
1506
|
+
|
|
1507
|
+
if response.get("success"):
|
|
1508
|
+
return response.get("result")
|
|
1509
|
+
else:
|
|
1510
|
+
raise RuntimeError(f"JavaScript error: {response.get('error')}")
|
|
1511
|
+
except json.JSONDecodeError as e:
|
|
1512
|
+
raise RuntimeError(f"Invalid JSON response from Node.js: {e}")
|
|
1513
|
+
|
|
1514
|
+
def call_method(self, instance: Any, method: str, *args) -> Any:
|
|
1515
|
+
"""For JS objects stored locally, call method."""
|
|
1516
|
+
if hasattr(instance, method):
|
|
1517
|
+
return getattr(instance, method)(*args)
|
|
1518
|
+
# For remote JS objects, use eval
|
|
1519
|
+
raise NotImplementedError("Remote JS method calls not yet supported")
|
|
1520
|
+
|
|
1521
|
+
def get_field(self, instance: Any, field: str) -> Any:
|
|
1522
|
+
return getattr(instance, field, None)
|
|
1523
|
+
|
|
1524
|
+
def set_field(self, instance: Any, field: str, value: Any) -> None:
|
|
1525
|
+
setattr(instance, field, value)
|
|
1526
|
+
|
|
1527
|
+
def require(self, module_name: str) -> Any:
|
|
1528
|
+
"""Require a Node.js module and return it."""
|
|
1529
|
+
return self.eval(f"require('{module_name}')")
|
|
1530
|
+
|
|
1531
|
+
def shutdown(self) -> None:
|
|
1532
|
+
"""Shutdown the Node.js process."""
|
|
1533
|
+
if self._process:
|
|
1534
|
+
try:
|
|
1535
|
+
self._process.terminate()
|
|
1536
|
+
self._process.wait(timeout=2)
|
|
1537
|
+
except:
|
|
1538
|
+
self._process.kill()
|
|
1539
|
+
self._process = None
|
|
1540
|
+
self._initialized = False
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
# Global bridge instances
|
|
1544
|
+
_cpp_bridge: Optional[CppRuntimeBridge] = None
|
|
1545
|
+
_java_bridge: Optional[JavaRuntimeBridge] = None
|
|
1546
|
+
_js_bridge: Optional[JavaScriptRuntimeBridge] = None
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
def get_cpp_bridge() -> CppRuntimeBridge:
|
|
1550
|
+
"""Get the global C++ runtime bridge."""
|
|
1551
|
+
global _cpp_bridge
|
|
1552
|
+
if _cpp_bridge is None:
|
|
1553
|
+
_cpp_bridge = CppRuntimeBridge()
|
|
1554
|
+
_cpp_bridge.initialize()
|
|
1555
|
+
return _cpp_bridge
|
|
1556
|
+
|
|
1557
|
+
|
|
1558
|
+
def get_java_bridge() -> JavaRuntimeBridge:
|
|
1559
|
+
"""Get the global Java runtime bridge."""
|
|
1560
|
+
global _java_bridge
|
|
1561
|
+
if _java_bridge is None:
|
|
1562
|
+
_java_bridge = JavaRuntimeBridge()
|
|
1563
|
+
return _java_bridge
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
def get_js_bridge() -> JavaScriptRuntimeBridge:
|
|
1567
|
+
"""Get the global JavaScript runtime bridge."""
|
|
1568
|
+
global _js_bridge
|
|
1569
|
+
if _js_bridge is None:
|
|
1570
|
+
_js_bridge = JavaScriptRuntimeBridge()
|
|
1571
|
+
return _js_bridge
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
# Language Registry
|
|
1575
|
+
LANGUAGE_DEFINITIONS: Dict[str, LanguageSupport] = {}
|
|
1576
|
+
|
|
1577
|
+
|
|
1578
|
+
def register_language(lang_id: str, lang_support: LanguageSupport) -> None:
|
|
1579
|
+
"""Register a language definition"""
|
|
1580
|
+
LANGUAGE_DEFINITIONS[lang_id.lower()] = lang_support
|
|
1581
|
+
|
|
1582
|
+
|
|
1583
|
+
def get_language(lang_id: str) -> Optional[LanguageSupport]:
|
|
1584
|
+
"""Get a language definition by ID"""
|
|
1585
|
+
return LANGUAGE_DEFINITIONS.get(lang_id.lower())
|
|
1586
|
+
|
|
1587
|
+
|
|
1588
|
+
def list_languages() -> List[str]:
|
|
1589
|
+
"""List all registered language IDs"""
|
|
1590
|
+
return list(LANGUAGE_DEFINITIONS.keys())
|
|
1591
|
+
|
|
1592
|
+
|
|
1593
|
+
def create_transformer(lang_support: LanguageSupport) -> LanguageTransformer:
|
|
1594
|
+
"""Create the appropriate transformer for a language"""
|
|
1595
|
+
if lang_support.language == SupportedLanguage.PYTHON:
|
|
1596
|
+
return PythonTransformer(lang_support)
|
|
1597
|
+
elif lang_support.language == SupportedLanguage.JAVASCRIPT:
|
|
1598
|
+
return JavaScriptTransformer(lang_support)
|
|
1599
|
+
elif lang_support.language == SupportedLanguage.JAVA:
|
|
1600
|
+
return JavaTransformer(lang_support)
|
|
1601
|
+
elif lang_support.language == SupportedLanguage.CSHARP:
|
|
1602
|
+
return CSharpTransformer(lang_support)
|
|
1603
|
+
elif lang_support.language == SupportedLanguage.CPP:
|
|
1604
|
+
return CppTransformer(lang_support)
|
|
1605
|
+
else:
|
|
1606
|
+
return LanguageTransformer(lang_support)
|
|
1607
|
+
|
|
1608
|
+
|
|
1609
|
+
def _init_languages() -> None:
|
|
1610
|
+
"""Initialize all built-in language definitions"""
|
|
1611
|
+
|
|
1612
|
+
# Python
|
|
1613
|
+
python_syntax = LanguageSyntax(
|
|
1614
|
+
name="Python",
|
|
1615
|
+
statement_terminator="\n",
|
|
1616
|
+
uses_braces=False,
|
|
1617
|
+
boolean_true="True",
|
|
1618
|
+
boolean_false="False",
|
|
1619
|
+
null_keyword="None",
|
|
1620
|
+
variable_keywords=[],
|
|
1621
|
+
function_keywords=["def"],
|
|
1622
|
+
class_keywords=["class"],
|
|
1623
|
+
constructor_name="__init__",
|
|
1624
|
+
print_function="print",
|
|
1625
|
+
comment_single="#",
|
|
1626
|
+
comment_multi_start='"""',
|
|
1627
|
+
comment_multi_end='"""'
|
|
1628
|
+
)
|
|
1629
|
+
python_support = LanguageSupport(
|
|
1630
|
+
language=SupportedLanguage.PYTHON,
|
|
1631
|
+
syntax=python_syntax,
|
|
1632
|
+
name="Python"
|
|
1633
|
+
)
|
|
1634
|
+
register_language("python", python_support)
|
|
1635
|
+
register_language("py", python_support)
|
|
1636
|
+
|
|
1637
|
+
# Java
|
|
1638
|
+
java_syntax = LanguageSyntax(
|
|
1639
|
+
name="Java",
|
|
1640
|
+
statement_terminator=";",
|
|
1641
|
+
uses_braces=True,
|
|
1642
|
+
boolean_true="true",
|
|
1643
|
+
boolean_false="false",
|
|
1644
|
+
null_keyword="null",
|
|
1645
|
+
variable_keywords=["int", "String", "boolean", "float", "double", "var"],
|
|
1646
|
+
function_keywords=["public", "private", "protected", "static", "void"],
|
|
1647
|
+
class_keywords=["class", "interface", "enum"],
|
|
1648
|
+
constructor_name="<classname>",
|
|
1649
|
+
print_function="System.out.println",
|
|
1650
|
+
comment_single="//",
|
|
1651
|
+
comment_multi_start="/*",
|
|
1652
|
+
comment_multi_end="*/"
|
|
1653
|
+
)
|
|
1654
|
+
java_support = LanguageSupport(
|
|
1655
|
+
language=SupportedLanguage.JAVA,
|
|
1656
|
+
syntax=java_syntax,
|
|
1657
|
+
name="Java"
|
|
1658
|
+
)
|
|
1659
|
+
register_language("java", java_support)
|
|
1660
|
+
|
|
1661
|
+
# C#
|
|
1662
|
+
csharp_syntax = LanguageSyntax(
|
|
1663
|
+
name="C#",
|
|
1664
|
+
statement_terminator=";",
|
|
1665
|
+
uses_braces=True,
|
|
1666
|
+
boolean_true="true",
|
|
1667
|
+
boolean_false="false",
|
|
1668
|
+
null_keyword="null",
|
|
1669
|
+
variable_keywords=["int", "string", "bool", "float", "double", "var", "dynamic"],
|
|
1670
|
+
function_keywords=["public", "private", "protected", "static", "void", "async"],
|
|
1671
|
+
class_keywords=["class", "interface", "struct", "enum"],
|
|
1672
|
+
constructor_name="<classname>",
|
|
1673
|
+
print_function="Console.WriteLine",
|
|
1674
|
+
comment_single="//",
|
|
1675
|
+
comment_multi_start="/*",
|
|
1676
|
+
comment_multi_end="*/"
|
|
1677
|
+
)
|
|
1678
|
+
csharp_support = LanguageSupport(
|
|
1679
|
+
language=SupportedLanguage.CSHARP,
|
|
1680
|
+
syntax=csharp_syntax,
|
|
1681
|
+
name="C#"
|
|
1682
|
+
)
|
|
1683
|
+
register_language("c#", csharp_support)
|
|
1684
|
+
register_language("csharp", csharp_support)
|
|
1685
|
+
|
|
1686
|
+
# C++
|
|
1687
|
+
cpp_syntax = LanguageSyntax(
|
|
1688
|
+
name="C++",
|
|
1689
|
+
statement_terminator=";",
|
|
1690
|
+
uses_braces=True,
|
|
1691
|
+
boolean_true="true",
|
|
1692
|
+
boolean_false="false",
|
|
1693
|
+
null_keyword="nullptr",
|
|
1694
|
+
variable_keywords=["int", "string", "bool", "float", "double", "auto", "const"],
|
|
1695
|
+
function_keywords=["void", "int", "string", "bool", "float", "double", "auto"],
|
|
1696
|
+
class_keywords=["class", "struct"],
|
|
1697
|
+
constructor_name="<classname>",
|
|
1698
|
+
print_function="std::cout",
|
|
1699
|
+
comment_single="//",
|
|
1700
|
+
comment_multi_start="/*",
|
|
1701
|
+
comment_multi_end="*/"
|
|
1702
|
+
)
|
|
1703
|
+
cpp_support = LanguageSupport(
|
|
1704
|
+
language=SupportedLanguage.CPP,
|
|
1705
|
+
syntax=cpp_syntax,
|
|
1706
|
+
name="C++"
|
|
1707
|
+
)
|
|
1708
|
+
register_language("c++", cpp_support)
|
|
1709
|
+
register_language("cpp", cpp_support)
|
|
1710
|
+
|
|
1711
|
+
# JavaScript
|
|
1712
|
+
js_syntax = LanguageSyntax(
|
|
1713
|
+
name="JavaScript",
|
|
1714
|
+
statement_terminator=";",
|
|
1715
|
+
uses_braces=True,
|
|
1716
|
+
boolean_true="true",
|
|
1717
|
+
boolean_false="false",
|
|
1718
|
+
null_keyword="null",
|
|
1719
|
+
variable_keywords=["let", "const", "var"],
|
|
1720
|
+
function_keywords=["function", "async"],
|
|
1721
|
+
class_keywords=["class"],
|
|
1722
|
+
constructor_name="constructor",
|
|
1723
|
+
print_function="console.log",
|
|
1724
|
+
comment_single="//",
|
|
1725
|
+
comment_multi_start="/*",
|
|
1726
|
+
comment_multi_end="*/"
|
|
1727
|
+
)
|
|
1728
|
+
js_support = LanguageSupport(
|
|
1729
|
+
language=SupportedLanguage.JAVASCRIPT,
|
|
1730
|
+
syntax=js_syntax,
|
|
1731
|
+
name="JavaScript"
|
|
1732
|
+
)
|
|
1733
|
+
register_language("javascript", js_support)
|
|
1734
|
+
register_language("js", js_support)
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
# Initialize languages on module load
|
|
1738
|
+
_init_languages()
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
# Export public API
|
|
1742
|
+
__all__ = [
|
|
1743
|
+
'SupportedLanguage',
|
|
1744
|
+
'LanguageSyntax',
|
|
1745
|
+
'LanguageSupport',
|
|
1746
|
+
'LanguageTransformer',
|
|
1747
|
+
'PythonTransformer',
|
|
1748
|
+
'JavaScriptTransformer',
|
|
1749
|
+
'JavaTransformer',
|
|
1750
|
+
'CSharpTransformer',
|
|
1751
|
+
'CppTransformer',
|
|
1752
|
+
'register_language',
|
|
1753
|
+
'get_language',
|
|
1754
|
+
'list_languages',
|
|
1755
|
+
'create_transformer',
|
|
1756
|
+
'LANGUAGE_DEFINITIONS',
|
|
1757
|
+
]
|