Skip to content

Instantly share code, notes, and snippets.

@Synthetica9
Created April 3, 2015 05:32
Show Gist options
  • Save Synthetica9/d6f2b38e72629b9ac07a to your computer and use it in GitHub Desktop.
Save Synthetica9/d6f2b38e72629b9ac07a to your computer and use it in GitHub Desktop.
for Joebo
#!/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