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'•']),
|
||||
)
|
||||
|
||||
@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):
|
||||
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
|
||||
#
|
||||
|
|
@ -406,194 +57,9 @@ S_swaack = Symbol('swaack')
|
|||
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
|
||||
|
|
|
|||
|
|
@ -307,6 +307,9 @@ def joy(stack, expression, dictionary):
|
|||
'''
|
||||
expr = push_quote(expression) # We keep a stack-of-stacks, see below.
|
||||
while expr:
|
||||
print(
|
||||
f'{stack_to_string(stack)} • {expr_to_string(expr)}'
|
||||
)
|
||||
term, expr = next_term(expr)
|
||||
if isinstance(term, Symbol):
|
||||
try:
|
||||
|
|
@ -625,6 +628,13 @@ def expression_to_string(expression):
|
|||
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):
|
||||
isnt_stack(stack)
|
||||
if not stack: # shortcut
|
||||
|
|
@ -1329,13 +1339,675 @@ inscribe(UnaryWrapper(isnt_bool))
|
|||
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__':
|
||||
import sys
|
||||
|
||||
J = interp if '-q' in sys.argv else repl
|
||||
dictionary = initialize()
|
||||
Def.load_definitions(__doc__.splitlines(), dictionary)
|
||||
try:
|
||||
stack = J(dictionary=dictionary)
|
||||
except SystemExit:
|
||||
pass
|
||||
## try:
|
||||
## stack = J(dictionary=dictionary)
|
||||
## except SystemExit:
|
||||
## pass
|
||||
stack, _ = run("5 [1] [*] primrec", (), dictionary)
|
||||
print(stack_to_string(stack), '•')
|
||||
|
|
|
|||
Loading…
Reference in New Issue