summaryrefslogtreecommitdiff
path: root/prolog
diff options
context:
space:
mode:
Diffstat (limited to 'prolog')
-rw-r--r--prolog/core.py1022
-rw-r--r--prolog/engine.py384
-rw-r--r--prolog/lib.pl462
3 files changed, 54 insertions, 1814 deletions
diff --git a/prolog/core.py b/prolog/core.py
deleted file mode 100644
index afcfdd0..0000000
--- a/prolog/core.py
+++ /dev/null
@@ -1,1022 +0,0 @@
-# -*- coding: utf-8 -*-
-
-
-# pyswip -- Python SWI-Prolog bridge
-# Copyright (c) 2007-2012 YĆ¼ce Tekol
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-
-import os
-import re
-import sys
-import locale
-import glob
-import warnings
-from subprocess import Popen, PIPE
-from ctypes import *
-from ctypes.util import find_library
-
-
-# To initialize the SWI-Prolog environment, two things need to be done: the
-# first is to find where the SO/DLL is located and the second is to find the
-# SWI-Prolog home, to get the saved state.
-#
-# The goal of the (entangled) process below is to make the library installation
-# independent.
-
-
-def _findSwiplPathFromFindLib():
- """
- This function resorts to ctype's find_library to find the path to the
- DLL. The biggest problem is that find_library does not give the path to the
- resource file.
-
- :returns:
- A path to the swipl SO/DLL or None if it is not found.
-
- :returns type:
- {str, None}
- """
-
- path = (find_library('swipl') or
- find_library('pl') or
- find_library('libswipl')) # This last one is for Windows
- return path
-
-
-def _findSwiplFromExec():
- """
- This function tries to use an executable on the path to find SWI-Prolog
- SO/DLL and the resource file.
-
- :returns:
- A tuple of (path to the swipl DLL, path to the resource file)
-
- :returns type:
- ({str, None}, {str, None})
- """
-
- platform = sys.platform[:3]
-
- fullName = None
- swiHome = None
-
- try: # try to get library path from swipl executable.
- # We may have pl or swipl as the executable
- try:
- cmd = Popen(['swipl', '-dump-runtime-variables'],
- stdout=PIPE, universal_newlines=True)
- except OSError:
- cmd = Popen(['pl', '-dump-runtime-variables'],
- stdout=PIPE, universal_newlines=True)
- ret = cmd.communicate()
-
- # Parse the output into a dictionary
- ret = ret[0].replace(';', '').splitlines()
- ret = [line.split('=', 1) for line in ret]
- rtvars = dict((name, value[1:-1]) for name, value in ret) # [1:-1] gets
- # rid of the
- # quotes
-
- if rtvars['PLSHARED'] == 'no':
- raise ImportError('SWI-Prolog is not installed as a shared '
- 'library.')
- else: # PLSHARED == 'yes'
- swiHome = rtvars['PLBASE'] # The environment is in PLBASE
- if not os.path.exists(swiHome):
- swiHome = None
-
- # determine platform specific path
- if platform == "win":
- dllName = rtvars['PLLIB'][:-4] + '.' + rtvars['PLSOEXT']
- path = os.path.join(rtvars['PLBASE'], 'bin')
- fullName = os.path.join(path, dllName)
-
- if not os.path.exists(fullName):
- fullName = None
-
- elif platform == "cyg":
- # e.g. /usr/lib/pl-5.6.36/bin/i686-cygwin/cygpl.dll
-
- dllName = 'cygpl.dll'
- path = os.path.join(rtvars['PLBASE'], 'bin', rtvars['PLARCH'])
- fullName = os.path.join(path, dllName)
-
- if not os.path.exists(fullName):
- fullName = None
-
- elif platform == "dar":
- dllName = 'lib' + rtvars['PLLIB'][2:] + '.' + rtvars['PLSOEXT']
- path = os.path.join(rtvars['PLBASE'], 'lib', rtvars['PLARCH'])
- baseName = os.path.join(path, dllName)
-
- if os.path.exists(baseName):
- fullName = baseName
- else: # We will search for versions
- fullName = None
-
- else: # assume UNIX-like
- # The SO name in some linuxes is of the form libswipl.so.5.10.2,
- # so we have to use glob to find the correct one
- dllName = 'lib' + rtvars['PLLIB'][2:] + '.' + rtvars['PLSOEXT']
- path = os.path.join(rtvars['PLBASE'], 'lib', rtvars['PLARCH'])
- baseName = os.path.join(path, dllName)
-
- if os.path.exists(baseName):
- fullName = baseName
- else: # We will search for versions
- pattern = baseName + '.*'
- files = glob.glob(pattern)
- if len(files) == 0:
- fullName = None
- elif len(files) == 1:
- fullName = files[0]
- else: # Will this ever happen?
- fullName = None
-
- except (OSError, KeyError): # KeyError from accessing rtvars
- pass
-
- return (fullName, swiHome)
-
-
-def _findSwiplWin():
- """
- This function uses several heuristics to gues where SWI-Prolog is installed
- in Windows. It always returns None as the path of the resource file because,
- in Windows, the way to find it is more robust so the SWI-Prolog DLL is
- always able to find it.
-
- :returns:
- A tuple of (path to the swipl DLL, path to the resource file)
-
- :returns type:
- ({str, None}, {str, None})
- """
-
- dllNames = ('swipl.dll', 'libswipl.dll')
-
- # First try: check the usual installation path (this is faster but
- # hardcoded)
- programFiles = os.getenv('ProgramFiles')
- paths = [os.path.join(programFiles, r'pl\bin', dllName)
- for dllName in dllNames]
- for path in paths:
- if os.path.exists(path):
- return (path, None)
-
- # Second try: use the find_library
- path = _findSwiplPathFromFindLib()
- if path is not None and os.path.exists(path):
- return (path, None)
-
- # Third try: use reg.exe to find the installation path in the registry
- # (reg should be installed in all Windows XPs)
- try:
- cmd = Popen(['reg', 'query',
- r'HKEY_LOCAL_MACHINE\Software\SWI\Prolog',
- '/v', 'home'], stdout=PIPE)
- ret = cmd.communicate()
-
- # Result is like:
- # ! REG.EXE VERSION 3.0
- #
- # HKEY_LOCAL_MACHINE\Software\SWI\Prolog
- # home REG_SZ C:\Program Files\pl
- # (Note: spaces may be \t or spaces in the output)
- ret = ret[0].splitlines()
- ret = [line for line in ret if len(line) > 0]
- pattern = re.compile('[^h]*home[^R]*REG_SZ( |\t)*(.*)$')
- match = pattern.match(ret[-1])
- if match is not None:
- path = match.group(2)
-
- paths = [os.path.join(path, 'bin', dllName)
- for dllName in dllNames]
- for path in paths:
- if os.path.exists(path):
- return (path, None)
-
- except OSError:
- # reg.exe not found? Weird...
- pass
-
- # May the exec is on path?
- (path, swiHome) = _findSwiplFromExec()
- if path is not None:
- return (path, swiHome)
-
- # Last try: maybe it is in the current dir
- for dllName in dllNames:
- if os.path.exists(dllName):
- return (dllName, None)
-
- return (None, None)
-
-def _findSwiplLin():
- """
- This function uses several heuristics to guess where SWI-Prolog is
- installed in Linuxes.
-
- :returns:
- A tuple of (path to the swipl so, path to the resource file)
-
- :returns type:
- ({str, None}, {str, None})
- """
-
- # Maybe the exec is on path?
- (path, swiHome) = _findSwiplFromExec()
- if path is not None:
- return (path, swiHome)
-
- # If it is not, use find_library
- path = _findSwiplPathFromFindLib()
- if path is not None:
- return (path, swiHome)
-
- # Our last try: some hardcoded paths.
- paths = ['/lib', '/usr/lib', '/usr/local/lib', '.', './lib']
- names = ['libswipl.so', 'libpl.so']
-
- path = None
- for name in names:
- for try_ in paths:
- try_ = os.path.join(try_, name)
- if os.path.exists(try_):
- path = try_
- break
-
- if path is not None:
- return (path, swiHome)
-
- return (None, None)
-
-
-def _findSwiplDar():
- """
- This function uses several heuristics to guess where SWI-Prolog is
- installed in MacOS.
-
- :returns:
- A tuple of (path to the swipl so, path to the resource file)
-
- :returns type:
- ({str, None}, {str, None})
- """
-
- # If the exec is in path
- (path, swiHome) = _findSwiplFromExec()
- if path is not None:
- return (path, swiHome)
-
- # If it is not, use find_library
- path = _findSwiplPathFromFindLib()
- if path is not None:
- return (path, swiHome)
-
- # Last guess, searching for the file
- paths = ['.', './lib', '/usr/lib/', '/usr/local/lib', '/opt/local/lib']
- names = ['libswipl.dylib', 'libpl.dylib']
-
- for name in names:
- for path in paths:
- path = os.path.join(path, name)
- if os.path.exists(path):
- return (path, None)
-
- return (None, None)
-
-
-def _findSwipl():
- """
- This function makes a big effort to find the path to the SWI-Prolog shared
- library. Since this is both OS dependent and installation dependent, we may
- not aways succeed. If we do, we return a name/path that can be used by
- CDLL(). Otherwise we raise an exception.
-
- :return: Tuple. Fist element is the name or path to the library that can be
- used by CDLL. Second element is the path were SWI-Prolog resource
- file may be found (this is needed in some Linuxes)
- :rtype: Tuple of strings
- :raises ImportError: If we cannot guess the name of the library
- """
-
- # Now begins the guesswork
- platform = sys.platform[:3]
- if platform == "win": # In Windows, we have the default installer
- # path and the registry to look
- (path, swiHome) = _findSwiplWin()
-
- elif platform in ("lin", "cyg"):
- (path, swiHome) = _findSwiplLin()
-
- elif platform == "dar": # Help with MacOS is welcome!!
- (path, swiHome) = _findSwiplDar()
-
- else:
- raise EnvironmentError('The platform %s is not supported by this '
- 'library. If you want it to be supported, '
- 'please open an issue.' % platform)
-
- # This is a catch all raise
- if path is None:
- raise ImportError('Could not find the SWI-Prolog library in this '
- 'platform. If you are sure it is installed, please '
- 'open an issue.')
- else:
- return (path, swiHome)
-
-
-def _fixWindowsPath(dll):
- """
- When the path to the DLL is not in Windows search path, Windows will not be
- able to find other DLLs on the same directory, so we have to add it to the
- path. This function takes care of it.
-
- :parameters:
- - `dll` (str) - File name of the DLL
- """
-
- if sys.platform[:3] != 'win':
- return # Nothing to do here
-
- pathToDll = os.path.dirname(dll)
- currentWindowsPath = os.getenv('PATH')
-
- if pathToDll not in currentWindowsPath:
- # We will prepend the path, to avoid conflicts between DLLs
- newPath = pathToDll + ';' + currentWindowsPath
- os.putenv('PATH', newPath)
-
-
-# Find the path and resource file. SWI_HOME_DIR shall be treated as a constant
-# by users of this module
-(_path, SWI_HOME_DIR) = _findSwipl()
-_fixWindowsPath(_path)
-
-
-# Load the library
-_lib = CDLL(_path)
-
-# PySWIP constants
-PYSWIP_MAXSTR = 1024
-c_int_p = c_void_p
-c_long_p = c_void_p
-c_double_p = c_void_p
-c_uint_p = c_void_p
-
-# constants (from SWI-Prolog.h)
-# PL_unify_term() arguments
-PL_VARIABLE = 1 # nothing
-PL_ATOM = 2 # const char
-PL_INTEGER = 3 # int
-PL_FLOAT = 4 # double
-PL_STRING = 5 # const char *
-PL_TERM = 6 #
-# PL_unify_term()
-PL_FUNCTOR = 10 # functor_t, arg ...
-PL_LIST = 11 # length, arg ...
-PL_CHARS = 12 # const char *
-PL_POINTER = 13 # void *
-# /* PlArg::PlArg(text, type) */
-#define PL_CODE_LIST (14) /* [ascii...] */
-#define PL_CHAR_LIST (15) /* [h,e,l,l,o] */
-#define PL_BOOL (16) /* PL_set_feature() */
-#define PL_FUNCTOR_CHARS (17) /* PL_unify_term() */
-#define _PL_PREDICATE_INDICATOR (18) /* predicate_t (Procedure) */
-#define PL_SHORT (19) /* short */
-#define PL_INT (20) /* int */
-#define PL_LONG (21) /* long */
-#define PL_DOUBLE (22) /* double */
-#define PL_NCHARS (23) /* unsigned, const char * */
-#define PL_UTF8_CHARS (24) /* const char * */
-#define PL_UTF8_STRING (25) /* const char * */
-#define PL_INT64 (26) /* int64_t */
-#define PL_NUTF8_CHARS (27) /* unsigned, const char * */
-#define PL_NUTF8_CODES (29) /* unsigned, const char * */
-#define PL_NUTF8_STRING (30) /* unsigned, const char * */
-#define PL_NWCHARS (31) /* unsigned, const wchar_t * */
-#define PL_NWCODES (32) /* unsigned, const wchar_t * */
-#define PL_NWSTRING (33) /* unsigned, const wchar_t * */
-#define PL_MBCHARS (34) /* const char * */
-#define PL_MBCODES (35) /* const char * */
-#define PL_MBSTRING (36) /* const char * */
-
-# /********************************
-# * NON-DETERMINISTIC CALL/RETURN *
-# *********************************/
-#
-# Note 1: Non-deterministic foreign functions may also use the deterministic
-# return methods PL_succeed and PL_fail.
-#
-# Note 2: The argument to PL_retry is a 30 bits signed integer (long).
-
-PL_FIRST_CALL = 0
-PL_CUTTED = 1
-PL_REDO = 2
-
-PL_FA_NOTRACE = 0x01 # foreign cannot be traced
-PL_FA_TRANSPARENT = 0x02 # foreign is module transparent
-PL_FA_NONDETERMINISTIC = 0x04 # foreign is non-deterministic
-PL_FA_VARARGS = 0x08 # call using t0, ac, ctx
-PL_FA_CREF = 0x10 # Internal: has clause-reference */
-
-# /*******************************
-# * CALL-BACK *
-# *******************************/
-
-PL_Q_DEBUG = 0x01 # = TRUE for backward compatibility
-PL_Q_NORMAL = 0x02 # normal usage
-PL_Q_NODEBUG = 0x04 # use this one
-PL_Q_CATCH_EXCEPTION = 0x08 # handle exceptions in C
-PL_Q_PASS_EXCEPTION = 0x10 # pass to parent environment
-PL_Q_DETERMINISTIC = 0x20 # call was deterministic
-
-# /*******************************
-# * BLOBS *
-# *******************************/
-
-#define PL_BLOB_MAGIC_B 0x75293a00 /* Magic to validate a blob-type */
-#define PL_BLOB_VERSION 1 /* Current version */
-#define PL_BLOB_MAGIC (PL_BLOB_MAGIC_B|PL_BLOB_VERSION)
-
-#define PL_BLOB_UNIQUE 0x01 /* Blob content is unique */
-#define PL_BLOB_TEXT 0x02 /* blob contains text */
-#define PL_BLOB_NOCOPY 0x04 /* do not copy the data */
-#define PL_BLOB_WCHAR 0x08 /* wide character string */
-
-# /*******************************
-# * CHAR BUFFERS *
-# *******************************/
-
-CVT_ATOM = 0x0001
-CVT_STRING = 0x0002
-CVT_LIST = 0x0004
-CVT_INTEGER = 0x0008
-CVT_FLOAT = 0x0010
-CVT_VARIABLE = 0x0020
-CVT_NUMBER = CVT_INTEGER | CVT_FLOAT
-CVT_ATOMIC = CVT_NUMBER | CVT_ATOM | CVT_STRING
-CVT_WRITE = 0x0040 # as of version 3.2.10
-CVT_ALL = CVT_ATOMIC | CVT_LIST
-CVT_MASK = 0x00ff
-
-BUF_DISCARDABLE = 0x0000
-BUF_RING = 0x0100
-BUF_MALLOC = 0x0200
-
-CVT_EXCEPTION = 0x10000 # throw exception on error
-
-# used to convert python strings to bytes (and back) when calling C functions
-encoding = locale.getpreferredencoding()
-
-argv = (c_char_p*(len(sys.argv) + 1))()
-for i, arg in enumerate(sys.argv):
- argv[i] = bytes(arg, encoding=encoding)
-
-argv[-1] = None
-argc = len(sys.argv)
-
-# /*******************************
-# * TYPES *
-# *******************************/
-#
-# typedef uintptr_t atom_t; /* Prolog atom */
-# typedef uintptr_t functor_t; /* Name/arity pair */
-# typedef void * module_t; /* Prolog module */
-# typedef void * predicate_t; /* Prolog procedure */
-# typedef void * record_t; /* Prolog recorded term */
-# typedef uintptr_t term_t; /* opaque term handle */
-# typedef uintptr_t qid_t; /* opaque query handle */
-# typedef uintptr_t PL_fid_t; /* opaque foreign context handle */
-# typedef void * control_t; /* non-deterministic control arg */
-# typedef void * PL_engine_t; /* opaque engine handle */
-# typedef uintptr_t PL_atomic_t; /* same a word */
-# typedef uintptr_t foreign_t; /* return type of foreign functions */
-# typedef wchar_t pl_wchar_t; /* Prolog wide character */
-# typedef foreign_t (*pl_function_t)(); /* foreign language functions */
-
-atom_t = c_uint_p
-functor_t = c_uint_p
-module_t = c_void_p
-predicate_t = c_void_p
-record_t = c_void_p
-term_t = c_uint_p
-qid_t = c_uint_p
-PL_fid_t = c_uint_p
-fid_t = c_uint_p
-control_t = c_void_p
-PL_engine_t = c_void_p
-PL_atomic_t = c_uint_p
-foreign_t = c_uint_p
-pl_wchar_t = c_wchar
-
-PL_initialise = _lib.PL_initialise
-#PL_initialise.argtypes = [c_int, c_c??
-
-PL_open_foreign_frame = _lib.PL_open_foreign_frame
-PL_open_foreign_frame.restype = fid_t
-
-PL_new_term_ref = _lib.PL_new_term_ref
-PL_new_term_ref.restype = term_t
-
-PL_new_term_refs = _lib.PL_new_term_refs
-PL_new_term_refs.argtypes = [c_int]
-PL_new_term_refs.restype = term_t
-
-PL_chars_to_term = _lib.PL_chars_to_term
-PL_chars_to_term.argtypes = [c_char_p, term_t]
-PL_chars_to_term.restype = c_int
-
-PL_call = _lib.PL_call
-PL_call.argtypes = [term_t, module_t]
-PL_call.restype = c_int
-
-PL_call_predicate = _lib.PL_call_predicate
-PL_call_predicate.argtypes = [module_t, c_int, predicate_t, term_t]
-PL_call_predicate.restype = fid_t
-
-PL_close_foreign_frame = _lib.PL_close_foreign_frame
-PL_close_foreign_frame.argtypes = [fid_t]
-PL_close_foreign_frame.restype = None
-
-PL_discard_foreign_frame = _lib.PL_discard_foreign_frame
-PL_discard_foreign_frame.argtypes = [fid_t]
-PL_discard_foreign_frame.restype = None
-
-PL_rewind_foreign_frame = _lib.PL_rewind_foreign_frame
-PL_rewind_foreign_frame.argtypes = [fid_t]
-PL_rewind_foreign_frame.restype = None
-
-PL_put_list_chars = _lib.PL_put_list_chars
-PL_put_list_chars.argtypes = [term_t, c_char_p]
-PL_put_list_chars.restype = c_int
-
-#PL_EXPORT(void) PL_register_atom(atom_t a);
-PL_register_atom = _lib.PL_register_atom
-PL_register_atom.argtypes = [atom_t]
-PL_register_atom.restype = None
-
-#PL_EXPORT(void) PL_unregister_atom(atom_t a);
-PL_unregister_atom = _lib.PL_unregister_atom
-PL_unregister_atom.argtypes = [atom_t]
-PL_unregister_atom.restype = None
-
-#PL_EXPORT(atom_t) PL_functor_name(functor_t f);
-PL_functor_name = _lib.PL_functor_name
-PL_functor_name.argtypes = [functor_t]
-PL_functor_name.restype = atom_t
-
-#PL_EXPORT(int) PL_functor_arity(functor_t f);
-PL_functor_arity = _lib.PL_functor_arity
-PL_functor_arity.argtypes = [functor_t]
-PL_functor_arity.restype = c_int
-
-# /* Get C-values from Prolog terms */
-#PL_EXPORT(int) PL_get_atom(term_t t, atom_t *a);
-PL_get_atom = _lib.PL_get_atom
-PL_get_atom.argtypes = [term_t, POINTER(atom_t)]
-PL_get_atom.restype = c_int
-
-#PL_EXPORT(int) PL_get_bool(term_t t, int *value);
-PL_get_bool = _lib.PL_get_bool
-PL_get_bool.argtypes = [term_t, POINTER(c_int)]
-PL_get_bool.restype = c_int
-
-#PL_EXPORT(int) PL_get_atom_chars(term_t t, char **a);
-PL_get_atom_chars = _lib.PL_get_atom_chars # FIXME
-PL_get_atom_chars.argtypes = [term_t, POINTER(c_char_p)]
-PL_get_atom_chars.restype = c_int
-
-##define PL_get_string_chars(t, s, l) PL_get_string(t,s,l)
-# /* PL_get_string() is depricated */
-#PL_EXPORT(int) PL_get_string(term_t t, char **s, size_t *len);
-PL_get_string = _lib.PL_get_string
-PL_get_string_chars = PL_get_string
-#PL_get_string_chars.argtypes = [term_t, POINTER(c_char_p), c_int_p]
-
-#PL_EXPORT(int) PL_get_chars(term_t t, char **s, unsigned int flags);
-PL_get_chars = _lib.PL_get_chars # FIXME:
-
-#PL_EXPORT(int) PL_get_list_chars(term_t l, char **s,
-# unsigned int flags);
-#PL_EXPORT(int) PL_get_atom_nchars(term_t t, size_t *len, char **a);
-#PL_EXPORT(int) PL_get_list_nchars(term_t l,
-# size_t *len, char **s,
-# unsigned int flags);
-#PL_EXPORT(int) PL_get_nchars(term_t t,
-# size_t *len, char **s,
-# unsigned int flags);
-#PL_EXPORT(int) PL_get_integer(term_t t, int *i);
-PL_get_integer = _lib.PL_get_integer
-PL_get_integer.argtypes = [term_t, POINTER(c_int)]
-PL_get_integer.restype = c_int
-
-#PL_EXPORT(int) PL_get_long(term_t t, long *i);
-PL_get_long = _lib.PL_get_long
-PL_get_long.argtypes = [term_t, POINTER(c_long)]
-PL_get_long.restype = c_int
-
-#PL_EXPORT(int) PL_get_pointer(term_t t, void **ptr);
-#PL_EXPORT(int) PL_get_float(term_t t, double *f);
-PL_get_float = _lib.PL_get_float
-PL_get_float.argtypes = [term_t, c_double_p]
-PL_get_float.restype = c_int
-
-#PL_EXPORT(int) PL_get_functor(term_t t, functor_t *f);
-PL_get_functor = _lib.PL_get_functor
-PL_get_functor.argtypes = [term_t, POINTER(functor_t)]
-PL_get_functor.restype = c_int
-
-#PL_EXPORT(int) PL_get_name_arity(term_t t, atom_t *name, int *arity);
-PL_get_name_arity = _lib.PL_get_name_arity
-PL_get_name_arity.argtypes = [term_t, POINTER(atom_t), POINTER(c_int)]
-PL_get_name_arity.restype = c_int
-
-#PL_EXPORT(int) PL_get_module(term_t t, module_t *module);
-#PL_EXPORT(int) PL_get_arg(int index, term_t t, term_t a);
-PL_get_arg = _lib.PL_get_arg
-PL_get_arg.argtypes = [c_int, term_t, term_t]
-PL_get_arg.restype = c_int
-
-#PL_EXPORT(int) PL_get_list(term_t l, term_t h, term_t t);
-#PL_EXPORT(int) PL_get_head(term_t l, term_t h);
-PL_get_head = _lib.PL_get_head
-PL_get_head.argtypes = [term_t, term_t]
-PL_get_head.restype = c_int
-
-#PL_EXPORT(int) PL_get_tail(term_t l, term_t t);
-PL_get_tail = _lib.PL_get_tail
-PL_get_tail.argtypes = [term_t, term_t]
-PL_get_tail.restype = c_int
-
-#PL_EXPORT(int) PL_get_nil(term_t l);
-PL_get_nil = _lib.PL_get_nil
-PL_get_nil.argtypes = [term_t]
-PL_get_nil.restype = c_int
-
-#PL_EXPORT(int) PL_get_term_value(term_t t, term_value_t *v);
-#PL_EXPORT(char *) PL_quote(int chr, const char *data);
-
-PL_put_atom_chars = _lib.PL_put_atom_chars
-PL_put_atom_chars.argtypes = [term_t, c_char_p]
-PL_put_atom_chars.restype = c_int
-
-PL_atom_chars = _lib.PL_atom_chars
-PL_atom_chars.argtypes = [atom_t]
-PL_atom_chars.restype = c_char_p
-
-PL_predicate = _lib.PL_predicate
-PL_predicate.argtypes = [c_char_p, c_int, c_char_p]
-PL_predicate.restype = predicate_t
-
-PL_pred = _lib.PL_pred
-PL_pred.argtypes = [functor_t, module_t]
-PL_pred.restype = predicate_t
-
-PL_open_query = _lib.PL_open_query
-PL_open_query.argtypes = [module_t, c_int, predicate_t, term_t]
-PL_open_query.restype = qid_t
-
-PL_next_solution = _lib.PL_next_solution
-PL_next_solution.argtypes = [qid_t]
-PL_next_solution.restype = c_int
-
-PL_copy_term_ref = _lib.PL_copy_term_ref
-PL_copy_term_ref.argtypes = [term_t]
-PL_copy_term_ref.restype = term_t
-
-PL_get_list = _lib.PL_get_list
-PL_get_list.argtypes = [term_t, term_t, term_t]
-PL_get_list.restype = c_int
-
-PL_get_chars = _lib.PL_get_chars # FIXME
-
-PL_close_query = _lib.PL_close_query
-PL_close_query.argtypes = [qid_t]
-PL_close_query.restype = None
-
-#void PL_cut_query(qid)
-PL_cut_query = _lib.PL_cut_query
-PL_cut_query.argtypes = [qid_t]
-PL_cut_query.restype = None
-
-PL_halt = _lib.PL_halt
-PL_halt.argtypes = [c_int]
-PL_halt.restype = None
-
-# PL_EXPORT(int) PL_cleanup(int status);
-PL_cleanup = _lib.PL_cleanup
-PL_cleanup.restype = c_int
-
-PL_unify_integer = _lib.PL_unify_integer
-PL_unify = _lib.PL_unify
-PL_unify.restype = c_int
-
-#PL_EXPORT(int) PL_unify_arg(int index, term_t t, term_t a) WUNUSED;
-PL_unify_arg = _lib.PL_unify_arg
-PL_unify_arg.argtypes = [c_int, term_t, term_t]
-PL_unify_arg.restype = c_int
-
-# Verify types
-
-PL_term_type = _lib.PL_term_type
-PL_term_type.argtypes = [term_t]
-PL_term_type.restype = c_int
-
-PL_is_variable = _lib.PL_is_variable
-PL_is_variable.argtypes = [term_t]
-PL_is_variable.restype = c_int
-
-PL_is_ground = _lib.PL_is_ground
-PL_is_ground.argtypes = [term_t]
-PL_is_ground.restype = c_int
-
-PL_is_atom = _lib.PL_is_atom
-PL_is_atom.argtypes = [term_t]
-PL_is_atom.restype = c_int
-
-PL_is_integer = _lib.PL_is_integer
-PL_is_integer.argtypes = [term_t]
-PL_is_integer.restype = c_int
-
-PL_is_string = _lib.PL_is_string
-PL_is_string.argtypes = [term_t]
-PL_is_string.restype = c_int
-
-PL_is_float = _lib.PL_is_float
-PL_is_float.argtypes = [term_t]
-PL_is_float.restype = c_int
-
-#PL_is_rational = _lib.PL_is_rational
-#PL_is_rational.argtypes = [term_t]
-#PL_is_rational.restype = c_int
-
-PL_is_compound = _lib.PL_is_compound
-PL_is_compound.argtypes = [term_t]
-PL_is_compound.restype = c_int
-
-PL_is_functor = _lib.PL_is_functor
-PL_is_functor.argtypes = [term_t, functor_t]
-PL_is_functor.restype = c_int
-
-PL_is_list = _lib.PL_is_list
-PL_is_list.argtypes = [term_t]
-PL_is_list.restype = c_int
-
-PL_is_atomic = _lib.PL_is_atomic
-PL_is_atomic.argtypes = [term_t]
-PL_is_atomic.restype = c_int
-
-PL_is_number = _lib.PL_is_number
-PL_is_number.argtypes = [term_t]
-PL_is_number.restype = c_int
-
-# /* Assign to term-references */
-#PL_EXPORT(void) PL_put_variable(term_t t);
-PL_put_variable = _lib.PL_put_variable
-PL_put_variable.argtypes = [term_t]
-PL_put_variable.restype = None
-
-#PL_EXPORT(void) PL_put_atom(term_t t, atom_t a);
-PL_put_atom = _lib.PL_put_atom
-PL_put_atom.argtypes = [term_t, atom_t]
-PL_put_atom.restype = None
-
-#PL_EXPORT(void) PL_put_atom_chars(term_t t, const char *chars);
-#PL_EXPORT(void) PL_put_string_chars(term_t t, const char *chars);
-#PL_EXPORT(void) PL_put_list_chars(term_t t, const char *chars);
-#PL_EXPORT(void) PL_put_list_codes(term_t t, const char *chars);
-#PL_EXPORT(void) PL_put_atom_nchars(term_t t, size_t l, const char *chars);
-#PL_EXPORT(void) PL_put_string_nchars(term_t t, size_t len, const char *chars);
-#PL_EXPORT(void) PL_put_list_nchars(term_t t, size_t l, const char *chars);
-#PL_EXPORT(void) PL_put_list_ncodes(term_t t, size_t l, const char *chars);
-#PL_EXPORT(void) PL_put_integer(term_t t, long i);
-PL_put_integer = _lib.PL_put_integer
-PL_put_integer.argtypes = [term_t, c_long]
-PL_put_integer.restype = None
-
-#PL_EXPORT(void) PL_put_pointer(term_t t, void *ptr);
-#PL_EXPORT(void) PL_put_float(term_t t, double f);
-PL_put_float = _lib.PL_put_float
-PL_put_float.argtypes = [term_t, c_double]
-PL_put_float.restype = None
-
-#PL_EXPORT(void) PL_put_functor(term_t t, functor_t functor);
-PL_put_functor = _lib.PL_put_functor
-PL_put_functor.argtypes = [term_t, functor_t]
-PL_put_functor.restype = None
-
-#PL_EXPORT(void) PL_put_list(term_t l);
-PL_put_list = _lib.PL_put_list
-PL_put_list.argtypes = [term_t]
-PL_put_list.restype = None
-
-#PL_EXPORT(void) PL_put_nil(term_t l);
-PL_put_nil = _lib.PL_put_nil
-PL_put_nil.argtypes = [term_t]
-PL_put_nil.restype = None
-
-#PL_EXPORT(void) PL_put_term(term_t t1, term_t t2);
-PL_put_term = _lib.PL_put_term
-PL_put_term.argtypes = [term_t, term_t]
-PL_put_term.restype = None
-
-# /* construct a functor or list-cell */
-#PL_EXPORT(void) PL_cons_functor(term_t h, functor_t f, ...);
-#class _PL_cons_functor(object):
-PL_cons_functor = _lib.PL_cons_functor # FIXME:
-
-#PL_EXPORT(void) PL_cons_functor_v(term_t h, functor_t fd, term_t a0);
-PL_cons_functor_v = _lib.PL_cons_functor_v
-PL_cons_functor_v.argtypes = [term_t, functor_t, term_t]
-PL_cons_functor_v.restype = None
-
-#PL_EXPORT(void) PL_cons_list(term_t l, term_t h, term_t t);
-PL_cons_list = _lib.PL_cons_list
-PL_cons_list.argtypes = [term_t, term_t, term_t]
-PL_cons_list.restype = None
-
-#
-# term_t PL_exception(qid_t qid)
-PL_exception = _lib.PL_exception
-PL_exception.argtypes = [qid_t]
-PL_exception.restype = term_t
-
-PL_clear_exception = _lib.PL_clear_exception
-PL_clear_exception.restype = None
-
-#
-PL_register_foreign = _lib.PL_register_foreign
-
-#
-#PL_EXPORT(atom_t) PL_new_atom(const char *s);
-PL_new_atom = _lib.PL_new_atom
-PL_new_atom.argtypes = [c_char_p]
-PL_new_atom.restype = atom_t
-
-#PL_EXPORT(functor_t) PL_new_functor(atom_t f, int a);
-PL_new_functor = _lib.PL_new_functor
-PL_new_functor.argtypes = [atom_t, c_int]
-PL_new_functor.restype = functor_t
-
-
-# /*******************************
-# * COMPARE *
-# *******************************/
-#
-#PL_EXPORT(int) PL_compare(term_t t1, term_t t2);
-#PL_EXPORT(int) PL_same_compound(term_t t1, term_t t2);
-PL_compare = _lib.PL_compare
-PL_compare.argtypes = [term_t, term_t]
-PL_compare.restype = c_int
-
-PL_same_compound = _lib.PL_same_compound
-PL_same_compound.argtypes = [term_t, term_t]
-PL_same_compound.restype = c_int
-
-
-# /*******************************
-# * RECORDED DATABASE *
-# *******************************/
-#
-#PL_EXPORT(record_t) PL_record(term_t term);
-PL_record = _lib.PL_record
-PL_record.argtypes = [term_t]
-PL_record.restype = record_t
-
-#PL_EXPORT(void) PL_recorded(record_t record, term_t term);
-PL_recorded = _lib.PL_recorded
-PL_recorded.argtypes = [record_t, term_t]
-PL_recorded.restype = None
-
-#PL_EXPORT(void) PL_erase(record_t record);
-PL_erase = _lib.PL_erase
-PL_erase.argtypes = [record_t]
-PL_erase.restype = None
-
-#
-#PL_EXPORT(char *) PL_record_external(term_t t, size_t *size);
-#PL_EXPORT(int) PL_recorded_external(const char *rec, term_t term);
-#PL_EXPORT(int) PL_erase_external(char *rec);
-
-PL_new_module = _lib.PL_new_module
-PL_new_module.argtypes = [atom_t]
-PL_new_module.restype = module_t
-
-intptr_t = c_long
-ssize_t = intptr_t
-wint_t = c_uint
-
-#typedef struct
-#{
-# int __count;
-# union
-# {
-# wint_t __wch;
-# char __wchb[4];
-# } __value; /* Value so far. */
-#} __mbstate_t;
-
-class _mbstate_t_value(Union):
- _fields_ = [("__wch",wint_t),
- ("__wchb",c_char*4)]
-
-class mbstate_t(Structure):
- _fields_ = [("__count",c_int),
- ("__value",_mbstate_t_value)]
-
-# stream related funcs
-Sread_function = CFUNCTYPE(ssize_t, c_void_p, c_char_p, c_size_t)
-Swrite_function = CFUNCTYPE(ssize_t, c_void_p, c_char_p, c_size_t)
-Sseek_function = CFUNCTYPE(c_long, c_void_p, c_long, c_int)
-Sseek64_function = CFUNCTYPE(c_int64, c_void_p, c_int64, c_int)
-Sclose_function = CFUNCTYPE(c_int, c_void_p)
-Scontrol_function = CFUNCTYPE(c_int, c_void_p, c_int, c_void_p)
-
-# IOLOCK
-IOLOCK = c_void_p
-
-# IOFUNCTIONS
-class IOFUNCTIONS(Structure):
- _fields_ = [("read",Sread_function),
- ("write",Swrite_function),
- ("seek",Sseek_function),
- ("close",Sclose_function),
- ("seek64",Sseek64_function),
- ("reserved",intptr_t*2)]
-
-# IOENC
-ENC_UNKNOWN,ENC_OCTET,ENC_ASCII,ENC_ISO_LATIN_1,ENC_ANSI,ENC_UTF8,ENC_UNICODE_BE,ENC_UNICODE_LE,ENC_WCHAR = list(range(9))
-IOENC = c_int
-
-# IOPOS
-class IOPOS(Structure):
- _fields_ = [("byteno",c_int64),
- ("charno",c_int64),
- ("lineno",c_int),
- ("linepos",c_int),
- ("reserved", intptr_t*2)]
-
-# IOSTREAM
-class IOSTREAM(Structure):
- _fields_ = [("bufp",c_char_p),
- ("limitp",c_char_p),
- ("buffer",c_char_p),
- ("unbuffer",c_char_p),
- ("lastc",c_int),
- ("magic",c_int),
- ("bufsize",c_int),
- ("flags",c_int),
- ("posbuf",IOPOS),
- ("position",POINTER(IOPOS)),
- ("handle",c_void_p),
- ("functions",IOFUNCTIONS),
- ("locks",c_int),
- ("mutex",IOLOCK),
- ("closure_hook",CFUNCTYPE(None, c_void_p)),
- ("closure",c_void_p),
- ("timeout",c_int),
- ("message",c_char_p),
- ("encoding",IOENC)]
-IOSTREAM._fields_.extend([("tee",IOSTREAM),
- ("mbstate",POINTER(mbstate_t)),
- ("reserved",intptr_t*6)])
-
-
-
-#PL_EXPORT(IOSTREAM *) Sopen_string(IOSTREAM *s, char *buf, size_t sz, const char *m);
-Sopen_string = _lib.Sopen_string
-Sopen_string.argtypes = [POINTER(IOSTREAM), c_char_p, c_size_t, c_char_p]
-Sopen_string.restype = POINTER(IOSTREAM)
-
-#PL_EXPORT(int) Sclose(IOSTREAM *s);
-Sclose = _lib.Sclose
-Sclose.argtypes = [POINTER(IOSTREAM)]
-
-
-#PL_EXPORT(int) PL_unify_stream(term_t t, IOSTREAM *s);
-PL_unify_stream = _lib.PL_unify_stream
-PL_unify_stream.argtypes = [term_t, POINTER(IOSTREAM)]
-
-PL_unify_atom_chars = _lib.PL_unify_atom_chars
-PL_unify_atom_chars.argtypes = [term_t, c_char_p]
-PL_unify_atom_chars.restype = c_int
diff --git a/prolog/engine.py b/prolog/engine.py
index 597ebc4..6627b9a 100644
--- a/prolog/engine.py
+++ b/prolog/engine.py
@@ -1,339 +1,63 @@
#!/usr/bin/python3
-from prolog.core import *
-import prolog.util
+import http.client
+import json
+import urllib
-class Atom(object):
- __slots__ = 'ref'
-
- def __init__(self, val=None, ref=None):
- if ref is not None:
- self.ref = ref
- return
- self.ref = PL_new_atom(bytes(val, encoding=encoding))
-
-class Term(object):
- __slots__ = 'ref'
-
- # Initialize from term reference [ref] if given, otherwise construct a new
- # term from [val] and possibly [args].
- def __init__(self, val=None, args=None, ref=None):
- if ref is not None:
- self.ref = ref
- return
- self.ref = PL_new_term_ref()
- if isinstance(val, str):
- if args is not None:
- # Explicitly constructed compound term with name [val] and arguments [args].
- name = PL_new_atom(bytes(val, encoding=encoding))
- PL_cons_functor_v(self.ref, PL_new_functor(name, len(args)), Termv(args).ref)
- else:
- # Parse term from [val].
- if not PL_chars_to_term(bytes(val, encoding=encoding), self.ref):
- raise ValueError('invalid compound term')
- elif isinstance(val, int):
- PL_put_integer(self.ref, val)
- elif isinstance(val, float):
- PL_put_float(self.ref, val)
- elif isinstance(val, list):
- PL_put_nil(self.ref)
- for t in val:
- PL_cons_list(self.ref, t.ref, self.ref)
- elif isinstance(val, Atom):
- PL_put_atom(self.ref, val.ref)
-
- def __iter__(self):
- if not PL_is_list(self.ref):
- raise TypeError('term is not a list')
- ref = self.ref
- while True:
- head, tail = Term(), Term()
- if not PL_get_list(ref, head.ref, tail.ref):
- break
- yield head
- ref = tail.ref
-
- def __str__(self):
- ptr = c_char_p()
- if PL_get_chars(self.ref, byref(ptr), CVT_WRITE|BUF_RING):
- return str(ptr.value, encoding=encoding)
-
-class Termv(object):
- __slots__ = 'ref'
-
- def __init__(self, terms):
- self.ref = PL_new_term_refs(len(terms))
- for i, term in enumerate(terms):
- PL_put_term(self.ref+i, term.ref)
-
-class Problem(object):
- def __init__(self, name, solution, facts, tests):
- self.name = name
- self.solution = solution
- self.facts = facts
- self.tests = {t: None for t in tests}
- self.answers = {}
+address, port = 'localhost', 3030
class PrologEngine(object):
- def __init__(self):
- # Initialize the swipl library.
- args = ['./', '-q', '--nosignals']
- if SWI_HOME_DIR is not None:
- args.append('--home={0}'.format(SWI_HOME_DIR))
- s_plargs = len(args)
- plargs = (c_char_p*s_plargs)()
- for i in range(s_plargs):
- plargs[i] = bytes(args[i], encoding)
- if not PL_initialise(s_plargs, plargs):
- raise EnvironmentError('Could not initialize Prolog environment.'
- 'PL_initialise returned {0}'.format(result))
-
- # 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),
- 'call_with_time_limit/2': PL_predicate(b'call_with_time_limit', 2, None),
- 'compile_predicates/1': PL_predicate(b'compile_predicates', 1, 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),
- 'set_prolog_flag/2': PL_predicate(b'set_prolog_flag', 2, None),
- 'set_prolog_stack/2': PL_predicate(b'set_prolog_stack', 2, None),
- 'use_module/1': PL_predicate(b'use_module', 1, None)
- }
- self.err_flags = PL_Q_NODEBUG|PL_Q_CATCH_EXCEPTION
-
- # Load the sandbox and compatibility library.
- self.call('consult/1', [Term(Atom('prolog/lib.pl'))])
-
- # Load the time module (for call_with_time_limit) then disable autoload.
- self.call('use_module/1', [Term('library(random)')])
- self.call('use_module/1', [Term('library(time)')])
- self.call('set_prolog_flag/2', [Term('autoload'), Term('false')])
-
- # Increase memory limits.
- self.call('set_prolog_stack/2', [Term('global'), Term('limit(2*10**9)')])
- self.call('set_prolog_stack/2', [Term('local'), Term('limit(2*10**9)')])
-
- # Discard messages from the swipl library.
- self.call('assertz/1', [Term('message_hook(_, _, _)')])
-
- # Problem data loaded with load_problem.
- self.problems = {}
-
- # The set of already loaded facts.
- self.facts = set()
-
- # Load the [solution] for problem [pid] called [name] and find answers to
- # [tests]. Also load [facts] in the main module, and import modules for
- # problems in [depends] into this problem's module.
- def load_problem(self, pid, name, solution, depends, facts, tests):
- self.problems[pid] = Problem(name, solution, facts, tests)
-
- # Load the solution in 'solution<pid>' module.
- mod_problem = 'problem{}'.format(pid)
-
- fid = PL_open_foreign_frame()
- predicates = self.load(solution, mod_problem)
- if facts and facts not in self.facts:
- predicates |= self.load(facts)
- self.facts.add(facts)
- self.call('compile_predicates/1', [Term([Term(p) for p in predicates])])
-
- # Import solutions for dependency predicates.
- for i in depends:
- mod_dependency = 'problem{}'.format(i)
- self.call('add_import_module/3', [Term(mod_problem), Term(mod_dependency), Term('end')])
-
- # Find the correct test answers.
- for query in tests:
- result = self.query(query, mod_problem)
- if result is None or len(result) < 1 or 'X' not in result[0]:
- raise Exception('Error finding correct answer to query "{}"'.format(query))
- self.problems[pid].tests[query] = result[0]['X']
- PL_discard_foreign_frame(fid)
-
- # Import the correct solution for problem [pid] into module for user [uid].
- def mark_solved(self, uid, pid):
- mod_user = 'user{}'.format(uid)
- mod_problem = 'problem{}'.format(pid)
-
- fid = PL_open_foreign_frame()
- result = self.call('add_import_module/3', [Term(mod_user), Term(mod_problem), Term('end')])
- PL_discard_foreign_frame(fid)
- return result
-
- # Get up to [n] solutions to query [q]. If there are no solutions, return
- # an empty list. Raise an exception on error (either from self.call, or due
- # to malformed/unsafe query or a timeout).
- def query(self, q, module=None, n=1):
- if module is not None:
- q = '{}:({})'.format(module, q)
-
- fid = PL_open_foreign_frame()
- qid = None
- try:
- # Parse the query and store variable names.
- goal = Term()
- var_names = Term()
- options = Term([Term('variable_names', [var_names])])
- if not self.call('read_term_from_atom/3', [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/1', [goal]):
- raise Exception('Warning: Unsafe goal: {}\n'.format(goal))
-
- solutions = Term()
- goal_aux = Term('findnsols', [Term(n), goal, goal, solutions])
- qid = PL_open_query(None, self.err_flags, self.p['call_with_time_limit/2'],
- Termv([Term(0.01), goal_aux]).ref)
-
- result = []
- if PL_next_solution(qid):
- solutions = list(solutions)
- fid_solution = PL_open_foreign_frame()
- for solution in solutions:
- PL_unify(goal.ref, solution.ref)
- variables = {}
- for var in var_names:
- name, value = Term(), Term()
- PL_get_arg(1, var.ref, name.ref)
- PL_get_arg(2, var.ref, value.ref)
- variables[str(name)] = str(value)
- result.append(variables)
- PL_rewind_foreign_frame(fid_solution)
- PL_discard_foreign_frame(fid_solution)
- else:
- # Check for exceptions.
- error_msg = self.error(qid)
- if error_msg:
- raise Exception(error_msg)
- finally:
- if qid:
- PL_close_query(qid)
- PL_discard_foreign_frame(fid)
-
- return result
-
- # Test whether [code] gives the same answer to [query] as the correct
- # solution for problem [pid]. The solution should be loaded beforehand.
- def test(self, uid, pid, code):
- mod_user = 'user{}'.format(uid)
-
- fid = PL_open_foreign_frame()
- correct = True
- predicates = set()
- try:
- self.load(code, mod_user, predicates)
- for query, answer in sorted(self.problems[pid].tests.items()):
- result = self.query(query, mod_user, n=1)
- if len(result) != 1 or result[0]['X'] != answer:
- correct = False
- break
-
- # If a correct solution was found, see if another (incorrect)
- # solution is found in the first 10 answers.
- try:
- result = self.query(query, mod_user, n=10)
- unique = set([r['X'] for r in result])
- if len(unique) != 1:
- correct = False
- break
- except Exception as ex:
- # Only a timeout exception can occur here; in this case, we
- # consider [code] correct.
- pass
- except Exception as ex:
- correct = False
-
- self.unload(predicates)
- PL_discard_foreign_frame(fid)
-
- return correct
-
- # 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:
- if not PL_next_solution(qid):
- error_msg = self.error(qid)
- if error_msg:
- raise Exception(error_msg)
- return False
- finally:
- PL_cut_query(qid)
- return True
-
- # Load rules from [program] into [module] and return the corresponding
- # predicate names. Since this function might not return due to exception,
- # the [predicates] argument can be passed where the names will be stored.
- def load(self, program, module=None, predicates=None):
- if predicates is None:
- predicates = set()
- for rule in prolog.util.split(program):
- name = self.predicate_indicator(rule)
- if module:
- rule = '{}:({})'.format(module, rule)
- name = '{}:{}'.format(module, name)
- self.call('assertz/1', [Term(rule)])
- predicates.add(name)
- return predicates
-
- # Unload and remove the "dynamic" property for all rules implementing
- # [predicates].
- def unload(self, predicates):
- for predicate in predicates:
- self.call('abolish/1', [Term(predicate)])
-
- # Return a description of the last exception, or None if no error occurred.
- def error(self, qid):
- error_ref = PL_exception(qid)
- if not error_ref:
- return None
- PL_clear_exception()
-
- # Get the Prolog error message.
- fid = PL_open_foreign_frame()
- msg = Term()
- if PL_call_predicate(None, self.err_flags, self.p['message_to_string/2'],
- Termv([Term(ref=error_ref), msg]).ref):
- error_str = str(msg)
+ def __init__(self, address=address, port=port, code=''):
+ self.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)
+
+ response = self.conn.getresponse()
+ data = response.read()
+ reply = json.loads(str(data, encoding='utf-8'))
+ if reply['event'] == 'create':
+ self.id = reply['id']
else:
- error_str = 'Unknown error'
- PL_discard_foreign_frame(fid)
-
- return error_str
-
- # 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
+ raise Exception('could not create engine: ' + reply['code'])
+
+ def send(self, event, fmt='json'):
+ 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)
+ return reply
+
+ def next(self, n=1):
+ event = 'next({})'.format(n)
+ reply = self.send(event)
+ return reply
+
+ def stop(self):
+ return self.send('stop')
+
+ def destroy(self):
+ reply = self.send('destroy')
+ self.id = None
+ self.conn.close()
+ self.conn = None
# Basic sanity check.
if __name__ == '__main__':
- engine = PrologEngine()
- engine.load_solution(0, 'a(2). a(2). a(3). ')
- result = engine.test(0, 0, 'a(2). ', ['a(X)', 'a(Y), Y=X'])
- print('{}: {}'.format(i, result))
+ engine = PrologEngine(code='dup([],[]). dup([H|T],[H,H|TT]) :- dup(T,TT).')
+ print('engine id is ' + engine.id)
+ print(engine.ask("run_tests({},'{}',Result)".format('dup/2', engine.id)))
+ engine.destroy()
diff --git a/prolog/lib.pl b/prolog/lib.pl
deleted file mode 100644
index 99dadf1..0000000
--- a/prolog/lib.pl
+++ /dev/null
@@ -1,462 +0,0 @@
-/* Part of SWI-Prolog
-
- Author: Jan Wielemaker
- E-mail: J.Wielemaker@cs.vu.nl
- WWW: http://www.swi-prolog.org
- Copyright (C): 2009-2013, VU University Amsterdam
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
- As a special exception, if you link this library with other files,
- compiled with a Free Software compiler, to produce an executable, this
- library does not by itself cause the resulting executable to be covered
- by the GNU General Public License. This exception does not however
- invalidate any other reasons why the executable file might be covered by
- the GNU General Public License.
-*/
-
-:- module(sandbox,
- [ safe_goal/1 % :Goal
- ]).
-:- use_module(library(assoc)).
-:- use_module(library(lists)).
-:- use_module(library(debug)).
-:- use_module(library(apply_macros), [expand_phrase/2]).
-
-:- multifile
- safe_primitive/1, % Goal
- safe_meta/2. % Goal, Calls
-
-% :- debug(sandbox).
-
-/** <module> Sandboxed Prolog code
-
-Prolog is a full-featured Turing complete programming language in which
-it is easy to write programs that can harm your computer. On the other
-hand, Prolog is a logic based _query language_ which can be exploited to
-query data interactively from, e.g., the web. This library provides
-safe_goal/1, which determines whether it is safe to call its argument.
-
-@tbd Handling of ^ and // meta predicates
-*/
-
-
-:- meta_predicate
- safe_goal(0).
-
-%% safe_goal(:Goal) is det.
-%
-% True if calling Goal provides no security risc. This implies
-% that:
-%
-% - The call-graph can be fully expanded. Full expansion *stops*
-% if a meta-goal is found for which we cannot determine enough
-% details to know which predicate will be called.
-%
-% - All predicates referenced from the fully expanded are
-% whitelisted by the predicate safe_primitive/1 and safe_meta/2.
-%
-% @error instantiation_error if the analysis encounters a term in
-% a callable position that is insufficiently instantiated
-% to determine the predicate called.
-% @error permission_error(call, sandboxed, Goal) if Goal is in
-% the call-tree and not white-listed.
-
-safe_goal(M:Goal) :-
- empty_assoc(Safe0),
- safe(Goal, M, [], Safe0, _).
-
-
-%% safe(+Goal, +Module, +Parents, +Safe0, -Safe) is semidet.
-%
-% Is true if Goal can only call safe code.
-
-safe(V, _, Parents, _, _) :-
- var(V), !,
- throw(error(instantiation_error, sandbox(V, Parents))).
-safe(M:G, _, Parent, Safe0, Safe) :- !,
- safe(G, M, Parent, Safe0, Safe).
-safe(G, _, Parents, _, _) :-
- debugging(sandbox(show)),
- length(Parents, Level),
- debug(sandbox(show), '[~D] SAFE ~q?', [Level, G]),
- fail.
-safe(G, _, _, Safe, Safe) :-
- safe_primitive(G),
- predicate_property(G, iso), !.
-safe(G, M, _, Safe, Safe) :-
- ( predicate_property(M:G, imported_from(M2))
- -> true
- ; M2 = M
- ),
- safe_primitive(M2:G), !.
-safe(G, M, Parents, Safe0, Safe) :-
- safe_meta(G, Called), !,
- safe_list(Called, M, Parents, Safe0, Safe).
-safe(G, M, Parents, Safe0, Safe) :-
- goal_id(M:G, Id, Gen),
- ( get_assoc(Id, Safe0, _)
- -> Safe = Safe0
- ; put_assoc(Id, Safe0, true, Safe1),
- safe_clauses(Gen, M, [Id|Parents], Safe1, Safe)
- ).
-
-safe_clauses(G, M, Parents, Safe0, Safe) :-
- predicate_property(M:G, interpreted), !,
-% \+ predicate_property(M:G, meta_predicate(_)), !,
- def_module(M:G, MD:QG),
- findall(Body, clause(MD:QG, Body), Bodies),
- safe_list(Bodies, MD, Parents, Safe0, Safe).
-safe_clauses(_, _M, [G|Parents], _, _) :-
- throw(error(permission_error(call, sandboxed, G),
- sandbox(G, Parents))).
-
-safe_list([], _, _, Safe, Safe).
-safe_list([H|T], M, Parents, Safe0, Safe) :-
- copy_term(H, H1),
- safe(H1, M, Parents, Safe0, Safe1),
- safe_list(T, M, Parents, Safe1, Safe).
-
-
-def_module(M:G, MD:QG) :-
- predicate_property(M:G, imported_from(MD)), !,
- meta_qualify(MD:G, M, QG).
-def_module(M:G, M:QG) :-
- meta_qualify(M:G, M, QG).
-
-%% meta_qualify(:G, +M, -QG) is det.
-%
-% Perform meta-qualification of the goal-argument
-
-meta_qualify(MD:G, M, QG) :-
- predicate_property(MD:G, meta_predicate(Head)), !,
- G =.. [Name|Args],
- Head =.. [_|Q],
- qualify_args(Q, M, Args, QArgs),
- QG =.. [Name|QArgs].
-meta_qualify(_:G, _, G).
-
-qualify_args([], _, [], []).
-qualify_args([H|T], M, [A|AT], [Q|QT]) :-
- qualify_arg(H, M, A, Q),
- qualify_args(T, M, AT, QT).
-
-qualify_arg(S, M, A, Q) :-
- q_arg(S), !,
- qualify(A, M, Q).
-qualify_arg(_, _, A, A).
-
-q_arg(I) :- integer(I), !.
-q_arg(:).
-
-qualify(A, M, MZ:Q) :-
- strip_module(M:A, MZ, Q).
-
-%% goal_id(:Goal, -Id, -Gen) is nondet.
-%
-% Generate an identifier for the goal proven to be safe. We
-% first try to prove the most general form of the goal. If
-% this fails, we try to prove more specific versions.
-%
-% @tbd Do step-by-step generalisation instead of the current
-% two levels (most general and most specific).
-%
-
-goal_id(M:Goal, M:Id, Gen) :- !,
- goal_id(Goal, Id, Gen).
-goal_id(Term, _, _) :-
- \+ callable(Term), !, fail.
-goal_id(Term, Name/Arity, Gen) :- % most general form
- functor(Term, Name, Arity),
- functor(Gen, Name, Arity).
-goal_id(Term, Skolem, Term) :- % most specific form
- copy_term(Term, Skolem),
- numbervars(Skolem, 0, _).
-
-%% safe_primitive(?Goal) is nondet.
-%
-% True if Goal is safe to call (i.e., cannot access dangerous
-% system-resources and cannot upset other parts of the Prolog
-% process). There are two types of facts. ISO built-ins are
-% declared without a module prefix. This is safe because it is not
-% allowed to (re-)define these primitives (i.e., give them an
-% unsafe implementation) and the way around
-% (redefine_system_predicate/1) is unsafe. The other group are
-% module-qualified and only match if the system infers that the
-% predicate is (or will be) imported from the given module.
-
-% First, all ISO system predicates that are considered safe
-
-safe_primitive(true).
-safe_primitive(fail).
-safe_primitive(repeat).
-safe_primitive(!).
- % types
-safe_primitive(var(_)).
-safe_primitive(nonvar(_)).
-safe_primitive(integer(_)).
-safe_primitive(float(_)).
-safe_primitive(number(_)).
-safe_primitive(atom(_)).
-safe_primitive(compound(_)).
-safe_primitive(ground(_)).
- % ordering
-safe_primitive(@>(_,_)).
-safe_primitive(@>=(_,_)).
-safe_primitive(==(_,_)).
-safe_primitive(@<(_,_)).
-safe_primitive(@=<(_,_)).
-safe_primitive(compare(_,_,_)).
-safe_primitive(sort(_,_)).
-safe_primitive(keysort(_,_)).
- % unification and equivalence
-safe_primitive(=(_,_)).
-safe_primitive(\=(_,_)).
-safe_primitive(\==(_,_)).
- % arithmetic
-safe_primitive(is(_,_)).
-safe_primitive(>(_,_)).
-safe_primitive(>=(_,_)).
-safe_primitive(=:=(_,_)).
-safe_primitive(=\=(_,_)).
-safe_primitive(=<(_,_)).
-safe_primitive(<(_,_)).
- % term-handling
-safe_primitive(arg(_,_,_)).
-safe_primitive(system:setarg(_,_,_)).
-safe_primitive(functor(_,_,_)).
-safe_primitive(_ =.. _).
-safe_primitive(copy_term(_,_)).
-safe_primitive(numbervars(_,_,_)).
- % atoms
-safe_primitive(atom_concat(_,_,_)).
-safe_primitive(atom_chars(_, _)).
- % Lists
-safe_primitive(length(_,_)).
- % exceptions
-safe_primitive(throw(_)).
- % misc
-safe_primitive(current_prolog_flag(_,_)).
-
-safe_primitive(clause(_,_)).
-safe_primitive(asserta(X)) :- safe_assert(X).
-safe_primitive(assertz(X)) :- safe_assert(X).
-safe_primitive(retract(X)) :- safe_assert(X).
-safe_primitive(retractall(X)) :- safe_assert(X).
-
-% The non-ISO system predicates. These can be redefined, so we must
-% be careful to ensure the system ones are used.
-
-safe_primitive(system:false).
-safe_primitive(system:cyclic_term(_)).
-safe_primitive(system:msort(_,_)).
-safe_primitive(system:between(_,_,_)).
-safe_primitive(system:succ(_,_)).
-safe_primitive(system:plus(_,_,_)).
-safe_primitive(system:term_variables(_,_)).
-safe_primitive(system:atom_to_term(_,_,_)).
-safe_primitive(system:term_to_atom(_,_)).
-safe_primitive(system:atomic_list_concat(_,_,_)).
-safe_primitive(system:atomic_list_concat(_,_)).
-safe_primitive(system:downcase_atom(_,_)).
-safe_primitive(system:upcase_atom(_,_)).
-safe_primitive(system:is_list(_)).
-safe_primitive(system:memberchk(_,_)).
-safe_primitive(system:'$skip_list'(_,_,_)).
- % attributes
-safe_primitive(system:get_attr(_,_,_)).
-safe_primitive(system:del_attr(_,_)).
- % globals
-safe_primitive(system:b_getval(_,_)).
-safe_primitive(system:b_setval(_,_)).
-safe_primitive(system:nb_current(_,_)).
-safe_primitive(system:assert(X)) :-
- safe_assert(X).
-
-% use_module/1. We only allow for .pl files that are loaded from
-% relative paths that do not contain /../
-
-safe_primitive(system:use_module(Spec)) :-
- ground(Spec),
- ( atom(Spec)
- -> Path = Spec
- ; Spec =.. [_Alias, Segments],
- phrase(segments_to_path(Segments), List),
- atomic_list_concat(List, Path)
- ),
- \+ is_absolute_file_name(Path),
- \+ sub_atom(Path, _, _, _, '/../'),
- absolute_file_name(Spec, AbsFile,
- [ access(read),
- file_type(prolog),
- file_errors(fail)
- ]),
- file_name_extension(_, Ext, AbsFile),
- save_extension(Ext).
-
-% Other library predicates.
-
- % rdf
-safe_primitive(rdf_db:rdf(_,_,_)).
-safe_primitive(rdf_db:rdf(_,_,_,_)).
- % http
-safe_primitive(http_session:http_session_data(_)).
-safe_primitive(http_session:http_session_id(_)).
- % random
-safe_primitive(random:random(_)).
-safe_primitive(random:random(_,_,_)).
-safe_primitive(random:random_between(_,_,_)).
- % porter
-safe_primitive(porter_stem:porter_stem(_,_)).
-safe_primitive(porter_stem:unaccent_atom(_,_)).
-safe_primitive(porter_stem:tokenize_atom(_,_)).
-safe_primitive(porter_stem:atom_to_stem_list(_,_)).
-
-% support predicates for safe_primitive, validating the safety of
-% arguments to certain goals.
-
-segments_to_path(A/B) --> !,
- segments_to_path(A),
- [/],
- segments_to_path(B).
-segments_to_path(X) -->
- [X].
-
-save_extension(pl).
-
-%% safe_assert(+Term) is semidet.
-%
-% True if assert(Term) is safe, which means it asserts in the
-% current module. Cross-module asserts are considered unsafe. We
-% only allow for adding facts. In theory, we could also allow for
-% rules if we prove the safety of the body.
-
-safe_assert(C) :- cyclic_term(C), !, fail.
-safe_assert(X) :- var(X), !, fail.
-safe_assert(_Head:-_Body) :- !, fail.
-safe_assert(_:_) :- !, fail.
-safe_assert(_).
-
-%% safe_meta(+Goal, -Called) is semidet.
-%
-% True if Goal is a meta-predicate that is considered safe iff all
-% elements in Called are safe.
-
-safe_meta(put_attr(_,M,A), [M:attr_unify_hook(A, _)]) :-
- atom(M), !.
-safe_meta(Phrase, [Goal]) :-
- expand_phrase(Phrase, Goal), !.
-safe_meta(Goal, Called) :-
- generic_goal(Goal, Gen),
- safe_meta(Gen),
- findall(C, called(Gen, Goal, C), Called).
-
-called(Gen, Goal, Called) :-
- arg(I, Gen, Spec),
- integer(Spec),
- arg(I, Goal, Called0),
- extend(Spec, Called0, Called).
-
-generic_goal(G, Gen) :-
- functor(G, Name, Arity),
- functor(Gen, Name, Arity).
-
-extend(0, G, G) :- !.
-extend(I, G0, G) :-
- G0 =.. List,
- length(Extra, I),
- append(List, Extra, All),
- G =.. All.
-
-safe_meta((0,0)).
-safe_meta((0;0)).
-safe_meta((0->0)).
-safe_meta(forall(0,0)).
-safe_meta(catch(0,_,0)).
-safe_meta(findall(_,0,_)).
-safe_meta(findall(_,0,_,_)).
-safe_meta(setof(_,0,_)). % TBD
-safe_meta(bagof(_,0,_)).
-safe_meta(^(_,0)).
-safe_meta(\+(0)).
-safe_meta(maplist(1, _)).
-safe_meta(maplist(2, _, _)).
-safe_meta(maplist(3, _, _, _)).
-safe_meta(call(0)).
-safe_meta(call(1, _)).
-safe_meta(call(2, _, _)).
-safe_meta(call(3, _, _, _)).
-safe_meta(call(4, _, _, _, _)).
-safe_meta(call(5, _, _, _, _, _)).
-
-
- /*******************************
- * SAFE COMPILATION HOOKS *
- *******************************/
-
-:- multifile
- prolog:sandbox_allowed_directive/1,
- prolog:sandbox_allowed_expansion/1.
-
-%% prolog:sandbox_allowed_directive(:G) is det.
-%
-% Throws an exception if G is not considered a safe directive.
-
-prolog:sandbox_allowed_directive(M:PredAttr) :-
- safe_directive(PredAttr),
- ( prolog_load_context(module, M)
- -> PredAttr =.. [Attr, Preds],
- safe_pattr(Preds, Attr)
- ; permission_error(directive, sandboxed, (:- M:PredAttr))
- ).
-prolog:sandbox_allowed_directive(G) :-
- safe_goal(G).
-
-safe_directive(dynamic(_)).
-safe_directive(thread_local(_)).
-safe_directive(discontiguous(_)).
-safe_directive(public(_)).
-
-safe_pattr(Var, _) :-
- var(Var), !,
- instantiation_error(Var).
-safe_pattr((A,B), Attr) :- !,
- safe_pattr(A, Attr),
- safe_pattr(B, Attr).
-safe_pattr(M:G, Attr) :- !,
- ( atom(M),
- prolog_load_context(module, M)
- -> true
- ; Goal =.. [Attr,M:G],
- permission_error(directive, sandboxed, (:- Goal))
- ).
-safe_pattr(_, _).
-
-
-%% prolog:sandbox_allowed_expansion(:G) is det.
-%
-% Throws an exception if G is not considered a safe expansion
-% goal. This deals with call-backs from the compiler for
-%
-% - goal_expansion/2
-% - term_expansion/2
-% - Quasi quotations.
-
-prolog:sandbox_allowed_expansion(G) :-
- safe_goal(G).
-
-% tuProlog compatibility
-system:quicksort(L, <, S) :- msort(L, S).
-system:quicksort(L, @<, S) :- msort(L, S).