minor cleanup

This commit is contained in:
sforman
2023-07-27 09:55:02 -07:00
parent ef504aa1c4
commit 08262ac861
9 changed files with 0 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
def f(string, start=0, acc=[]):
if start >= len(string): return acc
if '[' == string[start]: return [1] + f(string, start+1, acc)
if ']' == string[start]: return [0] + f(string, start+1, acc)
if ' ' == string[start]: return f(string, start+1, acc)
symbol, n = bar(string, start, start)
return [symbol] + f(string, n, acc)
def bar(string, start, end):
if end >= len(string) or string[end] in '[] ':
return string[start:end], end
return bar(string, start, end+1)
print(f("1[2[] 3]4"))
+106
View File
@@ -0,0 +1,106 @@
import multiprocessing as mp
from multiprocessing.connection import wait
from joy import (
default_defs,
initialize,
joy,
repl,
get_n_items,
isnt_stack,
inscribe,
)
class ForkException(Exception): pass
def fork_joy(send, stack, expr, dictionary):
try:
stack, dictionary = joy(stack, expr, dictionary)
result, _ = get_n_items(1, stack)
except Exception as err:
send.send((True, repr(err)))
else:
send.send((False, result))
@inscribe
def fork(stack, expr, dictionary):
'''
Take two quoted programs from the stack and
run them in parallel.
fork ≡ [i] app2
'''
q, p, stack = get_n_items(2, stack)
isnt_stack(q)
isnt_stack(p)
q_pipe_recv, q_pipe_send = mp.Pipe(False)
p_pipe_recv, p_pipe_send = mp.Pipe(False)
P = mp.Process(
target=fork_joy,
args=(p_pipe_send, stack, p, dictionary),
)
Q = mp.Process(
target=fork_joy,
args=(q_pipe_send, stack, q, dictionary),
)
P.start()
Q.start()
# Ensure "wait() will promptly report the readable end as being ready."
# See docs for multiprocessing.connection.wait().
q_pipe_send.close()
p_pipe_send.close()
readers = [q_pipe_recv, p_pipe_recv]
ready = wait(readers)
# We have one or two results or errors
if len(ready) == 1:
read_me = ready[0]
order = read_me is q_pipe_recv
wait_me = readers[order]
wait_proc = (Q, P)[order]
stack = one_result(stack, order, read_me, wait_me, wait_proc)
else: # both results/errors
p_err, p_result = p_pipe_recv.recv()
q_err, q_result = q_pipe_recv.recv()
if p_err:
raise ForkException(p_result)
if q_err:
raise ForkException(q_result)
stack = (q_result, (p_result, stack))
return stack, expr, dictionary
def one_result(stack, order, read_me, wait_me, wait_proc):
err, result = read_me.recv()
read_me.close()
if err:
wait_me.close()
wait_proc.kill()
raise ForkException(result)
wait([wait_me])
err, second_result = wait_me.recv()
wait_me.close()
if err:
raise ForkException(second_result)
if order:
stack = (result, (second_result, stack))
else:
stack = (second_result, (result, stack))
return stack
if __name__ == '__main__':
mp.set_start_method('fork')
dictionary = initialize()
default_defs(dictionary)
try:
stack = repl(dictionary=dictionary)
except SystemExit:
pass
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014, 2015, 2017 Simon Forman
#
# This file is part of Thun
#
# Thun is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Thun is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Thun. If not see <http://www.gnu.org/licenses/>.
#
import argparse, io, pkg_resources
from .library import initialize, inscribe, Def
from .joy import repl, interp
from .utils.pretty_print import trace
inscribe(trace)
argparser = argparse.ArgumentParser(
prog='thun',
description='Joy Interpreter',
)
argparser.add_argument(
'-d', '--defs',
action='append',
default=[
io.TextIOWrapper(
pkg_resources.resource_stream(__name__, 'defs.txt'),
encoding='UTF_8',
)
],
type=argparse.FileType('r', encoding='UTF_8'),
help=(
'Add additional definition files.'
' Can be used more than once.'
),
)
argparser.add_argument(
'-q', '--quiet',
help='Don\'t show the prompt.',
default=False,
action='store_true',
)
args = argparser.parse_args()
if args.quiet:
j = interp
else:
j = repl
print('''\
Thun - Copyright © 2017 Simon Forman
This program comes with ABSOLUTELY NO WARRANTY; for details type "warranty".
This is free software, and you are welcome to redistribute it
under certain conditions; type "sharing" for details.
Type "words" to see a list of all words, and "[<name>] help" to print the
docs for a word.
''')
dictionary=initialize()
for def_stream in args.defs:
Def.load_definitions(def_stream, dictionary)
stack = j(dictionary=dictionary)
+116
View File
@@ -0,0 +1,116 @@
-- 1 -
? dup bool
&& nulco [nullary [false]] dip branch
++ 1 +
|| nulco [nullary] dip [true] branch
!- 0 >=
<{} [] swap
<<{} [] rollup
abs dup 0 < [] [neg] branch
anamorphism [pop []] swap [dip swons] genrec
app1 grba infrst
app2 [grba swap grba swap] dip [infrst] cons ii
app3 3 appN
appN [grabN] codi map disenstacken
at drop first
average [sum] [size] cleave /
b [i] dip i
binary unary popd
ccccons ccons ccons
ccons cons cons
clear stack bool [pop stack bool] loop
cleave fork popdd
clop cleave popdd
codi cons dip
codireco codi reco
dinfrirst dip infrst
dipd [dip] codi
disenstacken ? [uncons ?] loop pop
down_to_zero [0 >] [dup --] while
drop [rest] times
dupd [dup] dip
dupdd [dup] dipd
dupdip dupd dip
dupdipd dup dipd
enstacken stack [clear] dip
flatten <{} [concat] step
fork [i] app2
fourth rest third
gcd true [tuck mod dup 0 >] loop pop
genrec [[genrec] ccccons] nullary swons concat ifte
grabN <{} [cons] times
grba [stack popd] dip
hypot [sqr] ii + sqrt
ifte [nullary] dipd swap branch
ii [dip] dupdip i
infra swons swaack [i] dip swaack
infrst infra first
make_generator [codireco] ccons
mod %
neg 0 swap -
not [true] [false] branch
nulco [nullary] cons
nullary [stack] dinfrirst
of swap at
pam [i] map
pm [+] [-] clop
popd [pop] dip
popdd [pop] dipd
popop pop pop
popopop pop popop
popopd [popop] dip
popopdd [popop] dipd
product 1 swap [*] step
quoted [unit] dip
range [0 <=] [1 - dup] anamorphism
range_to_zero unit [down_to_zero] infra
reco rest cons
rest [pop] infra
reverse <{} shunt
roll> swap swapd
roll< swapd swap
rollup roll>
rolldown roll<
rrest rest rest
run <{} infra
second rest first
shift uncons [swons] dip
shunt [swons] step
size [pop ++] step_zero
spiral_next [[[abs] ii <=] [[<>] [pop !-] ||] &&] [[!-] [[++]] [[--]] ifte dip] [[pop !-] [--] [++] ifte] ifte
split_at [drop] [take] clop
split_list [take reverse] [drop] clop
sqr dup *
stackd [stack] dip
step_zero 0 roll> step
sum [+] step_zero
swapd [swap] dip
swons swap cons
swoncat swap concat
sqr dup mul
tailrec [i] genrec
take [] roll> [shift] times pop
ternary binary popd
third rest second
tuck dup swapd
unary nullary popd
uncons [first] [rest] cleave
unit [] cons
unquoted [i] dip
unswons uncons swap
while swap nulco dupdipd concat loop
x dup i
step [_step0] x
_step0 _step1 [popopop] [_stept] branch
_step1 [?] dipd roll<
_stept [uncons] dipd [dupdipd] dip x
times [_times0] x
_times0 _times1 [popopop] [_timest] branch
_times1 [dup 0 >] dipd roll<
_timest [[--] dip dupdipd] dip x
map [_map0] cons [[] [_map?] [_mape]] dip tailrec
_map? pop bool not
_mape popd reverse
_map0 [_map1] dipd _map2
_map1 stackd shift
_map2 [infrst] cons dipd roll< swons
+126
View File
@@ -0,0 +1,126 @@
ALIASES = (
('bool', ['truthy']),
('mod', ['%', 'rem', 'remainder', 'modulus']),
('getitem', ['pick', 'at']),
('xor', ['^']),
)
#
# § 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 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
# 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
@@ -0,0 +1,40 @@
from collections import namedtuple
from re import compile as RE
Snippet = namedtuple('Snippet', 'sha offset length')
pat = (
'{'
'\s*'
'(?P<sha>[a-f0-9]+)'
'\s+'
'(?P<offset>\d+)'
'\s+'
'(?P<length>\d+)'
'\s*'
'}'
)
_PAT = RE(pat)
def to_string(snip):
return _ts(*snip)
def _ts(sha, offset, length):
return f'{{{sha.decode("ascii")} {offset} {length}}}'
def from_string(text):
m = _PAT.match(text)
if not m:
raise ValueError
return _fs(**m.groupdict())
def _fs(sha, offset, length):
return Snippet(sha.encode('ascii'), int(offset), int(length))
+35
View File
@@ -0,0 +1,35 @@
from pathlib import Path
import atexit
import os
import readline
NAMES = set(dir(__builtins__))
# Note that we WANT names here to be part of the closure of the
# function so it retains its value from call to call.
def completer(text, state, names=[]):
if 0 == state:
names[:] = filter(lambda name: name.startswith(text), NAMES)
try:
return names[state]
except IndexError:
return
readline.set_completer(completer)
readline.parse_and_bind('TAB: complete')
hfname = str(Path.home() / '.joy_history')
try:
readline.read_history_file(hfname)
except FileNotFoundError:
pass
atexit.register(readline.write_history_file, hfname)
while True:
try:
i = input('> ')
except (EOFError, KeyboardInterrupt):
i = ''
break
print(i)