Remove build dir from version control.
This commit is contained in:
parent
9805f479be
commit
f454014248
|
|
@ -1,31 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright © 2014, 2015, 2017 Simon Forman
|
|
||||||
#
|
|
||||||
# This file is part of joy.py
|
|
||||||
#
|
|
||||||
# joy.py 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.
|
|
||||||
#
|
|
||||||
# joy.py 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 joy.py. If not see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
from .library import initialize
|
|
||||||
from .joy import repl
|
|
||||||
|
|
||||||
print '''\
|
|
||||||
Joypy - 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.
|
|
||||||
'''
|
|
||||||
stack = repl(dictionary=initialize())
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
A dialect of Joy in Python.
|
|
||||||
|
|
||||||
|
|
||||||
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 script
|
|
||||||
is 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. A Tkinter GUI is provided as well.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright © 2014, 2016, 2017 Simon Forman
|
|
||||||
|
|
||||||
This file is part of Joypy.
|
|
||||||
|
|
||||||
Joypy 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.
|
|
||||||
|
|
||||||
Joypy 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 Joypy. If not see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
§ joy()
|
|
||||||
|
|
||||||
The basic joy() function is quite straightforward. It iterates through a
|
|
||||||
sequence of terms which are either literals (strings, numbers, sequences)
|
|
||||||
or functions. Literals are put onto the stack and functions are
|
|
||||||
executed.
|
|
||||||
|
|
||||||
Every Joy function is an unary mapping from stacks to stacks. Even
|
|
||||||
literals are considered to be functions that accept a stack and return a
|
|
||||||
new stack with the literal value on top.
|
|
||||||
|
|
||||||
Exports:
|
|
||||||
|
|
||||||
joy(stack, expression, dictionary, viewer=None)
|
|
||||||
|
|
||||||
run(text, stack, dictionary, viewer=None)
|
|
||||||
|
|
||||||
repl(stack=(), dictionary=())
|
|
||||||
|
|
||||||
'''
|
|
||||||
from __future__ import print_function
|
|
||||||
try:
|
|
||||||
input = raw_input
|
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
from traceback import print_exc, format_exc
|
|
||||||
from .parser import text_to_expression, ParseError, Symbol
|
|
||||||
from .utils.stack import stack_to_string
|
|
||||||
from .utils.pretty_print import TracePrinter
|
|
||||||
|
|
||||||
|
|
||||||
def joy(stack, expression, dictionary, viewer=None):
|
|
||||||
'''
|
|
||||||
Evaluate the Joy expression on the stack.
|
|
||||||
'''
|
|
||||||
while expression:
|
|
||||||
|
|
||||||
if viewer: viewer(stack, expression)
|
|
||||||
|
|
||||||
term, expression = expression
|
|
||||||
if isinstance(term, Symbol):
|
|
||||||
term = dictionary[term]
|
|
||||||
stack, expression, dictionary = term(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.
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
expression = text_to_expression(text)
|
|
||||||
except ParseError as err:
|
|
||||||
print('Err:', err.message)
|
|
||||||
return stack, (), dictionary
|
|
||||||
return joy(stack, expression, dictionary, viewer)
|
|
||||||
|
|
||||||
|
|
||||||
def repl(stack=(), dictionary=None):
|
|
||||||
'''
|
|
||||||
Read-Evaluate-Print Loop
|
|
||||||
|
|
||||||
Accept input and run it on the stack, loop.
|
|
||||||
'''
|
|
||||||
if dictionary is None:
|
|
||||||
dictionary = {}
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
print()
|
|
||||||
print(stack_to_string(stack), '<-top')
|
|
||||||
print()
|
|
||||||
try:
|
|
||||||
text = input('joy? ')
|
|
||||||
except (EOFError, KeyboardInterrupt):
|
|
||||||
break
|
|
||||||
viewer = TracePrinter()
|
|
||||||
try:
|
|
||||||
stack, _, dictionary = run(text, stack, dictionary, viewer.viewer)
|
|
||||||
except:
|
|
||||||
exc = format_exc() # Capture the exception.
|
|
||||||
viewer.print_() # Print the Joy trace.
|
|
||||||
print('-' * 73)
|
|
||||||
print(exc) # Print the original exception.
|
|
||||||
else:
|
|
||||||
viewer.print_()
|
|
||||||
except:
|
|
||||||
print_exc()
|
|
||||||
print()
|
|
||||||
return stack
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,110 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright © 2014, 2015, 2016, 2017 Simon Forman
|
|
||||||
#
|
|
||||||
# This file is part of Joypy.
|
|
||||||
#
|
|
||||||
# Joypy 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.
|
|
||||||
#
|
|
||||||
# Joypy 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 Joypy. If not see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
§ Converting text to a joy expression.
|
|
||||||
|
|
||||||
This module exports a single function:
|
|
||||||
|
|
||||||
text_to_expression(text)
|
|
||||||
|
|
||||||
As well as a single Symbol class and a single Exception type:
|
|
||||||
|
|
||||||
ParseError
|
|
||||||
|
|
||||||
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.
|
|
||||||
'''
|
|
||||||
from re import Scanner
|
|
||||||
from .utils.stack import list_to_stack
|
|
||||||
|
|
||||||
|
|
||||||
class Symbol(str):
|
|
||||||
__repr__ = str.__str__
|
|
||||||
|
|
||||||
|
|
||||||
def text_to_expression(text):
|
|
||||||
'''
|
|
||||||
Convert a text to a Joy expression.
|
|
||||||
'''
|
|
||||||
return _parse(_tokenize(text))
|
|
||||||
|
|
||||||
|
|
||||||
class ParseError(ValueError): pass
|
|
||||||
|
|
||||||
|
|
||||||
def _tokenize(text):
|
|
||||||
'''
|
|
||||||
Convert a text into a stream of tokens, converting symbols using
|
|
||||||
symbol(token). Raise ValueError (with some of the failing text)
|
|
||||||
if the scan fails.
|
|
||||||
'''
|
|
||||||
tokens, rest = _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 = []
|
|
||||||
stack[-1].append(frame)
|
|
||||||
elif tok == ']':
|
|
||||||
try:
|
|
||||||
frame = stack.pop()
|
|
||||||
except IndexError:
|
|
||||||
raise ParseError('One or more extra closing brackets.')
|
|
||||||
frame[-1] = list_to_stack(frame[-1])
|
|
||||||
else:
|
|
||||||
frame.append(tok)
|
|
||||||
if stack:
|
|
||||||
raise ParseError('One or more unclosed brackets.')
|
|
||||||
return list_to_stack(frame)
|
|
||||||
|
|
||||||
|
|
||||||
def _scan_identifier(scanner, token): return Symbol(token)
|
|
||||||
def _scan_bracket(scanner, token): return token
|
|
||||||
def _scan_float(scanner, token): return float(token)
|
|
||||||
def _scan_int(scanner, token): return int(token)
|
|
||||||
def _scan_dstr(scanner, token): return token[1:-1].replace('\\"', '"')
|
|
||||||
def _scan_sstr(scanner, token): return token[1:-1].replace("\\'", "'")
|
|
||||||
|
|
||||||
|
|
||||||
_scanner = Scanner([
|
|
||||||
(r'-?\d+\.\d*', _scan_float),
|
|
||||||
(r'-?\d+', _scan_int),
|
|
||||||
(r'[•\w!@$%^&*()_+<>?|\/;:`~,.=-]+', _scan_identifier),
|
|
||||||
(r'\[|\]', _scan_bracket),
|
|
||||||
(r'"(?:[^"\\]|\\.)*"', _scan_dstr),
|
|
||||||
(r"'(?:[^'\\]|\\.)*'", _scan_sstr),
|
|
||||||
(r'\s+', None),
|
|
||||||
])
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright © 2016 Simon Forman
|
|
||||||
#
|
|
||||||
# This file is part of Joypy.
|
|
||||||
#
|
|
||||||
# Joypy 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.
|
|
||||||
#
|
|
||||||
# Joypy 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 Joypy. If not see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
Pretty printing support.
|
|
||||||
|
|
||||||
This is what does the formatting, e.g.:
|
|
||||||
|
|
||||||
. 23 18 mul 99 add
|
|
||||||
23 . 18 mul 99 add
|
|
||||||
23 18 . mul 99 add
|
|
||||||
414 . 99 add
|
|
||||||
414 99 . add
|
|
||||||
513 .
|
|
||||||
|
|
||||||
'''
|
|
||||||
# (Kinda clunky and hacky. This should be swapped out in favor of much
|
|
||||||
# smarter stuff.)
|
|
||||||
from __future__ import print_function
|
|
||||||
from traceback import print_exc
|
|
||||||
from .stack import expression_to_string, stack_to_string
|
|
||||||
|
|
||||||
|
|
||||||
class TracePrinter(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.history = []
|
|
||||||
|
|
||||||
def viewer(self, stack, expression):
|
|
||||||
'''Pass this method as the viewer to joy() function.'''
|
|
||||||
self.history.append((stack, expression))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '\n'.join(self.go())
|
|
||||||
|
|
||||||
def go(self):
|
|
||||||
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)))
|
|
||||||
return [ # Prefix spaces to line up '.'s.
|
|
||||||
(' ' * (max_stack_length - length) + line)
|
|
||||||
for length, line in lines
|
|
||||||
]
|
|
||||||
|
|
||||||
def print_(self):
|
|
||||||
try:
|
|
||||||
print(self)
|
|
||||||
except:
|
|
||||||
print_exc()
|
|
||||||
print('Exception while printing viewer.')
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright © 2014, 2015, 2017 Simon Forman
|
|
||||||
#
|
|
||||||
# This file is part of joy.py
|
|
||||||
#
|
|
||||||
# joy.py 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.
|
|
||||||
#
|
|
||||||
# joy.py 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 joy.py. If not see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
§ Stack
|
|
||||||
|
|
||||||
|
|
||||||
When talking about Joy we use the terms "stack", "list", "sequence" and
|
|
||||||
"aggregate" to mean the same thing: a simple datatype that permits
|
|
||||||
certain operations such as iterating and pushing and popping values from
|
|
||||||
(at least) one end.
|
|
||||||
|
|
||||||
We use the venerable two-tuple recursive form of sequences 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.
|
|
||||||
|
|
||||||
()
|
|
||||||
(1, ())
|
|
||||||
(2, (1, ()))
|
|
||||||
(3, (2, (1, ())))
|
|
||||||
...
|
|
||||||
|
|
||||||
And so on.
|
|
||||||
|
|
||||||
|
|
||||||
We have two very simple functions to build up a stack from a Python
|
|
||||||
iterable and also to iterate through a stack and yield its items
|
|
||||||
one-by-one in order, and two functions to generate string representations
|
|
||||||
of stacks:
|
|
||||||
|
|
||||||
list_to_stack()
|
|
||||||
|
|
||||||
iter_stack()
|
|
||||||
|
|
||||||
expression_to_string() (prints left-to-right)
|
|
||||||
|
|
||||||
stack_to_string() (prints right-to-left)
|
|
||||||
|
|
||||||
|
|
||||||
A word about the stack data structure.
|
|
||||||
|
|
||||||
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(stack):
|
|
||||||
head, tail = stack
|
|
||||||
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 de-structuring the
|
|
||||||
incoming argument and assigning values to the names. Note that Python
|
|
||||||
syntax doesn't require parentheses around tuples used in expressions
|
|
||||||
where they would be redundant.
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def list_to_stack(el, stack=()):
|
|
||||||
'''Convert a list (or other sequence) to a stack.
|
|
||||||
|
|
||||||
[1, 2, 3] -> (1, (2, (3, ())))
|
|
||||||
|
|
||||||
'''
|
|
||||||
for item in reversed(el):
|
|
||||||
stack = item, stack
|
|
||||||
return stack
|
|
||||||
|
|
||||||
|
|
||||||
def iter_stack(stack):
|
|
||||||
'''Iterate through the items on the stack.'''
|
|
||||||
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'
|
|
||||||
'''
|
|
||||||
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 ...'
|
|
||||||
'''
|
|
||||||
return _to_string(expression, iter_stack)
|
|
||||||
|
|
||||||
|
|
||||||
def _to_string(stack, f):
|
|
||||||
if isinstance(stack, long): return str(stack).rstrip('L')
|
|
||||||
if not isinstance(stack, tuple): return 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 str(s).rstrip('L') if isinstance(s, long)
|
|
||||||
else repr(s)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def pushback(quote, expression):
|
|
||||||
'''Concatinate quote onto expression.
|
|
||||||
|
|
||||||
In joy [1 2] [3 4] would become [1 2 3 4].
|
|
||||||
'''
|
|
||||||
return list_to_stack(list(iter_stack(quote)), expression)
|
|
||||||
|
|
||||||
|
|
||||||
def pick(s, n):
|
|
||||||
'''
|
|
||||||
Find the nth item on the stack. (Pick with zero is the same as "dup".)
|
|
||||||
'''
|
|
||||||
if n < 0:
|
|
||||||
raise ValueError
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
item, s = s
|
|
||||||
except ValueError:
|
|
||||||
raise IndexError
|
|
||||||
n -= 1
|
|
||||||
if n < 0:
|
|
||||||
break
|
|
||||||
return item
|
|
||||||
Loading…
Reference in New Issue