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_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_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_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_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_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_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_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_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 recursive_eval_unquote(s_expression, _environment): """Return a copy of s_expression, with all occurrences of unquoted s-expressions replaced by their evaluated values. Note that we can only have unquote-splicing in a sublist, since we can only return one value, e.g `,@(1 2 3). """ if isinstance(s_expression, Atom): return (s_expression, _environment) elif isinstance(s_expression, Nil): return (s_expression, _environment) elif s_expression[0] == Symbol("unquote"): check_argument_number('unquote', arguments, 1, 1) return eval_s_expression(s_expression[1], _environment) else: # return a list of s_expressions that have been # recursively checked for unquote list_elements = [] for element in s_expression: if isinstance(element, Cons) and \ element[0] == Symbol('unquote-splicing'): check_argument_number('unquote-splicing', element.tail, 1, 1) (result, _environment) = eval_s_expression(element[1], _environment) if not isinstance(result, Cons) and not isinstance( result, Nil): raise SchemeArityError( "unquote-splicing requires a list.") for item in result: list_elements.append(item) else: (result, _environment) = recursive_eval_unquote( element, _environment) list_elements.append(result) return (Cons.from_list(list_elements), _environment)
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 expand_then_eval(arguments, _environment): """Expand this macro once, then continue evaluation.""" if is_variadic: if len(arguments) < len(macro_arguments): raise SchemeArityError( "Macro %s takes at least %d arguments, but got %d." % (macro_name, len(macro_arguments), len(arguments))) else: if len(arguments) != len(macro_arguments): raise SchemeArityError( "Macro %s takes %d arguments, but got %d." % (macro_name, len(macro_arguments), len(arguments))) new_environment = dict(_environment) for (variable_name, variable_value) in zip(macro_arguments, arguments): new_environment[variable_name] = variable_value if is_variadic: remaining_arguments = [] for index, arg in enumerate(arguments): if index >= len(macro_arguments): remaining_arguments.append(arg) new_environment[variadic_argument_name] = Cons.from_list( remaining_arguments) (s_expression_after_expansion, new_environment) = eval_s_expression(replacement_body, new_environment) # restore old environment, ignoring variables hidden by scope for variable_name in _environment: if variable_name not in macro_arguments: _environment[variable_name] = new_environment[variable_name] # continue evaluation where we left off return eval_s_expression(s_expression_after_expansion, _environment)
def test_vector_to_list(self): program = "(vector->list (vector 1 2))" self.assertEvaluatesTo(program, Cons.from_list([Integer(1), Integer(2)]))
def p_list_unquotesplicingsugar(p): "list : UNQUOTESPLICINGSUGAR sexpression" # convert ,foo to (unquote foo) p[0] = Cons(Symbol("unquote-splicing"), Cons(p[2]))
def test_set_car(self): program = "(define x (list 4 5 6)) (set-car! x 1) x" self.assertEvaluatesTo(program, Cons.from_list([Integer(1), Integer(5), Integer(6)]))
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_quote_sugar(self): program = "'(1 2 3)" self.assertEvaluatesTo(program, Cons.from_list([Integer(1), Integer(2), Integer(3)]))
def test_quote_sugar(self): program = "'(1 2 3)" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(2), Integer(3)]))
def cons(arguments): # TODO: check type as well as arity check_argument_number('cons', arguments, 2, 2) return Cons(arguments[0], arguments[1])
def test_set_car(self): program = "(define x (list 4 5 6)) (set-car! x 1) x" self.assertEvaluatesTo( program, Cons.from_list([Integer(1), Integer(5), Integer(6)]))
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_set_cdr(self): program = "(define x (list 4 5 6)) (set-cdr! x 1) x" result = self.evaluate(program) self.assertEqual(result, Cons(Integer(4), Integer(1)))
def p_list_quasiquotesugar(p): "list : QUASIQUOTESUGAR sexpression" # convert `foo to (quasiquote foo) p[0] = Cons(Symbol("quasiquote"), Cons(p[2]))
def p_list_unquotesugar(p): "list : UNQUOTESUGAR sexpression" # convert ,foo to (unquote foo) p[0] = Cons(Symbol("unquote"), Cons(p[2]))
def test_map(self): program = "(map (lambda (x) (+ x 1)) '(2 3))" self.assertEvaluatesTo(program, Cons(Integer(3), Cons(Integer(4))))
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 p_listarguments_one(p): "listarguments : sexpression listarguments" # a list is therefore a nested tuple: p[0] = Cons(p[1], p[2])
def p_program(p): "program : sexpression program" p[0] = Cons(p[1], p[2])