###
# Modern Albufeira Prolog Interpreter
#
# Warranty & Liability
# To the extent permitted by applicable law and unless explicitly
# otherwise agreed upon, XLOG Technologies AG makes no warranties
# regarding the provided information. XLOG Technologies AG assumes
# no liability that any problems might be solved with the information
# provided by XLOG Technologies AG.
#
# Rights & License
# All industrial property rights regarding the information - copyright
# and patent rights in particular - are the sole property of XLOG
# Technologies AG. If the company was not the originator of some
# excerpts, XLOG Technologies AG has at least obtained the right to
# reproduce, change and translate the information.
#
# Reproduction is restricted to the whole unaltered document. Reproduction
# of the information is only allowed for non-commercial uses. Selling,
# giving away or letting of the execution of the library is prohibited.
# The library can be distributed as part of your applications and libraries
# for execution provided this comment remains unchanged.
#
# Restrictions
# Only to be distributed with programs that add significant and primary
# functionality to the library. Not to be distributed with additional
# software intended to replace any components of the library.
#
# Trademarks
# Jekejeke is a registered trademark of XLOG Technologies AG.
##
from nova.core import (
set, deref, is_structure, make_check, MAX_ARITY, check_nil,
exec_build, exec_unify, int32, Compound, get_ctx)
import asyncio
import threading
MAX_TILT = 64
GROUP_SIZE = 8
class CpuMap:
def __init__(self):
self.code = None
self.offset = None
self.state = None
class CpuWarp:
def __init__(self):
super().__init__()
self.map = None
self.mfrom = 0
self.to = 0
self.id = 0
self.tilt = 0
self.cmd = False
self.buf = None
self.thread = None
def run(warp):
i = warp.mfrom
while i < warp.to:
warp.map.state[warp.map.offset[i] - 3] = i
i += 1
run_warp(warp, warp.mfrom, warp.to)
while warp.cmd:
warp.buf = read_ints()
run_warp(warp, warp.mfrom, warp.to)
def run_warp(warp, mfrom, to):
active = True
while active:
active = False
i = warp.id if warp.cmd else mfrom
while i < to:
if run_id(warp, i):
if warp.cmd:
warp.id = i
return
else:
active = True
i += 1
def run_id(warp, mid):
pi = warp.map
base = pi.offset[mid]
pc = pi.state[base-2]
accu = pi.state[base-1]
k = warp.tilt if warp.cmd else 0
while k < MAX_TILT:
if pc < len(pi.code):
instr = pi.code[pc]
value = hack_get(warp, pi, instr, base, pc)
if warp.cmd:
pi.state[base-2] = pc
pi.state[base-1] = accu
warp.tilt = k
return False
else:
accu = hack_fun(instr, accu, value)
hack_set(pi, instr, accu, base)
pc = pc+1+hack_jump(instr, accu)
else:
pi.state[base-2] = pc
pi.state[base-1] = accu
return False
k += 1
pi.state[base-2] = pc
pi.state[base-1] = accu
return True
def hack_get(warp, pi, instr, base, pc):
act = instr >> 20
obj = ((instr >> 10) & 0x03FF) - 512
mode = (act >> 4) & 0x0F
if mode == 0: # get
return pi.state[base+obj]
elif mode == 1: # cst
return obj
elif mode == 2: # tbl
return pi.code[pc+1+obj]
elif mode == 3: # set
return 0
elif mode == 4: # out
return 0
elif mode == 5: # in
extra_outline(warp, pi, obj, base)
return 0
else:
raise ValueError("illegal mode")
def hack_fun(instr, accu, value):
act = instr >> 20
fun = act >> 8
if fun == 0: # val
return value
elif fun == 1: # acc
return accu
elif fun == 2: # add
return int32(value + accu)
elif fun == 3: # sub
return int32(value - accu)
elif fun == 4: # mul
return int32(value * accu)
elif fun == 5: # zdiv
return int32(value // accu)
elif fun == 6: # rem
return int32(value % accu)
else:
raise ValueError("illegal fun")
def hack_set(pi, instr, accu, base):
act = instr >> 20
obj = ((instr >> 10) & 0x03FF) - 512
mode = (act >> 4) & 0x0F
if mode == 0: # get
return
elif mode == 1: # cst
return
elif mode == 2: # tbl
return
elif mode == 3: # set
pi.state[base+obj] = accu
return
elif mode == 4: # out
write_ints(load_ints(obj, pi.state, base))
return
elif mode == 5: # in
return
else:
raise ValueError("illegal mode")
def hack_jump(instr, accu):
act = instr >> 20
rel = (instr & 0x03FF) - 512
cond = act & 0x0F
if cond == 0: # true
return rel
elif cond == 1: # eq
return rel if accu == 0 else 0
elif cond == 2: # nq
return rel if accu != 0 else 0
elif cond == 3: # ls
return rel if accu < 0 else 0
elif cond == 4: # gr
return rel if accu > 0 else 0
elif cond == 5: # lq
return rel if accu <= 0 else 0
elif cond == 6: # gq
return rel if accu >= 0 else 0
else:
raise ValueError("illegal cond")
#################################################################
# π-WAM Input #
#################################################################
def read_ints():
print(end=": ")
return list(map(lambda y: int32(int(y)),
filter(lambda x: x != "", input().split())))
def write_ints(res):
i = 0
while i < len(res):
print(res[i], end=" ")
i += 1
print()
#################################################################
# π-WAM Store/Load #
#################################################################
def store_ints(res, mmax, state, base):
i = 0
while i < mmax:
if i < len(res):
state[base+i] = res[i]
else:
state[base+i] = 0
i += 1
def load_ints(mmax, state, base):
res = [0] * mmax
i = 0
while i < mmax:
res[i] = state[base+i]
i += 1
return res
def extra_outline(warp, pi, obj, base):
if warp.cmd:
store_ints(warp.buf, obj, pi.state, base)
warp.buf = None
warp.cmd = False
else:
warp.cmd = True
#################################################################
# π-WAM Constructors #
#################################################################
###
# test_cpu_data_new(A, B): internal only
# The predicate succeeds in B with a pi-WAM buffer for the list A.
##
def test_cpu_data_new(args):
alpha = exec_build(args[0])
res = cpu_list_ints(alpha)
return exec_unify(args[1], res)
def cpu_list_ints(obj):
peek = obj
i = 0
while (is_structure(peek) and
peek.functor == "." and
len(peek.args) == 2 and
i < MAX_ARITY):
i += 1
peek = deref(peek.args[1])
check_nil(peek)
args = [NotImplemented] * i
peek = obj
i = 0
while (is_structure(peek) and
peek.functor == "." and
len(peek.args) == 2):
val = deref(peek.args[0])
args[i] = int32(val)
i += 1
peek = deref(peek.args[1])
return args
###
# test_cpu_comp_new(C, O, S, M): internal only
# The predicate succeeds in M with a new CPU backed π-WAM for
# the code buffer C, the offsets O and the state buffer S,
##
def test_cpu_comp_new(args):
res = CpuMap()
res.code = exec_build(args[0])
res.offset = exec_build(args[1])
res.state = exec_build(args[2])
return exec_unify(args[3], res)
###
# test_cpu_comp_work(M, W): internal only
# The predicate succeeds in W with the current
# group size of the CPU backed π-WAM M.
##
def test_cpu_comp_work(args):
alpha = exec_build(args[0])
res = GROUP_SIZE
return exec_unify(args[1], res)
#################################################################
# π-WAM Interface #
#################################################################
###
# test_cpu_group_new(G, W): internal only
# The predicate succeeds in G with a
# new group for the CPU backed π-WAM M.
##
def test_cpu_group_new(args):
alpha = exec_build(args[0])
res = CpuWarp()
res.map = alpha
res.thread = threading.Thread(target=run, args=(res,))
return exec_unify(args[1], res)
###
# test_cpu_group_start(G, I): internal only
# The predicate succeeds. As a side effect it
# starts the warp G at group index I.
##
def test_cpu_group_start(args):
alpha = exec_build(args[0])
beta = exec_build(args[1])
at = int32(beta)*GROUP_SIZE
mlen = min(len(alpha.map.offset) - at, GROUP_SIZE)
alpha.mfrom = at
alpha.to = at+mlen
alpha.thread.start()
return True
###
# test_cpu_group_join(G, P): internal only
# The predicate succeeds in P with a new promise
# that waits for the group G to finish.
##
def test_cpu_group_join(args):
alpha = exec_build(args[0])
buf = get_ctx()
return exec_unify(args[1], lambda: join_promise(buf, alpha.thread))
async def join_promise(buf, alpha):
await asyncio.to_thread(alpha.join)
###
# test_cpu_group_free(G): internal only
# The predicate succeeds. As a side effect
# it frees the group G.
##
def test_cpu_group_free(args):
return True
#################################################################
# Hack Lib Init #
#################################################################
def main():
set("cpu_data_new", 2, make_check(test_cpu_data_new))
set("cpu_comp_new", 4, make_check(test_cpu_comp_new))
set("cpu_comp_work", 2, make_check(test_cpu_comp_work))
set("cpu_group_new", 2, make_check(test_cpu_group_new))
set("cpu_group_start", 2, make_check(test_cpu_group_start))
set("cpu_group_join", 2, make_check(test_cpu_group_join))
set("cpu_group_free", 1, make_check(test_cpu_group_free))