Move Python impl to impls dir.

This commit is contained in:
Simon Forman
2022-01-24 19:02:47 -08:00
parent 530deab19d
commit d42a8dfc0c
17 changed files with 0 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
include archive/*
include LICENSE
include joy/defs.txt
recursive-include joy *.py
+17
View File
@@ -0,0 +1,17 @@
# My make-fu style is old and tired. I just want to have a few helper commands.
WEBSERVER = [email protected]
.PHONY: clean docs upload-docs
clean:
$(RM) -r Thun.egg-info/ dist/ build/ __pycache__/
find . -name '*.pyc' | xargs $(RM)
docs:
cd ./docs && make && make mov && cd ./sphinx_docs && make html
upload-docs: docs
ssh $(WEBSERVER) /home/users/s/sf/sforman/backup-and-remove-htdocs
rsync -rv --progress ./docs/sphinx_docs/_build/html/ $(WEBSERVER):/home/groups/j/jo/joypy/htdocs/
+60
View File
@@ -0,0 +1,60 @@
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • [[] ccons] step i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] • step i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] • [_step0] x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • _step0 i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • _step1 [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • [?] dipd roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] [?] • dipd roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • ? [[] ccons] [_step0] roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • dup bool [[] ccons] [_step0] roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [dip dip infra dip infra dip infra] • bool [[] ccons] [_step0] roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] true • [[] ccons] [_step0] roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] true [[] ccons] • [_step0] roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] true [[] ccons] [_step0] • roll< [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] true • [popopop] [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] true [popopop] • [_stept] branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] true [popopop] [_stept] • branch i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • _stept i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] • [uncons] dipd [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[] ccons] [_step0] [uncons] • dipd [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • uncons [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • [first] [rest] cleave [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] • [rest] cleave [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] [rest] • cleave [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] [rest] • fork popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] [rest] • [i] app2 popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] [rest] [i] • app2 popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [[first] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] • infra first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [first] • i [[dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • first [[dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] dip • [[dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] dip [[dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] • swaack first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [dip [dup mul] [1 [2 [3 4 25 6] 7] 8]] • first [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip • [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] • [i] infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [[rest] [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] [i] • infra first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] [rest] • i [dip [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • rest [dip [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip infra dip infra dip infra] • [dip [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] swaack first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip infra dip infra dip infra] [dip [dip dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] • swaack first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [[dip infra dip infra dip infra] [dup mul] [1 [2 [3 4 25 6] 7] 8]] • first popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [dip infra dip infra dip infra] • popdd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [dip infra dip infra dip infra] • [pop] dipd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] dip [dip infra dip infra dip infra] [pop] • dipd [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] [dip dip infra dip infra dip infra] • pop dip [dip infra dip infra dip infra] [[] ccons] [_step0] [dupdipd] dip x i
[1 [2 [3 4 25 6] 7] 8] [dup mul] • dip [dip infra dip infra dip infra] [[] ccons] [_step0] [dupdipd] dip x i
• dup mul [1 [2 [3 4 25 6] 7] 8] [dip infra dip infra dip infra] [[] ccons] [_step0] [dupdipd] dip x i
-------------------------------------------------------------------------
not enough values to unpack (expected 2, got 0)
?
_step0
_step1
_stept
cleave
fork
grba
infrst
popdd
step
uncons
+226
View File
@@ -0,0 +1,226 @@
'''
In order to debug the problem I'm having with definitions breaking a
zipper expression I need to load a system, load the defs into a dictionary,
and then run the expression and see if it raises an exception, substituting
definitions one-at-a-time until I find the one that breaks it.
'''
from joy.library import default_defs, initialize, inscribe, Def
from joy.joy import joy
from joy.parser import text_to_expression
from joy.utils.pretty_print import trace
from joy.utils.stack import stack_to_string
inscribe(trace)
dictionary = initialize()
defs = {}
default_defs(defs)
expression = text_to_expression(
'[1 [2 [3 4 25 6] 7] 8]'
'[dup mul]'
'[dip dip infra dip infra dip infra]'
## '[[] ccons] step i'
# to trace replace last line above with:
'[[[] ccons] step i]'
'trace'
)
expected_result = '[1 [2 [3 4 625 6] 7] 8]'
expected_result_as_stack = text_to_expression(expected_result)
def test_expr(ds, e=expression):
'''
Run the test expression with the defs in ds.
Return the resulting stack as a string or the
exception raised if any.
'''
D = dictionary.copy()
D.update(ds)
try:
stack, _, _ = joy((), e, D)
except Exception as err:
return err
return stack_to_string(stack)
# The problem is that it works with the built-ins:
##print(test_expr({}))
# Results:
# [1 [2 [3 4 625 6] 7] 8]
#
# But not with the definitions:
##print(test_expr(defs))
# Results:
# not enough values to unpack (expected 2, got 0)
#
# This obviously sucks and is bad. :(
# First, because it's easy, let's try adding single defs
# one-at-a-time to the dictionary and see if any one of
# them breaks it.
# Only the defs that shadow the built-ins could be the problem:
candidates = set(dictionary) & set(defs)
##for def_name in candidates:
## stack_str = test_expr({def_name: defs[def_name]})
## if stack_str != expected_result:
## print(def_name, 'failed!')
## print(stack_str)
# Results:
# step failed!
# _step0
# Ah yes, step's definition has parts (and dependencies).
step_defs = {
d: defs[d]
for d in defs
if 'step' in d
}
for name in ('?', 'dupdipd', 'popopop'):
step_defs[name] = defs[name]
##print(sorted(step_defs))
##print(test_expr(step_defs))
# Results:
# [1 [2 [3 4 625 6] 7] 8]
#
# So it's not step by itself, it's some combination of defintions
# that is causing the bug.
its_is_probably_not = set('''
dipd roll< uncons
'''.split())
sus_defs = {
def_name: defs[def_name]
for def_name in defs
if (def_name in candidates
or def_name in step_defs)
and def_name not in its_is_probably_not
}
##print()
##print(test_expr(sus_defs))
d = step_defs.copy()
d['uncons'] = defs['uncons']
d['cleave'] = defs['cleave']
d['fork'] = defs['fork']
##print(test_expr(d))
CD = {
name: defs[name]
for name in candidates
}
CD.update(step_defs)
CD['codi'] = defs['codi']
CD['swapd'] = defs['swapd']
CD['cleave'] = defs['cleave']
CD['fork'] = defs['fork']
CD['grba'] = defs['grba']
CD['infrst'] = defs['infrst']
##print(test_expr(CD))
##print(sorted(CD))
# [++, --, '?', _step0, _step1, _stept, abs, app1, app2, app3, at, b, ccons, clear, 'cleave', 'codi', dipd, disenstacken, drop, dupd, dupdd, dupdip, 'dupdipd', 'fork', fourth, genrec, 'grba', ii, infra, 'infrst', map, mod, neg, not, pm, popd, popdd, popop, popopd, popopdd, 'popopop', rest, reverse, roll<, roll>, rolldown, rollup, rrest, second, shunt, step, step_zero, sum, 'swapd', swons, take, third, times, tuck, uncons, unit, unswons, x]
del CD['++']
del CD['--']
##del CD['?']
##del CD['_step0']
##del CD['_step1']
##del CD['_stept']
del CD['abs']
del CD['app1']
del CD['app2']
del CD['app3']
del CD['at']
del CD['b']
del CD['ccons']
del CD['clear']
##del CD['cleave'] # <-- dep
del CD['codi']
del CD['dipd']
del CD['disenstacken']
del CD['drop']
del CD['dupd']
del CD['dupdd']
del CD['dupdip']
del CD['dupdipd']
##del CD['fork'] # <-- dep
del CD['fourth']
del CD['genrec']
##del CD['grba'] # <-- dep
del CD['ii']
del CD['infra']
##del CD['infrst'] # <-- dep
del CD['map']
del CD['mod']
del CD['neg']
del CD['not']
del CD['pm']
del CD['popd']
##del CD['popdd'] # <-- !!!!!
del CD['popop']
del CD['popopd']
del CD['popopdd']
del CD['popopop']
del CD['rest']
del CD['reverse']
del CD['roll<']
del CD['roll>']
del CD['rolldown']
del CD['rollup']
del CD['rrest']
del CD['second']
del CD['shunt']
##del CD['step'] # <-- !!!!!
del CD['step_zero']
del CD['sum']
del CD['swapd']
del CD['swons']
del CD['take']
del CD['third']
del CD['times']
del CD['tuck']
##del CD['uncons'] # <-- popopop !?
del CD['unit']
del CD['unswons']
del CD['x']
##print(test_expr(CD))
##for n in sorted(CD):
## print(n)
## ?
## _step0
## _step1
## _stept
## cleave
## fork
## grba
## infrst
## popdd
## step
## uncons
expr = text_to_expression('[[dip]] [uncons] trace')
print(test_expr(CD, e=expr))
##print()
##print(set(dictionary) & set(defs))
+75
View File
@@ -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
+149
View File
@@ -0,0 +1,149 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014, 2015, 2017, 2018 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/>.
#
'''
This module implements an interpreter for a dialect of Joy that
attempts to stay very close to the spirit of Joy but does not precisely
match the behaviour of the original version(s) written in C.
'''
from builtins import input
from traceback import print_exc
from .parser import text_to_expression, ParseError, Symbol
from .utils.stack import stack_to_string
from .utils.errors import (
NotAListError,
NotAnIntError,
StackUnderflowError,
)
class UnknownSymbolError(KeyError): pass
def joy(stack, expression, dictionary, viewer=None):
'''Evaluate a Joy expression on a stack.
This function iterates through a sequence of terms which are either
literals (strings, numbers, sequences of terms) or function symbols.
Literals are put onto the stack and functions are looked up in the
dictionary and executed.
The viewer is a function that is called with the stack and expression
on every iteration, its return value is ignored.
:param stack stack: The stack.
:param stack expression: The expression to evaluate.
:param dict dictionary: A ``dict`` mapping names to Joy functions.
:param function viewer: Optional viewer function.
:rtype: (stack, (), dictionary)
'''
while expression:
if viewer: viewer(stack, expression)
term, expression = expression
if isinstance(term, Symbol):
if term not in dictionary:
raise UnknownSymbolError(term)
func = dictionary[term]
stack, expression, dictionary = func(stack, expression, dictionary)
else:
stack = term, stack
if viewer: viewer(stack, expression)
return stack, expression, dictionary
def run(text, stack, dictionary, viewer=None):
'''
Return the stack resulting from running the Joy code text on the stack.
:param str text: Joy code.
:param stack stack: The stack.
:param dict dictionary: A ``dict`` mapping names to Joy functions.
:param function viewer: Optional viewer function.
:rtype: (stack, (), dictionary)
'''
expression = text_to_expression(text)
return joy(stack, expression, dictionary, viewer)
def repl(stack=(), dictionary=None):
'''
Read-Evaluate-Print Loop
Accept input and run it on the stack, loop.
:param stack stack: The stack.
:param dict dictionary: A ``dict`` mapping names to Joy functions.
:rtype: stack
'''
if dictionary is None:
dictionary = {}
try:
while True:
print()
print(stack_to_string(stack), '<-top')
print()
try:
text = input('joy? ')
except (EOFError, KeyboardInterrupt):
break
try:
stack, _, dictionary = run(text, stack, dictionary)
except:
print_exc()
except:
print_exc()
print()
return stack
def interp(stack=(), dictionary=None):
'''
Simple REPL with no extra output, suitable for use in scripts.
'''
if dictionary is None:
dictionary = {}
try:
while True:
try:
text = input()
except (EOFError, KeyboardInterrupt):
break
try:
stack, _, dictionary = run(text, stack, dictionary)
except UnknownSymbolError as sym:
print('Unknown:', sym)
except StackUnderflowError as e:
print(e) # 'Not enough values on stack.'
except NotAnIntError:
print('Not an integer.')
except NotAListError as e:
print(e) # 'Not a list.'
except:
print_exc()
print(stack_to_string(stack))
except:
print_exc()
return stack
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014, 2015, 2016, 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/>.
#
'''
This module exports a single function for converting text to a joy
expression as well as a single Symbol class and a single Exception type.
The Symbol string class is used by the interpreter to recognize literals
by the fact that they are not Symbol objects.
A crude grammar::
joy = term*
term = int | float | string | '[' joy ']' | symbol
A Joy expression is a sequence of zero or more terms. A term is a
literal value (integer, float, string, or Joy expression) or a function
symbol. Function symbols are unquoted strings and cannot contain square
brackets. Terms must be separated by blanks, which can be omitted
around square brackets.
'''
from re import Scanner
from .utils.stack import list_to_stack
BRACKETS = r'\[|\]'
BLANKS = r'\s+'
WORDS = r'[^[\]\s]+'
token_scanner = Scanner([
(BRACKETS, lambda _, token: token),
(BLANKS, None),
(WORDS, lambda _, token: token),
])
class Symbol(str):
'''A string class that represents Joy function names.'''
__repr__ = str.__str__
def text_to_expression(text):
'''Convert a string to a Joy expression.
When supplied with a string this function returns a Python datastructure
that represents the Joy datastructure described by the text expression.
Any unbalanced square brackets will raise a ParseError.
:param str text: Text to convert.
:rtype: stack
:raises ParseError: if the parse fails.
'''
return _parse(_tokenize(text))
class ParseError(ValueError):
'''Raised when there is a error while parsing text.'''
def _tokenize(text):
'''Convert a text into a stream of tokens.
Converts function names to Symbols.
Raise ParseError (with some of the failing text) if the scan fails.
'''
tokens, rest = token_scanner.scan(text)
if rest:
raise ParseError(
'Scan failed at position %i, %r'
% (len(text) - len(rest), rest[:10])
)
return tokens
def _parse(tokens):
'''
Return a stack/list expression of the tokens.
'''
frame = []
stack = []
for tok in tokens:
if tok == '[':
stack.append(frame)
frame = []
elif tok == ']':
v = frame
try: frame = stack.pop()
except IndexError:
raise ParseError('Extra closing bracket.')
frame.append(list_to_stack(v))
elif tok == 'true':
frame.append(True)
elif tok == 'false':
frame.append(False)
else:
try:
thing = int(tok)
except ValueError:
thing = Symbol(tok)
frame.append(thing)
if stack:
raise ParseError('Unclosed bracket.')
return list_to_stack(frame)
@@ -0,0 +1,5 @@
class NotAListError(Exception): pass
class NotAnIntError(Exception): pass
class StackUnderflowError(Exception): pass
@@ -0,0 +1,414 @@
# GENERATED FILE. DO NOT EDIT.
# The code that generated these functions is in the repo history
# at the v0.4.0 tag.
from .errors import NotAListError, StackUnderflowError
def _Tree_add_Ee(stack):
"""
::
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
"""
(a1, (a2, (a3, ((a4, (a5, s1)), s2)))) = stack
return ((a2, (a3, s1)), s2)
def _Tree_delete_R0(stack):
"""
::
([a2 ...1] a1 -- [a2 ...1] a2 a1 a1)
"""
(a1, ((a2, s1), s2)) = stack
return (a1, (a1, (a2, ((a2, s1), s2))))
def _Tree_delete_clear_stuff(stack):
"""
::
(a3 a2 [a1 ...1] -- [...1])
"""
((a1, s1), (a2, (a3, s2))) = stack
return (s1, s2)
def _Tree_get_E(stack):
"""
::
([a3 a4 ...1] a2 a1 -- a4)
"""
(a1, (a2, ((a3, (a4, s1)), s2))) = stack
return (a4, s2)
def ccons(stack):
"""
::
(a2 a1 [...1] -- [a2 a1 ...1])
"""
(s1, (a1, (a2, s2))) = stack
return ((a2, (a1, s1)), s2)
def cons(stack):
"""
::
(a1 [...0] -- [a1 ...0])
"""
try: s0, stack = stack
except ValueError: raise StackUnderflowError('Not enough values on stack.')
if not isinstance(s0, tuple): raise NotAListError('Not a list.')
try: a1, s23 = stack
except ValueError: raise StackUnderflowError('Not enough values on stack.')
return ((a1, s0), s23)
def dup(stack):
"""
::
(a1 -- a1 a1)
"""
(a1, s23) = stack
return (a1, (a1, s23))
def dupd(stack):
"""
::
(a2 a1 -- a2 a2 a1)
"""
(a1, (a2, s23)) = stack
return (a1, (a2, (a2, s23)))
def dupdd(stack):
"""
::
(a3 a2 a1 -- a3 a3 a2 a1)
"""
(a1, (a2, (a3, s23))) = stack
return (a1, (a2, (a3, (a3, s23))))
def first(stack):
"""
::
([a1 ...1] -- a1)
"""
((a1, s1), s23) = stack
return (a1, s23)
def first_two(stack):
"""
::
([a1 a2 ...1] -- a1 a2)
"""
((a1, (a2, s1)), s2) = stack
return (a2, (a1, s2))
def fourth(stack):
"""
::
([a1 a2 a3 a4 ...1] -- a4)
"""
((a1, (a2, (a3, (a4, s1)))), s2) = stack
return (a4, s2)
def over(stack):
"""
::
(a2 a1 -- a2 a1 a2)
"""
(a1, (a2, s23)) = stack
return (a2, (a1, (a2, s23)))
def pop(stack):
"""
::
(a1 --)
"""
try:
(a1, s23) = stack
except ValueError:
raise StackUnderflowError('Cannot pop empty stack.')
return s23
def popd(stack):
"""
::
(a2 a1 -- a1)
"""
(a1, (a2, s23)) = stack
return (a1, s23)
def popdd(stack):
"""
::
(a3 a2 a1 -- a2 a1)
"""
(a1, (a2, (a3, s23))) = stack
return (a1, (a2, s23))
def popop(stack):
"""
::
(a2 a1 --)
"""
(a1, (a2, s23)) = stack
return s23
def popopd(stack):
"""
::
(a3 a2 a1 -- a1)
"""
(a1, (a2, (a3, s23))) = stack
return (a1, s23)
def popopdd(stack):
"""
::
(a4 a3 a2 a1 -- a2 a1)
"""
(a1, (a2, (a3, (a4, s23)))) = stack
return (a1, (a2, s23))
def rest(stack):
"""
::
([a1 ...0] -- [...0])
"""
try:
s0, stack = stack
except ValueError:
raise StackUnderflowError
if not isinstance(s0, tuple):
raise NotAListError('Not a list.')
try:
_, s1 = s0
except ValueError:
raise StackUnderflowError('Cannot take rest of empty list.')
return (s1, stack)
def rolldown(stack):
"""
::
(a1 a2 a3 -- a2 a3 a1)
"""
(a3, (a2, (a1, s23))) = stack
return (a1, (a3, (a2, s23)))
def rollup(stack):
"""
::
(a1 a2 a3 -- a3 a1 a2)
"""
(a3, (a2, (a1, s23))) = stack
return (a2, (a1, (a3, s23)))
def rrest(stack):
"""
::
([a1 a2 ...1] -- [...1])
"""
((a1, (a2, s1)), s2) = stack
return (s1, s2)
def second(stack):
"""
::
([a1 a2 ...1] -- a2)
"""
((a1, (a2, s1)), s2) = stack
return (a2, s2)
def stack(stack):
"""
::
(... -- ... [...])
"""
s0 = stack
return (s0, s0)
def stuncons(stack):
"""
::
(... a1 -- ... a1 a1 [...])
"""
(a1, s1) = stack
return (s1, (a1, (a1, s1)))
def stununcons(stack):
"""
::
(... a2 a1 -- ... a2 a1 a1 a2 [...])
"""
(a1, (a2, s1)) = stack
return (s1, (a2, (a1, (a1, (a2, s1)))))
def swaack(stack):
"""
::
([...1] -- [...0])
"""
try:
(s1, s0) = stack
except ValueError:
raise StackUnderflowError('Not enough values on stack.')
if not isinstance(s1, tuple):
raise NotAListError('Not a list.')
return (s0, s1)
def swap(stack):
"""
::
(a1 a2 -- a2 a1)
"""
try:
(a2, (a1, s23)) = stack
except ValueError:
raise StackUnderflowError('Not enough values on stack.')
return (a1, (a2, s23))
def swons(stack):
"""
::
([...1] a1 -- [a1 ...1])
"""
(a1, (s1, s2)) = stack
return ((a1, s1), s2)
def third(stack):
"""
::
([a1 a2 a3 ...1] -- a3)
"""
((a1, (a2, (a3, s1))), s2) = stack
return (a3, s2)
def tuck(stack):
"""
::
(a2 a1 -- a1 a2 a1)
"""
(a1, (a2, s23)) = stack
return (a1, (a2, (a1, s23)))
def uncons(stack):
"""
::
([a1 ...0] -- a1 [...0])
"""
((a1, s0), s23) = stack
return (s0, (a1, s23))
def unit(stack):
"""
::
(a1 -- [a1 ])
"""
(a1, s23) = stack
return ((a1, ()), s23)
def unswons(stack):
"""
::
([a1 ...1] -- [...1] a1)
"""
((a1, s1), s2) = stack
return (a1, (s1, s2))
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2016 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/>.
#
'''
Pretty printing support, e.g.::
Joy? [23 18 * 99 +] trace
• 23 18 mul 99 add
23 • 18 mul 99 add
23 18 • mul 99 add
414 • 99 add
414 99 • add
513 •
513 <-top
joy?
On each line the stack is printed with the top to the left, then a
bullet symbol,``•``, to represent the current locus of processing, then
the pending expression to the right.
'''
# (Kinda clunky and hacky. This should be swapped out in favor of much
# smarter stuff.)
from traceback import print_exc
from .stack import expression_to_string, stack_to_string
from ..joy import joy
from ..library import FunctionWrapper
@FunctionWrapper
def trace(stack, expression, dictionary):
'''Evaluate a Joy expression on a stack and print a trace.
This function is just like the `i` combinator but it also prints a
trace of the evaluation
:param stack stack: The stack.
:param stack expression: The expression to evaluate.
:param dict dictionary: A ``dict`` mapping names to Joy functions.
:rtype: (stack, (), dictionary)
'''
tp = TracePrinter()
quote, stack = stack
try:
s, _, d = joy(stack, quote, dictionary, tp.viewer)
except:
tp.print_()
print('-' * 73)
raise
else:
tp.print_()
return s, expression, d
class TracePrinter(object):
'''
This is what does the formatting. You instantiate it and pass the ``viewer()``
method to the :py:func:`joy.joy.joy` function, then print it to see the
trace.
'''
def __init__(self):
self.history = []
def viewer(self, stack, expression):
'''
Record the current stack and expression in the TracePrinter's history.
Pass this method as the ``viewer`` argument to the :py:func:`joy.joy.joy` function.
:param stack quote: A stack.
:param stack expression: A stack.
'''
self.history.append((stack, expression))
def __str__(self):
return '\n'.join(self.go())
def go(self):
'''
Return a list of strings, one for each entry in the history, prefixed
with enough spaces to align all the interpreter dots.
This method is called internally by the ``__str__()`` method.
:rtype: list(str)
'''
max_stack_length = 0
lines = []
for stack, expression in self.history:
stack = stack_to_string(stack)
expression = expression_to_string(expression)
n = len(stack)
if n > max_stack_length:
max_stack_length = n
lines.append((n, '%s%s' % (stack, expression)))
for i in range(len(lines)): # Prefix spaces to line up '•'s.
length, line = lines[i]
lines[i] = (' ' * (max_stack_length - length) + line)
return lines
def print_(self):
try:
print(self)
except:
print_exc()
print('Exception while printing viewer.')
+253
View File
@@ -0,0 +1,253 @@
# -*- 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/>.
#
'''
When talking about Joy we use the terms "stack", "quote", "sequence",
"list", and others to mean the same thing: a simple linear datatype that
permits certain operations such as iterating and pushing and popping
values from (at least) one end.
There is no "Stack" Python class, instead we use the `cons list`_, a
venerable two-tuple recursive sequence datastructure, where the
empty tuple ``()`` is the empty stack and ``(head, rest)`` gives the
recursive form of a stack with one or more items on it::
stack := () | (item, stack)
Putting some numbers onto a stack::
()
(1, ())
(2, (1, ()))
(3, (2, (1, ())))
...
Python has very nice "tuple packing and unpacking" in its syntax which
means we can directly "unpack" the expected arguments to a Joy function.
For example::
def dup((head, tail)):
return head, (head, tail)
We replace the argument "stack" by the expected structure of the stack,
in this case "(head, tail)", and Python takes care of unpacking the
incoming tuple and assigning values to the names. (Note that Python
syntax doesn't require parentheses around tuples used in expressions
where they would be redundant.)
Unfortunately, the Sphinx documentation generator, which is used to generate this
web page, doesn't handle tuples in the function parameters. And in Python 3, this
syntax was removed entirely. Instead you would have to write::
def dup(stack):
head, tail = stack
return head, (head, tail)
We have two very simple functions, one to build up a stack from a Python
iterable and another to iterate through a stack and yield its items
one-by-one in order. There are also two functions to generate string representations
of stacks. They only differ in that one prints the terms in stack from left-to-right while the other prints from right-to-left. In both functions *internal stacks* are
printed left-to-right. These functions are written to support :doc:`../pretty`.
.. _cons list: https://en.wikipedia.org/wiki/Cons#Lists
'''
from .errors import NotAListError
def list_to_stack(el, stack=()):
'''Convert a Python list (or other sequence) to a Joy stack::
[1, 2, 3] -> (1, (2, (3, ())))
:param list el: A Python list or other sequence (iterators and generators
won't work because ``reverse()`` is called on ``el``.)
:param stack stack: A stack, optional, defaults to the empty stack.
:rtype: stack
'''
for item in reversed(el):
stack = item, stack
return stack
def iter_stack(stack):
'''Iterate through the items on the stack.
:param stack stack: A stack.
:rtype: iterator
'''
while stack:
item, stack = stack
yield item
def stack_to_string(stack):
'''
Return a "pretty print" string for a stack.
The items are written right-to-left::
(top, (second, ...)) -> '... second top'
:param stack stack: A stack.
:rtype: str
'''
f = lambda stack: reversed(list(iter_stack(stack)))
return _to_string(stack, f)
def expression_to_string(expression):
'''
Return a "pretty print" string for a expression.
The items are written left-to-right::
(top, (second, ...)) -> 'top second ...'
:param stack expression: A stack.
:rtype: str
'''
return _to_string(expression, iter_stack)
_JOY_BOOL_LITS = 'false', 'true'
def _joy_repr(thing):
if isinstance(thing, bool):
return _JOY_BOOL_LITS[thing]
return repr(thing)
def _to_string(stack, f):
if not isinstance(stack, tuple): return _joy_repr(stack)
if not stack: return '' # shortcut
return ' '.join(map(_s, f(stack)))
_s = lambda s: (
'[%s]' % expression_to_string(s)
if isinstance(s, tuple)
else _joy_repr(s)
)
def concat(quote, expression):
'''Concatinate quote onto expression.
In joy [1 2] [3 4] would become [1 2 3 4].
:param stack quote: A stack.
:param stack expression: A stack.
:raises RuntimeError: if quote is larger than sys.getrecursionlimit().
:rtype: stack
'''
# This is the fastest implementation, but will trigger
# RuntimeError: maximum recursion depth exceeded
# on quotes longer than sys.getrecursionlimit().
## return (quote[0], concat(quote[1], expression)) if quote else expression
# Original implementation.
## return list_to_stack(list(iter_stack(quote)), expression)
# In-lining is slightly faster (and won't break the
# recursion limit on long quotes.)
temp = []
while quote:
if not isinstance(quote, tuple):
raise NotAListError(repr(quote))
item, quote = quote
temp.append(item)
for item in reversed(temp):
expression = item, expression
return expression
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
+2
View File
@@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel"]
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014, 2015, 2017, 2019 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/>.
#
from setuptools import setup
from textwrap import dedent
setup(
name='Thun',
version='0.4.1',
description='Python Implementation of Joy',
long_description=dedent('''\
Joy is a programming language created by Manfred von Thun that is easy to
use and understand and has many other nice properties. This Python
package implements an interpreter for a dialect of Joy that attempts to
stay very close to the spirit of Joy but does not precisely match the
behaviour of the original version written in C.'''),
author='Simon Forman',
author_email='[email protected]',
url='https://joypy.osdn.io',
license='GPLv3+',
packages=['joy', 'joy.utils'],
package_data={
'joy': ['defs.txt'],
},
classifiers=[
'Development Status :: 4 - Beta',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Programming Language :: Python :: 3',
'Programming Language :: Other',
'Topic :: Software Development :: Interpreters',
],
extras_require={
'build-docs': [
'sphinx',
'ipython',
'nbconvert',
],
}
)