From 1a99d21e12dad4c01d4c892ed4e8a0506bcf36aa Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Wed, 29 Jan 2014 13:23:04 +0100 Subject: Initial commit for pymonkey --- prolog/engine.py | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100755 prolog/engine.py (limited to 'prolog/engine.py') 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 -- cgit v1.2.1