Bringing over some of the "upgrades".
This commit is contained in:
parent
cb553a1a65
commit
4bd32f2c0b
|
|
@ -0,0 +1,14 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SEP = '•'
|
||||||
|
|
||||||
|
lines = sys.stdin.readlines()
|
||||||
|
|
||||||
|
indicies = [line.index(SEP) for line in lines if SEP in line]
|
||||||
|
|
||||||
|
MAX = max(indicies)
|
||||||
|
|
||||||
|
prefix_counts = [MAX - i for i in indicies]
|
||||||
|
|
||||||
|
for count, line in zip(prefix_counts, lines):
|
||||||
|
print(' ' * count, line, sep='', end = '')
|
||||||
|
|
@ -7,322 +7,6 @@ ALIASES = (
|
||||||
('id', [u'•']),
|
('id', [u'•']),
|
||||||
)
|
)
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def inscribe_(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
Create a new Joy function definition in the Joy dictionary. A
|
|
||||||
definition is given as a quote with a name followed by a Joy
|
|
||||||
expression. for example:
|
|
||||||
|
|
||||||
[sqr dup mul] inscribe
|
|
||||||
|
|
||||||
'''
|
|
||||||
(name, body), stack = stack
|
|
||||||
inscribe(Def(name, body), dictionary)
|
|
||||||
return stack, expression, dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def getitem(stack):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
getitem == drop first
|
|
||||||
|
|
||||||
Expects an integer and a quote on the stack and returns the item at the
|
|
||||||
nth position in the quote counting from 0.
|
|
||||||
::
|
|
||||||
|
|
||||||
[a b c d] 0 getitem
|
|
||||||
-------------------------
|
|
||||||
a
|
|
||||||
|
|
||||||
'''
|
|
||||||
n, (Q, stack) = stack
|
|
||||||
return pick(Q, n), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def drop(stack):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
drop == [rest] times
|
|
||||||
|
|
||||||
Expects an integer and a quote on the stack and returns the quote with
|
|
||||||
n items removed off the top.
|
|
||||||
::
|
|
||||||
|
|
||||||
[a b c d] 2 drop
|
|
||||||
----------------------
|
|
||||||
[c d]
|
|
||||||
|
|
||||||
'''
|
|
||||||
n, (Q, stack) = stack
|
|
||||||
while n > 0:
|
|
||||||
try:
|
|
||||||
_, Q = Q
|
|
||||||
except ValueError:
|
|
||||||
raise IndexError
|
|
||||||
n -= 1
|
|
||||||
return Q, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def take(stack):
|
|
||||||
'''
|
|
||||||
Expects an integer and a quote on the stack and returns the quote with
|
|
||||||
just the top n items in reverse order (because that's easier and you can
|
|
||||||
use reverse if needed.)
|
|
||||||
::
|
|
||||||
|
|
||||||
[a b c d] 2 take
|
|
||||||
----------------------
|
|
||||||
[b a]
|
|
||||||
|
|
||||||
'''
|
|
||||||
n, (Q, stack) = stack
|
|
||||||
x = ()
|
|
||||||
while n > 0:
|
|
||||||
try:
|
|
||||||
item, Q = Q
|
|
||||||
except ValueError:
|
|
||||||
raise IndexError
|
|
||||||
x = item, x
|
|
||||||
n -= 1
|
|
||||||
return x, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def gcd2(stack, expression, dictionary):
|
|
||||||
'''Compiled GCD function.'''
|
|
||||||
(v1, (v2, stack)) = stack
|
|
||||||
tos = True
|
|
||||||
while tos:
|
|
||||||
v3 = v2 % v1
|
|
||||||
tos = v3 > 0
|
|
||||||
(v1, (v2, stack)) = (v3, (v1, stack))
|
|
||||||
return (v2, stack), expression, dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def choice(stack):
|
|
||||||
'''
|
|
||||||
Use a Boolean value to select one of two items.
|
|
||||||
::
|
|
||||||
|
|
||||||
A B false choice
|
|
||||||
----------------------
|
|
||||||
A
|
|
||||||
|
|
||||||
|
|
||||||
A B true choice
|
|
||||||
---------------------
|
|
||||||
B
|
|
||||||
|
|
||||||
'''
|
|
||||||
(if_, (then, (else_, stack))) = stack
|
|
||||||
assert isinstance(if_, bool), repr(if_)
|
|
||||||
return then if if_ else else_, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def select(stack):
|
|
||||||
'''
|
|
||||||
Use a Boolean value to select one of two items from a sequence.
|
|
||||||
::
|
|
||||||
|
|
||||||
[A B] false select
|
|
||||||
------------------------
|
|
||||||
A
|
|
||||||
|
|
||||||
|
|
||||||
[A B] true select
|
|
||||||
-----------------------
|
|
||||||
B
|
|
||||||
|
|
||||||
The sequence can contain more than two items but not fewer.
|
|
||||||
Currently Python semantics are used to evaluate the "truthiness" of the
|
|
||||||
Boolean value (so empty string, zero, etc. are counted as false, etc.)
|
|
||||||
'''
|
|
||||||
(flag, (choices, stack)) = stack
|
|
||||||
(else_, (then, _)) = choices
|
|
||||||
return then if flag else else_, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def max_(S):
|
|
||||||
'''Given a list find the maximum.'''
|
|
||||||
tos, stack = S
|
|
||||||
return max(iter_stack(tos)), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def min_(S):
|
|
||||||
'''Given a list find the minimum.'''
|
|
||||||
tos, stack = S
|
|
||||||
return min(iter_stack(tos)), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def sum_(S):
|
|
||||||
'''
|
|
||||||
Given a quoted sequence of numbers return the sum.
|
|
||||||
::
|
|
||||||
|
|
||||||
sum == 0 swap [+] step
|
|
||||||
|
|
||||||
'''
|
|
||||||
tos, stack = S
|
|
||||||
return sum(iter_stack(tos)), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def remove(S):
|
|
||||||
'''
|
|
||||||
Expects an item on the stack and a quote under it and removes that item
|
|
||||||
from the the quote. The item is only removed once. If the list is
|
|
||||||
empty or the item isn't in the list then the list is unchanged.
|
|
||||||
::
|
|
||||||
|
|
||||||
[1 2 3 1] 1 remove
|
|
||||||
------------------------
|
|
||||||
[2 3 1]
|
|
||||||
|
|
||||||
'''
|
|
||||||
(item, (quote, stack)) = S
|
|
||||||
return _remove(item, quote), stack
|
|
||||||
|
|
||||||
|
|
||||||
def _remove(item, quote):
|
|
||||||
try: head, tail = quote
|
|
||||||
except ValueError: return quote
|
|
||||||
return tail if head == item else (head, _remove(item, tail))
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def unique(S):
|
|
||||||
'''Given a list remove duplicate items.'''
|
|
||||||
tos, stack = S
|
|
||||||
I = list(iter_stack(tos))
|
|
||||||
return list_to_stack(sorted(set(I), key=I.index)), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def sort_(S):
|
|
||||||
'''Given a list return it sorted.'''
|
|
||||||
tos, stack = S
|
|
||||||
return list_to_stack(sorted(iter_stack(tos))), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def disenstacken(stack):
|
|
||||||
'''
|
|
||||||
The disenstacken operator expects a list on top of the stack and makes that
|
|
||||||
the stack discarding the rest of the stack.
|
|
||||||
'''
|
|
||||||
return stack[0]
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def reverse(S):
|
|
||||||
'''
|
|
||||||
Reverse the list on the top of the stack.
|
|
||||||
::
|
|
||||||
|
|
||||||
reverse == [] swap shunt
|
|
||||||
'''
|
|
||||||
(tos, stack) = S
|
|
||||||
res = ()
|
|
||||||
for term in iter_stack(tos):
|
|
||||||
res = term, res
|
|
||||||
return res, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def shunt(stack):
|
|
||||||
'''
|
|
||||||
Like concat but reverses the top list into the second.
|
|
||||||
::
|
|
||||||
|
|
||||||
shunt == [swons] step == reverse swap concat
|
|
||||||
|
|
||||||
[a b c] [d e f] shunt
|
|
||||||
---------------------------
|
|
||||||
[f e d a b c]
|
|
||||||
|
|
||||||
'''
|
|
||||||
(tos, (second, stack)) = stack
|
|
||||||
while tos:
|
|
||||||
term, tos = tos
|
|
||||||
second = term, second
|
|
||||||
return second, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def zip_(S):
|
|
||||||
'''
|
|
||||||
Replace the two lists on the top of the stack with a list of the pairs
|
|
||||||
from each list. The smallest list sets the length of the result list.
|
|
||||||
'''
|
|
||||||
(tos, (second, stack)) = S
|
|
||||||
accumulator = [
|
|
||||||
(a, (b, ()))
|
|
||||||
for a, b in zip(iter_stack(tos), iter_stack(second))
|
|
||||||
]
|
|
||||||
return list_to_stack(accumulator), stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def succ(S):
|
|
||||||
'''Increment TOS.'''
|
|
||||||
(tos, stack) = S
|
|
||||||
return tos + 1, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def pred(S):
|
|
||||||
'''Decrement TOS.'''
|
|
||||||
(tos, stack) = S
|
|
||||||
return tos - 1, stack
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@SimpleFunctionWrapper
|
|
||||||
def pm(stack):
|
|
||||||
'''
|
|
||||||
Plus or minus
|
|
||||||
::
|
|
||||||
|
|
||||||
a b pm
|
|
||||||
-------------
|
|
||||||
a+b a-b
|
|
||||||
|
|
||||||
'''
|
|
||||||
a, (b, stack) = stack
|
|
||||||
p, m, = b + a, b - a
|
|
||||||
return m, (p, stack)
|
|
||||||
|
|
||||||
|
|
||||||
def floor(n):
|
def floor(n):
|
||||||
return int(math.floor(n))
|
return int(math.floor(n))
|
||||||
|
|
||||||
|
|
@ -350,39 +34,6 @@ def id_(stack):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def sharing(stack, expression, dictionary):
|
|
||||||
'''Print redistribution information.'''
|
|
||||||
print("You may convey verbatim copies of the Program's source code as"
|
|
||||||
' you receive it, in any medium, provided that you conspicuously'
|
|
||||||
' and appropriately publish on each copy an appropriate copyright'
|
|
||||||
' notice; keep intact all notices stating that this License and'
|
|
||||||
' any non-permissive terms added in accord with section 7 apply'
|
|
||||||
' to the code; keep intact all notices of the absence of any'
|
|
||||||
' warranty; and give all recipients a copy of this License along'
|
|
||||||
' with the Program.'
|
|
||||||
' You should have received a copy of the GNU General Public License'
|
|
||||||
' along with Thun. If not see <http://www.gnu.org/licenses/>.')
|
|
||||||
return stack, expression, dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def warranty(stack, expression, dictionary):
|
|
||||||
'''Print warranty information.'''
|
|
||||||
print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
|
|
||||||
' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
|
|
||||||
' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
|
|
||||||
' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
|
|
||||||
' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
|
|
||||||
' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
|
|
||||||
' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
|
|
||||||
' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
|
|
||||||
' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
|
|
||||||
return stack, expression, dictionary
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# § Combinators
|
# § Combinators
|
||||||
#
|
#
|
||||||
|
|
@ -406,194 +57,9 @@ S_swaack = Symbol('swaack')
|
||||||
S_times = Symbol('times')
|
S_times = Symbol('times')
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def i(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
The i combinator expects a quoted program on the stack and unpacks it
|
|
||||||
onto the pending expression for evaluation.
|
|
||||||
::
|
|
||||||
|
|
||||||
[Q] i
|
|
||||||
-----------
|
|
||||||
Q
|
|
||||||
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
quote, stack = stack
|
|
||||||
except ValueError:
|
|
||||||
raise StackUnderflowError('Not enough values on stack.')
|
|
||||||
return stack, concat(quote, expression), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def x(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
x == dup i
|
|
||||||
|
|
||||||
... [Q] x = ... [Q] dup i
|
|
||||||
... [Q] x = ... [Q] [Q] i
|
|
||||||
... [Q] x = ... [Q] Q
|
|
||||||
|
|
||||||
'''
|
|
||||||
quote, _ = stack
|
|
||||||
return stack, concat(quote, expression), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def b(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
b == [i] dip i
|
|
||||||
|
|
||||||
... [P] [Q] b == ... [P] i [Q] i
|
|
||||||
... [P] [Q] b == ... P Q
|
|
||||||
|
|
||||||
'''
|
|
||||||
q, (p, (stack)) = stack
|
|
||||||
return stack, concat(p, concat(q, expression)), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def ii(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
... a [Q] ii
|
|
||||||
------------------
|
|
||||||
... Q a Q
|
|
||||||
|
|
||||||
'''
|
|
||||||
quote, (a, stack) = stack
|
|
||||||
expression = concat(quote, (a, concat(quote, expression)))
|
|
||||||
return stack, expression, dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def dupdip(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
::
|
|
||||||
|
|
||||||
[F] dupdip == dup [F] dip
|
|
||||||
|
|
||||||
... a [F] dupdip
|
|
||||||
... a dup [F] dip
|
|
||||||
... a a [F] dip
|
|
||||||
... a F a
|
|
||||||
|
|
||||||
'''
|
|
||||||
F, stack = stack
|
|
||||||
a = stack[0]
|
|
||||||
return stack, concat(F, (a, expression)), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def infra(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
Accept a quoted program and a list on the stack and run the program
|
|
||||||
with the list as its stack. Does not affect the rest of the stack.
|
|
||||||
::
|
|
||||||
|
|
||||||
... [a b c] [Q] . infra
|
|
||||||
-----------------------------
|
|
||||||
c b a . Q [...] swaack
|
|
||||||
|
|
||||||
'''
|
|
||||||
(quote, (aggregate, stack)) = stack
|
|
||||||
return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def genrec(stack, expression, dictionary):
|
|
||||||
'''
|
|
||||||
General Recursion Combinator.
|
|
||||||
::
|
|
||||||
|
|
||||||
[if] [then] [rec1] [rec2] genrec
|
|
||||||
---------------------------------------------------------------------
|
|
||||||
[if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
|
|
||||||
|
|
||||||
From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
|
|
||||||
"The genrec combinator takes four program parameters in addition to
|
|
||||||
whatever data parameters it needs. Fourth from the top is an if-part,
|
|
||||||
followed by a then-part. If the if-part yields true, then the then-part
|
|
||||||
is executed and the combinator terminates. The other two parameters are
|
|
||||||
the rec1-part and the rec2-part. If the if-part yields false, the
|
|
||||||
rec1-part is executed. Following that the four program parameters and
|
|
||||||
the combinator are again pushed onto the stack bundled up in a quoted
|
|
||||||
form. Then the rec2-part is executed, where it will find the bundled
|
|
||||||
form. Typically it will then execute the bundled form, either with i or
|
|
||||||
with app2, or some other combinator."
|
|
||||||
|
|
||||||
The way to design one of these is to fix your base case [then] and the
|
|
||||||
test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
|
|
||||||
a quotation of the whole function.
|
|
||||||
|
|
||||||
For example, given a (general recursive) function 'F':
|
|
||||||
::
|
|
||||||
|
|
||||||
F == [I] [T] [R1] [R2] genrec
|
|
||||||
|
|
||||||
If the [I] if-part fails you must derive R1 and R2 from:
|
|
||||||
::
|
|
||||||
|
|
||||||
... R1 [F] R2
|
|
||||||
|
|
||||||
Just set the stack arguments in front, and figure out what R1 and R2
|
|
||||||
have to do to apply the quoted [F] in the proper way. In effect, the
|
|
||||||
genrec combinator turns into an ifte combinator with a quoted copy of
|
|
||||||
the original definition in the else-part:
|
|
||||||
::
|
|
||||||
|
|
||||||
F == [I] [T] [R1] [R2] genrec
|
|
||||||
== [I] [T] [R1 [F] R2] ifte
|
|
||||||
|
|
||||||
Primitive recursive functions are those where R2 == i.
|
|
||||||
::
|
|
||||||
|
|
||||||
P == [I] [T] [R] tailrec
|
|
||||||
== [I] [T] [R [P] i] ifte
|
|
||||||
== [I] [T] [R P] ifte
|
|
||||||
|
|
||||||
'''
|
|
||||||
(rec2, (rec1, stack)) = stack
|
|
||||||
(then, (if_, _)) = stack
|
|
||||||
F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
|
|
||||||
else_ = concat(rec1, (F, rec2))
|
|
||||||
return (else_, stack), (S_ifte, expression), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
|
||||||
@FunctionWrapper
|
|
||||||
def map_(S, expression, dictionary):
|
|
||||||
'''
|
|
||||||
Run the quoted program on TOS on the items in the list under it, push a
|
|
||||||
new list with the results in place of the program and original list.
|
|
||||||
'''
|
|
||||||
# (quote, (aggregate, stack)) = S
|
|
||||||
# results = list_to_stack([
|
|
||||||
# joy((term, stack), quote, dictionary)[0][0]
|
|
||||||
# for term in iter_stack(aggregate)
|
|
||||||
# ])
|
|
||||||
# return (results, stack), expression, dictionary
|
|
||||||
(quote, (aggregate, stack)) = S
|
|
||||||
if not aggregate:
|
|
||||||
return (aggregate, stack), expression, dictionary
|
|
||||||
batch = ()
|
|
||||||
for term in iter_stack(aggregate):
|
|
||||||
s = term, stack
|
|
||||||
batch = (s, (quote, (S_infra, (S_first, batch))))
|
|
||||||
stack = (batch, ((), stack))
|
|
||||||
return stack, (S_infra, expression), dictionary
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,9 @@ def joy(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
expr = push_quote(expression) # We keep a stack-of-stacks, see below.
|
expr = push_quote(expression) # We keep a stack-of-stacks, see below.
|
||||||
while expr:
|
while expr:
|
||||||
|
print(
|
||||||
|
f'{stack_to_string(stack)} • {expr_to_string(expr)}'
|
||||||
|
)
|
||||||
term, expr = next_term(expr)
|
term, expr = next_term(expr)
|
||||||
if isinstance(term, Symbol):
|
if isinstance(term, Symbol):
|
||||||
try:
|
try:
|
||||||
|
|
@ -625,6 +628,13 @@ def expression_to_string(expression):
|
||||||
return _stack_to_string(expression, iter_stack)
|
return _stack_to_string(expression, iter_stack)
|
||||||
|
|
||||||
|
|
||||||
|
def expr_to_string(expr):
|
||||||
|
'''
|
||||||
|
Return a "pretty print" string for a stack-of-stacks expression.
|
||||||
|
'''
|
||||||
|
return ' '.join(map(expression_to_string, iter_stack(expr)))
|
||||||
|
|
||||||
|
|
||||||
def _stack_to_string(stack, iterator):
|
def _stack_to_string(stack, iterator):
|
||||||
isnt_stack(stack)
|
isnt_stack(stack)
|
||||||
if not stack: # shortcut
|
if not stack: # shortcut
|
||||||
|
|
@ -1329,13 +1339,675 @@ inscribe(UnaryWrapper(isnt_bool))
|
||||||
inscribe(UnaryWrapper(isnt_stack))
|
inscribe(UnaryWrapper(isnt_stack))
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
███████╗██╗ ██╗████████╗██████╗ █████╗
|
||||||
|
██╔════╝╚██╗██╔╝╚══██╔══╝██╔══██╗██╔══██╗
|
||||||
|
█████╗ ╚███╔╝ ██║ ██████╔╝███████║
|
||||||
|
██╔══╝ ██╔██╗ ██║ ██╔══██╗██╔══██║
|
||||||
|
███████╗██╔╝ ██╗ ██║ ██║ ██║██║ ██║
|
||||||
|
╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def dnd(stack, from_index, to_index):
|
||||||
|
'''
|
||||||
|
Given a stack and two indices return a rearranged stack.
|
||||||
|
First remove the item at from_index and then insert it at to_index,
|
||||||
|
the second index is relative to the stack after removal of the item
|
||||||
|
at from_index.
|
||||||
|
|
||||||
|
This function reuses all of the items and as much of the stack as it
|
||||||
|
can. It's meant to be used by remote clients to support drag-n-drop
|
||||||
|
rearranging of the stack from e.g. the StackListbox.
|
||||||
|
'''
|
||||||
|
assert 0 <= from_index
|
||||||
|
assert 0 <= to_index
|
||||||
|
if from_index == to_index:
|
||||||
|
return stack
|
||||||
|
head, n = [], from_index
|
||||||
|
while True:
|
||||||
|
item, stack = stack
|
||||||
|
n -= 1
|
||||||
|
if n < 0:
|
||||||
|
break
|
||||||
|
head.append(item)
|
||||||
|
assert len(head) == from_index
|
||||||
|
# now we have two cases:
|
||||||
|
diff = from_index - to_index
|
||||||
|
if diff < 0:
|
||||||
|
# from < to
|
||||||
|
# so the destination index is still in the stack
|
||||||
|
while diff:
|
||||||
|
h, stack = stack
|
||||||
|
head.append(h)
|
||||||
|
diff += 1
|
||||||
|
else:
|
||||||
|
# from > to
|
||||||
|
# so the destination is in the head list
|
||||||
|
while diff:
|
||||||
|
stack = head.pop(), stack
|
||||||
|
diff -= 1
|
||||||
|
stack = item, stack
|
||||||
|
while head:
|
||||||
|
stack = head.pop(), stack
|
||||||
|
return stack
|
||||||
|
|
||||||
|
|
||||||
|
def pick(stack, n):
|
||||||
|
'''
|
||||||
|
Return the nth item on the stack.
|
||||||
|
|
||||||
|
:param stack stack: A stack.
|
||||||
|
:param int n: An index into the stack.
|
||||||
|
:raises ValueError: if ``n`` is less than zero.
|
||||||
|
:raises IndexError: if ``n`` is equal to or greater than the length of ``stack``.
|
||||||
|
:rtype: whatever
|
||||||
|
'''
|
||||||
|
if n < 0:
|
||||||
|
raise ValueError
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
item, stack = stack
|
||||||
|
except ValueError:
|
||||||
|
raise IndexError
|
||||||
|
n -= 1
|
||||||
|
if n < 0:
|
||||||
|
break
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def inscribe_(stack, expression, dictionary):
|
||||||
|
'''
|
||||||
|
Create a new Joy function definition in the Joy dictionary. A
|
||||||
|
definition is given as a quote with a name followed by a Joy
|
||||||
|
expression. for example:
|
||||||
|
|
||||||
|
[sqr dup mul] inscribe
|
||||||
|
|
||||||
|
'''
|
||||||
|
(name, body), stack = stack
|
||||||
|
inscribe(Def(name, body), dictionary)
|
||||||
|
return stack, expression, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def getitem(stack):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
getitem == drop first
|
||||||
|
|
||||||
|
Expects an integer and a quote on the stack and returns the item at the
|
||||||
|
nth position in the quote counting from 0.
|
||||||
|
::
|
||||||
|
|
||||||
|
[a b c d] 0 getitem
|
||||||
|
-------------------------
|
||||||
|
a
|
||||||
|
|
||||||
|
'''
|
||||||
|
n, (Q, stack) = stack
|
||||||
|
return pick(Q, n), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def drop(stack):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
drop == [rest] times
|
||||||
|
|
||||||
|
Expects an integer and a quote on the stack and returns the quote with
|
||||||
|
n items removed off the top.
|
||||||
|
::
|
||||||
|
|
||||||
|
[a b c d] 2 drop
|
||||||
|
----------------------
|
||||||
|
[c d]
|
||||||
|
|
||||||
|
'''
|
||||||
|
n, (Q, stack) = stack
|
||||||
|
while n > 0:
|
||||||
|
try:
|
||||||
|
_, Q = Q
|
||||||
|
except ValueError:
|
||||||
|
raise StackUnderflowError
|
||||||
|
n -= 1
|
||||||
|
return Q, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def take(stack):
|
||||||
|
'''
|
||||||
|
Expects an integer and a quote on the stack and returns the quote with
|
||||||
|
just the top n items in reverse order (because that's easier and you can
|
||||||
|
use reverse if needed.)
|
||||||
|
::
|
||||||
|
|
||||||
|
[a b c d] 2 take
|
||||||
|
----------------------
|
||||||
|
[b a]
|
||||||
|
|
||||||
|
'''
|
||||||
|
n, (Q, stack) = stack
|
||||||
|
x = ()
|
||||||
|
while n > 0:
|
||||||
|
try:
|
||||||
|
item, Q = Q
|
||||||
|
except ValueError:
|
||||||
|
raise StackUnderflowError
|
||||||
|
x = item, x
|
||||||
|
n -= 1
|
||||||
|
return x, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def gcd2(stack, expression, dictionary):
|
||||||
|
'''Compiled GCD function.'''
|
||||||
|
(v1, (v2, stack)) = stack
|
||||||
|
tos = True
|
||||||
|
while tos:
|
||||||
|
v3 = v2 % v1
|
||||||
|
tos = v3 > 0
|
||||||
|
(v1, (v2, stack)) = (v3, (v1, stack))
|
||||||
|
return (v2, stack), expression, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def choice(stack):
|
||||||
|
'''
|
||||||
|
Use a Boolean value to select one of two items.
|
||||||
|
::
|
||||||
|
|
||||||
|
A B false choice
|
||||||
|
----------------------
|
||||||
|
A
|
||||||
|
|
||||||
|
|
||||||
|
A B true choice
|
||||||
|
---------------------
|
||||||
|
B
|
||||||
|
|
||||||
|
'''
|
||||||
|
(if_, (then, (else_, stack))) = stack
|
||||||
|
assert isinstance(if_, bool), repr(if_)
|
||||||
|
return then if if_ else else_, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def select(stack):
|
||||||
|
'''
|
||||||
|
Use a Boolean value to select one of two items from a sequence.
|
||||||
|
::
|
||||||
|
|
||||||
|
[A B] false select
|
||||||
|
------------------------
|
||||||
|
A
|
||||||
|
|
||||||
|
|
||||||
|
[A B] true select
|
||||||
|
-----------------------
|
||||||
|
B
|
||||||
|
|
||||||
|
The sequence can contain more than two items but not fewer.
|
||||||
|
Currently Python semantics are used to evaluate the "truthiness" of the
|
||||||
|
Boolean value (so empty string, zero, etc. are counted as false, etc.)
|
||||||
|
'''
|
||||||
|
(flag, (choices, stack)) = stack
|
||||||
|
(else_, (then, _)) = choices
|
||||||
|
return then if flag else else_, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def max_(S):
|
||||||
|
'''Given a list find the maximum.'''
|
||||||
|
tos, stack = S
|
||||||
|
return max(iter_stack(tos)), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def min_(S):
|
||||||
|
'''Given a list find the minimum.'''
|
||||||
|
tos, stack = S
|
||||||
|
return min(iter_stack(tos)), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def sum_(S):
|
||||||
|
'''
|
||||||
|
Given a quoted sequence of numbers return the sum.
|
||||||
|
::
|
||||||
|
|
||||||
|
sum == 0 swap [+] step
|
||||||
|
|
||||||
|
'''
|
||||||
|
tos, stack = S
|
||||||
|
return sum(iter_stack(tos)), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def remove(S):
|
||||||
|
'''
|
||||||
|
Expects an item on the stack and a quote under it and removes that item
|
||||||
|
from the the quote. The item is only removed once. If the list is
|
||||||
|
empty or the item isn't in the list then the list is unchanged.
|
||||||
|
::
|
||||||
|
|
||||||
|
[1 2 3 1] 1 remove
|
||||||
|
------------------------
|
||||||
|
[2 3 1]
|
||||||
|
|
||||||
|
'''
|
||||||
|
(item, (quote, stack)) = S
|
||||||
|
return _remove(item, quote), stack
|
||||||
|
|
||||||
|
|
||||||
|
def _remove(item, quote):
|
||||||
|
try: head, tail = quote
|
||||||
|
except ValueError: return quote
|
||||||
|
return tail if head == item else (head, _remove(item, tail))
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def unique(S):
|
||||||
|
'''Given a list remove duplicate items.'''
|
||||||
|
tos, stack = S
|
||||||
|
I = list(iter_stack(tos))
|
||||||
|
return list_to_stack(sorted(set(I), key=I.index)), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def sort_(S):
|
||||||
|
'''Given a list return it sorted.'''
|
||||||
|
tos, stack = S
|
||||||
|
return list_to_stack(sorted(iter_stack(tos))), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def disenstacken(stack):
|
||||||
|
'''
|
||||||
|
The disenstacken operator expects a list on top of the stack and makes that
|
||||||
|
the stack discarding the rest of the stack.
|
||||||
|
'''
|
||||||
|
return stack[0]
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def reverse(S):
|
||||||
|
'''
|
||||||
|
Reverse the list on the top of the stack.
|
||||||
|
::
|
||||||
|
|
||||||
|
reverse == [] swap shunt
|
||||||
|
'''
|
||||||
|
(tos, stack) = S
|
||||||
|
res = ()
|
||||||
|
for term in iter_stack(tos):
|
||||||
|
res = term, res
|
||||||
|
return res, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def shunt(stack):
|
||||||
|
'''
|
||||||
|
Like concat but reverses the top list into the second.
|
||||||
|
::
|
||||||
|
|
||||||
|
shunt == [swons] step == reverse swap concat
|
||||||
|
|
||||||
|
[a b c] [d e f] shunt
|
||||||
|
---------------------------
|
||||||
|
[f e d a b c]
|
||||||
|
|
||||||
|
'''
|
||||||
|
(tos, (second, stack)) = stack
|
||||||
|
while tos:
|
||||||
|
term, tos = tos
|
||||||
|
second = term, second
|
||||||
|
return second, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def zip_(S):
|
||||||
|
'''
|
||||||
|
Replace the two lists on the top of the stack with a list of the pairs
|
||||||
|
from each list. The smallest list sets the length of the result list.
|
||||||
|
'''
|
||||||
|
(tos, (second, stack)) = S
|
||||||
|
accumulator = [
|
||||||
|
(a, (b, ()))
|
||||||
|
for a, b in zip(iter_stack(tos), iter_stack(second))
|
||||||
|
]
|
||||||
|
return list_to_stack(accumulator), stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def succ(S):
|
||||||
|
'''Increment TOS.'''
|
||||||
|
(tos, stack) = S
|
||||||
|
return tos + 1, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def pred(S):
|
||||||
|
'''Decrement TOS.'''
|
||||||
|
(tos, stack) = S
|
||||||
|
return tos - 1, stack
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
@SimpleFunctionWrapper
|
||||||
|
def pm(stack):
|
||||||
|
'''
|
||||||
|
Plus or minus
|
||||||
|
::
|
||||||
|
|
||||||
|
a b pm
|
||||||
|
-------------
|
||||||
|
a+b a-b
|
||||||
|
|
||||||
|
'''
|
||||||
|
a, (b, stack) = stack
|
||||||
|
p, m, = b + a, b - a
|
||||||
|
return m, (p, stack)
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def sharing(stack, expression, dictionary):
|
||||||
|
'''Print redistribution information.'''
|
||||||
|
print("You may convey verbatim copies of the Program's source code as"
|
||||||
|
' you receive it, in any medium, provided that you conspicuously'
|
||||||
|
' and appropriately publish on each copy an appropriate copyright'
|
||||||
|
' notice; keep intact all notices stating that this License and'
|
||||||
|
' any non-permissive terms added in accord with section 7 apply'
|
||||||
|
' to the code; keep intact all notices of the absence of any'
|
||||||
|
' warranty; and give all recipients a copy of this License along'
|
||||||
|
' with the Program.'
|
||||||
|
' You should have received a copy of the GNU General Public License'
|
||||||
|
' along with Thun. If not see <http://www.gnu.org/licenses/>.')
|
||||||
|
return stack, expression, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def warranty(stack, expression, dictionary):
|
||||||
|
'''Print warranty information.'''
|
||||||
|
print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
|
||||||
|
' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
|
||||||
|
' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
|
||||||
|
' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
|
||||||
|
' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
|
||||||
|
' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
|
||||||
|
' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
|
||||||
|
' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
|
||||||
|
' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
|
||||||
|
return stack, expression, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def x(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
x == dup i
|
||||||
|
|
||||||
|
... [Q] x = ... [Q] dup i
|
||||||
|
... [Q] x = ... [Q] [Q] i
|
||||||
|
... [Q] x = ... [Q] Q
|
||||||
|
|
||||||
|
'''
|
||||||
|
quote, _ = stack
|
||||||
|
isnt_stack(quote)
|
||||||
|
return stack, push_quote(quote, expr), dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def b(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
b == [i] dip i
|
||||||
|
|
||||||
|
... [P] [Q] b == ... [P] i [Q] i
|
||||||
|
... [P] [Q] b == ... P Q
|
||||||
|
|
||||||
|
'''
|
||||||
|
q, (p, (stack)) = stack
|
||||||
|
isnt_stack(q)
|
||||||
|
isnt_stack(p)
|
||||||
|
expr = push_quote(q, expr)
|
||||||
|
expr = push_quote(p, expr)
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def ii(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
... a [Q] ii
|
||||||
|
------------------
|
||||||
|
... Q a Q
|
||||||
|
|
||||||
|
'''
|
||||||
|
quote, (a, stack) = stack
|
||||||
|
isnt_stack(quote)
|
||||||
|
expr = push_quote((a, quote), expr)
|
||||||
|
expr = push_quote(quote, expr)
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def dupdip(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
::
|
||||||
|
|
||||||
|
[F] dupdip == dup [F] dip
|
||||||
|
|
||||||
|
... a [F] dupdip
|
||||||
|
... a dup [F] dip
|
||||||
|
... a a [F] dip
|
||||||
|
... a F a
|
||||||
|
|
||||||
|
'''
|
||||||
|
quote, stack = stack
|
||||||
|
isnt_stack(quote)
|
||||||
|
a = stack[0]
|
||||||
|
expr = push_quote((a, ()), expr)
|
||||||
|
expr = push_quote(quote, expr)
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
S_swaack = Symbol('swaack')
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def infra(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
Accept a quoted program and a list on the stack and run the program
|
||||||
|
with the list as its stack. Does not affect the rest of the stack.
|
||||||
|
::
|
||||||
|
|
||||||
|
... [a b c] [Q] . infra
|
||||||
|
-----------------------------
|
||||||
|
c b a . Q [...] swaack
|
||||||
|
|
||||||
|
'''
|
||||||
|
quote, aggregate, stack = get_n_items(2, stack)
|
||||||
|
isnt_stack(quote)
|
||||||
|
isnt_stack(aggregate)
|
||||||
|
expr = push_quote((stack, (S_swaack, ())), expr)
|
||||||
|
expr = push_quote(quote, expr)
|
||||||
|
return aggregate, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
S_genrec = Symbol('genrec')
|
||||||
|
S_ifte = Symbol('ifte')
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def genrec(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
General Recursion Combinator.
|
||||||
|
::
|
||||||
|
|
||||||
|
[if] [then] [rec1] [rec2] genrec
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
|
||||||
|
|
||||||
|
From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
|
||||||
|
"The genrec combinator takes four program parameters in addition to
|
||||||
|
whatever data parameters it needs. Fourth from the top is an if-part,
|
||||||
|
followed by a then-part. If the if-part yields true, then the then-part
|
||||||
|
is executed and the combinator terminates. The other two parameters are
|
||||||
|
the rec1-part and the rec2-part. If the if-part yields false, the
|
||||||
|
rec1-part is executed. Following that the four program parameters and
|
||||||
|
the combinator are again pushed onto the stack bundled up in a quoted
|
||||||
|
form. Then the rec2-part is executed, where it will find the bundled
|
||||||
|
form. Typically it will then execute the bundled form, either with i or
|
||||||
|
with app2, or some other combinator."
|
||||||
|
|
||||||
|
The way to design one of these is to fix your base case [then] and the
|
||||||
|
test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
|
||||||
|
a quotation of the whole function.
|
||||||
|
|
||||||
|
For example, given a (general recursive) function 'F':
|
||||||
|
::
|
||||||
|
|
||||||
|
F == [I] [T] [R1] [R2] genrec
|
||||||
|
|
||||||
|
If the [I] if-part fails you must derive R1 and R2 from:
|
||||||
|
::
|
||||||
|
|
||||||
|
... R1 [F] R2
|
||||||
|
|
||||||
|
Just set the stack arguments in front, and figure out what R1 and R2
|
||||||
|
have to do to apply the quoted [F] in the proper way. In effect, the
|
||||||
|
genrec combinator turns into an ifte combinator with a quoted copy of
|
||||||
|
the original definition in the else-part:
|
||||||
|
::
|
||||||
|
|
||||||
|
F == [I] [T] [R1] [R2] genrec
|
||||||
|
== [I] [T] [R1 [F] R2] ifte
|
||||||
|
|
||||||
|
Primitive recursive functions are those where R2 == i.
|
||||||
|
::
|
||||||
|
|
||||||
|
P == [I] [T] [R] tailrec
|
||||||
|
== [I] [T] [R [P] i] ifte
|
||||||
|
== [I] [T] [R P] ifte
|
||||||
|
|
||||||
|
'''
|
||||||
|
rec2, rec1, then, if_, stack = get_n_items(4, stack)
|
||||||
|
isnt_stack(if_)
|
||||||
|
isnt_stack(then)
|
||||||
|
isnt_stack(rec1)
|
||||||
|
isnt_stack(rec2)
|
||||||
|
F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
|
||||||
|
else_ = concat(rec1, (F, rec2))
|
||||||
|
stack = (else_, (then, (if_, stack)))
|
||||||
|
expr = push_quote((S_ifte, ()), expr)
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
S_infra = Symbol('infra')
|
||||||
|
S_first = Symbol('first')
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def map_(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
Run the quoted program on TOS on the items in the list under it, push a
|
||||||
|
new list with the results in place of the program and original list.
|
||||||
|
'''
|
||||||
|
quote, aggregate, stack = get_n_items(2, stack)
|
||||||
|
isnt_stack(quote)
|
||||||
|
isnt_stack(aggregate)
|
||||||
|
if not aggregate:
|
||||||
|
return (aggregate, stack), expr, dictionary
|
||||||
|
batch = ()
|
||||||
|
for term in iter_stack(aggregate):
|
||||||
|
s = term, stack
|
||||||
|
batch = (s, (quote, (S_infra, (S_first, batch))))
|
||||||
|
stack = (batch, ((), stack))
|
||||||
|
expr = push_quote((S_infra, ()), expr)
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
|
S_primrec = Symbol('primrec')
|
||||||
|
|
||||||
|
|
||||||
|
@inscribe
|
||||||
|
def primrec(stack, expr, dictionary):
|
||||||
|
'''
|
||||||
|
From the "Overview of the language JOY":
|
||||||
|
|
||||||
|
> The primrec combinator expects two quoted programs in addition to a
|
||||||
|
data parameter. For an integer data parameter it works like this: If
|
||||||
|
the data parameter is zero, then the first quotation has to produce
|
||||||
|
the value to be returned. If the data parameter is positive then the
|
||||||
|
second has to combine the data parameter with the result of applying
|
||||||
|
the function to its predecessor.::
|
||||||
|
|
||||||
|
5 [1] [*] primrec
|
||||||
|
|
||||||
|
> Then primrec tests whether the top element on the stack (initially
|
||||||
|
the 5) is equal to zero. If it is, it pops it off and executes one of
|
||||||
|
the quotations, the [1] which leaves 1 on the stack as the result.
|
||||||
|
Otherwise it pushes a decremented copy of the top element and
|
||||||
|
recurses. On the way back from the recursion it uses the other
|
||||||
|
quotation, [*], to multiply what is now a factorial on top of the
|
||||||
|
stack by the second element on the stack.::
|
||||||
|
|
||||||
|
n [Base] [Recur] primrec
|
||||||
|
|
||||||
|
0 [Base] [Recur] primrec
|
||||||
|
------------------------------
|
||||||
|
Base
|
||||||
|
|
||||||
|
n [Base] [Recur] primrec
|
||||||
|
------------------------------------------ n > 0
|
||||||
|
n (n-1) [Base] [Recur] primrec Recur
|
||||||
|
|
||||||
|
'''
|
||||||
|
recur, base, n, stack = get_n_items(3, stack)
|
||||||
|
isnt_stack(recur)
|
||||||
|
isnt_stack(base)
|
||||||
|
if n <= 0:
|
||||||
|
expr = push_quote(base, expr)
|
||||||
|
else:
|
||||||
|
expr = push_quote(recur, expr)
|
||||||
|
expr = push_quote((S_primrec, ()), expr)
|
||||||
|
stack = recur, (base, (n - 1, (n, stack)))
|
||||||
|
return stack, expr, dictionary
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
J = interp if '-q' in sys.argv else repl
|
J = interp if '-q' in sys.argv else repl
|
||||||
dictionary = initialize()
|
dictionary = initialize()
|
||||||
Def.load_definitions(__doc__.splitlines(), dictionary)
|
Def.load_definitions(__doc__.splitlines(), dictionary)
|
||||||
try:
|
## try:
|
||||||
stack = J(dictionary=dictionary)
|
## stack = J(dictionary=dictionary)
|
||||||
except SystemExit:
|
## except SystemExit:
|
||||||
pass
|
## pass
|
||||||
|
stack, _ = run("5 [1] [*] primrec", (), dictionary)
|
||||||
|
print(stack_to_string(stack), '•')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue