IncludeCPP 3.5.0__py3-none-any.whl → 3.7.1__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.
@@ -162,6 +162,10 @@ class CSSLBuiltins:
162
162
  self._functions['abspath'] = self.builtin_abspath
163
163
  self._functions['normpath'] = self.builtin_normpath
164
164
  # File I/O functions
165
+ self._functions['read'] = self.builtin_read
166
+ self._functions['readline'] = self.builtin_readline
167
+ self._functions['write'] = self.builtin_write
168
+ self._functions['writeline'] = self.builtin_writeline
165
169
  self._functions['readfile'] = self.builtin_readfile
166
170
  self._functions['writefile'] = self.builtin_writefile
167
171
  self._functions['appendfile'] = self.builtin_appendfile
@@ -177,6 +181,29 @@ class CSSLBuiltins:
177
181
  # JSON functions
178
182
  self._functions['tojson'] = self.builtin_tojson
179
183
  self._functions['fromjson'] = self.builtin_fromjson
184
+ # JSON namespace functions (json::read, json::write, etc.)
185
+ self._functions['json::read'] = self.builtin_json_read
186
+ self._functions['json::write'] = self.builtin_json_write
187
+ self._functions['json::parse'] = self.builtin_fromjson
188
+ self._functions['json::stringify'] = self.builtin_tojson
189
+ self._functions['json::pretty'] = self.builtin_json_pretty
190
+ self._functions['json::keys'] = self.builtin_json_keys
191
+ self._functions['json::values'] = self.builtin_json_values
192
+ self._functions['json::get'] = self.builtin_json_get
193
+ self._functions['json::set'] = self.builtin_json_set
194
+ self._functions['json::has'] = self.builtin_json_has
195
+ self._functions['json::merge'] = self.builtin_json_merge
196
+
197
+ # Instance introspection functions (instance::getMethods, etc.)
198
+ self._functions['instance::getMethods'] = self.builtin_instance_getMethods
199
+ self._functions['instance::getClasses'] = self.builtin_instance_getClasses
200
+ self._functions['instance::getVars'] = self.builtin_instance_getVars
201
+ self._functions['instance::getAll'] = self.builtin_instance_getAll
202
+ self._functions['instance::call'] = self.builtin_instance_call
203
+ self._functions['instance::has'] = self.builtin_instance_has
204
+ self._functions['instance::type'] = self.builtin_instance_type
205
+ self._functions['isavailable'] = self.builtin_isavailable
206
+ self._functions['instance::exists'] = self.builtin_isavailable # Alias
180
207
 
181
208
  # Regex functions
182
209
  self._functions['match'] = self.builtin_match
@@ -801,6 +828,54 @@ class CSSLBuiltins:
801
828
 
802
829
  # ============= File I/O Functions =============
803
830
 
831
+ def builtin_read(self, path: str, encoding: str = 'utf-8') -> str:
832
+ """Read entire file content.
833
+ Usage: read('/path/to/file.txt')
834
+ """
835
+ with open(path, 'r', encoding=encoding) as f:
836
+ return f.read()
837
+
838
+ def builtin_readline(self, line: int, path: str, encoding: str = 'utf-8') -> str:
839
+ """Read specific line from file (1-indexed).
840
+ Usage: readline(5, '/path/to/file.txt') -> returns line 5
841
+ """
842
+ with open(path, 'r', encoding=encoding) as f:
843
+ for i, file_line in enumerate(f, 1):
844
+ if i == line:
845
+ return file_line.rstrip('\n\r')
846
+ return "" # Line not found
847
+
848
+ def builtin_write(self, path: str, content: str, encoding: str = 'utf-8') -> int:
849
+ """Write content to file, returns chars written.
850
+ Usage: write('/path/to/file.txt', 'Hello World')
851
+ """
852
+ with open(path, 'w', encoding=encoding) as f:
853
+ return f.write(content)
854
+
855
+ def builtin_writeline(self, line: int, content: str, path: str, encoding: str = 'utf-8') -> bool:
856
+ """Write/replace specific line in file (1-indexed).
857
+ Usage: writeline(5, 'New content', '/path/to/file.txt')
858
+ """
859
+ # Read all lines
860
+ lines = []
861
+ if os.path.exists(path):
862
+ with open(path, 'r', encoding=encoding) as f:
863
+ lines = f.readlines()
864
+
865
+ # Ensure we have enough lines
866
+ while len(lines) < line:
867
+ lines.append('\n')
868
+
869
+ # Replace the specific line (1-indexed)
870
+ if not content.endswith('\n'):
871
+ content = content + '\n'
872
+ lines[line - 1] = content
873
+
874
+ # Write back
875
+ with open(path, 'w', encoding=encoding) as f:
876
+ f.writelines(lines)
877
+ return True
878
+
804
879
  def builtin_readfile(self, path: str, encoding: str = 'utf-8') -> str:
805
880
  """Read entire file content"""
806
881
  with open(path, 'r', encoding=encoding) as f:
@@ -862,6 +937,209 @@ class CSSLBuiltins:
862
937
  def builtin_fromjson(self, s: str) -> Any:
863
938
  return json.loads(s)
864
939
 
940
+ # JSON namespace functions (json::read, json::write, etc.)
941
+ def builtin_json_read(self, path: str, encoding: str = 'utf-8') -> Any:
942
+ """Read and parse JSON file.
943
+ Usage: json::read('/path/to/file.json')
944
+ """
945
+ with open(path, 'r', encoding=encoding) as f:
946
+ return json.load(f)
947
+
948
+ def builtin_json_write(self, path: str, data: Any, indent: int = 2, encoding: str = 'utf-8') -> bool:
949
+ """Write data to JSON file.
950
+ Usage: json::write('/path/to/file.json', data)
951
+ """
952
+ with open(path, 'w', encoding=encoding) as f:
953
+ json.dump(data, f, indent=indent, ensure_ascii=False)
954
+ return True
955
+
956
+ def builtin_json_pretty(self, value: Any, indent: int = 2) -> str:
957
+ """Pretty print JSON.
958
+ Usage: json::pretty(data)
959
+ """
960
+ return json.dumps(value, indent=indent, ensure_ascii=False)
961
+
962
+ def builtin_json_keys(self, data: Any) -> list:
963
+ """Get all keys from JSON object.
964
+ Usage: json::keys(data)
965
+ """
966
+ if isinstance(data, dict):
967
+ return list(data.keys())
968
+ return []
969
+
970
+ def builtin_json_values(self, data: Any) -> list:
971
+ """Get all values from JSON object.
972
+ Usage: json::values(data)
973
+ """
974
+ if isinstance(data, dict):
975
+ return list(data.values())
976
+ return []
977
+
978
+ def builtin_json_get(self, data: Any, path: str, default: Any = None) -> Any:
979
+ """Get value by dot-path from JSON.
980
+ Usage: json::get(data, 'user.name')
981
+ """
982
+ if not isinstance(data, dict):
983
+ return default
984
+ keys = path.split('.')
985
+ current = data
986
+ for key in keys:
987
+ if isinstance(current, dict) and key in current:
988
+ current = current[key]
989
+ elif isinstance(current, list):
990
+ try:
991
+ current = current[int(key)]
992
+ except (ValueError, IndexError):
993
+ return default
994
+ else:
995
+ return default
996
+ return current
997
+
998
+ def builtin_json_set(self, data: Any, path: str, value: Any) -> Any:
999
+ """Set value by dot-path in JSON object.
1000
+ Usage: json::set(data, 'user.name', 'John')
1001
+ """
1002
+ if not isinstance(data, dict):
1003
+ return data
1004
+ data = dict(data) # Copy
1005
+ keys = path.split('.')
1006
+ current = data
1007
+ for i, key in enumerate(keys[:-1]):
1008
+ if key not in current or not isinstance(current[key], dict):
1009
+ current[key] = {}
1010
+ current = current[key]
1011
+ current[keys[-1]] = value
1012
+ return data
1013
+
1014
+ def builtin_json_has(self, data: Any, path: str) -> bool:
1015
+ """Check if path exists in JSON.
1016
+ Usage: json::has(data, 'user.name')
1017
+ """
1018
+ result = self.builtin_json_get(data, path, _MISSING := object())
1019
+ return result is not _MISSING
1020
+
1021
+ def builtin_json_merge(self, *dicts) -> dict:
1022
+ """Deep merge multiple JSON objects.
1023
+ Usage: json::merge(obj1, obj2, obj3)
1024
+ """
1025
+ def deep_merge(base, update):
1026
+ result = dict(base)
1027
+ for key, value in update.items():
1028
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
1029
+ result[key] = deep_merge(result[key], value)
1030
+ else:
1031
+ result[key] = value
1032
+ return result
1033
+
1034
+ result = {}
1035
+ for d in dicts:
1036
+ if isinstance(d, dict):
1037
+ result = deep_merge(result, d)
1038
+ return result
1039
+
1040
+ # ============= Instance Introspection Functions =============
1041
+
1042
+ def builtin_instance_getMethods(self, obj: Any) -> list:
1043
+ """Get all methods from an object/module.
1044
+ Usage: instance::getMethods(@module) or instance::getMethods($obj)
1045
+ Returns list of method names.
1046
+ """
1047
+ import inspect
1048
+ methods = []
1049
+ for name in dir(obj):
1050
+ if not name.startswith('_'):
1051
+ attr = getattr(obj, name, None)
1052
+ if callable(attr):
1053
+ methods.append(name)
1054
+ return methods
1055
+
1056
+ def builtin_instance_getClasses(self, obj: Any) -> list:
1057
+ """Get all classes from an object/module.
1058
+ Usage: instance::getClasses(@module)
1059
+ Returns list of class names.
1060
+ """
1061
+ import inspect
1062
+ classes = []
1063
+ for name in dir(obj):
1064
+ if not name.startswith('_'):
1065
+ attr = getattr(obj, name, None)
1066
+ if inspect.isclass(attr):
1067
+ classes.append(name)
1068
+ return classes
1069
+
1070
+ def builtin_instance_getVars(self, obj: Any) -> list:
1071
+ """Get all variables/attributes (non-callable) from an object.
1072
+ Usage: instance::getVars(@module)
1073
+ Returns list of variable names.
1074
+ """
1075
+ vars_list = []
1076
+ for name in dir(obj):
1077
+ if not name.startswith('_'):
1078
+ attr = getattr(obj, name, None)
1079
+ if not callable(attr):
1080
+ vars_list.append(name)
1081
+ return vars_list
1082
+
1083
+ def builtin_instance_getAll(self, obj: Any) -> dict:
1084
+ """Get all attributes from an object, categorized.
1085
+ Usage: instance::getAll(@module)
1086
+ Returns dict with 'methods', 'classes', 'vars' keys.
1087
+ """
1088
+ import inspect
1089
+ result = {
1090
+ 'methods': [],
1091
+ 'classes': [],
1092
+ 'vars': []
1093
+ }
1094
+ for name in dir(obj):
1095
+ if not name.startswith('_'):
1096
+ attr = getattr(obj, name, None)
1097
+ if inspect.isclass(attr):
1098
+ result['classes'].append(name)
1099
+ elif callable(attr):
1100
+ result['methods'].append(name)
1101
+ else:
1102
+ result['vars'].append(name)
1103
+ return result
1104
+
1105
+ def builtin_instance_call(self, obj: Any, method_name: str, *args, **kwargs) -> Any:
1106
+ """Dynamically call a method on an object.
1107
+ Usage: instance::call(@module, 'methodName', arg1, arg2)
1108
+ """
1109
+ method = getattr(obj, method_name, None)
1110
+ if method and callable(method):
1111
+ return method(*args, **kwargs)
1112
+ raise RuntimeError(f"Method '{method_name}' not found on object")
1113
+
1114
+ def builtin_instance_has(self, obj: Any, name: str) -> bool:
1115
+ """Check if object has an attribute.
1116
+ Usage: instance::has(@module, 'methodName')
1117
+ """
1118
+ return hasattr(obj, name)
1119
+
1120
+ def builtin_instance_type(self, obj: Any) -> str:
1121
+ """Get the type name of an object.
1122
+ Usage: instance::type(@module)
1123
+ """
1124
+ return type(obj).__name__
1125
+
1126
+ def builtin_isavailable(self, name_or_obj: Any) -> bool:
1127
+ """Check if a shared instance exists.
1128
+ Usage:
1129
+ isavailable("MyInstance") - check by name string
1130
+ isavailable($MyInstance) - check shared ref (returns True if not None)
1131
+ instance::exists("MyInstance") - alias
1132
+ """
1133
+ from ..cssl_bridge import _live_objects
1134
+
1135
+
1136
+ # If it's a string, check by name
1137
+ if isinstance(name_or_obj, str):
1138
+ return name_or_obj in _live_objects
1139
+
1140
+ # Otherwise, check if the object is not None (for $name or instance<"name">)
1141
+ return name_or_obj is not None
1142
+
865
1143
  # ============= Regex Functions =============
866
1144
 
867
1145
  def builtin_match(self, pattern: str, string: str) -> Optional[dict]:
@@ -913,6 +1191,29 @@ class CSSLBuiltins:
913
1191
  else:
914
1192
  raise SystemExit(code)
915
1193
 
1194
+ def builtin_original(self, func_name: str, *args) -> Any:
1195
+ """Call the original version of a replaced function.
1196
+
1197
+ Usage:
1198
+ exit <<== { printl("custom exit"); }
1199
+ original("exit"); // Calls the ORIGINAL exit, not the replacement
1200
+
1201
+ // In an injection that was defined BEFORE replacement:
1202
+ old_exit <<== { original("exit"); } // Calls original exit
1203
+ """
1204
+ if self.runtime and hasattr(self.runtime, '_original_functions'):
1205
+ original_func = self.runtime._original_functions.get(func_name)
1206
+ if original_func is not None:
1207
+ if callable(original_func):
1208
+ return original_func(*args)
1209
+ elif isinstance(original_func, type(lambda: None).__class__.__bases__[0]): # Check if bound method
1210
+ return original_func(*args)
1211
+ # Fallback: try to call builtin directly
1212
+ builtin_method = getattr(self, f'builtin_{func_name}', None)
1213
+ if builtin_method:
1214
+ return builtin_method(*args)
1215
+ raise CSSLBuiltinError(f"No original function '{func_name}' found")
1216
+
916
1217
  def builtin_env(self, name: str, default: str = None) -> Optional[str]:
917
1218
  return os.environ.get(name, default)
918
1219
 
@@ -1902,15 +2203,54 @@ class CSSLBuiltins:
1902
2203
  from .cssl_types import OpenQuote
1903
2204
  return OpenQuote(db_ref)
1904
2205
 
1905
- def builtin_openfind(self, combo_or_type: Any, index: int = 0) -> Any:
2206
+ def builtin_openfind(self, combo_or_type: Any, index: int = 0, params: list = None) -> Any:
1906
2207
  """Find open parameter by type or combo space.
1907
2208
 
1908
- Usage: OpenFind<string>(0) or OpenFind(&@comboSpace)
2209
+ Usage:
2210
+ OpenFind<string>(0) # Find first string at position 0
2211
+ OpenFind(&@comboSpace) # Find using combo filter
2212
+
2213
+ When using with open parameters:
2214
+ open define myFunc(open Params) {
2215
+ string name = OpenFind<string>(0); // Find nearest string at index 0
2216
+ int age = OpenFind<int>(1); // Find nearest int at index 1
2217
+ }
1909
2218
  """
1910
2219
  from .cssl_types import Combo
1911
2220
 
1912
2221
  if isinstance(combo_or_type, Combo):
2222
+ # Find by combo space
2223
+ if params:
2224
+ return combo_or_type.find_match(params)
1913
2225
  return combo_or_type.find_match([])
2226
+
2227
+ # Type-based search
2228
+ target_type = combo_or_type
2229
+ if params is None:
2230
+ params = []
2231
+
2232
+ # Map type names to Python types
2233
+ type_map = {
2234
+ 'string': str, 'str': str,
2235
+ 'int': int, 'integer': int,
2236
+ 'float': float, 'double': float,
2237
+ 'bool': bool, 'boolean': bool,
2238
+ 'list': list, 'array': list,
2239
+ 'dict': dict, 'dictionary': dict
2240
+ }
2241
+
2242
+ python_type = type_map.get(str(target_type).lower(), None)
2243
+ if python_type is None:
2244
+ return None
2245
+
2246
+ # Find the nearest matching type from index position
2247
+ matches_found = 0
2248
+ for i, param in enumerate(params):
2249
+ if isinstance(param, python_type):
2250
+ if matches_found == index:
2251
+ return param
2252
+ matches_found += 1
2253
+
1914
2254
  return None
1915
2255
 
1916
2256