Created
April 3, 2015 05:32
-
-
Save Synthetica9/d6f2b38e72629b9ac07a to your computer and use it in GitHub Desktop.
for Joebo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# from TypeConversions import JRepr | |
# TODO: Fix implicit string conversions caused by str.format | |
__author__ = 'Synthetica' | |
import ctypes as ct | |
import jBindings.locals as lc | |
from jBindings import tools | |
import types | |
import string | |
import os | |
from inspect import getsourcefile | |
tpelist32 = {lc.BOOL: ct.c_bool, | |
lc.STRING: ct.c_char, | |
lc.INT: ct.c_int32, | |
lc.FLOAT: ct.c_double, | |
lc.UNICODE: ct.c_wchar} | |
_path = os.path.abspath(os.path.dirname(getsourcefile(lambda: None))) | |
#TODO: Replace getsourcefile with something a bit more robust. | |
tools.flag(path=_path) | |
if os.name == 'nt': | |
std_lib = ct.oledll.LoadLibrary(os.path.join(_path, 'j.dll')) | |
elif os.name == 'posix': | |
std_lib = ct.CDLL(os.path.join(_path, 'libj.so')) | |
else: | |
raise OSError('Unsupported os {o}'.format(repr(os.name))) | |
#std_lib = ct.CDLL('j.dll') | |
class JInstance(object): | |
def __init__(self, support_unix_pipe=True): | |
self.j_dll = std_lib | |
self.start_address = self.j_dll.JInit() | |
self.support_unix_pipe = support_unix_pipe | |
def __call__(self, command, mode=0): | |
temp_name = 'TMP__' | |
repr_name = 'REPR__' | |
type_name = 'TYPE__' | |
kind_name = 'KIND__' | |
error_name = 'ERROR__' | |
command = str(command) | |
#Delete all variables that are going to be used: | |
self.delete_var(temp_name, repr_name, type_name, kind_name) | |
self.execute_command(command, temp_name, return_result=False) | |
return_type = self.execute_command( | |
"4!:0 < '{tmp}'".format(tmp=temp_name), type_name) | |
assert tools.flag(return_type=return_type) | |
# Return the result, or raise an error: | |
if return_type == -2: | |
raise Exception("Invalid. That's all we know, sorry. " | |
"It would probably be wise to contact your library" | |
" developer about this error, " | |
"because this shouldn't ever happen") | |
elif return_type == -1: | |
error_text = self.execute_command("(13 !: 12)''", | |
var_name=error_name) | |
raise Exception("Undefined, an error probably " | |
"occurred somewhere along the way.\n" | |
"The last error raised:\n:" | |
"{t}".format(t=error_text)) | |
elif mode == 1: | |
if return_type == 0: # Only nouns allowed. | |
# self.JGetM(temp_name, *data_pointers) | |
# return JRepr(*data_pointers) | |
return self.get_var(temp_name) | |
else: | |
raise TypeError( | |
"Impossible to return type {0}, must be 0".format( | |
return_type)) | |
elif mode == 0: | |
repr_data = self.execute_command( | |
"5!:5 <('{tmp}')".format(tmp=temp_name), repr_name) | |
# PS H:\PycharmProjects\jBindings> ls .\jBindings | |
# | |
# | |
# Directory: H:\PycharmProjects\jBindings\jBindings | |
# | |
# | |
# Mode LastWriteTime Length Name | |
# ---- ------------- ------ ---- | |
# -a--- 8/8/2014 3:38 AM 1662976 j.dll | |
# -a--- 11/7/2014 3:27 PM 2169915 libj.so | |
# -a--- 10/23/2014 5:20 PM 188 locals.py | |
# -a--- 11/29/2014 11:27 PM 1332 tools.py | |
# -a--- 11/29/2014 11:45 PM 18366 __init__.pyc | |
# -a--- 11/29/2014 11:45 PM 1367 tools.pyc | |
# -a--- 11/29/2014 11:45 PM 398 locals.pyc | |
# -a--- 11/30/2014 12:06 PM 17842 __init__.py | |
# | |
# | |
# PS H:\PycharmProjects\jBindings> python | |
# Python 2.7.5 |Anaconda 1.8.0 (64-bit)| (default, Jul 1 2013, 12:37:52) [MSC v.1500 64 bit (AMD64)] on win32 | |
# Type "help", "copyright", "credits" or "license" for more information. | |
# >>> import jBindings | |
# >>> J('12') | |
# Traceback (most recent call last): | |
# File "<stdin>", line 1, in <module> | |
# NameError: name 'J' is not defined | |
# >>> jBindings.J('12') | |
# Traceback (most recent call last): | |
# File "<stdin>", line 1, in <module> | |
# File "jBindings\__init__.py", line 84, in __call__ | |
# "5!:5 <('{tmp}')".format(tmp=temp_name), repr_name) | |
# File "jBindings\__init__.py", line 149, in execute_command | |
# self.set_var_raw(var_name, command) | |
# File "jBindings\__init__.py", line 165, in set_var_raw | |
# self.execute_command('{n} =: {v}'.format(n=name, v=value)) | |
# File "jBindings\__init__.py", line 153, in execute_command | |
# self.JDo(command) | |
# File "jBindings\__init__.py", line 176, in wrapper | |
# **kwargs) | |
# WindowsError: exception: access violation reading 0xFFFFFFFFFFFFFFFF | |
# >>> jBindings.J('12') | |
# JInt('12') | |
# TODO: Fix this ^ | |
if return_type == 0: | |
# kind_pointers = [ct.pointer(ct.c_int()) for _ in range(4)] | |
# kind_query = "{0} =: 3!:0 {1}".format(kind_name, temp_name) | |
# self.JDo(kind_query) | |
# self.JGetM(kind_name, *kind_pointers) | |
kind = JTypes[ | |
self.execute_command('3!:0 ({tmp})'.format(tmp=temp_name), | |
kind_name)] | |
return kind(repr_data) | |
elif return_type == 1: | |
#raise TypeError("Adverbs are currently not supported.") | |
return JAdverb(repr_data) | |
elif return_type == 2: | |
#raise TypeError("Conjunctions are currently not supported") | |
return JConjunction(repr_data) | |
elif return_type == 3: | |
return JVerb(repr_data) | |
def __or__(self, other): | |
if self.support_unix_pipe: | |
return self.__call__(other) | |
else: | |
raise AttributeError() | |
def __ror__(self, other): | |
if self.support_unix_pipe: | |
return self.__call__(other) | |
else: | |
raise AttributeError() | |
def __del__(self): | |
self.JFree() | |
def delete_var(self, *names): | |
if len(names) == 0: | |
return | |
elif len(names) == 1: | |
query = "4!:55 <'{name}'".format(name=names[0]) | |
else: | |
names = ["'{}'".format(name) for name in | |
names] # Surround with quotes | |
query = "4!:55 {names}".format(names='; '.join(names)) | |
assert tools.flag(query=query) | |
self.execute_command(query) | |
def execute_command(self, command, var_name=None, return_result=True): | |
if var_name is not None: | |
self.set_var_raw(var_name, command) | |
if return_result: | |
return self.get_var(var_name) | |
else: | |
self.JDo(command) | |
if var_name is not None: | |
if return_result: | |
return self.get_var(var_name) | |
def set_var(self, name, value): | |
self.execute_command('{n} =: {v}'.format(n=name, v=pyToJ(value))) | |
def set_var_raw(self, name, value): | |
self.execute_command('{n} =: {v}'.format(n=name, v=value)) | |
def get_var(self, var_name): | |
pointers = [ct.pointer(ct.c_int()) for _ in range(4)] | |
self.JGetM(var_name, *pointers) | |
data = JRepr(*pointers) | |
return data | |
def __getattr__(self, item): | |
def wrapper(*args, **kwargs): | |
return getattr(self.j_dll, item)(self.start_address, *args, | |
**kwargs) | |
wrapper.__name__ = item | |
return wrapper | |
default_instance_type = JInstance | |
J = default_instance = default_instance_type() | |
# intFuncDict = {'add': '+', 'sub': '-', 'mul': '*', 'div': '%', 'mod': '|~', 'pow': '^', 'neg': '-', 'abs': '|', | |
# 'len': '#', 'invert': '<:@:-', 'lshift': '(* 2&^)~', 'rshift': '<.@:(% (2&^))', 'floordiv': '>.@:%'} | |
# containerFuncDict = {'add': ',', 'mul': '(,/) @: > @: (# <)~'} | |
# verbFuncDict = {'pow': '^:', 'invert': '~', 'and': '&'} | |
# | |
# | |
# #The following might seem a bit... Non-Pythonic. I think it's still good to have it tho, because it closely emulates J. | |
# directFuncDict = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/', 'mod': '%', 'and': '&', 'xor': '^', 'or': '|', | |
# 'neg': '-', 'pos': '+', | |
# } | |
class JType(): | |
def __init__(self, string, instance=None, func_dict=None, mutable=True, | |
cmple=True): | |
#self.JFunc = F = JFuncGen(instance) | |
if instance is None: | |
instance = default_instance | |
# if func_dict is None: | |
# func_dict = intFuncDict | |
self.func_dict = funcDicts[self.__class__] \ | |
if func_dict is None else func_dict | |
self.string = string | |
self.instance = self.i = instance | |
self.mutable = mutable | |
#Reference: https://docs.python.org/2/reference/datamodel.html#emulating-numeric-types | |
#DIADS | |
def __getattr__(self, item): | |
dct = self.func_dict | |
if dct is None: | |
dct = funcDicts[self.__class__] | |
assert tools.flag(item=item) | |
if item.startswith('__') and item.endswith('__'): | |
item = item.strip('_') | |
assert tools.flag(item=item) | |
if item in dct: | |
func = self.instance(dct[item]) | |
elif item[1:] in dct: | |
if item.startswith('r'): | |
func = self.instance(dct[item[1:]]) | |
if isinstance(func, JVerb): | |
func = ~func | |
elif isinstance(func, JConjunction): | |
func.invert = True | |
else: | |
raise AttributeError() | |
return types.MethodType(func, self) | |
else: | |
raise AttributeError() | |
#MONADS (Yadeyadeya, I'm just gonna call them that, deal with it) | |
def __str__(self): | |
return self.string | |
def __repr__(self): | |
return '{n}({s})'.format(n=self.__class__.__name__, s=repr(str(self))) | |
# #Things for lists and such. | |
# def __len__(self): | |
# return JFunc('#', self.i, mode=1)(self) | |
def tree_repr(self, name='TMP2__'): | |
self.i.set_var_raw(name, self) | |
result = self.i("5!:4 < '{name}'".format(name=name), mode=1) | |
self.i.delete_var(name) | |
return '\n'.join(result) | |
class JNoun(JType): | |
def __getitem__(self, item): | |
assert tools.flag(item) | |
if type(item) == int: | |
return JVerb('{~', self.i)(self, item) | |
elif type(item) == slice: | |
start = item.start | |
start = 0 if start is None else start | |
stop = item.stop | |
stop = len(self) - 1 if stop is None else stop | |
step = item.step | |
step = 1 if step is None else step | |
slc = range(start, stop, step) | |
slc = map(str, slc) | |
slc = " ".join(slc) | |
return JVerb('{~', self.i)(self, slc) | |
else: | |
raise TypeError() | |
def __contains__(self, item): | |
# This is how J determines this sort of thing. | |
# Might change it up to be a bit more 'Pythonic'. | |
# TODO: look at this. | |
result = JVerb('{.@:,@:(e.~)', self.i)(self, item) | |
assert tools.flag(result=result) | |
result = self.i(result, mode=1) | |
return result | |
def __nonzero__(self): | |
#Again, this is how J does it. I didn't come up with this. | |
#TODO: Look at this as well. | |
#self.__nonzero__ = lambda: bool(instance('{. ,' + '({0})'.format(string), mode=1)) | |
return self.i(JVerb('| @: * @: {. @: ,', self.i)(self), mode=1) | |
class JNumeric(JNoun): | |
def __int__(self): | |
return int(self.i(self, mode=1)) | |
def __float__(self): | |
result = self.i(self, mode=1) | |
assert tools.flag(result_from_float=result) | |
return float(result) | |
class JInt(JNumeric): | |
def __bin__(self): | |
return self.instance("'0b' , '01' {~ #:", mode=1) | |
# def __init__(self, *args, **kwargs): | |
# JType.__init__(self, *args, **kwargs) | |
# if 'func_dict' not in kwargs: | |
# self.func_dict = intFuncDict | |
# assert tools.flag(func_dict=self.func_dict) | |
class JBool(JInt): | |
pass | |
class JFloat(JNumeric): | |
pass | |
class JString(JNoun): | |
pass | |
class JBoxed(JNoun): | |
pass | |
class JUnicode(JNoun): | |
pass | |
class JExecutable(JType): | |
def __nonzero__(self): # JNoun overwrites this. | |
return True | |
class JVerb(JExecutable): | |
def __call__(self, arg1, arg2=None): | |
if any(isinstance(x, JExecutable) for x in (arg1, arg2)): | |
raise Exception # TODO: determine what exception exactly, this is too broad | |
arg1 = pyToJ(arg1) | |
assert tools.flag(arg1=arg1, arg2=arg2) | |
if arg2 is not None: | |
arg2 = pyToJ(arg2) | |
query = '({0})({1})({2})'.format(arg1, str(self), arg2) | |
assert tools.flag('Diad:', query=query) | |
else: | |
query = '({0})({1})'.format(str(self), arg1) | |
assert tools.flag('Monad:', query=query) | |
return self.instance(query) | |
def fix(self): | |
return self.instance('f.')(self) | |
def __add__(self, other): | |
return | |
def derivative(self, n=1): | |
return self.instance('d.')(self, n) | |
class JAdverb(JExecutable): | |
def __call__(self, arg1): | |
arg1 = pyToJ(arg1) | |
query = '({0})({1})'.format(arg1, self.string) | |
return self.instance(query) | |
class JConjunction(JExecutable): | |
def __init__(self, string, invert=False, *args, **kwargs): | |
JExecutable.__init__(self, string, *args, **kwargs) | |
self.invert = invert | |
def __call__(self, first_arg=None, second_arg=None): | |
if self.invert: | |
first_arg, second_arg = second_arg, first_arg | |
query = string.join('({})'.format(pyToJ(i)) | |
for i in (first_arg, self, second_arg) | |
if i is not None) | |
assert tools.flag(query=query) | |
return self.instance(query) | |
JTypes = {2 ** 0: JBool, # BOOL | |
2 ** 1: JString, # LITERAL | |
2 ** 2: JInt, # INT | |
2 ** 3: JFloat, # FLOAT | |
2 ** 4: JNumeric, # COMPLEX | |
2 ** 5: JBoxed, # BOXED | |
2 ** 6: JInt, # EXTENDED INT | |
2 ** 7: JNumeric, # RATIONAL | |
2 ** 10: JInt, | |
2 ** 11: JString, | |
2 ** 12: JInt, | |
2 ** 13: JFloat, | |
2 ** 14: JNumeric, | |
2 ** 15: JNoun, | |
2 ** 16: JNoun, | |
2 ** 17: JUnicode | |
} | |
numericFuncDict = {'add': '+', 'sub': '-', 'mul': '*', 'div': '%', 'mod': '|~', | |
'pow': '^', 'neg': '-', 'abs': '|', 'len': '#'} | |
intFuncDict = numericFuncDict.copy() | |
intFuncDict.update({'invert': '<:@:-', 'lshift': '(* 2&^)~', | |
'rshift': '<.@:(% (2&^))', 'floordiv': '>.@:%'}) | |
containerFuncDict = {'add': ',', 'mul': '(,/) @: > @: (# <)~', 'len': '#'} | |
verbFuncDict = {'pow': '^:', 'invert': '~', 'and': '&', 'mul': '@:'} | |
funcDicts = {JType: None, | |
JNumeric: numericFuncDict, | |
JFloat: numericFuncDict, | |
JInt: intFuncDict, | |
JVerb: verbFuncDict, | |
JString: containerFuncDict, | |
JBoxed: containerFuncDict, | |
JUnicode: containerFuncDict, | |
JConjunction: None, | |
JAdverb: None, | |
JBool: JNumeric | |
} | |
def JRepr(tpe, rnk, shp, pos, typelist=tpelist32): | |
#TODO: Sparse support | |
tpe, rnk, shp, pos = (i.contents.value for i in (tpe, rnk, shp, pos)) | |
assert tools.flag(type=tpe, rank=rnk, shape=shp, pos=pos) | |
shape = [typelist[4].from_address(shp + i*ct.sizeof(typelist[4])).value | |
for i in range(rnk)] | |
assert tools.flag(shape=shape) | |
datalen = tools.product(*shape) | |
assert tools.flag(datalen=datalen) | |
ctype = typelist[tpe] | |
assert tools.flag(ctype=ctype) | |
ctypesize = ct.sizeof(ctype) | |
assert tools.flag(ctypesize=ctypesize) | |
data = [ctype.from_address(pos + i * ctypesize).value for i in | |
range(datalen)] | |
if tpe == lc.STRING or tpe == lc.UNICODE: | |
data = str.join('', data) | |
#flag(bin(data[0]), data) | |
#if rnk == 0: | |
# return data[0] | |
#data = data[::-1] | |
assert tools.flag(shape=shape) | |
for i in shape[::-1]: | |
assert tools.flag(shape_i=i) | |
data = tools.chunks(data, i) | |
return data[0] | |
def JExplicit(string, instance=default_instance, mode=13): | |
return instance('{m} : {s}'.format(m=mode, s=pyToJ(string))) | |
def pyToJ(obj): | |
tpe = type(obj) | |
if tpe in customConversions: | |
return customConversions[tpe](obj) | |
elif isinstance(obj, JType): | |
return obj.string | |
elif tpe == int: | |
return str(obj) | |
elif tpe == float: | |
return str(obj) | |
elif tpe == str: | |
#if all(item in string.printable and item != "'" for item in obj): | |
if all(item in string.printable for item in obj): | |
return "'{string}'".format(string=obj.replace("'", "''")) | |
else: | |
return string.join( | |
(string.join(str(ord(item)) for item in obj), '{ a.')) | |
else: | |
raise TypeError( | |
"Couldn't convert Python to J for {object} of type '{type}'".format( | |
type=tpe.__name__, object=obj)) | |
customConversions = {} | |
def pyToJType(obj, instance=default_instance): | |
return instance(pyToJ(obj)) | |
def JConsole(): | |
while True: | |
command = raw_input(' ') | |
try: | |
result = str(J(command)) | |
except Exception as e: | |
result = e | |
print result | |
def JDebug(command=None, profile=False): | |
if command is None: | |
#J('12')+J('12') | |
JDebug('>') | |
#import pdb | |
#pdb.set_trace() | |
# from IPython import embed_kernel, embed | |
# embed() | |
import code | |
try: | |
__test() | |
except NameError: | |
pass | |
code.interact(local=globals()) | |
else: | |
if profile: | |
tools.profile('J({c})'.format(c=repr(command)), repeats=profile) | |
else: | |
DEBUG = tools.DEBUG | |
tools.DEBUG = True | |
J(command) | |
tools.DEBUG = DEBUG | |
if __name__ == '__main__': | |
# Just start a debugger. | |
def __test(): | |
from string import letters as l | |
for i in l: | |
try: tmp = J('1{}1'.format(i)) | |
except: pass | |
else: print i, str(tmp) | |
JDebug() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment