summaryrefslogtreecommitdiff
path: root/prolog
diff options
context:
space:
mode:
Diffstat (limited to 'prolog')
-rw-r--r--prolog/engine.py109
1 files changed, 84 insertions, 25 deletions
diff --git a/prolog/engine.py b/prolog/engine.py
index 6627b9a..a25a07d 100644
--- a/prolog/engine.py
+++ b/prolog/engine.py
@@ -2,43 +2,53 @@
import http.client
import json
+import re
import urllib
address, port = 'localhost', 3030
class PrologEngine(object):
- def __init__(self, address=address, port=port, code=''):
- self.id = None
+ def __init__(self, address=address, port=port, code='', destroy=False, id=None):
self.conn = http.client.HTTPConnection(address, port, timeout=10)
- hdrs = {'Content-Type': 'application/json; charset=utf-8'}
- opts = json.dumps({'destroy': False, 'src_text': code, 'format': 'json'})
- self.conn.request('POST', '/pengine/create', body=opts, headers=hdrs)
+ # If existing engine ID is given, use it.
+ if id:
+ self.id = id
+ return
- response = self.conn.getresponse()
- data = response.read()
- reply = json.loads(str(data, encoding='utf-8'))
- if reply['event'] == 'create':
- self.id = reply['id']
- else:
- raise Exception('could not create engine: ' + reply['code'])
+ # Otherwise, create a new engine.
+ hdrs = {'Content-Type': 'application/json;charset=utf-8'}
+ opts = json.dumps({'destroy': destroy, 'src_text': code, 'format': 'json-s'})
+ reply, outputs = self.request('POST', '/pengine/create', body=opts, headers=hdrs)
+
+ failed = (reply['event'] != 'create')
+ warnings = []
+ errors = []
+ for output in outputs:
+ print(output)
+ message = PrologEngine.parse_prolog_output(output)
+ if output['message'] == 'warning':
+ warnings.append(message)
+ elif output['message'] == 'error':
+ failed = True
+ errors.append(message)
+
+ if failed:
+ raise Exception('\n'.join(errors))
- def send(self, event, fmt='json'):
+ self.id = reply['id']
+
+ def send(self, event):
params = urllib.parse.urlencode({
'id': self.id,
'event': event,
- 'format': fmt})
- self.conn.request('GET', '/pengine/send?' + params)
-
- response = self.conn.getresponse()
- data = str(response.read(), encoding='utf-8')
- if response.status == http.client.OK:
- return json.loads(data) if fmt == 'json' else data
- return None
-
- def ask(self, query, template='', fmt='json'):
- event = 'ask(({}),[template({})])'.format(query, template)
- reply = self.send(event, fmt=fmt)
+ 'format': 'json-s'})
+ reply, outputs = self.request('GET', '/pengine/send?' + params)
+ return reply
+
+ def ask(self, query):
+ event = 'ask(({}),[])'.format(query)
+ reply = self.send(event)
return reply
def next(self, n=1):
@@ -55,6 +65,55 @@ class PrologEngine(object):
self.conn.close()
self.conn = None
+ # Return the main reply and possible output replies.
+ def request(self, method, path, body=None, headers={}):
+ self.conn.request(method, path, body, headers)
+ outputs = []
+ while True:
+ response = self.conn.getresponse()
+ if response.status != http.client.OK:
+ raise Exception('server returned {}'.format(response.status))
+ reply = json.loads(response.read().decode('utf-8'))
+ self.id = reply['id']
+ if reply['event'] == 'output':
+ outputs.append(reply)
+ params = urllib.parse.urlencode({
+ 'id': self.id,
+ 'format': 'json-s'})
+ self.conn.request('GET', '/pengine/pull_response?' + params)
+ else:
+ return reply, outputs
+
+ # Check if output is an error message and return a prettified version of it.
+ def parse_prolog_output(output):
+ match = re.match(r'.*<pre class="[^"]*">(.*)</pre>.*',
+ output['data'], flags=re.DOTALL)
+ data = match.group(1).strip()
+ message = ''
+ if output['message'] == 'error':
+ if 'location' in output:
+ loc = output['location']
+ message += 'near line ' + str(loc['line'])
+ if 'ch' in loc:
+ message += ', character ' + str(loc['ch'])
+ message += ': '
+
+ if output.get('code') == 'syntax_error':
+ match = re.match(r'^.*Syntax error: (.*)$', data, flags=re.DOTALL)
+ message += match.group(1)
+ elif output.get('code') == 'permission_error':
+ match = re.match(r'^.*(No permission [^\n]*)', data, flags=re.DOTALL)
+ message += match.group(1)
+ elif output.get('code') == 'type_error':
+ match = re.match(r'^.*(Type error: [^\n]*)', data, flags=re.DOTALL)
+ message += match.group(1)
+ else:
+ message += data
+
+ # Replace anonymous variable names with _.
+ message = re.sub(r'_G[0-9]*', '_', message)
+ return message
+
# Basic sanity check.
if __name__ == '__main__':
engine = PrologEngine(code='dup([],[]). dup([H|T],[H,H|TT]) :- dup(T,TT).')