def test_list(self): program = "(list)" self.assertEvaluatesTo(program, Nil()) program = "(list 1 (+ 2 3))" self.assertEvaluatesTo(program, Cons.from_list([Integer(1), Integer(5)]))
def test_modulo(self): program = "(modulo 4 2)" self.assertEvaluatesTo(program, Integer(0)) program = "(modulo 5 2)" self.assertEvaluatesTo(program, Integer(1)) program = "(modulo -13 4)" self.assertEvaluatesTo(program, Integer(3))
def test_quotient(self): program = "(quotient 3 2)" self.assertEvaluatesTo(program, Integer(1)) program = "(quotient 4 2)" self.assertEvaluatesTo(program, Integer(2)) program = "(quotient -13 4)" self.assertEvaluatesTo(program, Integer(-3))
def test_multiplication(self): program = "(* 2 2 3)" self.assertEvaluatesTo(program, Integer(12)) program = "(*)" self.assertEvaluatesTo(program, Integer(1)) program = "(* 3 0.5)" self.assertEvaluatesTo(program, FloatingPoint(1.5))
def test_addition(self): program = "(+ 1 2 3)" self.assertEvaluatesTo(program, Integer(6)) program = "(+)" self.assertEvaluatesTo(program, Integer(0)) program = "(+ 1 2.5)" self.assertEvaluatesTo(program, FloatingPoint(3.5))
def test_cons(self): program = "(cons 1 (quote (2 3)))" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(2), Integer(3)])) program = "(cons 1 2)" self.assertEvaluatesTo(program, Cons(Integer(1), Integer(2)))
def test_cdr(self): program = "(cdr (quote (1 2 3)))" self.assertEvaluatesTo(program, Cons.from_list([Integer(2), Integer(3)])) program = "(cdr (quote (1 2 3)) 1)" with self.assertRaises(SchemeArityError): self.evaluate(program)
def test_quasiquote_sugar(self): program = "`,1" self.assertEvaluatesTo(program, Integer(1)) program = "`(1 ,@'(2 2))" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(2), Integer(2)]))
def test_cond(self): program = "(cond ((else 1)))" self.assertEvaluatesTo(program, Integer(1)) program = "(define x 1) (cond (((> x 0) 3) (else 1)))" self.assertEvaluatesTo(program, Integer(3)) program = "(define y 1) (cond (((< y 0) 3) (else 1)))" self.assertEvaluatesTo(program, Integer(1))
def test_quote(self): program = "(quote (1 2 3))" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(2), Integer(3)])) program = "(quote ())" self.assertEvaluatesTo(program, Nil())
def test_subtraction(self): program = "(- 1 2 3)" self.assertEvaluatesTo(program, Integer(-4)) program = "(- 2)" self.assertEvaluatesTo(program, Integer(-2)) program = "(- 0.1)" self.assertEvaluatesTo(program, FloatingPoint(-0.1)) program = "(- 10.0 0.5)" self.assertEvaluatesTo(program, FloatingPoint(9.5))
def test_car_cdr_compositions(self): program = "(caar '((1 3) 2))" self.assertEvaluatesTo(program, Integer(1)) program = "(cadr '((1 3) 2))" self.assertEvaluatesTo(program, Integer(2)) program = "(cdar '((1 3) 2))" self.assertEvaluatesTo(program, Cons(Integer(3))) program = "(cddr '((1 3) 2))" self.assertEvaluatesTo(program, Nil())
def test_remainder(self): program = "(remainder 4 2)" self.assertEvaluatesTo(program, Integer(0)) program = "(remainder 5 2)" self.assertEvaluatesTo(program, Integer(1)) program = "(remainder -13 4)" self.assertEvaluatesTo(program, Integer(-1)) program = "(remainder 13 -4)" self.assertEvaluatesTo(program, Integer(1))
def test_variadic_function_definition(self): # test that we can put nothing in the impure list parameter program = "(define (foo . args) 1) (foo)" self.assertEvaluatesTo(program, Integer(1)) # test that we can put multiple things in the impure list parameter program = "(define (f . everything) everything) (f 1 2 3)" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(2), Integer(3)])) # test that the improper list parameter is evaluated program = "(define (g . everything) everything) (g (+ 2 3))" self.assertEvaluatesTo(program, Cons(Integer(5)))
def test_for_each(self): program = """(let ((total 0)) (for-each (lambda (x) (set! total (+ total x))) '(1 2 3)) total)""" self.assertEvaluatesTo(program, Integer(6))
def subtract(arguments): check_argument_number('-', arguments, 1) if len(arguments) == 1: # we just negate a single argument if isinstance(arguments[0], Integer): return Integer(-1 * arguments[0].value) elif isinstance(arguments[0], FloatingPoint): return FloatingPoint(-1 * arguments[0].value) else: raise SchemeTypeError("Subtraction is only defined for integers and " "floating point, you gave me %s." % arguments[0].__class__) total = copy(arguments[0]) for argument in arguments.tail: if not isinstance(argument, Number): raise SchemeTypeError("Subtraction is only defined for numbers, " "you gave me %s." % argument.__class__) # subtracting a float from an integer gives us a float if isinstance(total, Integer) and isinstance(argument, FloatingPoint): total = FloatingPoint(float(total.value)) total.value -= argument.value return total
def vector_length(arguments): check_argument_number('vector-length', arguments, 1, 1) # todo: check type vector = arguments[0] return Integer(len(vector))
def modulo(arguments): check_argument_number('modulo', arguments, 2, 2) if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer): raise SchemeTypeError("modulo is only defined for integers, " "got %s and %s." % (arguments[0].__class__, arguments[1].__class__)) return Integer(arguments[0].value % arguments[1].value)
def string_length(arguments): check_argument_number('string-length', arguments, 1, 1) string_atom = arguments[0] if not isinstance(string_atom, String): raise SchemeTypeError("string-length takes a string as its argument, " "not a %s." % string_atom.__class__) string_length = len(string_atom.value) return Integer(string_length)
def add(arguments): if not arguments: return Integer(0) if isinstance(arguments[0], Integer): total = Integer(0) elif isinstance(arguments[0], FloatingPoint): total = FloatingPoint(0.0) for argument in arguments: if not isinstance(argument, Number): raise SchemeTypeError("Addition is only defined for numbers, " "you gave me %s." % argument.__class__) # adding a float to an integer gives us a float if isinstance(total, Integer) and isinstance(argument, FloatingPoint): total = FloatingPoint(float(total.value)) total.value += argument.value return total
def multiply(arguments): if not arguments: return Integer(1) if isinstance(arguments[0], Integer): product = Integer(1) elif isinstance(arguments[0], FloatingPoint): product = FloatingPoint(1.0) for argument in arguments: if not isinstance(argument, Number): raise SchemeTypeError("Multiplication is only defined for numbers, " "you gave me %s." % argument.__class__) if isinstance(product, Integer) and isinstance(argument, FloatingPoint): product = FloatingPoint(float(product.value)) product.value *= argument.value return product
def test_quasiquote(self): program = "(quasiquote (1 1))" self.assertEvaluatesTo(program, Cons.from_list([Integer(1), Integer(1)])) program = "(quasiquote (unquote 1))" self.assertEvaluatesTo(program, Integer(1)) program = "(quasiquote (1 (unquote (+ 2 2))))" self.assertEvaluatesTo(program, Cons(Integer(1), Cons(Integer(4)))) program = "(quasiquote (1 (unquote-splicing '(2 2))))" self.assertEvaluatesTo( program, Cons(Integer(1), Cons(Integer(2), Cons(Integer(2)))))
def remainder(arguments): check_argument_number('remainder', arguments, 2, 2) if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer): raise SchemeTypeError("remainder is only defined for integers, " "got %s and %s." % (arguments[0].__class__, arguments[1].__class__)) # as with quotient, we can't use Python's integer division here because it floors rather than truncates x1 = arguments[0].value x2 = arguments[1].value value = x1 - (math.trunc(x1 / x2) * x2) return Integer(value)
def quotient(arguments): # integer division check_argument_number('quotient', arguments, 2, 2) if not isinstance(arguments[0], Integer) or not isinstance(arguments[1], Integer): raise SchemeTypeError("quotient is only defined for integers, " "got %s and %s." % (arguments[0].__class__, arguments[1].__class__)) # Python's integer division floors, whereas Scheme rounds towards zero x1 = arguments[0].value x2 = arguments[1].value result = math.trunc(x1 / x2) return Integer(result)
def test_if_two_arguments(self): program = "(if #t 1)" self.assertEvaluatesTo(program, Integer(1))
def test_procedure_call(self): program = "((if #f + *) 3 4)" self.assertEvaluatesTo(program, Integer(12))
def test_variable_evaluation(self): program = "(define x 28) x" self.assertEvaluatesTo(program, Integer(28))
def test_let_last_argument(self): program = "(let ((x 1)) 2 x)" self.assertEvaluatesTo(program, Integer(1))
def test_let(self): program = "(let ((x 1)) x)" self.assertEvaluatesTo(program, Integer(1))
def test_defmacro(self): program = '(defmacro inc (argument) `(+ 1 ,argument)) (inc 5)' self.assertEvaluatesTo(program, Integer(6))