438 lines
11 KiB
Python
438 lines
11 KiB
Python
ALIASES = (
|
|
('bool', ['truthy']),
|
|
('mod', ['%', 'rem', 'remainder', 'modulus']),
|
|
('getitem', ['pick', 'at']),
|
|
('xor', ['^']),
|
|
('eh', ['?']),
|
|
('id', [u'•']),
|
|
)
|
|
|
|
def floor(n):
|
|
return int(math.floor(n))
|
|
|
|
floor.__doc__ = math.floor.__doc__
|
|
|
|
|
|
@inscribe
|
|
@SimpleFunctionWrapper
|
|
def divmod_(S):
|
|
'''
|
|
divmod(x, y) -> (quotient, remainder)
|
|
|
|
Return the tuple (x//y, x%y). Invariant: q * y + r == x.
|
|
'''
|
|
y, (x, stack) = S
|
|
q, r = divmod(x, y)
|
|
return r, (q, stack)
|
|
|
|
|
|
@inscribe
|
|
@SimpleFunctionWrapper
|
|
def id_(stack):
|
|
'''The identity function.'''
|
|
return stack
|
|
|
|
|
|
|
|
#
|
|
# § Combinators
|
|
#
|
|
|
|
|
|
# Several combinators depend on other words in their definitions,
|
|
# we use symbols to prevent hard-coding these, so in theory, you
|
|
# could change the word in the dictionary to use different semantics.
|
|
S_choice = Symbol('choice')
|
|
S_first = Symbol('first')
|
|
S_genrec = Symbol('genrec')
|
|
S_getitem = Symbol('getitem')
|
|
S_i = Symbol('i')
|
|
S_ifte = Symbol('ifte')
|
|
S_infra = Symbol('infra')
|
|
S_loop = Symbol('loop')
|
|
S_pop = Symbol('pop')
|
|
S_primrec = Symbol('primrec')
|
|
S_step = Symbol('step')
|
|
S_swaack = Symbol('swaack')
|
|
S_times = Symbol('times')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#def cleave(S, expression, dictionary):
|
|
# '''
|
|
# The cleave combinator expects two quotations, and below that an item X.
|
|
# It first executes [P], with X on top, and saves the top result element.
|
|
# Then it executes [Q], again with X, and saves the top result.
|
|
# Finally it restores the stack to what it was below X and pushes the two
|
|
# results P(X) and Q(X).
|
|
# '''
|
|
# (Q, (P, (x, stack))) = S
|
|
# p = joy((x, stack), P, dictionary)[0][0]
|
|
# q = joy((x, stack), Q, dictionary)[0][0]
|
|
# return (q, (p, stack)), expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def branch(stack, expression, dictionary):
|
|
'''
|
|
Use a Boolean value to select one of two quoted programs to run.
|
|
|
|
::
|
|
|
|
branch == roll< choice i
|
|
|
|
::
|
|
|
|
False [F] [T] branch
|
|
--------------------------
|
|
F
|
|
|
|
True [F] [T] branch
|
|
-------------------------
|
|
T
|
|
|
|
'''
|
|
(then, (else_, (flag, stack))) = stack
|
|
return stack, concat(then if flag else else_, expression), dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def ifte(stack, expression, dictionary):
|
|
'''
|
|
If-Then-Else Combinator
|
|
::
|
|
|
|
... [if] [then] [else] ifte
|
|
---------------------------------------------------
|
|
... [[else] [then]] [...] [if] infra select i
|
|
|
|
|
|
|
|
|
|
... [if] [then] [else] ifte
|
|
-------------------------------------------------------
|
|
... [else] [then] [...] [if] infra first choice i
|
|
|
|
|
|
Has the effect of grabbing a copy of the stack on which to run the
|
|
if-part using infra.
|
|
'''
|
|
(else_, (then, (if_, stack))) = stack
|
|
expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
|
|
stack = (if_, (stack, (then, (else_, stack))))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def cond(stack, expression, dictionary):
|
|
'''
|
|
This combinator works like a case statement. It expects a single quote
|
|
on the stack that must contain zero or more condition quotes and a
|
|
default quote. Each condition clause should contain a quoted predicate
|
|
followed by the function expression to run if that predicate returns
|
|
true. If no predicates return true the default function runs.
|
|
|
|
It works by rewriting into a chain of nested `ifte` expressions, e.g.::
|
|
|
|
[[[B0] T0] [[B1] T1] [D]] cond
|
|
-----------------------------------------
|
|
[B0] [T0] [[B1] [T1] [D] ifte] ifte
|
|
|
|
'''
|
|
conditions, stack = stack
|
|
if conditions:
|
|
expression = _cond(conditions, expression)
|
|
try:
|
|
# Attempt to preload the args to first ifte.
|
|
(P, (T, (E, expression))) = expression
|
|
except ValueError:
|
|
# If, for any reason, the argument to cond should happen to contain
|
|
# only the default clause then this optimization will fail.
|
|
pass
|
|
else:
|
|
stack = (E, (T, (P, stack)))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
def _cond(conditions, expression):
|
|
(clause, rest) = conditions
|
|
if not rest: # clause is [D]
|
|
return clause
|
|
P, T = clause
|
|
return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def dip(stack, expression, dictionary):
|
|
'''
|
|
The dip combinator expects a quoted program on the stack and below it
|
|
some item, it hoists the item into the expression and runs the program
|
|
on the rest of the stack.
|
|
::
|
|
|
|
... x [Q] dip
|
|
-------------------
|
|
... Q x
|
|
|
|
'''
|
|
try:
|
|
(quote, (x, stack)) = stack
|
|
except ValueError:
|
|
raise StackUnderflowError('Not enough values on stack.')
|
|
expression = (x, expression)
|
|
return stack, concat(quote, expression), dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def dipd(S, expression, dictionary):
|
|
'''
|
|
Like dip but expects two items.
|
|
::
|
|
|
|
... y x [Q] dip
|
|
---------------------
|
|
... Q y x
|
|
|
|
'''
|
|
(quote, (x, (y, stack))) = S
|
|
expression = (y, (x, expression))
|
|
return stack, concat(quote, expression), dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def dipdd(S, expression, dictionary):
|
|
'''
|
|
Like dip but expects three items.
|
|
::
|
|
|
|
... z y x [Q] dip
|
|
-----------------------
|
|
... Q z y x
|
|
|
|
'''
|
|
(quote, (x, (y, (z, stack)))) = S
|
|
expression = (z, (y, (x, expression)))
|
|
return stack, concat(quote, expression), dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def app1(S, expression, dictionary):
|
|
'''
|
|
Given a quoted program on TOS and anything as the second stack item run
|
|
the program and replace the two args with the first result of the
|
|
program.
|
|
::
|
|
|
|
... x [Q] . app1
|
|
-----------------------------------
|
|
... [x ...] [Q] . infra first
|
|
|
|
'''
|
|
(quote, (x, stack)) = S
|
|
stack = (quote, ((x, stack), stack))
|
|
expression = (S_infra, (S_first, expression))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def app2(S, expression, dictionary):
|
|
'''Like app1 with two items.
|
|
::
|
|
|
|
... y x [Q] . app2
|
|
-----------------------------------
|
|
... [y ...] [Q] . infra first
|
|
[x ...] [Q] infra first
|
|
|
|
'''
|
|
(quote, (x, (y, stack))) = S
|
|
expression = (S_infra, (S_first,
|
|
((x, stack), (quote, (S_infra, (S_first,
|
|
expression))))))
|
|
stack = (quote, ((y, stack), stack))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def app3(S, expression, dictionary):
|
|
'''Like app1 with three items.
|
|
::
|
|
|
|
... z y x [Q] . app3
|
|
-----------------------------------
|
|
... [z ...] [Q] . infra first
|
|
[y ...] [Q] infra first
|
|
[x ...] [Q] infra first
|
|
|
|
'''
|
|
(quote, (x, (y, (z, stack)))) = S
|
|
expression = (S_infra, (S_first,
|
|
((y, stack), (quote, (S_infra, (S_first,
|
|
((x, stack), (quote, (S_infra, (S_first,
|
|
expression))))))))))
|
|
stack = (quote, ((z, stack), stack))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def step(S, expression, dictionary):
|
|
'''
|
|
Run a quoted program on each item in a sequence.
|
|
::
|
|
|
|
... [] [Q] . step
|
|
-----------------------
|
|
... .
|
|
|
|
|
|
... [a] [Q] . step
|
|
------------------------
|
|
... a . Q
|
|
|
|
|
|
... [a b c] [Q] . step
|
|
----------------------------------------
|
|
... a . Q [b c] [Q] step
|
|
|
|
The step combinator executes the quotation on each member of the list
|
|
on top of the stack.
|
|
'''
|
|
(quote, (aggregate, stack)) = S
|
|
if not aggregate:
|
|
return stack, expression, dictionary
|
|
head, tail = aggregate
|
|
stack = quote, (head, stack)
|
|
if tail:
|
|
expression = tail, (quote, (S_step, expression))
|
|
expression = S_i, expression
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def times(stack, expression, dictionary):
|
|
'''
|
|
times == [-- dip] cons [swap] infra [0 >] swap while pop
|
|
::
|
|
|
|
... n [Q] . times
|
|
--------------------- w/ n <= 0
|
|
... .
|
|
|
|
|
|
... 1 [Q] . times
|
|
-----------------------
|
|
... . Q
|
|
|
|
|
|
... n [Q] . times
|
|
------------------------------------- w/ n > 1
|
|
... . Q (n - 1) [Q] times
|
|
|
|
'''
|
|
# times == [-- dip] cons [swap] infra [0 >] swap while pop
|
|
(quote, (n, stack)) = stack
|
|
if n <= 0:
|
|
return stack, expression, dictionary
|
|
n -= 1
|
|
if n:
|
|
expression = n, (quote, (S_times, expression))
|
|
expression = concat(quote, expression)
|
|
return stack, expression, dictionary
|
|
|
|
|
|
# The current definition above works like this:
|
|
|
|
# [P] [Q] while
|
|
# --------------------------------------
|
|
# [P] nullary [Q [P] nullary] loop
|
|
|
|
# while == [pop i not] [popop] [dudipd] tailrec
|
|
|
|
#def while_(S, expression, dictionary):
|
|
# '''[if] [body] while'''
|
|
# (body, (if_, stack)) = S
|
|
# while joy(stack, if_, dictionary)[0][0]:
|
|
# stack = joy(stack, body, dictionary)[0]
|
|
# return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def loop(stack, expression, dictionary):
|
|
'''
|
|
Basic loop combinator.
|
|
::
|
|
|
|
... True [Q] loop
|
|
-----------------------
|
|
... Q [Q] loop
|
|
|
|
... False [Q] loop
|
|
------------------------
|
|
...
|
|
|
|
'''
|
|
try:
|
|
quote, stack = stack
|
|
except ValueError:
|
|
raise StackUnderflowError('Not enough values on stack.')
|
|
if not isinstance(quote, tuple):
|
|
raise NotAListError('Loop body not a list.')
|
|
try:
|
|
(flag, stack) = stack
|
|
except ValueError:
|
|
raise StackUnderflowError('Not enough values on stack.')
|
|
if flag:
|
|
expression = concat(quote, (quote, (S_loop, expression)))
|
|
return stack, expression, dictionary
|
|
|
|
|
|
@inscribe
|
|
@FunctionWrapper
|
|
def cmp_(stack, expression, dictionary):
|
|
'''
|
|
cmp takes two values and three quoted programs on the stack and runs
|
|
one of the three depending on the results of comparing the two values:
|
|
::
|
|
|
|
a b [G] [E] [L] cmp
|
|
------------------------- a > b
|
|
G
|
|
|
|
a b [G] [E] [L] cmp
|
|
------------------------- a = b
|
|
E
|
|
|
|
a b [G] [E] [L] cmp
|
|
------------------------- a < b
|
|
L
|
|
'''
|
|
L, (E, (G, (b, (a, stack)))) = stack
|
|
expression = concat(G if a > b else L if a < b else E, expression)
|
|
return stack, expression, dictionary
|
|
|
|
|
|
# FunctionWrapper(cleave),
|
|
# FunctionWrapper(while_),
|
|
|
|
|
|
|
|
for name, primitive in getmembers(genlib, isfunction):
|
|
inscribe(SimpleFunctionWrapper(primitive))
|
|
|
|
|