def test_classes(): res = Number(1337) assert res.is_real(), f"{res}.is_real()" assert not res.is_vector(), f"not {res}.is_vector()" assert not res.is_matrix(), f"not {res}.is_matrix()" assert not res.is_error(), f"not {res}.is_error()" res = Vector([13, 37]) assert [i for i in res] == [Number(13), Number(37)] # iterable assert not res.is_real(), f"not {res}.is_real()" assert res.is_vector(), f"{res}.is_vector()" assert not res.is_matrix(), f"not {res}.is_matrix()" assert not res.is_error(), f"not {res}.is_error()" res = Matrix([Vector([13, 37])]) assert [i for i in res] == [Vector([13, 37])] # iterable assert not res.is_real(), f"not {res}.is_real()" assert not res.is_vector(), f"not {res}.is_vector()" assert res.is_matrix(), f"{res}.is_matrix()" assert not res.is_error(), f"not {res}.is_error()" res = Error("kek") assert not res.is_real(), f"not {res}.is_real()" assert not res.is_vector(), f"not {res}.is_vector()" assert not res.is_matrix(), f"not {res}.is_matrix()" assert res.is_error(), f"{res}.is_error()" print("test_classes OK")
def test_eval_dot(): # • ‹(dot <vector> <vector>)› # → ‹real› -- dot product res = eval_root(parse('(dot (vector 2 1 6) (vector 1 2 1))')) want = Number(10.0) assert res == Number(10.0), f'{res} == {want}' assert_throw(lambda: eval_root(parse('(dot (vector 0 1) (vector 1 2 3))'))) res = eval_root( parse('(dot (+ (vector 1 1 2) (vector 1 0 4)) (vector 1 2 1))')) want = Number(10.0) assert res == Number(10.0), f'{res} == {want}' print("test_eval_dot OK")
def test_eval_det(): # • ‹(det <matrix>)› # → ‹real› -- determinant of the matrix res = eval_root(parse('(det (matrix (vector 1 2) (vector 2 3)))')) want = Number(-1) assert res == want, f'{res} == {want}' assert_throw(lambda: eval_root( parse('(det (matrix (vector 1 2 3) (vector 1 2 3)))'))) res = eval_root( parse('(det (matrix (+ (vector 0 1) (vector 1 1)) (vector 2 3)))')) want = Number(-1) assert res == want, f'{res} == {want}' print("test_eval_det OK")
def parser(token): if token == '': return Atom('') if type(token) == list: if len(token) == 0: raise Exception("empty compounds not allowed") return Compound([parser(x) for x in token]) if type(token) == str: if len(token) >= 2 and token[0] == '"' and token[-1] == '"': return String(token[1:-1]) if token == '#f': return Boolean(False) if token == '#t': return Boolean(True) num = maybe_number(token) if num is not None: return Number(num) if is_identifier(token): return Identifier(token) raise Exception(f"invalid token: {token}")
def parser(token): if token == '': return Atom('') if type(token) == list: ## What about empty compounds, are they allowed by the grammar? return Compound([parser(x) for x in token]) if type(token) == str: if len(token) >= 2 and token[0] == '"' and token[-1] == '"': return String(token[1:-1]) if token == '#f': return Boolean(False) if token == '#t': return Boolean(True) num = maybe_number(token) if num is not None: return Number(num) if is_identifier(token): return Identifier(token) ## Actually nice error messages raise Exception(f"invalid token: {token}")
def __next__(self): if self.number == len(self.values): raise StopIteration res = self.values[self.number] self.number += 1 return Number(res)
def eval_det(root): # • ‹(det <matrix>)› # → ‹real› -- determinant of the matrix if len(root) != 2: raise Exception( f"invalid number of arguments. want 2, got {len(root)}") arg = root[1] if type(root[1]) == Matrix else eval_root(root[1]) return Number(arg.det())
def make_message(channel, timestamp, nickname, text): # (message "{channel}" {unix timestamp} "{nickname}" "{text}") root = Compound([ Identifier("message"), String(channel), Number(int(timestamp)), String(nickname), String(text), ]) return str(root) + '\n'
def eval_dot(root): # • ‹(dot <vector> <vector>)› # → ‹real› -- dot product if len(root) != 3: raise Exception( f"invalid number of arguments. want 3, got {len(root)}") args = [a if type(a) is Vector else eval_root(a) for a in root[1:]] a1, a2 = args[0], args[1] if type(a1) is Vector and type(a2) is Vector: return Number(a1.dot(a2)) raise Exception( f"invalid argument types, want Vector, got {type(a1)} and {type(a2)}")
def process_number(self): position = self.position char = self.expr[position] whole_number = "" if char not in SIGN and not char.isnumeric(): return False if char in SIGN: # after sign there has to be some numeric if not (position + 1 < len(self.expr) and self.expr[position + 1].isnumeric()): return False whole_number += char position += 1 while True: if position == len(self.expr): break char = self.expr[position] if self.is_atom_ending(char): break if not (char == "." or char.isnumeric()): # single dot check later return False whole_number += char position += 1 floating = '.' in whole_number if floating: try: float(whole_number) except ValueError: return False split = whole_number.split('.') if (len(split) != 2 or not split[0] or not split[1]): return False number = float(whole_number) if floating else int(whole_number) self.expressions.append(Number(number)) self.position = position return True
def __str__(self): exp = Compound([ Identifier("vector"), *[Number(v) for v in self.values] ]) return str(exp)