Harden up the types.
This commit is contained in:
parent
d7b445fdd4
commit
7594fb887f
|
|
@ -64,6 +64,31 @@ class UnknownSymbolError(KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def isnt_int(i):
|
||||||
|
'''
|
||||||
|
Raise NotAnIntError if i isn't an integer.
|
||||||
|
(Booleans are not integers in Joy.)
|
||||||
|
'''
|
||||||
|
if not isinstance(i, int) or isinstance(i, bool):
|
||||||
|
raise NotAnIntError(f'Not an integer: {_s(i)}')
|
||||||
|
|
||||||
|
|
||||||
|
def isnt_bool(b):
|
||||||
|
'''
|
||||||
|
Raise NotABoolError if b isn't a Boolean.
|
||||||
|
'''
|
||||||
|
if not isinstance(b, bool):
|
||||||
|
raise NotABoolError(f'Not a Boolean value: {_s(b)}')
|
||||||
|
|
||||||
|
|
||||||
|
def isnt_stack(el):
|
||||||
|
'''
|
||||||
|
Raise NotAListError if el isn't a stack/quote/list.
|
||||||
|
'''
|
||||||
|
if not isinstance(el, tuple):
|
||||||
|
raise NotAListError(f'Not a list {_s(el)}')
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
██╗███╗ ██╗████████╗███████╗██████╗ ██████╗ ██████╗ ███████╗████████╗███████╗██████╗
|
██╗███╗ ██╗████████╗███████╗██████╗ ██████╗ ██████╗ ███████╗████████╗███████╗██████╗
|
||||||
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔══██╗
|
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔══██╗
|
||||||
|
|
@ -118,8 +143,8 @@ the fact that they are not Symbol objects.
|
||||||
|
|
||||||
A crude grammar::
|
A crude grammar::
|
||||||
|
|
||||||
joy = <term>*
|
joy := <term>*
|
||||||
term = <integer> | 'true' | 'false' | '[' <joy> ']' | <symbol>
|
term := <integer> | 'true' | 'false' | '[' <joy> ']' | <symbol>
|
||||||
|
|
||||||
A Joy expression is a sequence of zero or more terms. A term is a
|
A Joy expression is a sequence of zero or more terms. A term is a
|
||||||
literal value (integer, Boolean, or quoted Joy expression) or a function symbol.
|
literal value (integer, Boolean, or quoted Joy expression) or a function symbol.
|
||||||
|
|
@ -129,7 +154,7 @@ around square brackets.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
JOY_BOOL_LITERALS = _T, _F = 'false', 'true'
|
JOY_BOOL_LITERALS = _F, _T = 'false', 'true'
|
||||||
|
|
||||||
|
|
||||||
BRACKETS = r'\[|\]' # Left or right square bracket.
|
BRACKETS = r'\[|\]' # Left or right square bracket.
|
||||||
|
|
@ -266,27 +291,16 @@ Putting some numbers onto a stack::
|
||||||
|
|
||||||
Python has very nice "tuple packing and unpacking" in its syntax which
|
Python has very nice "tuple packing and unpacking" in its syntax which
|
||||||
means we can directly "unpack" the expected arguments to a Joy function.
|
means we can directly "unpack" the expected arguments to a Joy function.
|
||||||
|
We assign the argument stack to the expected structure of the stack and
|
||||||
For example::
|
Python takes care of unpacking the
|
||||||
|
|
||||||
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
|
incoming tuple and assigning values to the names. (Note that Python
|
||||||
syntax doesn't require parentheses around tuples used in expressions
|
syntax doesn't require parentheses around tuples used in expressions
|
||||||
where they would be redundant.)
|
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):
|
def dup(stack):
|
||||||
head, tail = stack
|
head, tail = stack
|
||||||
return head, (head, tail)
|
return head, (head, tail)
|
||||||
|
|
||||||
|
|
||||||
We have two very simple functions, one to build up a stack from a Python
|
We have two very simple functions, one to build up a stack from a Python
|
||||||
list and another to iterate through a stack and yield its items
|
list 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
|
one-by-one in order. There are also two functions to generate string representations
|
||||||
|
|
@ -350,8 +364,8 @@ def concat(quote, expression):
|
||||||
# In-lining is slightly faster (and won't break the
|
# In-lining is slightly faster (and won't break the
|
||||||
# recursion limit on long quotes.)
|
# recursion limit on long quotes.)
|
||||||
|
|
||||||
if not isinstance(quote, tuple):
|
isnt_stack(quote)
|
||||||
raise NotAListError(f'Not a list {_s(quote)}')
|
isnt_stack(expression)
|
||||||
temp = []
|
temp = []
|
||||||
while quote:
|
while quote:
|
||||||
item, quote = quote
|
item, quote = quote
|
||||||
|
|
@ -361,6 +375,31 @@ def concat(quote, expression):
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
||||||
|
def get_n_items(n, stack):
|
||||||
|
'''
|
||||||
|
Return items and remainder of stack.
|
||||||
|
Raise StackUnderflowError if there are fewer than n items on the stack.
|
||||||
|
'''
|
||||||
|
assert n > 0, repr(n)
|
||||||
|
temp = []
|
||||||
|
while n > 0:
|
||||||
|
n -= 1
|
||||||
|
try:
|
||||||
|
item, stack = stack
|
||||||
|
except ValueError:
|
||||||
|
raise StackUnderflowError('Not enough values on stack.') from None
|
||||||
|
temp.append(item)
|
||||||
|
temp.append(stack)
|
||||||
|
return tuple(temp)
|
||||||
|
|
||||||
|
|
||||||
|
def reversed_stack(stack):
|
||||||
|
'''
|
||||||
|
Return list_reverseiterator object for a stack.
|
||||||
|
'''
|
||||||
|
return reversed(list(iter_stack(stack)))
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
██████╗ ██████╗ ██╗███╗ ██╗████████╗███████╗██████╗
|
██████╗ ██████╗ ██╗███╗ ██╗████████╗███████╗██████╗
|
||||||
██╔══██╗██╔══██╗██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗
|
██╔══██╗██╔══██╗██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗
|
||||||
|
|
@ -382,8 +421,7 @@ def stack_to_string(stack):
|
||||||
:param stack stack: A stack.
|
:param stack stack: A stack.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
'''
|
'''
|
||||||
f = lambda stack: reversed(list(iter_stack(stack)))
|
return _to_string(stack, reversed_stack)
|
||||||
return _to_string(stack, f)
|
|
||||||
|
|
||||||
|
|
||||||
def expression_to_string(expression):
|
def expression_to_string(expression):
|
||||||
|
|
@ -416,11 +454,12 @@ def _to_string(stack, f):
|
||||||
return ' '.join(map(_s, f(stack)))
|
return ' '.join(map(_s, f(stack)))
|
||||||
|
|
||||||
|
|
||||||
_s = lambda s: (
|
def _s(s):
|
||||||
'[%s]' % expression_to_string(s)
|
return (
|
||||||
if isinstance(s, tuple)
|
'[%s]' % expression_to_string(s)
|
||||||
else _joy_repr(s)
|
if isinstance(s, tuple)
|
||||||
)
|
else _joy_repr(s)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
@ -500,8 +539,8 @@ def interp(stack=(), dictionary=None):
|
||||||
print('Unknown:', sym)
|
print('Unknown:', sym)
|
||||||
except StackUnderflowError as e:
|
except StackUnderflowError as e:
|
||||||
print(e) # 'Not enough values on stack.'
|
print(e) # 'Not enough values on stack.'
|
||||||
except NotAnIntError:
|
except NotAnIntError as e:
|
||||||
print('Not an integer.')
|
print(e)
|
||||||
except NotAListError as e:
|
except NotAListError as e:
|
||||||
print(e)
|
print(e)
|
||||||
except:
|
except:
|
||||||
|
|
@ -547,10 +586,10 @@ def SimpleFunctionWrapper(f):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(stack, expr, dictionary):
|
def SimpleFunctionWrapper_inner(stack, expr, dictionary):
|
||||||
return f(stack), expr, dictionary
|
return f(stack), expr, dictionary
|
||||||
|
|
||||||
return inner
|
return SimpleFunctionWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
@ -617,13 +656,10 @@ def branch(stack, expr, dictionary):
|
||||||
T
|
T
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(then, (else_, (flag, stack))) = stack
|
then, else_, flag, stack = get_n_items(3, stack)
|
||||||
if not isinstance(flag, bool):
|
isnt_bool(flag)
|
||||||
raise NotABoolError(f'Not a Boolean value: {_s(flag)}')
|
isnt_stack(else_)
|
||||||
if not isinstance(else_, tuple):
|
isnt_stack(then)
|
||||||
raise NotAListError(f'Not a list {_s(else_)}')
|
|
||||||
if not isinstance(then, tuple):
|
|
||||||
raise NotAListError(f'Not a list {_s(then)}')
|
|
||||||
do = then if flag else else_
|
do = then if flag else else_
|
||||||
return stack, concat(do, expr), dictionary
|
return stack, concat(do, expr), dictionary
|
||||||
|
|
||||||
|
|
@ -641,7 +677,7 @@ def dip(stack, expr, dictionary):
|
||||||
... Q x
|
... Q x
|
||||||
|
|
||||||
'''
|
'''
|
||||||
quote, (x, stack) = stack
|
quote, x, stack = get_n_items(2, stack)
|
||||||
return stack, concat(quote, (x, expr)), dictionary
|
return stack, concat(quote, (x, expr)), dictionary
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -657,7 +693,7 @@ def i(stack, expr, dictionary):
|
||||||
Q
|
Q
|
||||||
|
|
||||||
'''
|
'''
|
||||||
quote, stack = stack
|
quote, stack = get_n_items(1, stack)
|
||||||
return stack, concat(quote, expr), dictionary
|
return stack, concat(quote, expr), dictionary
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -679,7 +715,9 @@ def loop(stack, expr, dictionary):
|
||||||
...
|
...
|
||||||
|
|
||||||
'''
|
'''
|
||||||
quote, (flag, stack) = stack
|
quote, flag, stack = get_n_items(2, stack)
|
||||||
|
isnt_bool(flag)
|
||||||
|
isnt_stack(quote)
|
||||||
if flag:
|
if flag:
|
||||||
expr = concat(quote, (quote, (LOOP, expr)))
|
expr = concat(quote, (quote, (LOOP, expr)))
|
||||||
return stack, expr, dictionary
|
return stack, expr, dictionary
|
||||||
|
|
@ -723,7 +761,7 @@ def concat_(stack):
|
||||||
[a b c d e f]
|
[a b c d e f]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(tos, (second, stack)) = stack
|
tos, second, stack = get_n_items(2, stack)
|
||||||
return concat(second, tos), stack
|
return concat(second, tos), stack
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -742,7 +780,8 @@ def cons(stack):
|
||||||
( https://en.wikipedia.org/wiki/Cons#Lists ).
|
( https://en.wikipedia.org/wiki/Cons#Lists ).
|
||||||
Its inverse operation is uncons.
|
Its inverse operation is uncons.
|
||||||
'''
|
'''
|
||||||
s0, (a1, stack) = stack
|
s0, a1, stack = get_n_items(2, stack)
|
||||||
|
isnt_stack(s0)
|
||||||
return ((a1, s0), stack)
|
return ((a1, s0), stack)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -758,8 +797,8 @@ def dup(stack):
|
||||||
a a
|
a a
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(a1, s23) = stack
|
a1, stack = get_n_items(1, stack)
|
||||||
return (a1, (a1, s23))
|
return a1, (a1, stack)
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
@ -773,8 +812,10 @@ def first(stack):
|
||||||
a
|
a
|
||||||
|
|
||||||
'''
|
'''
|
||||||
((a1, s1), s23) = stack
|
s0, stack = get_n_items(1, stack)
|
||||||
return (a1, s23)
|
isnt_stack(s0)
|
||||||
|
a1, _ = get_n_items(1, s0)
|
||||||
|
return a1, stack
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
@ -787,7 +828,7 @@ def pop(stack):
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(_, s23) = stack
|
_, s23 = get_n_items(1, stack)
|
||||||
return s23
|
return s23
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -802,8 +843,10 @@ def rest(stack):
|
||||||
[b c]
|
[b c]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(_, s1), stack = stack
|
s0, stack = get_n_items(1, stack)
|
||||||
return (s1, stack)
|
isnt_stack(s0)
|
||||||
|
_, s1 = get_n_items(1, s0)
|
||||||
|
return s1, stack
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
@ -832,8 +875,9 @@ def swaack(stack):
|
||||||
6 5 4 [3 2 1]
|
6 5 4 [3 2 1]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(s1, s0) = stack
|
s1, s0 = get_n_items(1, stack)
|
||||||
return (s0, s1)
|
isnt_stack(s1)
|
||||||
|
return s0, s1
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
|
@ -847,8 +891,8 @@ def swap(stack):
|
||||||
b a
|
b a
|
||||||
|
|
||||||
'''
|
'''
|
||||||
(a2, (a1, s23)) = stack
|
a2, a1, stack = get_n_items(2, stack)
|
||||||
return (a1, (a2, s23))
|
return (a1, (a2, stack))
|
||||||
|
|
||||||
|
|
||||||
def BinaryLogicWrapper(f):
|
def BinaryLogicWrapper(f):
|
||||||
|
|
@ -857,17 +901,14 @@ def BinaryLogicWrapper(f):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(stack, expression, dictionary):
|
def BinaryLogicWrapper_inner(stack, expression, dictionary):
|
||||||
try:
|
a, b, stack = get_n_items(2, stack)
|
||||||
(a, (b, stack)) = stack
|
isnt_bool(a)
|
||||||
except ValueError:
|
isnt_bool(b)
|
||||||
raise StackUnderflowError('Not enough values on stack.')
|
|
||||||
if not isinstance(a, bool) or not isinstance(b, bool):
|
|
||||||
raise NotABoolError
|
|
||||||
result = f(b, a)
|
result = f(b, a)
|
||||||
return (result, stack), expression, dictionary
|
return (result, stack), expression, dictionary
|
||||||
|
|
||||||
return inner
|
return BinaryLogicWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
def BinaryMathWrapper(func):
|
def BinaryMathWrapper(func):
|
||||||
|
|
@ -876,22 +917,14 @@ def BinaryMathWrapper(func):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def inner(stack, expression, dictionary):
|
def BinaryMathWrapper_inner(stack, expression, dictionary):
|
||||||
try:
|
a, b, stack = get_n_items(2, stack)
|
||||||
(a, (b, stack)) = stack
|
isnt_int(a)
|
||||||
except ValueError:
|
isnt_int(b)
|
||||||
raise StackUnderflowError('Not enough values on stack.')
|
|
||||||
if (
|
|
||||||
not isinstance(a, int)
|
|
||||||
or not isinstance(b, int)
|
|
||||||
or isinstance(a, bool)
|
|
||||||
or isinstance(b, bool)
|
|
||||||
):
|
|
||||||
raise NotAnIntError
|
|
||||||
result = func(b, a)
|
result = func(b, a)
|
||||||
return (result, stack), expression, dictionary
|
return (result, stack), expression, dictionary
|
||||||
|
|
||||||
return inner
|
return BinaryMathWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
def UnaryLogicWrapper(f):
|
def UnaryLogicWrapper(f):
|
||||||
|
|
@ -900,14 +933,13 @@ def UnaryLogicWrapper(f):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(stack, expression, dictionary):
|
def UnaryLogicWrapper_inner(stack, expression, dictionary):
|
||||||
(a, stack) = stack
|
a, stack = get_n_items(1, stack)
|
||||||
if not isinstance(a, bool):
|
isnt_bool(a)
|
||||||
raise NotABoolError
|
|
||||||
result = f(a)
|
result = f(a)
|
||||||
return (result, stack), expression, dictionary
|
return (result, stack), expression, dictionary
|
||||||
|
|
||||||
return inner
|
return UnaryLogicWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
def UnaryMathWrapper(f):
|
def UnaryMathWrapper(f):
|
||||||
|
|
@ -916,14 +948,13 @@ def UnaryMathWrapper(f):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(stack, expression, dictionary):
|
def UnaryMathWrapper_inner(stack, expression, dictionary):
|
||||||
(a, stack) = stack
|
a, stack = get_n_items(1, stack)
|
||||||
if not isinstance(b, int) or isinstance(a, bool):
|
isnt_int(a)
|
||||||
raise NotAnIntError
|
|
||||||
result = f(a)
|
result = f(a)
|
||||||
return (result, stack), expression, dictionary
|
return (result, stack), expression, dictionary
|
||||||
|
|
||||||
return inner
|
return UnaryMathWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
def UnaryWrapper(f):
|
def UnaryWrapper(f):
|
||||||
|
|
@ -932,12 +963,12 @@ def UnaryWrapper(f):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(stack, expression, dictionary):
|
def UnaryWrapper_inner(stack, expression, dictionary):
|
||||||
(a, stack) = stack
|
a, stack = get_n_items(1, stack)
|
||||||
result = f(a)
|
result = f(a)
|
||||||
return (result, stack), expression, dictionary
|
return (result, stack), expression, dictionary
|
||||||
|
|
||||||
return inner
|
return UnaryWrapper_inner
|
||||||
|
|
||||||
|
|
||||||
for F in (
|
for F in (
|
||||||
|
|
@ -1144,7 +1175,7 @@ third rest second
|
||||||
tuck dup swapd
|
tuck dup swapd
|
||||||
unary nullary popd
|
unary nullary popd
|
||||||
uncons ≡ [first] [rest] cleave
|
uncons ≡ [first] [rest] cleave
|
||||||
unit [] cons
|
unit ≡ [] cons
|
||||||
unquoted [i] dip
|
unquoted [i] dip
|
||||||
unswons uncons swap
|
unswons uncons swap
|
||||||
while swap nulco dupdipd concat loop
|
while swap nulco dupdipd concat loop
|
||||||
|
|
@ -1168,6 +1199,7 @@ _map2 [infrst] cons dipd roll< swons
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
J = interp if '-q' in sys.argv else repl
|
J = interp if '-q' in sys.argv else repl
|
||||||
dictionary = initialize()
|
dictionary = initialize()
|
||||||
Def.load_definitions(DEFS.splitlines(), dictionary)
|
Def.load_definitions(DEFS.splitlines(), dictionary)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue