From bc084afb2b171cfc0b1c77a529211d7885c7cb6b Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Thu, 2 Oct 2014 16:06:31 +0200 Subject: Rework prolog query engine Use exceptions to report errors. Used in the server branch. --- prolog/engine.py | 80 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 31 deletions(-) (limited to 'prolog') diff --git a/prolog/engine.py b/prolog/engine.py index d18b08b..6087bbc 100755 --- a/prolog/engine.py +++ b/prolog/engine.py @@ -30,7 +30,7 @@ class Term(object): else: # Parse term from [val]. if not PL_chars_to_term(bytes(val, encoding=encoding), self.ref): - raise ValueError("invalid compound term") + raise ValueError('invalid compound term') elif isinstance(val, int): PL_put_integer(self.ref, val) elif isinstance(val, float): @@ -104,9 +104,31 @@ class PrologEngine(object): # discard messages from swipl library self.load('message_hook(_, _, _). ') + def exception(self, qid): + ref = PL_exception(qid) + if ref is None: + return False + try: + ex = Term(ref=ref) + if ex is not None: + msg = Term() + if PL_call_predicate(None, self.err_flags, + self.p['message_to_string'], Termv([ex, msg]).ref): + raise Exception(str(msg)) + raise Exception('Unknown error') + finally: + PL_clear_exception() + return False + def call(self, name, args): - return PL_call_predicate(None, self.err_flags, - self.p[name], Termv(args).ref) == 1 + qid = PL_open_query(None, self.err_flags, self.p[name], Termv(args).ref) + try: + if not PL_next_solution(qid): + if not self.exception(qid): + return False + finally: + PL_cut_query(qid) + return True def consult(self, filename): return self.call('consult', [Term(Atom(filename))]) @@ -120,27 +142,32 @@ class PrologEngine(object): if tokens[idx] != ('PERIOD', '.') or idx - start <= 1: continue rule = stringify(tokens[start:idx]) + orig_rule = rule start = idx + 1 - if module != None: + if module is not None: rule = module + ':(' + rule + ')' - Ref = Term('Ref') - if self.call('assertz', [Term(rule), Ref]): - refs.append(Ref) - except: + ref = Term() + if self.call('assertz', [Term(rule), ref]): + refs.append(ref) + except Exception as ex: # unload already loaded rules (hacky, mustfix, todo) for ref in refs: self.call('erase', [ref]) - return None + raise Exception('Error loading program: {}\nBad rule: {}'.format(ex, orig_rule)) self.max_index += 1 self.refs[self.max_index] = refs return self.max_index def unload(self, index): - for ref in self.refs[index]: - self.call('erase', [ref]) - del self.refs[index] + try: + for ref in self.refs[index]: + self.call('erase', [ref]) + del self.refs[index] + except Exception as ex: + # This should never happen. + sys.stderr.write(str(ex) + '\n') # Get up to [n] solutions to query [q]. def query(self, q, module=None, n=1): @@ -149,18 +176,18 @@ class PrologEngine(object): fid = PL_open_foreign_frame() - # Parse term and store variable names. var_names = Term() options = Term([Term('variable_names', [var_names])]) goal = Term() - if not self.call('read_term_from_atom', [Term(Atom(q)), goal, options]): - sys.stderr.write('Warning: Could not read term from {}\n'.format(q)) - return None - - # Check if goal is safe with currently loaded rules. - if not self.call('safe_goal', [goal]): - sys.stderr.write('Warning: Unsafe goal: {}\n'.format(goal)) - return None + try: + # Parse term and store variable names. + if not self.call('read_term_from_atom', [Term(Atom(q)), goal, options]): + raise Exception('Warning: Could not read term from {}\n'.format(q)) + # Check if goal is safe with currently loaded rules. + if not self.call('safe_goal', [goal]): + raise Exception('Warning: Unsafe goal: {}\n'.format(goal)) + except Exception as ex: + raise solutions = Term() goal_aux = Term('findnsols', [Term(n), goal, goal, solutions]) @@ -181,16 +208,7 @@ class PrologEngine(object): result.append(variables) PL_rewind_foreign_frame(fid_solution) else: - ex = PL_exception(qid) - if ex != None: - ex = Term(ref=PL_exception(qid)) - if ex != None: - Msg = Term() - if self.call('message_to_string', [ex, Msg]): - sys.stderr.write('Error: ' + str(Msg) + '\n') - else: - sys.stderr.write('Unknown error\n') - PL_clear_exception() + self.exception(qid) PL_close_query(qid) PL_discard_foreign_frame(fid) -- cgit v1.2.1