Move Python impl to impls dir.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
include archive/*
|
||||
include LICENSE
|
||||
include joy/defs.txt
|
||||
recursive-include joy *.py
|
||||
@@ -0,0 +1,17 @@
|
||||
# My make-fu style is old and tired. I just want to have a few helper commands.
|
||||
|
||||
WEBSERVER = sforman@shell.osdn.net
|
||||
|
||||
.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/
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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.')
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
Executable
+57
@@ -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='sforman@hushmail.com',
|
||||
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',
|
||||
],
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user