objective-lol 0.0.1__cp313-cp313-win_amd64.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.
- objective_lol/__init__.py +5 -0
- objective_lol/_api.cp313-win_amd64.h +543 -0
- objective_lol/_api.cp313-win_amd64.pyd +0 -0
- objective_lol/api.c +5934 -0
- objective_lol/api.go +3562 -0
- objective_lol/api.py +1805 -0
- objective_lol/api_go.h +543 -0
- objective_lol/build.py +341 -0
- objective_lol/go.py +1852 -0
- objective_lol/olol.py +581 -0
- objective_lol-0.0.1.dist-info/METADATA +125 -0
- objective_lol-0.0.1.dist-info/RECORD +14 -0
- objective_lol-0.0.1.dist-info/WHEEL +5 -0
- objective_lol-0.0.1.dist-info/top_level.txt +1 -0
objective_lol/olol.py
ADDED
@@ -0,0 +1,581 @@
|
|
1
|
+
import asyncio
|
2
|
+
import concurrent.futures
|
3
|
+
import functools
|
4
|
+
import inspect
|
5
|
+
import json
|
6
|
+
import threading
|
7
|
+
from typing import Any, Callable, Dict, Tuple, Type
|
8
|
+
import uuid
|
9
|
+
|
10
|
+
from .api import (
|
11
|
+
VM,
|
12
|
+
VMCompatibilityShim,
|
13
|
+
NewVM,
|
14
|
+
DefaultConfig,
|
15
|
+
WrapInt,
|
16
|
+
WrapFloat,
|
17
|
+
WrapString,
|
18
|
+
WrapBool,
|
19
|
+
GoValue,
|
20
|
+
Slice_api_GoValue,
|
21
|
+
Map_string_api_GoValue,
|
22
|
+
ClassDefinition,
|
23
|
+
ClassVariable,
|
24
|
+
ClassMethod,
|
25
|
+
NewClassDefinition,
|
26
|
+
GoValueIDKey,
|
27
|
+
ForeignModuleNamespace,
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
defined_functions: Dict[str, Tuple['ObjectiveLOLVM', Callable]] = {}
|
32
|
+
defined_classes: Dict[str, Type] = {}
|
33
|
+
object_instances: Dict[str, Any] = {}
|
34
|
+
|
35
|
+
|
36
|
+
# gopy does not support passing complex types directly,
|
37
|
+
# so we wrap arguments and return values as JSON strings.
|
38
|
+
# Additionally, using closures seems to result in a segfault
|
39
|
+
# at https://github.com/python/cpython/blob/v3.13.5/Python/generated_cases.c.h#L2462
|
40
|
+
# so we use a global dictionary to store the actual functions.
|
41
|
+
def gopy_wrapper(id: str, json_args: str) -> bytes:
|
42
|
+
args = json.loads(json_args)
|
43
|
+
try:
|
44
|
+
vm, fn = defined_functions[id]
|
45
|
+
converted_args = [vm.convert_from_go_value(arg) for arg in args]
|
46
|
+
result = fn(*converted_args)
|
47
|
+
return json.dumps({"result": vm.convert_to_go_value(result), "error": None}, default=vm.serialize_go_value).encode('utf-8')
|
48
|
+
except Exception as e:
|
49
|
+
return json.dumps({"result": None, "error": str(e)}).encode('utf-8')
|
50
|
+
|
51
|
+
|
52
|
+
def convert_to_simple_mro(mro: list[str]) -> list[str]:
|
53
|
+
simple_mro = []
|
54
|
+
for cls_name in mro:
|
55
|
+
if cls_name.startswith(ForeignModuleNamespace):
|
56
|
+
simple_mro.append(cls_name[len(ForeignModuleNamespace)+1:])
|
57
|
+
return simple_mro
|
58
|
+
|
59
|
+
|
60
|
+
def generate_case_permutations(fname):
|
61
|
+
"""Generate all possible case combinations of fname"""
|
62
|
+
if not fname:
|
63
|
+
return ['']
|
64
|
+
|
65
|
+
result = []
|
66
|
+
first_char = fname[0]
|
67
|
+
rest_permutations = generate_case_permutations(fname[1:])
|
68
|
+
|
69
|
+
for rest in rest_permutations:
|
70
|
+
if first_char.isalpha():
|
71
|
+
result.append(first_char.lower() + rest)
|
72
|
+
result.append(first_char.upper() + rest)
|
73
|
+
else:
|
74
|
+
result.append(first_char + rest)
|
75
|
+
|
76
|
+
return result
|
77
|
+
|
78
|
+
|
79
|
+
class ProxyMeta(type):
|
80
|
+
def __new__(mcs: Type, name: str, bases: tuple, attrs: dict, go_value: GoValue = None):
|
81
|
+
cls = super().__new__(mcs, name, bases, attrs)
|
82
|
+
cls._go_value = go_value
|
83
|
+
return cls
|
84
|
+
|
85
|
+
def __call__(cls, *args, **kwargs):
|
86
|
+
instance = super().__call__(*args, **kwargs)
|
87
|
+
instance._go_value = cls._go_value
|
88
|
+
return instance
|
89
|
+
|
90
|
+
|
91
|
+
class ObjectiveLOLVM:
|
92
|
+
class ClassBuilder:
|
93
|
+
_vm: 'ObjectiveLOLVM'
|
94
|
+
_class: ClassDefinition
|
95
|
+
|
96
|
+
def __init__(self, vm: 'ObjectiveLOLVM'):
|
97
|
+
self._vm = vm
|
98
|
+
self._class = NewClassDefinition()
|
99
|
+
|
100
|
+
def get(self) -> ClassDefinition:
|
101
|
+
return self._class
|
102
|
+
|
103
|
+
def set_name(self, name: str) -> 'ObjectiveLOLVM.ClassBuilder':
|
104
|
+
self._class.Name = name
|
105
|
+
return self
|
106
|
+
|
107
|
+
def __build_variable(self, name: str, value, locked: bool, getter=None, setter=None) -> ClassVariable:
|
108
|
+
class_variable = ClassVariable()
|
109
|
+
class_variable.Name = name
|
110
|
+
class_variable.Value = self._vm.convert_to_go_value(value)
|
111
|
+
class_variable.Locked = locked
|
112
|
+
if getter is not None:
|
113
|
+
unique_id = str(uuid.uuid4())
|
114
|
+
|
115
|
+
def wrapper(this_id):
|
116
|
+
return self._vm.convert_to_go_value(getter(object_instances[this_id]))
|
117
|
+
|
118
|
+
defined_functions[unique_id] = (self._vm, wrapper)
|
119
|
+
self._vm._compat.BuildNewClassVariableWithGetter(class_variable, unique_id, gopy_wrapper)
|
120
|
+
if setter is not None:
|
121
|
+
unique_id = str(uuid.uuid4())
|
122
|
+
|
123
|
+
def wrapper(this_id, value):
|
124
|
+
setter(object_instances[this_id], self._vm.convert_from_go_value(value))
|
125
|
+
|
126
|
+
defined_functions[unique_id] = (self._vm, wrapper)
|
127
|
+
self._vm._compat.BuildNewClassVariableWithSetter(class_variable, unique_id, gopy_wrapper)
|
128
|
+
return class_variable
|
129
|
+
|
130
|
+
def add_public_variable(self, name: str, value = None, locked: bool = False, getter=None, setter=None) -> 'ObjectiveLOLVM.ClassBuilder':
|
131
|
+
variable = self.__build_variable(name, value, locked, getter, setter)
|
132
|
+
self._class.PublicVariables[name] = variable
|
133
|
+
return self
|
134
|
+
|
135
|
+
def add_private_variable(self, name: str, value = None, locked: bool = False, getter=None, setter=None) -> 'ObjectiveLOLVM.ClassBuilder':
|
136
|
+
variable = self.__build_variable(name, value, locked, getter, setter)
|
137
|
+
self._class.PrivateVariables[name] = variable
|
138
|
+
return self
|
139
|
+
|
140
|
+
def add_shared_variable(self, name: str, value = None, locked: bool = False, getter=None, setter=None) -> 'ObjectiveLOLVM.ClassBuilder':
|
141
|
+
variable = self.__build_variable(name, value, locked, getter, setter)
|
142
|
+
self._class.SharedVariables[name] = variable
|
143
|
+
return self
|
144
|
+
|
145
|
+
def __build_method(self, name: str, function, argc: int = None) -> ClassMethod:
|
146
|
+
argc = len(inspect.signature(function).parameters) - 1 if argc is None else argc
|
147
|
+
unique_id = str(uuid.uuid4())
|
148
|
+
|
149
|
+
def wrapper(this_id, *args):
|
150
|
+
return self._vm.convert_to_go_value(function(object_instances[this_id], *args))
|
151
|
+
|
152
|
+
defined_functions[unique_id] = (self._vm, wrapper)
|
153
|
+
class_method = ClassMethod()
|
154
|
+
class_method.Name = name
|
155
|
+
class_method.Argc = argc
|
156
|
+
|
157
|
+
self._vm._compat.BuildNewClassMethod(class_method, unique_id, gopy_wrapper)
|
158
|
+
return class_method
|
159
|
+
|
160
|
+
def add_constructor(self, typ: type) -> 'ObjectiveLOLVM.ClassBuilder':
|
161
|
+
# get init function
|
162
|
+
init_function = typ.__init__
|
163
|
+
argc = len(inspect.signature(init_function).parameters) - 1
|
164
|
+
|
165
|
+
# ignore args and kwargs
|
166
|
+
for param in inspect.signature(init_function).parameters.values():
|
167
|
+
if param.kind in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
|
168
|
+
argc = argc - 1
|
169
|
+
|
170
|
+
unique_id = str(uuid.uuid4())
|
171
|
+
|
172
|
+
def ctor_wrapper(this_id, *args):
|
173
|
+
mro = self._vm._compat.GetObjectMRO(this_id)
|
174
|
+
simple_mro = convert_to_simple_mro(mro)
|
175
|
+
|
176
|
+
instance_class = typ
|
177
|
+
if len(mro) > 1:
|
178
|
+
go_value = self._vm._compat.LookupObject(this_id)
|
179
|
+
instance_class = self._vm.create_proxy_class([defined_classes[cls_name.upper()] for cls_name in simple_mro if cls_name.upper() in defined_classes], go_value)
|
180
|
+
|
181
|
+
instance = instance_class(*args)
|
182
|
+
object_instances[this_id] = instance
|
183
|
+
|
184
|
+
defined_functions[unique_id] = (self._vm, ctor_wrapper)
|
185
|
+
class_method = ClassMethod()
|
186
|
+
class_method.Name = typ.__name__
|
187
|
+
class_method.Argc = argc
|
188
|
+
|
189
|
+
self._vm._compat.BuildNewClassMethod(class_method, unique_id, gopy_wrapper)
|
190
|
+
self._class.PublicMethods[typ.__name__] = class_method
|
191
|
+
return self
|
192
|
+
|
193
|
+
def add_public_method(self, name: str, function, argc: int = None) -> 'ObjectiveLOLVM.ClassBuilder':
|
194
|
+
method = self.__build_method(name, function, argc)
|
195
|
+
self._class.PublicMethods[name] = method
|
196
|
+
return self
|
197
|
+
|
198
|
+
def add_public_coroutine(self, name: str, function) -> 'ObjectiveLOLVM.ClassBuilder':
|
199
|
+
argc = len(inspect.signature(function).parameters) - 1
|
200
|
+
|
201
|
+
def wrapper(this, *args):
|
202
|
+
fut = concurrent.futures.Future()
|
203
|
+
def do():
|
204
|
+
try:
|
205
|
+
result = asyncio.run_coroutine_threadsafe(function(this, *args), self._vm._loop).result()
|
206
|
+
fut.set_result(result)
|
207
|
+
except Exception as e:
|
208
|
+
fut.set_exception(e)
|
209
|
+
threading.Thread(target=do).start()
|
210
|
+
return fut.result()
|
211
|
+
|
212
|
+
method = self.__build_method(name, wrapper, argc)
|
213
|
+
self._class.PublicMethods[name] = method
|
214
|
+
return self
|
215
|
+
|
216
|
+
def add_private_method(self, name: str, function, argc: int = None) -> 'ObjectiveLOLVM.ClassBuilder':
|
217
|
+
method = self.__build_method(name, function, argc)
|
218
|
+
self._class.PrivateMethods[name] = method
|
219
|
+
return self
|
220
|
+
|
221
|
+
def add_private_coroutine(self, name: str, function) -> 'ObjectiveLOLVM.ClassBuilder':
|
222
|
+
argc = len(inspect.signature(function).parameters) - 1
|
223
|
+
|
224
|
+
def wrapper(this, *args):
|
225
|
+
fut = concurrent.futures.Future()
|
226
|
+
def do():
|
227
|
+
try:
|
228
|
+
result = asyncio.run_coroutine_threadsafe(function(this, *args), self._vm._loop).result()
|
229
|
+
fut.set_result(result)
|
230
|
+
except Exception as e:
|
231
|
+
fut.set_exception(e)
|
232
|
+
threading.Thread(target=do).start()
|
233
|
+
return fut.result()
|
234
|
+
|
235
|
+
method = self.__build_method(name, wrapper, argc)
|
236
|
+
self._class.PrivateMethods[name] = method
|
237
|
+
return self
|
238
|
+
|
239
|
+
def add_unknown_function_handler(self, function) -> 'ObjectiveLOLVM.ClassBuilder':
|
240
|
+
def handler(this_id: str, fname: str, from_context: str, *args):
|
241
|
+
return function(object_instances[this_id], fname, from_context, *args)
|
242
|
+
|
243
|
+
unique_id = str(uuid.uuid4())
|
244
|
+
defined_functions[unique_id] = (self._vm, handler)
|
245
|
+
self._class.UnknownFunctionHandler = self._vm._compat.BuildNewUnknownFunctionHandler(unique_id, gopy_wrapper)
|
246
|
+
return self
|
247
|
+
|
248
|
+
def add_unknown_coroutine_handler(self, function) -> 'ObjectiveLOLVM.ClassBuilder':
|
249
|
+
def handler(this_id: str, fname: str, from_context: str, *args):
|
250
|
+
fut = concurrent.futures.Future()
|
251
|
+
def do():
|
252
|
+
try:
|
253
|
+
result = asyncio.run_coroutine_threadsafe(function(object_instances[this_id], fname, from_context, *args), self._vm._loop).result()
|
254
|
+
fut.set_result(result)
|
255
|
+
except Exception as e:
|
256
|
+
fut.set_exception(e)
|
257
|
+
threading.Thread(target=do).start()
|
258
|
+
return fut.result()
|
259
|
+
|
260
|
+
unique_id = str(uuid.uuid4())
|
261
|
+
defined_functions[unique_id] = (self._vm, handler)
|
262
|
+
self._class.UnknownFunctionHandler = self._vm._compat.BuildNewUnknownFunctionHandler(unique_id, gopy_wrapper)
|
263
|
+
return self
|
264
|
+
|
265
|
+
_vm: VM
|
266
|
+
_compat: VMCompatibilityShim
|
267
|
+
_loop: asyncio.AbstractEventLoop
|
268
|
+
_prefer_async_loop: bool
|
269
|
+
|
270
|
+
def __init__(self, prefer_async_loop: bool = True):
|
271
|
+
# todo: figure out how to bridge stdout/stdin
|
272
|
+
self._vm = NewVM(DefaultConfig())
|
273
|
+
self._compat = self._vm.GetCompatibilityShim()
|
274
|
+
self._loop = asyncio.get_event_loop()
|
275
|
+
self._prefer_async_loop = prefer_async_loop
|
276
|
+
|
277
|
+
def convert_from_go_value(self, go_value: GoValue):
|
278
|
+
if not isinstance(go_value, GoValue):
|
279
|
+
if go_value and GoValueIDKey in go_value:
|
280
|
+
go_value = self._compat.LookupObject(go_value[GoValueIDKey])
|
281
|
+
return self.convert_from_go_value(go_value)
|
282
|
+
return go_value
|
283
|
+
typ = go_value.Type()
|
284
|
+
if typ == "INTEGR":
|
285
|
+
return go_value.Int()
|
286
|
+
elif typ == "DUBBLE":
|
287
|
+
return go_value.Float()
|
288
|
+
elif typ == "STRIN":
|
289
|
+
return go_value.String()
|
290
|
+
elif typ == "BOOL":
|
291
|
+
return go_value.Bool()
|
292
|
+
elif typ == "NOTHIN":
|
293
|
+
return None
|
294
|
+
elif typ == "BUKKIT":
|
295
|
+
return [self.convert_from_go_value(v) for v in go_value.Slice()]
|
296
|
+
elif typ == "BASKIT":
|
297
|
+
return {k: self.convert_from_go_value(v) for k, v in go_value.Map().items()}
|
298
|
+
else:
|
299
|
+
# object handle
|
300
|
+
if go_value.ID() in object_instances:
|
301
|
+
return object_instances[go_value.ID()]
|
302
|
+
|
303
|
+
mro = self._compat.GetObjectMRO(go_value.ID())
|
304
|
+
simple_mro = convert_to_simple_mro(mro)
|
305
|
+
instance_class = self.create_proxy_class([defined_classes[cls_name.upper()] for cls_name in simple_mro if cls_name.upper() in defined_classes], go_value)
|
306
|
+
instance = instance_class()
|
307
|
+
object_instances[go_value.ID()] = instance
|
308
|
+
return instance
|
309
|
+
|
310
|
+
def convert_to_go_value(self, value):
|
311
|
+
if value is None:
|
312
|
+
return GoValue()
|
313
|
+
if isinstance(value, int):
|
314
|
+
return WrapInt(value)
|
315
|
+
elif isinstance(value, float):
|
316
|
+
return WrapFloat(value)
|
317
|
+
elif isinstance(value, str):
|
318
|
+
return WrapString(value)
|
319
|
+
elif isinstance(value, bool):
|
320
|
+
return WrapBool(value)
|
321
|
+
elif isinstance(value, GoValue):
|
322
|
+
# object handle, pass through
|
323
|
+
return value
|
324
|
+
elif isinstance(value, (list, tuple)):
|
325
|
+
slice = Slice_api_GoValue()
|
326
|
+
for v in value:
|
327
|
+
slice.append(self.convert_to_go_value(v))
|
328
|
+
return slice
|
329
|
+
elif isinstance(value, dict):
|
330
|
+
map = Map_string_api_GoValue()
|
331
|
+
for k, v in value.items():
|
332
|
+
map[k] = self.convert_to_go_value(v)
|
333
|
+
return map
|
334
|
+
elif isinstance(type(value), ProxyMeta):
|
335
|
+
return value._go_value
|
336
|
+
else:
|
337
|
+
self.define_class(type(value), fully_qualified=True)
|
338
|
+
instance = self._vm.NewObjectInstance("{}.{}".format(type(value).__module__, type(value).__name__))
|
339
|
+
|
340
|
+
# for attributes added at runtime (e.g. in __init__),
|
341
|
+
# inject getters and setters for them
|
342
|
+
for a in dir(value):
|
343
|
+
if hasattr(type(value), a):
|
344
|
+
continue
|
345
|
+
|
346
|
+
class_variable = ClassVariable()
|
347
|
+
class_variable.Name = a.upper()
|
348
|
+
|
349
|
+
getter = lambda obj, attr=a: getattr(obj, attr)
|
350
|
+
unique_id = str(uuid.uuid4())
|
351
|
+
def wrapper(this_id, getter=getter):
|
352
|
+
return self.convert_to_go_value(getter(object_instances[this_id]))
|
353
|
+
defined_functions[unique_id] = (self, wrapper)
|
354
|
+
self._compat.BuildNewClassVariableWithGetter(class_variable, unique_id, gopy_wrapper)
|
355
|
+
|
356
|
+
setter = lambda obj, val, attr=a: setattr(obj, attr, val)
|
357
|
+
unique_id = str(uuid.uuid4())
|
358
|
+
def wrapper(this_id, value, setter=setter):
|
359
|
+
setter(object_instances[this_id], self.convert_from_go_value(value))
|
360
|
+
defined_functions[unique_id] = (self, wrapper)
|
361
|
+
self._compat.BuildNewClassVariableWithSetter(class_variable, unique_id, gopy_wrapper)
|
362
|
+
|
363
|
+
self._compat.AddVariableToObject(instance.ID(), class_variable)
|
364
|
+
|
365
|
+
object_instances[instance.ID()] = value
|
366
|
+
return instance
|
367
|
+
|
368
|
+
def serialize_go_value(self, go_value: GoValue):
|
369
|
+
if isinstance(go_value, GoValue):
|
370
|
+
if go_value.ID() != "":
|
371
|
+
return {GoValueIDKey: go_value.ID()}
|
372
|
+
return self.convert_from_go_value(go_value)
|
373
|
+
else:
|
374
|
+
return go_value
|
375
|
+
|
376
|
+
def create_proxy_class(self, mro: list[type], go_value: GoValue) -> type:
|
377
|
+
instance_immediate_functions = self._compat.GetObjectImmediateFunctions(go_value.ID())
|
378
|
+
superself = self
|
379
|
+
|
380
|
+
class Proxy(*mro, metaclass=ProxyMeta, go_value=go_value):
|
381
|
+
def __getattribute__(self, name):
|
382
|
+
# Handles basic object attributes to avoid infinite recursion
|
383
|
+
if name in ('_go_value', '_create_proxy_method', '__class__', '__dict__'):
|
384
|
+
return super().__getattribute__(name)
|
385
|
+
|
386
|
+
# Check if this method should be proxied to the VM
|
387
|
+
if name.upper() in instance_immediate_functions:
|
388
|
+
# This method belongs to the immediate class, proxy to VM
|
389
|
+
return self._create_proxy_method(name)
|
390
|
+
|
391
|
+
# For everything else, get it normally
|
392
|
+
return super().__getattribute__(name)
|
393
|
+
|
394
|
+
def _create_proxy_method(self, method_name):
|
395
|
+
is_async = False
|
396
|
+
try:
|
397
|
+
method = super().__getattribute__(method_name)
|
398
|
+
if callable(method):
|
399
|
+
if inspect.iscoroutinefunction(method):
|
400
|
+
is_async = True
|
401
|
+
except:
|
402
|
+
pass
|
403
|
+
|
404
|
+
if is_async:
|
405
|
+
return functools.partial(superself.call_method_async, self._go_value, method_name)
|
406
|
+
else:
|
407
|
+
return functools.partial(superself.call_method, self._go_value, method_name)
|
408
|
+
|
409
|
+
return Proxy
|
410
|
+
|
411
|
+
def define_variable(self, name: str, value, constant: bool = False) -> None:
|
412
|
+
goValue = self.convert_to_go_value(value)
|
413
|
+
self._vm.DefineVariable(name, goValue, constant)
|
414
|
+
|
415
|
+
def define_function(self, name: str, function, argc: int = None) -> None:
|
416
|
+
argc = len(inspect.signature(function).parameters) if argc is None else argc
|
417
|
+
unique_id = str(uuid.uuid4())
|
418
|
+
defined_functions[unique_id] = (self, function)
|
419
|
+
self._compat.DefineFunction(unique_id, name, argc, gopy_wrapper)
|
420
|
+
|
421
|
+
def define_coroutine(self, name: str, function) -> None:
|
422
|
+
argc = len(inspect.signature(function).parameters)
|
423
|
+
|
424
|
+
def wrapper(*args):
|
425
|
+
fut = concurrent.futures.Future()
|
426
|
+
def do():
|
427
|
+
try:
|
428
|
+
result = asyncio.run_coroutine_threadsafe(function(*args), self._loop).result()
|
429
|
+
fut.set_result(result)
|
430
|
+
except Exception as e:
|
431
|
+
fut.set_exception(e)
|
432
|
+
threading.Thread(target=do).start()
|
433
|
+
return fut.result()
|
434
|
+
|
435
|
+
self.define_function(name, wrapper, argc)
|
436
|
+
|
437
|
+
def define_class(self, python_class: type, fully_qualified: bool = False) -> None:
|
438
|
+
class_name = f"{python_class.__module__}.{python_class.__name__}" if fully_qualified else python_class.__name__
|
439
|
+
if self._compat.IsClassDefined(class_name):
|
440
|
+
return
|
441
|
+
|
442
|
+
# Use class builder to introspect and build the class definition
|
443
|
+
builder = ObjectiveLOLVM.ClassBuilder(self)
|
444
|
+
builder.set_name(class_name)
|
445
|
+
builder.add_constructor(python_class)
|
446
|
+
|
447
|
+
# Add class attributes as variables with getters/setters
|
448
|
+
for attr_name in dir(python_class):
|
449
|
+
if not attr_name.startswith('_') and not callable(getattr(python_class, attr_name)):
|
450
|
+
builder.add_public_variable(
|
451
|
+
attr_name,
|
452
|
+
getter=lambda self, attr=attr_name: getattr(self, attr),
|
453
|
+
setter=lambda self, value, attr=attr_name: setattr(self, attr, value)
|
454
|
+
)
|
455
|
+
|
456
|
+
# Add methods
|
457
|
+
for method_name in dir(python_class):
|
458
|
+
if not method_name.startswith('_') and callable(getattr(python_class, method_name)):
|
459
|
+
method = getattr(python_class, method_name)
|
460
|
+
if not method_name == python_class.__name__: # Skip constructor
|
461
|
+
if inspect.iscoroutinefunction(method):
|
462
|
+
builder.add_public_coroutine(method_name, method)
|
463
|
+
else:
|
464
|
+
builder.add_public_method(method_name, method)
|
465
|
+
|
466
|
+
# Dynamic handler for unknown function calls
|
467
|
+
# We assume all unknown functions are publicly available, so
|
468
|
+
# no context checking is needed. However, since Objective-LOL
|
469
|
+
# is case-insensitive for method names, we need to try all
|
470
|
+
# case permutations to find a match.
|
471
|
+
|
472
|
+
# Cache for case-insensitive method name lookups
|
473
|
+
_method_name_cache = {}
|
474
|
+
if self._prefer_async_loop:
|
475
|
+
async def async_handler(this, fname: str, from_context: str, *args):
|
476
|
+
# Check cache first
|
477
|
+
cache_key = (id(this), fname.upper())
|
478
|
+
if cache_key in _method_name_cache:
|
479
|
+
actual_method_name = _method_name_cache[cache_key]
|
480
|
+
if actual_method_name:
|
481
|
+
method = getattr(this, actual_method_name)
|
482
|
+
return await method(*args)
|
483
|
+
else:
|
484
|
+
# Cached as not found
|
485
|
+
raise AttributeError(f"'{type(this).__name__}' object has no attribute '{fname}'")
|
486
|
+
|
487
|
+
# Try all case permutations
|
488
|
+
for candidate in generate_case_permutations(fname):
|
489
|
+
try:
|
490
|
+
if hasattr(this, candidate):
|
491
|
+
method = getattr(this, candidate)
|
492
|
+
if callable(method):
|
493
|
+
_method_name_cache[cache_key] = candidate
|
494
|
+
return await method(*args)
|
495
|
+
except Exception as e:
|
496
|
+
continue
|
497
|
+
|
498
|
+
# Cache as not found
|
499
|
+
_method_name_cache[cache_key] = None
|
500
|
+
raise AttributeError(f"'{type(this).__name__}' object has no attribute '{fname}'")
|
501
|
+
builder.add_unknown_coroutine_handler(async_handler)
|
502
|
+
else:
|
503
|
+
def handler(this, fname: str, from_context: str, *args):
|
504
|
+
# Check cache first
|
505
|
+
cache_key = (id(this), fname.upper())
|
506
|
+
if cache_key in _method_name_cache:
|
507
|
+
actual_method_name = _method_name_cache[cache_key]
|
508
|
+
if actual_method_name:
|
509
|
+
method = getattr(this, actual_method_name)
|
510
|
+
return method(*args)
|
511
|
+
else:
|
512
|
+
# Cached as not found
|
513
|
+
raise AttributeError(f"'{type(this).__name__}' object has no attribute '{fname}'")
|
514
|
+
|
515
|
+
# Try all case permutations
|
516
|
+
for candidate in generate_case_permutations(fname):
|
517
|
+
if hasattr(this, candidate):
|
518
|
+
method = getattr(this, candidate)
|
519
|
+
if callable(method):
|
520
|
+
_method_name_cache[cache_key] = candidate
|
521
|
+
return method(*args)
|
522
|
+
|
523
|
+
# Cache as not found
|
524
|
+
_method_name_cache[cache_key] = None
|
525
|
+
raise AttributeError(f"'{type(this).__name__}' object has no attribute '{fname}'")
|
526
|
+
builder.add_unknown_function_handler(handler)
|
527
|
+
|
528
|
+
class_def = builder.get()
|
529
|
+
self._vm.DefineClass(class_def)
|
530
|
+
|
531
|
+
defined_classes[class_name.upper()] = python_class
|
532
|
+
|
533
|
+
def call(self, name: str, *args):
|
534
|
+
goArgs = self.convert_to_go_value(args)
|
535
|
+
result = self._vm.Call(name, goArgs)
|
536
|
+
return self.convert_from_go_value(result)
|
537
|
+
|
538
|
+
async def call_async(self, name: str, *args):
|
539
|
+
goArgs = self.convert_to_go_value(args)
|
540
|
+
fut = concurrent.futures.Future()
|
541
|
+
def do():
|
542
|
+
try:
|
543
|
+
result = self._vm.Call(name, goArgs)
|
544
|
+
fut.set_result(self.convert_from_go_value(result))
|
545
|
+
except Exception as e:
|
546
|
+
fut.set_exception(e)
|
547
|
+
threading.Thread(target=do).start()
|
548
|
+
return await asyncio.wrap_future(fut)
|
549
|
+
|
550
|
+
def call_method(self, receiver: GoValue, name: str, *args):
|
551
|
+
goArgs = self.convert_to_go_value(args)
|
552
|
+
result = self._vm.CallMethod(receiver, name, goArgs)
|
553
|
+
return self.convert_from_go_value(result)
|
554
|
+
|
555
|
+
async def call_method_async(self, receiver: GoValue, name: str, *args):
|
556
|
+
goArgs = self.convert_to_go_value(args)
|
557
|
+
fut = concurrent.futures.Future()
|
558
|
+
def do():
|
559
|
+
try:
|
560
|
+
result = self._vm.CallMethod(receiver, name, goArgs)
|
561
|
+
fut.set_result(self.convert_from_go_value(result))
|
562
|
+
except Exception as e:
|
563
|
+
fut.set_exception(e)
|
564
|
+
threading.Thread(target=do).start()
|
565
|
+
result = await asyncio.wrap_future(fut)
|
566
|
+
return result
|
567
|
+
|
568
|
+
def execute(self, code: str) -> None:
|
569
|
+
return self._vm.Execute(code)
|
570
|
+
|
571
|
+
async def execute_async(self, code: str) -> None:
|
572
|
+
fut = concurrent.futures.Future()
|
573
|
+
def do():
|
574
|
+
try:
|
575
|
+
result = self._vm.Execute(code)
|
576
|
+
fut.set_result(result)
|
577
|
+
except Exception as e:
|
578
|
+
fut.set_exception(e)
|
579
|
+
threading.Thread(target=do).start()
|
580
|
+
return await asyncio.wrap_future(fut)
|
581
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: objective_lol
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Python bindings for Objective-LOL
|
5
|
+
Home-page: https://github.com/bjia56/objective-lol
|
6
|
+
Author: Brett Jia
|
7
|
+
Author-email: dev.bjia56@gmail.com
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
Dynamic: author
|
12
|
+
Dynamic: author-email
|
13
|
+
Dynamic: classifier
|
14
|
+
Dynamic: description
|
15
|
+
Dynamic: description-content-type
|
16
|
+
Dynamic: home-page
|
17
|
+
Dynamic: summary
|
18
|
+
|
19
|
+
# Objective-LOL Python Bindings
|
20
|
+
|
21
|
+
Python bindings for the Objective-LOL programming language - a modern, strongly-typed language inspired by LOLCODE.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
```bash
|
26
|
+
pip install objective-lol
|
27
|
+
```
|
28
|
+
|
29
|
+
## Quick Start
|
30
|
+
|
31
|
+
```python
|
32
|
+
import objective_lol as olol
|
33
|
+
|
34
|
+
# Create a VM instance
|
35
|
+
vm = olol.ObjectiveLOLVM()
|
36
|
+
|
37
|
+
# Execute Objective-LOL code
|
38
|
+
code = """
|
39
|
+
I CAN HAS STDIO?
|
40
|
+
HAI ME TEH FUNCSHUN MAIN
|
41
|
+
I HAS A VARIABLE X TEH INTEGR ITZ 42
|
42
|
+
SAYZ WIT X
|
43
|
+
KTHXBAI
|
44
|
+
"""
|
45
|
+
|
46
|
+
vm.execute(code) # Prints: 42
|
47
|
+
```
|
48
|
+
|
49
|
+
## Features
|
50
|
+
|
51
|
+
- **Execute Objective-LOL code**: Run complete programs or code snippets
|
52
|
+
- **Python integration**: Call Python functions from Objective-LOL and vice versa
|
53
|
+
- **Type conversion**: Automatic conversion between Python and Objective-LOL types
|
54
|
+
- **Module system**: Import and use custom modules
|
55
|
+
- **Class definitions**: Define and use classes from Python
|
56
|
+
|
57
|
+
## Advanced Usage
|
58
|
+
|
59
|
+
### Defining Python Functions for Objective-LOL
|
60
|
+
|
61
|
+
```python
|
62
|
+
vm = olol.ObjectiveLOLVM()
|
63
|
+
|
64
|
+
def add_numbers(a, b):
|
65
|
+
return a + b
|
66
|
+
|
67
|
+
vm.define_function("add_numbers", add_numbers)
|
68
|
+
|
69
|
+
code = """
|
70
|
+
I CAN HAS STDIO?
|
71
|
+
HAI ME TEH FUNCSHUN MAIN
|
72
|
+
I HAS A VARIABLE RESULT TEH INTEGR ITZ add_numbers WIT 10 AN WIT 20
|
73
|
+
SAYZ WIT RESULT
|
74
|
+
KTHXBAI
|
75
|
+
"""
|
76
|
+
|
77
|
+
vm.execute(code) # Prints: 30
|
78
|
+
```
|
79
|
+
|
80
|
+
### Working with Classes
|
81
|
+
|
82
|
+
```python
|
83
|
+
vm = olol.ObjectiveLOLVM()
|
84
|
+
|
85
|
+
class Calculator:
|
86
|
+
def add(self, x, y):
|
87
|
+
return x + y
|
88
|
+
|
89
|
+
def multiply(self, x, y):
|
90
|
+
return x * y
|
91
|
+
|
92
|
+
vm.define_class(Calculator)
|
93
|
+
|
94
|
+
code = """
|
95
|
+
I CAN HAS STDIO?
|
96
|
+
HAI ME TEH FUNCSHUN MAIN
|
97
|
+
I HAS A VARIABLE CALC TEH Calculator ITZ NEW Calculator
|
98
|
+
I HAS A VARIABLE SUM TEH INTEGR ITZ CALC DO add WIT 5 AN WIT 3
|
99
|
+
SAYZ WIT SUM
|
100
|
+
KTHXBAI
|
101
|
+
"""
|
102
|
+
|
103
|
+
vm.execute(code) # Prints: 8
|
104
|
+
```
|
105
|
+
|
106
|
+
## Type Mapping
|
107
|
+
|
108
|
+
| Objective-LOL Type | Python Type |
|
109
|
+
|-------------------|-------------|
|
110
|
+
| INTEGR | int |
|
111
|
+
| DUBBLE | float |
|
112
|
+
| STRIN | str |
|
113
|
+
| BOOL | bool |
|
114
|
+
| NOTHIN | None |
|
115
|
+
| BUKKIT (array) | list |
|
116
|
+
| BASKIT (map) | dict |
|
117
|
+
|
118
|
+
## Links
|
119
|
+
|
120
|
+
- [Main Project](https://github.com/bjia56/objective-lol)
|
121
|
+
- [Language Documentation](https://github.com/bjia56/objective-lol/tree/main/docs)
|
122
|
+
|
123
|
+
## License
|
124
|
+
|
125
|
+
MIT License
|