summaryrefslogtreecommitdiff
path: root/prolog
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@araneo.org>2014-12-11 12:28:02 +0100
committerAleš Smodiš <aless@guru.si>2015-08-11 14:26:00 +0200
commit34ba119fc56404f6d7e3b010602f752438372347 (patch)
tree4a0d78bac1c34a5892d5665030b37b53b1ef05da /prolog
parentbc177e287136888b388a4c61cdd0c413c7b9831d (diff)
Use abolish/1 instead of erase/1 to remove clauses
Using erase/1 keeps the dynamic property for all defined predicates in the testing module. This overrides the clauses in solution modules. For example, if the user defines conc/3 in the solution for the dup/2 problem, the correct conc/3 would not get used even after unloading all user's clauses. This commit also plugs a couple of memory leaks by opening (and later discarding) a new Prolog frame in functions test, load_solution and mark_solved.
Diffstat (limited to 'prolog')
-rw-r--r--prolog/engine.py46
1 files changed, 37 insertions, 9 deletions
diff --git a/prolog/engine.py b/prolog/engine.py
index 7c90d1a..d15704a 100644
--- a/prolog/engine.py
+++ b/prolog/engine.py
@@ -80,12 +80,13 @@ class PrologEngine(object):
# Construct some predicates.
self.p = {
+ 'abolish/1': PL_predicate(b'abolish', 1, None),
'add_import_module/3': PL_predicate(b'add_import_module', 3, None),
+ 'arg/3': PL_predicate(b'arg', 3, None),
'assertz/1': PL_predicate(b'assertz', 1, None),
- 'assertz/2': PL_predicate(b'assertz', 2, None),
- 'erase/1': PL_predicate(b'erase', 1, None),
'call_with_time_limit/2': PL_predicate(b'call_with_time_limit', 2, None),
'consult/1': PL_predicate(b'consult', 1, None),
+ 'functor/3': PL_predicate(b'functor', 3, None),
'message_to_string/2': PL_predicate(b'message_to_string', 2, None),
'read_term_from_atom/3': PL_predicate(b'read_term_from_atom', 3, None),
'safe_goal/1': PL_predicate(b'safe_goal', 1, None),
@@ -131,7 +132,8 @@ class PrologEngine(object):
return error_str
- # Call the Prolog predicate [name]. Raise an exception on error.
+ # Call the Prolog predicate [name]. Raise an exception on error. Since this
+ # creates a Termv object, it should be called within an open foreign frame.
def call(self, name, args):
qid = PL_open_query(None, self.err_flags, self.p[name], Termv(args).ref)
try:
@@ -199,15 +201,40 @@ class PrologEngine(object):
# Loads the correct solution [code] to problem [pid].
# TODO handle library loading.
def load_solution(self, pid, code):
+ fid = PL_open_foreign_frame()
module = 'solution{}'.format(pid)
for rule in prolog.util.split(code):
self.call('assertz/1', [Term('{}:({})'.format(module, rule))])
+ PL_discard_foreign_frame(fid)
# Import the correct solution for problem [pid] into module for user [uid].
def mark_solved(self, uid, pid):
+ fid = PL_open_foreign_frame()
m_user = 'user{}'.format(uid)
m_solution = 'solution{}'.format(pid)
return self.call('add_import_module/3', [Term(m_user), Term(m_solution), Term('end')])
+ PL_discard_foreign_frame(fid)
+
+ # Return the main functor defined by [clause], e.g. dup/2.
+ def predicate_indicator(self, clause):
+ # Return the main functor for [term].
+ def main_functor(term):
+ name = Term()
+ arity = Term()
+ self.call('functor/3', [term, name, arity])
+ return '{}/{}'.format(name, arity)
+
+ fid = PL_open_foreign_frame()
+ clause = Term(clause)
+ functor = main_functor(clause)
+ # Check whether [clause] is a rule or a fact.
+ if functor == ':-/2':
+ # [clause] is a rule, return the main functor for the head.
+ head = Term()
+ self.call('arg/3', [Term(1), clause, head])
+ functor = main_functor(head)
+ PL_discard_foreign_frame(fid)
+ return functor
# Test whether [code] gives the same answer to [query] as the correct
# solution. The solution for problem [pid] should be loaded beforehand.
@@ -216,6 +243,7 @@ class PrologEngine(object):
m_user = 'user{}'.format(uid)
m_solution = 'solution{}'.format(pid)
+ fid = PL_open_foreign_frame()
# Find the correct answers if not already known.
for query in queries:
if (pid, query) not in self.answers:
@@ -225,13 +253,12 @@ class PrologEngine(object):
self.answers[(pid, query)] = result[0]['X'] # TODO maybe we could check all vars?
correct = True
- refs = []
+ predicates = set()
try:
# Load the user program [code].
for rule in prolog.util.split(code):
- ref = Term()
- if self.call('assertz/2', [Term('{}:({})'.format(m_user, rule)), ref]):
- refs.append(ref)
+ self.call('assertz/1', [Term('{}:({})'.format(m_user, rule))])
+ predicates.add(self.predicate_indicator(rule))
for i, query in enumerate(queries):
result = self.query(query, m_user)
@@ -240,8 +267,9 @@ class PrologEngine(object):
correct = False
# Unload all loaded rules.
- for ref in refs:
- self.call('erase/1', [ref])
+ for predicate in predicates:
+ self.call('abolish/1', [Term(m_user + ':' + predicate)])
+ PL_discard_foreign_frame(fid)
return correct