summaryrefslogtreecommitdiff
path: root/prolog
diff options
context:
space:
mode:
Diffstat (limited to 'prolog')
-rw-r--r--prolog/engine.py98
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__':