diff options
Diffstat (limited to 'prolog')
-rw-r--r-- | prolog/engine.py | 98 |
1 files changed, 73 insertions, 25 deletions
diff --git a/prolog/engine.py b/prolog/engine.py index 91c6172..50582c8 100644 --- a/prolog/engine.py +++ b/prolog/engine.py @@ -115,32 +115,8 @@ def pretty_vars(data): # Run [query] in the pengine with id [engine] and return the list of answers # found within [timeout] seconds. If a timeout occurs before the query is done, # 'timed out' is appended as the last answer. -def ask_all(engine, query, timeout): +def ask_all(engine, query, timeout=10): # Returns a tuple ((bindings, constraints), error, more?) for one answer. - def process_answer(reply): - # When an engine is destroyed, a nested data object has the actual - # query result. - if reply['event'] == 'destroy': - reply = reply['data'] - - if reply['event'] == 'success': - # Return a dictionary {var: value} and a list of constraints (as - # strings) from the JSON object returned by Prolog. - data = reply['data'][0] - bindings = {} - for binding in data['variables']: - value = strip_html(binding['value']) - for name in binding['variables']: - bindings[name] = value - constraints = [strip_html(r) for r in data.get('residuals', [])] - return (bindings, constraints), None, reply['more'] - elif reply['event'] == 'failure': - return None, None, False - elif reply['event'] == 'error': - # Remove potential module name (engine ID) from the error message. - error = ('error', reply['data'].replace("'{}':".format(reply['id']), '')) - return None, error, False - start = time.monotonic() answers, messages = [], [] try: @@ -171,6 +147,78 @@ def ask_all(engine, query, timeout): answers.append('timed out') return answers, messages +# Run [query] in the pengine with id [engine] and return the first answer only +# found within [timeout] seconds. +# used for quick hint debugging by Sasha +def ask_one(engine, query, timeout=1): + # quicker than ask_all as there could be many hint-triggering tests + # Returns either an error message, true, false, timeout (see below), or bindings + # Timeout is "returned" as an unhandled exception -- deliberately so + + # Run the query. + reply, output = ask(engine, query, timeout) + if 'error' in map(itemgetter(0), output): + return 'query syntax error' + # TODO: Tim, this doesn't really work. It just proceeds and returns as a runtime error. + answer, error, more = process_answer(reply) + if error: + # query caused a runtime error + return 'runtime error: ' + error[-1] + elif answer: + # no error, query satisfied + if not answer[0]: # empty bindings + return 'true' + else: + return answer[0] # for now return just bindings without constraints + else: + # no error, query failed + return 'false' + +def ask_truth(engine, query, timeout=1): + # quicker than ask_all as there could be many hint-triggering tests + # Returns either True or False + # (Runtime) error is False! + # Timeout is an unhandled exception -- deliberately so + + # Run the query. + reply, output = ask(engine, query, timeout) + if 'error' in map(itemgetter(0), output): + return False + # TODO: Tim, this doesn't really work. It just proceeds and returns as a runtime error. + answer, error, more = process_answer(reply) + if error: + # query caused a runtime error + return False + elif answer: + return True + else: + # no error, query failed + return False + +# reformats prolog's answer into a neater form +def process_answer(reply): + # When an engine is destroyed, a nested data object has the actual + # query result. + if reply['event'] == 'destroy': + reply = reply['data'] + + if reply['event'] == 'success': + # Return a dictionary {var: value} and a list of constraints (as + # strings) from the JSON object returned by Prolog. + data = reply['data'][0] + bindings = {} + for binding in data['variables']: + value = strip_html(binding['value']) + for name in binding['variables']: + bindings[name] = value + constraints = [strip_html(r) for r in data.get('residuals', [])] + return (bindings, constraints), None, reply['more'] + elif reply['event'] == 'failure': + return None, None, False + elif reply['event'] == 'error': + # Remove potential module name (engine ID) from the error message. + error = ('error', reply['data'].replace("'{}':".format(reply['id']), '')) + return None, error, False # Basic sanity check. if __name__ == '__main__': |