Type-guard the ops.

This commit is contained in:
Simon Forman 2022-09-07 14:18:55 -07:00
parent 67fd88f68a
commit 4f48ffbb5f
1 changed files with 111 additions and 30 deletions

View File

@ -44,6 +44,10 @@ class NotAnIntError(Exception):
pass pass
class NotABoolError(Exception):
pass
class StackUnderflowError(Exception): class StackUnderflowError(Exception):
pass pass
@ -142,13 +146,16 @@ token_scanner = Scanner(
class Symbol(str): class Symbol(str):
'''A string class that represents Joy function names.''' '''
A string class that represents Joy function names.
'''
__repr__ = str.__str__ __repr__ = str.__str__
def text_to_expression(text): def text_to_expression(text):
'''Convert a string to a Joy expression. '''
Convert a string to a Joy expression.
When supplied with a string this function returns a Python datastructure When supplied with a string this function returns a Python datastructure
that represents the Joy datastructure described by the text expression. that represents the Joy datastructure described by the text expression.
@ -162,7 +169,9 @@ def text_to_expression(text):
class ParseError(ValueError): class ParseError(ValueError):
'''Raised when there is a error while parsing text.''' '''
Raised when there is a error while parsing text.
'''
def _tokenize(text): def _tokenize(text):
@ -804,21 +813,80 @@ def swap(stack):
return (a1, (a2, s23)) return (a1, (a2, s23))
def BinaryFunc(f): def BinaryLogicWrapper(f):
''' '''
Wrap functions that take two arguments and return a single result. Wrap functions that take two numbers and return a single result.
'''
@wraps(f)
def inner(stack, expression, dictionary):
try:
(a, (b, stack)) = stack
except ValueError:
raise StackUnderflowError('Not enough values on stack.')
if (not isinstance(a, bool)
or not isinstance(b, bool)
):
raise NotABoolError
result = f(b, a)
return (result, stack), expression, dictionary
return inner
def BinaryMathWrapper(func):
'''
Wrap functions that take two numbers and return a single result.
'''
@wraps(func)
def inner(stack, expression, dictionary):
try:
(a, (b, stack)) = stack
except ValueError:
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)
return (result, stack), expression, dictionary
return inner
def UnaryLogicWrapper(f):
'''
Wrap functions that take one argument and return a single result.
''' '''
@wraps(f) @wraps(f)
def inner(stack, expression, dictionary): def inner(stack, expression, dictionary):
(a, (b, stack)) = stack (a, stack) = stack
result = f(b, a) if not isinstance(a, bool):
raise NotABoolError
result = f(a)
return (result, stack), expression, dictionary return (result, stack), expression, dictionary
return inner return inner
def UnaryBuiltinWrapper(f): def UnaryMathWrapper(f):
'''
Wrap functions that take one argument and return a single result.
'''
@wraps(f)
def inner(stack, expression, dictionary):
(a, stack) = stack
if (not isinstance(b, int)
or isinstance(a, bool)):
raise NotAnIntError
result = f(a)
return (result, stack), expression, dictionary
return inner
def UnaryWrapper(f):
''' '''
Wrap functions that take one argument and return a single result. Wrap functions that take one argument and return a single result.
''' '''
@ -839,39 +907,40 @@ for F in (
##██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██╔══██║██╔══██╗██║╚════██║██║██║ ██║██║╚██╗██║ ##██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██╔══██║██╔══██╗██║╚════██║██║██║ ██║██║╚██╗██║
##╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║██║ ██║██║███████║██║╚██████╔╝██║ ╚████║ ##╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║██║ ██║██║███████║██║╚██████╔╝██║ ╚████║
## ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ## ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝
BinaryFunc(operator.eq), BinaryMathWrapper(operator.eq),
BinaryFunc(operator.ge), BinaryMathWrapper(operator.ge),
BinaryFunc(operator.gt), BinaryMathWrapper(operator.gt),
BinaryFunc(operator.le), BinaryMathWrapper(operator.le),
BinaryFunc(operator.lt), BinaryMathWrapper(operator.lt),
BinaryFunc(operator.ne), BinaryMathWrapper(operator.ne),
##██╗ ██████╗ ██████╗ ██╗ ██████╗ ##██╗ ██████╗ ██████╗ ██╗ ██████╗
##██║ ██╔═══██╗██╔════╝ ██║██╔════╝ ##██║ ██╔═══██╗██╔════╝ ██║██╔════╝
##██║ ██║ ██║██║ ███╗██║██║ ##██║ ██║ ██║██║ ███╗██║██║
##██║ ██║ ██║██║ ██║██║██║ ##██║ ██║ ██║██║ ██║██║██║
##███████╗╚██████╔╝╚██████╔╝██║╚██████╗ ##███████╗╚██████╔╝╚██████╔╝██║╚██████╗
##╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ##╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝
BinaryFunc(operator.xor), UnaryWrapper(bool), # Convert any value to Boolean.
BinaryFunc(operator.and_), # (The only polymorphic function.)
BinaryFunc(operator.or_), BinaryLogicWrapper(operator.xor),
UnaryBuiltinWrapper(operator.not_), BinaryLogicWrapper(operator.and_),
BinaryLogicWrapper(operator.or_),
UnaryLogicWrapper(operator.not_),
##███╗ ███╗ █████╗ ████████╗██╗ ██╗ ##███╗ ███╗ █████╗ ████████╗██╗ ██╗
##████╗ ████║██╔══██╗╚══██╔══╝██║ ██║ ##████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
##██╔████╔██║███████║ ██║ ███████║ ##██╔████╔██║███████║ ██║ ███████║
##██║╚██╔╝██║██╔══██║ ██║ ██╔══██║ ##██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
##██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║ ##██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
##╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ##╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
BinaryFunc(operator.lshift), BinaryMathWrapper(operator.lshift),
BinaryFunc(operator.rshift), BinaryMathWrapper(operator.rshift),
BinaryFunc(operator.add), BinaryMathWrapper(operator.add),
BinaryFunc(operator.floordiv), BinaryMathWrapper(operator.floordiv),
BinaryFunc(operator.mod), BinaryMathWrapper(operator.mod),
BinaryFunc(operator.mul), BinaryMathWrapper(operator.mul),
BinaryFunc(operator.pow), BinaryMathWrapper(operator.pow),
BinaryFunc(operator.sub), BinaryMathWrapper(operator.sub),
UnaryBuiltinWrapper(abs), UnaryMathWrapper(abs),
UnaryBuiltinWrapper(bool), UnaryMathWrapper(operator.neg),
UnaryBuiltinWrapper(operator.neg),
): ):
inscribe(F) inscribe(F)
@ -887,8 +956,16 @@ for F in (
class Def(object): class Def(object):
'''
Definitions are given by equations:
tribar = '\u2261' # ≡ name foo bar baz ...
When a definition symbol is evaluated its body expression is put onto
the pending expression.
'''
tribar = '\u2261' # '≡'
def __init__(self, name, body): def __init__(self, name, body):
self.__doc__ = f'{name} {self.tribar} {expression_to_string(body)}' self.__doc__ = f'{name} {self.tribar} {expression_to_string(body)}'
@ -901,6 +978,10 @@ class Def(object):
@classmethod @classmethod
def load_definitions(class_, stream, dictionary): def load_definitions(class_, stream, dictionary):
'''
Given an iterable of lines (strings) and a dictionary put any
definitions (lines with '' in them) into the dictionary.
'''
for line in stream: for line in stream:
if class_.tribar not in line: if class_.tribar not in line:
continue continue