summaryrefslogtreecommitdiff
path: root/prolog/engine.py
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@araneo.org>2014-01-29 13:23:04 +0100
committerAleš Smodiš <aless@guru.si>2015-08-11 14:25:59 +0200
commit1a99d21e12dad4c01d4c892ed4e8a0506bcf36aa (patch)
tree5b98529f8fd2d546b1a7338fbe6765d4989aa5b3 /prolog/engine.py
Initial commit for pymonkey
Diffstat (limited to 'prolog/engine.py')
-rwxr-xr-xprolog/engine.py182
1 files changed, 182 insertions, 0 deletions
diff --git a/prolog/engine.py b/prolog/engine.py
new file mode 100755
index 0000000..9e7340f
--- /dev/null
+++ b/prolog/engine.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python3
+
+import re
+
+from prolog.core import *
+
+class Atom(object):
+ __slots__ = 'ref'
+
+ def __init__(self, src=None, ref=None):
+ if ref != None:
+ self.ref = ref
+ else:
+ self.ref = PL_new_atom(bytes(src, encoding=encoding))
+
+class Term(object):
+ __slots__ = 'ref'
+
+ def __init__(self, src=None, ref=None):
+ if ref != None:
+ self.ref = ref
+ else:
+ self.ref = PL_new_term_ref()
+ if isinstance(src, str):
+ if not PL_chars_to_term(bytes(src, encoding=encoding), self.ref):
+ raise ValueError("invalid compound term")
+ elif isinstance(src, int):
+ PL_put_integer(self.ref, src)
+ elif isinstance(src, float):
+ PL_put_float(self.ref, src)
+ elif isinstance(src, list):
+ PL_put_nil(self.ref)
+ for t in src:
+ PL_cons_list(self.ref, t.ref, self.ref)
+ elif isinstance(src, tuple) and len(src) == 2:
+ name = PL_new_atom(bytes(src[0], encoding=encoding))
+ args = src[1]
+ PL_cons_functor_v(self.ref, PL_new_functor(name, len(args)), args.ref)
+ elif isinstance(src, Atom):
+ PL_put_atom(self.ref, src.ref)
+
+ def __str__(self):
+ ptr = c_char_p()
+ if PL_get_chars(self.ref, byref(ptr), CVT_ALL|CVT_WRITE|BUF_RING):
+ return str(ptr.value, encoding=encoding)
+
+class Termv(object):
+ __slots__ = 'ref', 'size'
+
+ def __init__(self, terms):
+ self.size = len(terms)
+ self.ref = PL_new_term_refs(self.size)
+ for i in range(len(terms)):
+ PL_put_term(self.ref+i, terms[i].ref)
+
+ def __len__(self):
+ return self.size
+
+ def __getitem__(self, i):
+ if not isinstance(i, int) or len(self) == 0 or i < 0 or i >= len(self):
+ raise TypeError
+ return Term(ref=self.ref+i)
+
+ def __iter__(self):
+ for i in range(len(self)):
+ yield Term(ref=self.ref+i)
+ return
+
+ def __str__(self):
+ return '[' + ','.join([str(term) for term in self]) + ']'
+
+class PrologEngine(object):
+ def __init__(self):
+ # initialize Prolog library
+ args = ['./', '-q', '--nosignals']
+ if SWI_HOME_DIR is not None:
+ args.append('--home={0}'.format(SWI_HOME_DIR))
+ s_plargs = len(args)
+ plargs = (c_char_p*s_plargs)()
+ for i in range(s_plargs):
+ plargs[i] = bytes(args[i], encoding)
+ if not PL_initialise(s_plargs, plargs):
+ raise EnvironmentError('Could not initialize Prolog environment.'
+ 'PL_initialise returned {0}'.format(result))
+
+ # construct needed predicates
+ self.p = {
+ 'assertz': PL_predicate(b'assertz', 2, None),
+ 'erase': PL_predicate(b'erase', 1, None),
+ 'call_with_depth_limit': PL_predicate(b'call_with_depth_limit', 3, None),
+ 'call_with_time_limit': PL_predicate(b'call_with_time_limit', 2, None),
+ 'consult': PL_predicate(b'consult', 1, None),
+ 'read_term_from_atom': PL_predicate(b'read_term_from_atom', 3, None),
+ 'safe_goal': PL_predicate(b'safe_goal', 1, None),
+ 'set_prolog_stack': PL_predicate(b'set_prolog_stack', 2, None)
+ }
+ self.err_flags = PL_Q_NODEBUG|PL_Q_CATCH_EXCEPTION
+
+ # database of current asserts; each load gets a new index
+ self.refs = {}
+ self.max_index = 0
+
+ PL_call_predicate(None, self.err_flags, self.p['set_prolog_stack'],
+ Termv([Term('global'), Term('limit(2*10**9)')]).ref)
+ PL_call_predicate(None, self.err_flags, self.p['set_prolog_stack'],
+ Termv([Term('local'), Term('limit(2*10**9)')]).ref)
+
+ def consult(self, filename):
+ args = Termv([Term(Atom(filename))])
+ out = PL_call_predicate(None, self.err_flags, self.p['consult'], args.ref)
+ return out == 1
+
+ def load(self, program, module=None):
+ refs = []
+ try:
+ for rule in re.split('\.(?:\s+|\Z)', program):
+ if rule == '':
+ continue
+ if module != None:
+ rule = module + ':(' + rule + ')'
+ args = Termv([Term(rule), Term('Ref')])
+ out = PL_call_predicate(None, self.err_flags, self.p['assertz'], args.ref)
+ refs.append(args[1])
+ except:
+ # unload already loaded rules (hacky, mustfix, todo)
+ for ref in refs:
+ args = Termv([ref])
+ out = PL_call_predicate(None, self.err_flags, self.p['erase'], args.ref)
+ return None
+
+ self.max_index += 1
+ self.refs[self.max_index] = refs
+ return self.max_index
+
+ def unload(self, index):
+ for ref in self.refs[index]:
+ args = Termv([ref])
+ out = PL_call_predicate(None, self.err_flags, self.p['erase'], args.ref)
+ del self.refs[index]
+
+ def query(self, q, module=None):
+ if module != None:
+ q = module + ':(' + q + ')'
+
+ fid = PL_open_foreign_frame()
+ goal = Term()
+ var_list = Term()
+ options = Term([Term(('variable_names', Termv([var_list])))])
+
+ # parse term
+ if not PL_call_predicate(None, self.err_flags, self.p['read_term_from_atom'],
+ Termv([Term(Atom(q)), goal, options]).ref):
+ return None
+ # check if goal is safe with currently loaded rules
+ if not PL_call_predicate(None, self.err_flags, self.p['safe_goal'],
+ Termv([goal]).ref):
+ return None
+
+ solutions = []
+ #depth_limit = Term(('call_with_depth_limit', Termv([goal, Term(50), Term('_')])))
+ qid = PL_open_query(None, self.err_flags, self.p['call_with_time_limit'],
+ Termv([Term(0.01), goal]).ref)
+ while len(solutions) < 10:
+ if PL_next_solution(qid):
+ solutions += [str(var_list)]
+ else:
+ ex = PL_exception(qid)
+ if ex != None:
+ ex = Term(ref=PL_exception(qid))
+ if ex != None:
+ message_to_string = PL_predicate(b'message_to_string', 2, None)
+ args = Termv([ex, Term()])
+
+ if PL_call_predicate(None, self.err_flags, message_to_string, args.ref):
+ sys.stderr.write('Error: ' + str(args[1]) + '\n')
+ else:
+ sys.stderr.write('Unknown error\n')
+ break
+ PL_close_query(qid)
+ PL_discard_foreign_frame(fid)
+
+ return solutions