IncludeCPP 4.0.0__py3-none-any.whl → 4.0.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 CHANGED
@@ -2,7 +2,7 @@ from .core.cpp_api import CppApi
2
2
  from .core import cssl_bridge as CSSL
3
3
  import warnings
4
4
 
5
- __version__ = "4.0.0"
5
+ __version__ = "4.0.2"
6
6
  __all__ = ["CppApi", "CSSL"]
7
7
 
8
8
  # Module-level cache for C++ modules
@@ -9,8 +9,12 @@ import re
9
9
  import urllib.request
10
10
  import urllib.error
11
11
  from pathlib import Path
12
+ from colorama import init as colorama_init, Fore, Style
12
13
  from .config_parser import CppProjectConfig
13
14
 
15
+ # Initialize colorama for Windows ANSI color support
16
+ colorama_init()
17
+
14
18
 
15
19
  def _is_experimental_enabled() -> bool:
16
20
  """Check if experimental features (cppy, ai) are enabled."""
@@ -7320,7 +7324,6 @@ def exec_repl(lang, path, import_all):
7320
7324
  code_lines = imports + [''] + lines if imports else lines
7321
7325
 
7322
7326
  click.echo()
7323
- click.secho("--- Output ---", fg='green')
7324
7327
 
7325
7328
  if is_python:
7326
7329
  # Execute Python code
@@ -7443,9 +7446,6 @@ def exec_repl(lang, path, import_all):
7443
7446
  except Exception:
7444
7447
  pass
7445
7448
 
7446
- click.echo()
7447
- click.secho("--------------", fg='green')
7448
-
7449
7449
 
7450
7450
  # ============================================================================
7451
7451
  # CSSL - Hidden Command Group
@@ -7526,19 +7526,12 @@ def _cssl_execute(path, code):
7526
7526
  source = '\n'.join(lines)
7527
7527
 
7528
7528
  # Execute
7529
- click.secho("--- Output ---", fg='green')
7530
7529
  try:
7531
7530
  result = cssl_lang.run(source)
7532
-
7533
- # Output is already printed to stdout during execution via runtime.output()
7534
- # Don't print "Result:" automatically - users should use printl() for output
7535
- # This prevents unwanted output for function calls like: Function();
7536
- pass
7537
-
7538
7531
  except Exception as e:
7539
- click.secho(f"CSSL Error: {e}", fg='red')
7540
-
7541
- click.secho("--------------", fg='green')
7532
+ error_msg = str(e)
7533
+ # Clean display - single CSSL Error: prefix with colorama
7534
+ click.echo(f"{Fore.RED}CSSL Error: {error_msg}{Style.RESET_ALL}")
7542
7535
 
7543
7536
 
7544
7537
  @cssl.command(name='makemodule')
@@ -1084,7 +1084,7 @@ class CSSLParser:
1084
1084
  append_ref_class = None
1085
1085
  append_ref_member = None
1086
1086
 
1087
- # Check for &ClassName::member reference
1087
+ # Check for &ClassName::member or &ClassName.member or &function reference
1088
1088
  if self._match(TokenType.AMPERSAND):
1089
1089
  if self._check(TokenType.IDENTIFIER):
1090
1090
  append_ref_class = self._advance().value
@@ -1095,8 +1095,8 @@ class CSSLParser:
1095
1095
  elif self._check(TokenType.SHARED_REF):
1096
1096
  append_ref_class = f'${self._advance().value}'
1097
1097
 
1098
- # Check for ::member
1099
- if self._match(TokenType.DOUBLE_COLON):
1098
+ # Check for ::member or .member (support both syntaxes)
1099
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1100
1100
  if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1101
1101
  append_ref_member = self._advance().value
1102
1102
 
@@ -1108,7 +1108,7 @@ class CSSLParser:
1108
1108
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1109
1109
  while True:
1110
1110
  if self._match_keyword('extends'):
1111
- # Parse target: @ModuleName, $PythonObject, Parent::method
1111
+ # Parse target: @ModuleName, $PythonObject, Parent::method, Parent.method
1112
1112
  if self._check(TokenType.AT):
1113
1113
  self._advance()
1114
1114
  extends_func = '@' + self._advance().value
@@ -1117,7 +1117,8 @@ class CSSLParser:
1117
1117
  extends_func = self._advance().value
1118
1118
  elif self._check(TokenType.IDENTIFIER):
1119
1119
  first_part = self._advance().value
1120
- if self._match(TokenType.DOUBLE_COLON):
1120
+ # Support both :: and . for class method access
1121
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1121
1122
  extends_class_ref = first_part
1122
1123
  extends_method_ref = self._advance().value
1123
1124
  else:
@@ -1134,7 +1135,8 @@ class CSSLParser:
1134
1135
  overwrites_func = self._advance().value
1135
1136
  elif self._check(TokenType.IDENTIFIER):
1136
1137
  first_part = self._advance().value
1137
- if self._match(TokenType.DOUBLE_COLON):
1138
+ # Support both :: and . for class method access
1139
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1138
1140
  overwrites_class_ref = first_part
1139
1141
  overwrites_method_ref = self._advance().value
1140
1142
  else:
@@ -68,11 +68,11 @@ class CSSLRuntimeError(Exception):
68
68
  # Build detailed error message
69
69
  error_parts = []
70
70
 
71
- # Main error message
71
+ # Main error message (no "Error:" prefix - CLI handles that)
72
72
  if line:
73
- error_parts.append(f"Error at line {line}: {message}")
73
+ error_parts.append(f"Line {line}: {message}")
74
74
  else:
75
- error_parts.append(f"Error: {message}")
75
+ error_parts.append(message)
76
76
 
77
77
  # Add context if available
78
78
  if context:
@@ -363,11 +363,11 @@ class CSSLRuntime:
363
363
  """Format a detailed error with source context"""
364
364
  error_parts = []
365
365
 
366
- # Main error header
366
+ # Main error header (no "Error:" prefix - CLI handles that)
367
367
  if line and line > 0:
368
- error_parts.append(f"Error at line {line} in {self._current_file}:")
368
+ error_parts.append(f"Line {line} in {self._current_file}:")
369
369
  else:
370
- error_parts.append(f"Error in {self._current_file}:")
370
+ error_parts.append(f"In {self._current_file}:")
371
371
 
372
372
  # Extract message without existing line info
373
373
  clean_msg = message
@@ -1005,13 +1005,29 @@ class CSSLRuntime:
1005
1005
  overwrites_func = func_info.get('overwrites')
1006
1006
  overwrites_is_python = func_info.get('overwrites_is_python', False)
1007
1007
 
1008
+ # Get append/overwrite reference info (&Class::method syntax)
1009
+ append_mode = func_info.get('append_mode', False)
1010
+ append_ref_class = func_info.get('append_ref_class')
1011
+ append_ref_member = func_info.get('append_ref_member')
1012
+
1008
1013
  # Store function extends info for runtime use
1009
1014
  if extends_func:
1010
1015
  node.value['_extends_resolved'] = self._resolve_function_target(
1011
1016
  extends_func, extends_is_python
1012
1017
  )
1013
1018
 
1014
- # Handle overwrites - replace the target function
1019
+ # Handle &Class::method syntax
1020
+ # Without ++ = full replacement
1021
+ # With ++ = append (run original first, then new code)
1022
+ if append_ref_class:
1023
+ if append_mode:
1024
+ # Append mode: wrap original to run original + new
1025
+ self._append_to_target(append_ref_class, append_ref_member, node)
1026
+ else:
1027
+ # Full replacement
1028
+ self._overwrite_target(append_ref_class, append_ref_member, node)
1029
+
1030
+ # Handle overwrites keyword - replace the target function
1015
1031
  if overwrites_func:
1016
1032
  target = self._resolve_function_target(overwrites_func, overwrites_is_python)
1017
1033
  if target is not None:
@@ -1054,6 +1070,142 @@ class CSSLRuntime:
1054
1070
  return self._call_function(func_node, list(args), kwargs)
1055
1071
  return wrapper
1056
1072
 
1073
+ def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
1074
+ """Overwrite a class method or function with the replacement.
1075
+
1076
+ Handles:
1077
+ - &ClassName::method - Overwrite method in CSSL class
1078
+ - &$PyObject.method - Overwrite method in Python shared object
1079
+ - &functionName - Overwrite standalone function
1080
+ """
1081
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1082
+
1083
+ # Handle Python shared objects
1084
+ if ref_class.startswith('$'):
1085
+ var_name = ref_class[1:]
1086
+ ref_obj = _live_objects.get(var_name)
1087
+ if ref_obj is None:
1088
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
1089
+
1090
+ if ref_obj is None:
1091
+ return
1092
+
1093
+ # Unwrap SharedObjectProxy
1094
+ if isinstance(ref_obj, SharedObjectProxy):
1095
+ ref_obj = ref_obj._obj
1096
+
1097
+ # Overwrite Python object method
1098
+ if ref_member and hasattr(ref_obj, ref_member):
1099
+ wrapper = self._create_python_wrapper(replacement_node)
1100
+ try:
1101
+ setattr(ref_obj, ref_member, wrapper)
1102
+ except (AttributeError, TypeError):
1103
+ pass # Can't overwrite (immutable or builtin)
1104
+ return
1105
+
1106
+ # Handle CSSL class method overwrite
1107
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1108
+ if target_class is None:
1109
+ # Maybe it's a standalone function reference (no ::member)
1110
+ if ref_member is None:
1111
+ # &functionName - overwrite the function
1112
+ self.scope.set(ref_class, replacement_node)
1113
+ self.global_scope.set(ref_class, replacement_node)
1114
+ return
1115
+
1116
+ if isinstance(target_class, CSSLClass) and ref_member:
1117
+ # Overwrite method in the class
1118
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1119
+ target_class.methods[ref_member] = replacement_node
1120
+ # Also check members list
1121
+ if hasattr(target_class, 'members'):
1122
+ for i, member in enumerate(target_class.members):
1123
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1124
+ target_class.members[i] = replacement_node
1125
+ break
1126
+
1127
+ def _append_to_target(self, ref_class: str, ref_member: str, append_node: ASTNode):
1128
+ """Append new code to an existing class method or function.
1129
+
1130
+ Creates a wrapper that runs original first, then the appended code.
1131
+ Handles:
1132
+ - &ClassName::method ++ - Append to method in CSSL class
1133
+ - &$PyObject.method ++ - Append to method in Python shared object
1134
+ - &functionName ++ - Append to standalone function
1135
+ """
1136
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1137
+
1138
+ # Handle Python shared objects
1139
+ if ref_class.startswith('$'):
1140
+ var_name = ref_class[1:]
1141
+ ref_obj = _live_objects.get(var_name)
1142
+ if ref_obj is None:
1143
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
1144
+
1145
+ if ref_obj is None:
1146
+ return
1147
+
1148
+ if isinstance(ref_obj, SharedObjectProxy):
1149
+ ref_obj = ref_obj._obj
1150
+
1151
+ # Create wrapper that calls original + new
1152
+ if ref_member and hasattr(ref_obj, ref_member):
1153
+ original_method = getattr(ref_obj, ref_member)
1154
+ runtime = self
1155
+ def appended_wrapper(*args, **kwargs):
1156
+ # Run original first
1157
+ result = None
1158
+ if callable(original_method):
1159
+ try:
1160
+ result = original_method(*args, **kwargs)
1161
+ except:
1162
+ pass
1163
+ # Then run appended code
1164
+ return runtime._call_function(append_node, list(args), kwargs)
1165
+ try:
1166
+ setattr(ref_obj, ref_member, appended_wrapper)
1167
+ except (AttributeError, TypeError):
1168
+ pass
1169
+ return
1170
+
1171
+ # Handle CSSL class method append
1172
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1173
+ if target_class is None:
1174
+ # Standalone function append
1175
+ if ref_member is None:
1176
+ original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1177
+ if original_func:
1178
+ # Store original in append_node so it can run first
1179
+ append_node.value['_original_func'] = original_func
1180
+ self.scope.set(ref_class, append_node)
1181
+ self.global_scope.set(ref_class, append_node)
1182
+ return
1183
+
1184
+ if isinstance(target_class, CSSLClass) and ref_member:
1185
+ # Find original method
1186
+ original_method = None
1187
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1188
+ original_method = target_class.methods.get(ref_member)
1189
+
1190
+ if original_method is None and hasattr(target_class, 'members'):
1191
+ for member in target_class.members:
1192
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1193
+ original_method = member
1194
+ break
1195
+
1196
+ # Store original in append_node for runtime execution
1197
+ if original_method:
1198
+ append_node.value['_original_method'] = original_method
1199
+
1200
+ # Replace with append_node (which will call original first via _call_function)
1201
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1202
+ target_class.methods[ref_member] = append_node
1203
+ if hasattr(target_class, 'members'):
1204
+ for i, member in enumerate(target_class.members):
1205
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1206
+ target_class.members[i] = append_node
1207
+ break
1208
+
1057
1209
  def _exec_typed_declaration(self, node: ASTNode) -> Any:
1058
1210
  """Execute typed variable declaration: type<T> varName = value;
1059
1211
 
@@ -1352,12 +1504,15 @@ class CSSLRuntime:
1352
1504
  if not self._running:
1353
1505
  break
1354
1506
  self._execute_node(child)
1355
- # Copy all local vars to new scope
1356
- for name, value in temp_scope._vars.items():
1357
- new_scope.set(name, value)
1507
+ except CSSLReturn:
1508
+ # Parent returned - that's fine, we just want the local vars
1509
+ pass
1358
1510
  except:
1359
1511
  pass
1360
1512
  finally:
1513
+ # ALWAYS copy local vars (even if parent returned)
1514
+ for name, value in temp_scope.variables.items():
1515
+ new_scope.set(name, value)
1361
1516
  self.scope = old_scope
1362
1517
 
1363
1518
  # Bind parameters - handle both positional and named arguments
@@ -1420,7 +1575,7 @@ class CSSLRuntime:
1420
1575
 
1421
1576
  # Check exclude_type: *[type] - must NOT return excluded type
1422
1577
  exclude_type = func_info.get('exclude_type')
1423
- if exclude_type:
1578
+ if exclude_type and isinstance(exclude_type, str):
1424
1579
  type_map = {
1425
1580
  'string': str, 'int': int, 'float': float, 'bool': bool,
1426
1581
  'null': type(None), 'none': type(None),
@@ -3076,7 +3231,7 @@ class CSSLRuntime:
3076
3231
  'json': dict,
3077
3232
  }
3078
3233
 
3079
- excluded_py_type = type_map.get(exclude_type.lower())
3234
+ excluded_py_type = type_map.get(exclude_type.lower() if isinstance(exclude_type, str) else exclude_type)
3080
3235
  if excluded_py_type and isinstance(value, excluded_py_type):
3081
3236
  raise self._format_error(
3082
3237
  node.line if hasattr(node, 'line') else 0,
@@ -3628,12 +3783,34 @@ class CSSLRuntime:
3628
3783
 
3629
3784
  # Resolve the class/instance reference
3630
3785
  if ref_class.startswith('$'):
3631
- # Dynamic instance reference: &$instanceVar::member
3786
+ # Dynamic instance reference: &$instanceVar::member or &$PyObject.method
3632
3787
  var_name = ref_class[1:]
3633
- ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3788
+
3789
+ # First check in _live_objects for Python shared objects
3790
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
3791
+ ref_obj = _live_objects.get(var_name)
3792
+ if ref_obj is None:
3793
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3794
+
3634
3795
  if ref_obj is None:
3635
3796
  return # Instance not found, skip silently
3636
3797
 
3798
+ # Handle Python shared objects
3799
+ if isinstance(ref_obj, SharedObjectProxy):
3800
+ ref_obj = ref_obj._obj
3801
+
3802
+ # If it's a Python object (not CSSL), call the method directly
3803
+ if not isinstance(ref_obj, (CSSLInstance, CSSLClass)):
3804
+ if ref_member and hasattr(ref_obj, ref_member):
3805
+ method = getattr(ref_obj, ref_member)
3806
+ if callable(method):
3807
+ try:
3808
+ method(*args, **kwargs)
3809
+ except TypeError:
3810
+ # Try without args
3811
+ method()
3812
+ return
3813
+
3637
3814
  if isinstance(ref_obj, CSSLInstance):
3638
3815
  # Get the class definition from the instance
3639
3816
  target_class = ref_obj.class_def
@@ -3853,9 +4030,19 @@ class CSSLRuntime:
3853
4030
  self.scope = new_scope
3854
4031
  self._current_instance = instance
3855
4032
 
4033
+ original_return = None
3856
4034
  try:
4035
+ # Handle append mode via _append_to_target (stored original)
4036
+ original_method = func_info.get('_original_method')
4037
+ if original_method:
4038
+ # Execute original method first - capture return for fallback
4039
+ try:
4040
+ original_return = self._call_method(instance, original_method, args, kwargs)
4041
+ except CSSLReturn as ret:
4042
+ original_return = ret.value
4043
+
3857
4044
  # Handle append mode (++) - execute referenced parent method first
3858
- if append_mode and append_ref_class:
4045
+ elif append_mode and append_ref_class:
3859
4046
  self._execute_append_reference(
3860
4047
  instance, append_ref_class, append_ref_member,
3861
4048
  args, kwargs, {}, is_constructor=False
@@ -3876,7 +4063,8 @@ class CSSLRuntime:
3876
4063
  self.scope = old_scope
3877
4064
  self._current_instance = old_instance
3878
4065
 
3879
- return None
4066
+ # If no return in appended code, use original's return
4067
+ return original_return
3880
4068
 
3881
4069
  def _eval_member_access(self, node: ASTNode) -> Any:
3882
4070
  """Evaluate member access"""
@@ -455,12 +455,12 @@ class CsslLang:
455
455
 
456
456
  return result
457
457
  except Exception as e:
458
- # Format error message nicely
458
+ # Format error message nicely - don't add prefixes, let CLI handle that
459
459
  error_msg = str(e)
460
- # Don't double-wrap error messages
460
+ # Strip any existing CSSL Error: prefix to avoid duplication
461
461
  if error_msg.startswith("CSSL Error:"):
462
- raise RuntimeError(error_msg) from e
463
- raise RuntimeError(f"CSSL Error:\n{error_msg}") from e
462
+ error_msg = error_msg[11:].strip()
463
+ raise RuntimeError(error_msg) from e
464
464
 
465
465
  def exec(self, path_or_code: str, *args) -> Any:
466
466
  """