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.
- includecpp/__init__.py +1 -1
- includecpp/cli/commands.py +418 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +613 -20
- includecpp/core/cssl/cssl_builtins.py +342 -2
- includecpp/core/cssl/cssl_builtins.pyi +1393 -0
- includecpp/core/cssl/cssl_parser.py +368 -64
- includecpp/core/cssl/cssl_runtime.py +637 -38
- includecpp/core/cssl/cssl_types.py +561 -2
- includecpp/core/cssl_bridge.py +100 -4
- includecpp/core/cssl_bridge.pyi +177 -0
- includecpp/vscode/cssl/package.json +24 -4
- includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +127 -7
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/METADATA +1 -1
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/RECORD +19 -17
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/WHEEL +0 -0
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/entry_points.txt +0 -0
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.5.0.dist-info → includecpp-3.7.1.dist-info}/top_level.txt +0 -0
|
@@ -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:
|
|
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
|
|