Python "machine"

Admin User, erstellt 02. Mai 2024
         
###
# 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.
##
import nova.store as store
from nova.store import (is_logical, Clause, is_cache, ensure_link,
snapshot_data, pred_link, is_provable, MASK_PRED_ARITHMETIC,
MASK_PRED_CHECK, MASK_PRED_SPECIAL,
set_engine, Engine, Variable, is_variable,
Compound, is_compound, is_skeleton, is_place)
import time
import asyncio
VOID_ARGS = []
trail = None
redo = None
call = "[]"
count = 0
###
# Create an error term from a message.
#
# @param beta The message.
# @return The error term.
##
def make_error(beta):
return Exception(Compound("error", [beta, fetch_stack()]))
###
# Retrieve the current stack.
#
# @return The current stack.
##
def fetch_stack():
temp = pred_link("sys_including", 3)
if temp is NotImplemented or not is_logical(temp.rope):
return "[]"
data = snapshot_data(temp.rope)
back = None
res = None
i = 0
while i < len(data):
elem = fetch_frame(data[i])
if not atomic_equal(elem.args[1], ctx):
i += 1
continue
elem = Compound("sys_including", [elem.args[0], elem.args[2]])
elem = Compound(".", [elem, NotImplemented])
if back is None:
res = elem
else:
back.args[1] = elem
back = elem
i += 1
if back is None:
res = "[]"
else:
back.args[1] = "[]"
return res
def fetch_frame(clause):
global temp_display
if clause.size != 0:
display = [NotImplemented] * clause.size
else:
display = None
temp_display = display
args = [NotImplemented] * len(clause.head)
i = 0
while i < len(args):
args[i] = exec_build(clause.head[i])
i += 1
temp_display = None
return Compound("sys_including", args)
###
# Create an indicator from a functor and an arity.
#
# @param functor The functor.
# @param arity The arity.
# @return The indicator.
##
def make_indicator(functor, arity):
return Compound("/", [functor, arity])
################################################################
# Garbage Collection #
################################################################
VAR_MASK_EVEN = 0x40000000
VAR_MASK_ODD = 0x20000000
VAR_MASK_STATE = VAR_MASK_EVEN | VAR_MASK_ODD
GC_MASK_ASYNC_MODE = 0x00000001
GC_MASK_ALLOW_YIELD = 0x00000008
GC_MASK_IMPORT_ASYNC = 0x00000010
GC_MASK_PROP_ASYNC = 0x00000020
GC_MASK_READ_ASYNC = 0x00000040
GC_MASK_FULL_ASYNC = GC_MASK_ASYNC_MODE | GC_MASK_ALLOW_YIELD
GC_MAX_TRAIL = 3000000
# LIPS / 60
GC_MAX_INFERS = 55000
# LIPS * 60
GC_MAX_DIRTY = 198000000
gc_flags = (VAR_MASK_ODD |
GC_MASK_IMPORT_ASYNC | GC_MASK_PROP_ASYNC | GC_MASK_READ_ASYNC)
gc_time = 0
gc_enter = 0
gc_tick = GC_MAX_INFERS
gc_tock = GC_MAX_DIRTY
###
# Retrieve the real time.
#
# @return The real time.
##
def real_time():
return int(round(time.monotonic()*1000))
###
# Set the garbage collector flags.
#
# @param flags The garbage collector flags.
##
def set_gc_flags(flags):
global gc_flags
gc_flags = flags
################################################################
# Major Marking #
################################################################
###
# Perform major garbage collection.
##
def gc_major():
global gc_time
global gc_flags
gc_time -= real_time()
gc_flags ^= VAR_MASK_STATE
# mark phase
store.engine.low = 0
store.engine.high = store.engine.serno
store.engine.serno = 0
mark_call(call)
last = redo
while last is not None:
mark_call(last.cont)
last = last.tail
# sweep phase
last = redo
while last is not None:
last.mark = adjust_mark(last.mark)
last = last.tail
sweep_trail(None)
gc_time += real_time()
###
# Major mark a term.
#
# @param term The term.
##
def mark_term(term):
while True:
if is_variable(term):
val = term.flags
if (val & VAR_MASK_STATE) == (gc_flags & VAR_MASK_STATE):
break
val = val & ~VAR_MASK_STATE
if val > store.engine.serno:
store.engine.serno = val + 1
if store.engine.low <= val and val < store.engine.high:
if val-store.engine.low > store.engine.high-val:
store.engine.high = val
else:
store.engine.low = val + 1
term.flags = val | (gc_flags & VAR_MASK_STATE)
if term.instantiated is not NotImplemented:
term = term.instantiated
else:
return
elif is_compound(term):
term = term.args
i = 0
while i < len(term) - 1:
mark_term(term[i])
i += 1
term = term[i]
else:
return
###
# Major mark a continuation.
#
# @param term The continuation.
##
def mark_call(term):
gc_color = "E" if ((gc_flags & VAR_MASK_EVEN) != 0) else "O"
while is_compound(term) and term.functor != gc_color:
term.functor = gc_color
mark_term(term.args[0])
term = term.args[1]
################################################################
# Minor Marking #
################################################################
###
# Perform minor garbage collection.
##
def gc_minor():
global gc_time
gc_time -= real_time()
# mark phase
mark2_call(call)
last = redo
while last is not None:
mark2_call(last.cont)
last = last.tail
mark2_trail(store.engine.backtrail)
# sweep phase
last = redo
while last is not None:
last.mark = adjust_mark(last.mark)
last = last.tail
sweep_trail(store.engine.backtrail)
gc_time += real_time()
###
# Minor mark a term.
#
# @param term The term.
##
def mark2_term(term):
while True:
if is_variable(term):
val = term.flags
if (val & VAR_MASK_STATE) == (gc_flags & VAR_MASK_STATE):
break
val = val & ~VAR_MASK_STATE
term.flags = val | (gc_flags & VAR_MASK_STATE)
if term.instantiated is not NotImplemented:
term = term.instantiated
else:
return
elif is_compound(term):
term = term.args
i = 0
while i < len(term) - 1:
mark2_term(term[i])
i += 1
term = term[i]
else:
return
###
# Minor mark a continuation.
#
# @param term The continuation.
##
def mark2_call(term):
gc_color = "E" if ((gc_flags & VAR_MASK_EVEN) != 0) else "O"
while is_compound(term) and term.functor != gc_color:
term.functor = gc_color
mark2_term(term.args[0])
term = term.args[1]
###
# Minor mark the trail
#
# @param stop The stop.
##
def mark2_trail(stop):
temp = trail
while temp is not stop:
if (temp.flags & VAR_MASK_STATE) == VAR_MASK_STATE:
mark2_term(temp)
temp = temp.tail
################################################################
# Variable Sweep #
################################################################
###
# Adjust a marker into the trail.
#
# @param temp The trail.
# @return The adjusted trail.
##
def adjust_mark(temp):
while temp is not None:
if (temp.flags & VAR_MASK_STATE) == (gc_flags & VAR_MASK_STATE):
return temp
else:
temp = temp.tail
return None
###
# Sweep the trail.
#
# @param stop The stop.
##
def sweep_trail(stop):
global trail
global count
temp = trail
back = None
while temp is not stop:
term = temp
temp = term.tail
if (term.flags & VAR_MASK_STATE) == (gc_flags & VAR_MASK_STATE):
if back is not None:
back.tail = term
else:
trail = term
back = term
else:
count -= 1
term.instantiated = NotImplemented
term.tail = None
if back is not None:
back.tail = stop
else:
trail = stop
store.engine.backtrail = trail
store.engine.backcount = count
################################################################
# Signal Handling #
################################################################
###
# Check the signal message.
##
def solve_signal(rope, at, choice):
if store.engine.signal is not NotImplemented:
message = store.engine.signal
store.engine.signal = NotImplemented
raise make_error(message)
return True
###
# Copy a term.
#
# @param alpha The term.
# @return The copy.
##
def copy_term(alpha):
assoc = None
def copy_term2(alpha2):
nonlocal assoc
back = None
while True:
alpha2 = deref(alpha2)
if is_variable(alpha2):
if assoc is None:
assoc = {}
peek = NotImplemented
else:
peek = assoc.get(alpha2, NotImplemented)
if peek is NotImplemented:
peek = Variable()
assoc[alpha2] = peek
alpha2 = peek
break
elif is_compound(alpha2):
t1 = alpha2.args
args = [NotImplemented] * len(t1)
alpha2 = Compound(alpha2.functor, args)
i = 0
while i < len(args) - 1:
args[i] = copy_term2(t1[i])
i += 1
args[i] = back
back = alpha2
alpha2 = t1[i]
else:
break
while back is not None:
peek = back.args[len(back.args) - 1]
back.args[len(back.args) - 1] = alpha2
alpha2 = back
back = peek
return alpha2
return copy_term2(alpha)
################################################################
# Clause Loops #
################################################################
###
# Set the continuation.
#
# @param term The continuation.
##
def cont(term):
global call
call = term
###
# Solve Prolog goals.
#
# @param snap The choice point boundary.
# @param found True for call, and false for redo.
# @return True if execution succeeds, otherwise false.
##
def solve(snap, found):
global call
global redo
global gc_enter
global gc_tick
global gc_tock
while True:
if found is True:
if gc_enter >= gc_tick:
gc_tick += GC_MAX_INFERS
if count > GC_MAX_TRAIL:
gc_major()
if count > GC_MAX_TRAIL:
raise make_error(Compound("system_error",
["stack_overflow"]))
elif gc_enter >= gc_tock:
gc_tock += GC_MAX_DIRTY
gc_major()
elif (3 * (count - store.engine.backcount) > GC_MAX_TRAIL and
3 * store.engine.backcount > GC_MAX_TRAIL):
gc_minor()
if (gc_flags & GC_MASK_ASYNC_MODE) != 0:
more(Choice(solve_signal, None, None, trail))
return lambda: immediate_promise()
goal = call
if is_compound(goal):
gc_enter += 1
goal = deref(goal.args[0])
peek = lookup_pred(goal)
if peek is NotImplemented or (peek.flags & MASK_PRED_ARITHMETIC) != 0:
raise make_error(Compound("existence_error",
["procedure", make_indicator_term(goal)]))
if is_compound(goal):
goal = goal.args
else:
goal = VOID_ARGS
if (peek.flags & MASK_PRED_CHECK) != 0:
if not peek.func(goal):
found = False
else:
cont(call.args[1])
found = True
elif (peek.flags & MASK_PRED_SPECIAL) != 0:
found = peek.func(goal)
else:
peek = defined_clauses(peek, goal)
found = solve2_rope(goal, snapshot_data(peek), 0, None)
else:
break
elif found is False:
if redo is not snap:
choice = redo
redo = choice.tail
unbind(choice.mark)
call = choice.cont
found = choice.func(choice.data, choice.at, choice)
else:
break
else:
break
return found
async def immediate_promise():
try:
await asyncio.sleep(0)
except asyncio.CancelledError:
pass
###
# Lookup a predicate.
#
# @param goal The goal.
# @return The predicate or NotImplemented
##
def lookup_pred(goal):
if is_compound(goal):
functor = goal.functor
arity = len(goal.args)
else:
functor = goal
arity = 0
return resolve_functor(functor, arity)
def resolve_functor(functor, arity):
if is_cache(functor):
return ensure_link(functor, arity)
elif is_atom(functor):
return pred_link(functor, arity)
elif is_provable(functor):
return functor
else:
check_callable(functor)
return NotImplemented
def make_indicator_term(goal):
if is_compound(goal):
functor = goal.functor
arity = len(goal.args)
else:
functor = goal
arity = 0
if is_cache(functor):
functor = functor.name
return make_indicator(functor, arity)
def solve_rope(rope, at, choice):
goal = deref(call.args[0])
if is_compound(goal):
goal = goal.args
else:
goal = VOID_ARGS
return solve2_rope(goal, rope, at, choice)
###
# Search a Prolog clause and add it to the continuation.
#
# @param rope The clausse list.
# @param at The clause index.
# @param choice The choice point for reuse or None.
# @return True if search succeeds, otherwise false.
##
def solve2_rope(args, rope, at, choice):
mark = trail
while at < len(rope):
clause = rope[at]
at += 1
if clause.size != 0:
display = [NotImplemented] * clause.size
else:
display = None
if exec_head(clause.head, display, args):
peek = clause.cutvar
if peek != -1:
display[peek] = redo
if at < len(rope):
if choice is None:
choice = Choice(solve_rope, rope, at, mark)
else:
choice.at = at
more(choice)
if exec_check(clause.body, display):
return True
if redo is not choice:
return False
more(choice.tail)
else:
return exec_check(clause.body, display)
unbind(mark)
return False
###
# Remove choice points.
#
# @param last The last choice point.
##
def cut(last):
global redo
redo = last
###
# Advance the choice points.
#
# @param choice The new choice point.
##
def more(choice):
global redo
redo = choice
###
# Create a choice point.
#
# @param func The choice point handler.
# @param data The choice point data.
# @param at The choice point index.
# @param mark The trail mark.
# @constructor The choice point.
##
class Choice:
def __init__(self, func, data, at, mark):
self.func = func
self.data = data
self.at = at
self.mark = mark
self.cont = call
self.tail = redo
###
# Create a goal.
#
# @param size The number of variables.
# @param body The build Albufeira code.
##
class Goal:
def __init__(self, size, body):
self.size = size
self.body = body
################################################################
# Clauses Retrieval #
################################################################
###
# Find the logical for a call.
#
# @param pred The predicate.
# @param args The arguments.
# @return The logical.
##
def defined_clauses(pred, args):
peek = pred.rope
if peek.count < 2:
return peek
if len(args) > 0 and len(pred.idxmap) > 0:
key = key_value(args[0])
if key is NotImplemented:
return peek
else:
peek = pred.idxmap.get(key, NotImplemented)
if peek is NotImplemented:
return pred.nonguard
else:
return peek
else:
return peek
def key_value(term):
term = deref(term)
if is_variable(term):
return NotImplemented
elif is_compound(term):
return term.functor
elif isinstance(term, dict) or isinstance(term, list):
return id(term)
else:
return term
################################################################
# Directives #
################################################################
###
# Run a compiled goal once. The goal is run with auto-yield
# disabled and promises are not accepted.
#
# @param goal The compiled goal.
# @throw If false.
##
def run(goal):
if not launch(goal, "main", VOID_ARGS):
raise make_error(Compound("syntax_error", ["directive_failed"]))
def snap_setup():
global redo
redo = Choice(solve_setup, None, None, trail)
return redo
def solve_setup(rope, at, choice):
return False
def snap_cleanup(snap):
global call
cut(snap.tail)
unbind(snap.mark)
call = snap.cont
################################################################
# Terms #
################################################################
###
# Check whether an object is an atom.
#
# @param obj The object.
# @return True if the object is an atom, otherwise false.
##
def is_atom(obj):
return isinstance(obj, str)
###
# Check whether an object is a number.
#
# @param obj The object.
# @return True if the object is a number, otherwise false.
##
def is_number(obj):
return is_integer(obj) or is_float(obj)
###
# Check whether an object is an integer.
#
# @param obj The object.
# @return True if the object is an integer, otherwise false.
##
def is_integer(obj):
return isinstance(obj, int) and not isinstance(obj, bool)
###
# Check whether an object is a float.
#
# @param obj The object.
# @return True if the object is a float, otherwise false.
##
def is_float(obj):
return isinstance(obj, float)
################################################################
# Albufeira code #
################################################################
temp_display = None
def exec_build(template):
back = None
while True:
if is_place(template):
index = template.index
if index == -1:
template = Variable()
elif index < -1:
template = Variable()
temp_display[(-index)-2] = template
else:
template = temp_display[index]
break
elif is_skeleton(template):
t1 = template.args
args = [NotImplemented] * len(t1)
template = Compound(template.functor, args)
i = 0
while i < len(args) - 1:
args[i] = exec_build(t1[i])
i += 1
args[i] = back
back = template
template = t1[i]
else:
break
while back is not None:
peek = back.args[len(back.args) - 1]
back.args[len(back.args) - 1] = template
template = back
back = peek
return template
def exec_unify(template, alpha):
while True:
if is_place(template):
template = template.index
if template == -1:
return True
elif template < -1:
temp_display[(-template)-2] = deref(alpha)
return True
else:
return unify(temp_display[template], alpha)
elif is_skeleton(template):
alpha = deref(alpha)
if is_variable(alpha):
t1 = template.args
args = [NotImplemented] * len(t1)
template = Compound(template.functor, args)
i = 0
while i < len(args):
args[i] = exec_build(t1[i])
i += 1
bind(template, alpha)
return True
elif is_compound(alpha):
if len(template.args) != len(alpha.args):
return False
if template.functor != alpha.functor:
return False
template = template.args
alpha = alpha.args
i = 0
while i < len(template) - 1:
if not exec_unify(template[i], alpha[i]):
return False
i += 1
template = template[i]
alpha = alpha[i]
else:
return False
else:
return unify(template, alpha)
def exec_body(code, display):
global temp_display
temp_display = display
back = None
res = None
i = 0
while i < len(code):
goal = exec_build(code[i])
temp = Compound(".", [goal, NotImplemented])
if back is None:
res = temp
else:
back.args[1] = temp
back = temp
i += 1
if back is None:
res = "[]"
else:
back.args[1] = "[]"
temp_display = None
return res
def exec_head(code, display, aux):
global temp_display
temp_display = display
i = 0
while i < len(code):
if not exec_unify(code[i], aux[i]):
temp_display = None
return False
i += 1
temp_display = None
return True
################################################################
# Head Check #
################################################################
def exec_eval(template):
if is_skeleton(template):
peek = resolve_functor(template.functor, len(template.args) + 1)
if peek is NotImplemented or (peek.flags & MASK_PRED_ARITHMETIC) == 0:
raise make_error(Compound("type_error",
["evaluable", make_indicator(template.functor, len(template.args))]))
return peek.func(template.args)
else:
if is_place(template):
template = template.index
if template < 0:
raise make_error("instantiation_error")
else:
template = temp_display[template]
template = deref(template)
if is_number(template):
return template
peek = lookup_eval(template)
if peek is NotImplemented or (peek.flags & MASK_PRED_ARITHMETIC) == 0:
raise make_error(Compound("type_error",
["evaluable", make_indicator_term(template)]))
if is_compound(template):
template = template.args
else:
template = VOID_ARGS
return peek.func(template)
def exec_test(template):
global gc_enter
if is_skeleton(template):
peek = resolve_functor(template.functor, len(template.args))
if peek is NotImplemented or (peek.flags & MASK_PRED_CHECK) == 0:
args = [NotImplemented] * len(template.args)
i = 0
while i < len(args):
args[i] = exec_build(template.args[i])
i += 1
if peek is NotImplemented or (peek.flags & MASK_PRED_ARITHMETIC) != 0:
return Compound(template.functor, args)
else:
return Compound(peek, args)
else:
gc_enter += 1
return peek.func(template.args)
else:
if is_place(template):
template = template.index
if template < 0:
raise make_error("instantiation_error")
else:
template = temp_display[template]
template = deref(template)
peek = lookup_pred(template)
if peek is NotImplemented or (peek.flags & MASK_PRED_CHECK) == 0:
return template
else:
gc_enter += 1
if is_compound(template):
template = template.args
else:
template = VOID_ARGS
return peek.func(template)
def exec_check(code, display):
global temp_display
temp_display = display
check = True
back = None
res = None
i = 0
while i < len(code):
goal = exec_test(code[i]) if check else exec_build(code[i])
if True is goal:
i += 1
continue
if False is goal:
temp_display = None
return False
temp = Compound(".", [goal, NotImplemented])
if back is None:
res = temp
else:
back.args[1] = temp
back = temp
check = False
i += 1
if back is None:
res = call.args[1]
else:
back.args[1] = call.args[1]
temp_display = None
cont(res)
return True
################################################################
# Unification #
################################################################
###
# Determine whether two atomics are equal.
#
# @param alpha The first atomic.
# @param beta The second atomic.
##
def atomic_equal(alpha, beta):
if is_atom(alpha) or is_number(alpha):
if is_float(alpha):
return is_float(beta) and alpha == beta
elif is_float(beta):
return False
else:
return alpha == beta
elif is_atom(beta) or is_number(beta):
return False
else:
return alpha is beta
###
# Determine whether two terms unify.
# As a side effect the trail is extended, even if unification fails.
# Tail recursive solution.
#
# @param first The first term.
# @param second The second term.
# @return True if the two terms unify, otherwise false.
##
def unify(first, second):
while True:
first = deref(first)
second = deref(second)
if is_variable(first):
if is_variable(second) and first is second:
return True
bind(second, first)
return True
if is_variable(second):
bind(first, second)
return True
if not is_compound(first):
return atomic_equal(first, second)
if not is_compound(second):
return False
if len(first.args) != len(second.args):
return False
if first.functor != second.functor:
return False
first = first.args
second = second.args
i = 0
while i < len(first) - 1:
if not unify(first[i], second[i]):
return False
i += 1
first = first[i]
second = second[i]
###
# Dereference a Prolog term.
#
# @param term The Prolog term.
# @return The derferenced Prolog term.
##
def deref(term):
while is_variable(term) and term.instantiated is not NotImplemented:
term = term.instantiated
return term
###
# Bind a variable to a term.
#
# @param source The Prolog term.
# @param term The variable.
##
def bind(source, term):
global trail
global count
term.instantiated = source
term.tail = trail
if (term.flags & VAR_MASK_STATE) == (gc_flags & VAR_MASK_STATE):
term.flags |= VAR_MASK_STATE
trail = term
count += 1
###
# Unbind variable binds.
#
# @param mark The trail mark.
##
def unbind(mark):
global trail
global count
while mark is not trail:
term = trail
if store.engine.backtrail is term:
store.engine.backtrail = term.tail
store.engine.backcount -= 1
trail = term.tail
count -= 1
term.instantiated = NotImplemented
term.tail = None
################################################################
# Eval Service #
################################################################
###
# Assure that the object is a nonvar.
#
# @param beta The object.
##
def check_nonvar(beta):
if is_variable(beta):
raise make_error("instantiation_error")
###
# Assure that the object is a callable.
#
# @param beta The object.
##
def check_callable(beta):
if is_variable(beta) or is_number(beta):
check_nonvar(beta)
beta = copy_term(beta)
raise make_error(Compound("type_error", ["callable", beta]))
###
# Lookup an evaluable function.
#
# @param expr The arithmetic expression.
# @return The evaluable function or NotImplemented
##
def lookup_eval(expr):
if is_compound(expr):
functor = expr.functor
arity = len(expr.args)
else:
functor = expr
arity = 0
return resolve_functor(functor, arity + 1)
################################################################
# Context #
################################################################
###
# Create a task context.
#
# @param goal The list of goals.
# @constructor The task context.
##
class Context:
def __init__(self):
self.trail = None
self.redo = None
self.call = "[]"
self.count = 0
self.gc_flags = (VAR_MASK_ODD |
GC_MASK_IMPORT_ASYNC | GC_MASK_PROP_ASYNC | GC_MASK_READ_ASYNC)
self.engine = Engine()
ctx = "main"
###
# Set the task context.
#
# @param buf The task context.
##
def ctx_set(buf):
global ctx
ctx = buf
###
# Switch the task context.
#
# @param buf The context.
##
def ctx_switch(buf):
global trail
global redo
global call
global count
global gc_flags
temp = trail
trail = buf.trail
buf.trail = temp
temp = redo
redo = buf.redo
buf.redo = temp
temp = call
call = buf.call
buf.call = temp
temp = count
count = buf.count
buf.count = temp
temp = gc_flags
gc_flags = buf.gc_flags
buf.gc_flags = temp
temp = store.engine
set_engine(buf.engine)
buf.engine = temp
################################################################
# Callback #
################################################################
###
# Run a callback once, i.e. no choice point or trailing left
# behind. Callbacks are run with auto-yield disabled and
# promises are not accepted, i.e. run "stackless" on top of the
# given main stack or side stack. "stackless" because completion,
# i.e. return or exception by the callback, is the only context switch.
#
# @param form The goal or closure.
# @param buf The context or "main"
# @param params The actual parameters.
# @return True or false.
##
def launch(form, buf, params):
global call
if buf != "main":
ctx_set(buf)
ctx_switch(buf)
back = gc_flags & GC_MASK_FULL_ASYNC
set_gc_flags(gc_flags & ~GC_MASK_FULL_ASYNC)
snap = snap_setup()
if isinstance(form, Clause):
call = melt_clause(form, params)
elif isinstance(form, Goal):
call = melt_directive(form)
else:
call = form
try:
found = solve(snap, True)
finally:
snap_cleanup(snap)
set_gc_flags((gc_flags & ~GC_MASK_FULL_ASYNC) | back)
if buf != "main":
ctx_switch(buf)
ctx_set("main")
return found
def melt_directive(goal):
if goal.size != 0:
display = [NotImplemented] * goal.size
else:
display = None
return exec_body(goal.body, display)
def melt_clause(clause, params):
if clause.size != 0:
display = [NotImplemented] * clause.size
else:
display = None
if exec_head(clause.head, display, params):
temp = clause.cutvar
if temp != -1:
display[temp] = redo
return exec_body(clause.body, display)
else:
return "[]"
################################################################
# Task #
################################################################
###
# Run a task once, i.e. no choice point or trailing left
# behind. Tasks are run with auto-yield enabled and promises are
# accepted, i.e. run "stackfull" on top of the given main stack
# or side stack. "stackfull" because not only completion, i.e.
# return or exception by the task, cause a context switch, but
# also await of an auto-yield or promise.
#
# @param form The goal or closure.
# @param buf The context or "main".
# @param params The actual parameters.
# @return True or false.
##
async def launch_async(form, buf, params):
global call
if buf != "main":
ctx_set(buf)
ctx_switch(buf)
back = gc_flags & GC_MASK_FULL_ASYNC
set_gc_flags(gc_flags | GC_MASK_FULL_ASYNC)
found = True
snap = snap_setup()
if isinstance(form, Clause):
call = melt_clause(form, params)
elif isinstance(form, Goal):
call = melt_directive(form)
else:
call = form
try:
while True:
found = solve(snap, found)
if found is False:
break
elif found is not True:
if buf != "main":
ctx_switch(buf)
ctx_set("main")
await found()
if buf != "main":
ctx_set(buf)
ctx_switch(buf)
found = False
else:
break
finally:
snap_cleanup(snap)
set_gc_flags((gc_flags & ~GC_MASK_FULL_ASYNC) | back)
if buf != "main":
ctx_switch(buf)
ctx_set("main")
return found
###
# Register an interrupt function in a context.
#
# @param buf The context.
# @param func The function.
##
def register_interrupt(buf, func):
en = determine_engine(buf)
en.interrupt = func
###
# Register a signal in a context.
#
# @param buf The context.
# @param msg The signal.
##
def register_signal(buf, msg):
en = determine_engine(buf)
en.signal = msg
###
# Invoke the interrupt handler of a context.
#
# @param buf The context.
##
def invoke_interrupt(buf):
en = determine_engine(buf)
en.interrupt()
def determine_engine(buf):
if buf != "main":
if buf is not ctx:
return buf.engine
else:
return store.engine
else:
if ctx != "main":
return ctx.engine
else:
return store.engine