144 lines
4.5 KiB
Python
Executable File
144 lines
4.5 KiB
Python
Executable File
from typing import Optional
|
|
|
|
from pytest import mark, fixture
|
|
|
|
from lis import parse, evaluate, Expression, Environment, standard_env
|
|
|
|
|
|
@mark.parametrize( 'source, expected', [
|
|
("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]),
|
|
("(+ 2 2)", 4),
|
|
("(+ (* 2 100) (* 1 10))", 210),
|
|
("(if (> 6 5) (+ 1 1) (+ 2 2))", 2),
|
|
("(if (< 6 5) (+ 1 1) (+ 2 2))", 4),
|
|
("(define x 3)", None), ("x", 3), ("(+ x x)", 6),
|
|
("((lambda (x) (+ x x)) 5)", 10),
|
|
("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10),
|
|
("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None),
|
|
("((compose list twice) 5)", [10]),
|
|
("(define repeat (lambda (f) (compose f f)))", None),
|
|
("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80),
|
|
("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None),
|
|
("(fact 3)", 6),
|
|
("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000),
|
|
("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None),
|
|
("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]),
|
|
("""(define combine (lambda (f)
|
|
(lambda (x y)
|
|
(if (null? x) (quote ())
|
|
(f (list (car x) (car y))
|
|
((combine f) (cdr x) (cdr y)))))))""", None),
|
|
("(define zip (combine cons))", None),
|
|
("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]),
|
|
("""(define riff-shuffle (lambda (deck) (begin
|
|
(define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq))))))
|
|
(define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq)))))
|
|
(define mid (lambda (seq) (/ (length seq) 2)))
|
|
((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None),
|
|
("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]),
|
|
("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]),
|
|
("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]),
|
|
])
|
|
def test_evaluate(source: str, expected: Optional[Expression]) -> None:
|
|
got = evaluate(parse(source))
|
|
assert got == expected
|
|
|
|
|
|
@fixture
|
|
def std_env() -> Environment:
|
|
return standard_env()
|
|
|
|
# tests for each of the cases in evaluate
|
|
|
|
def test_evaluate_variable() -> None:
|
|
env: Environment = dict(x=10)
|
|
source = 'x'
|
|
expected = 10
|
|
got = evaluate(parse(source), env)
|
|
assert got == expected
|
|
|
|
|
|
def test_evaluate_literal() -> None:
|
|
source = '3.3'
|
|
expected = 3.3
|
|
got = evaluate(parse(source))
|
|
assert got == expected
|
|
|
|
|
|
def test_evaluate_quote() -> None:
|
|
source = '(quote (1.1 is not 1))'
|
|
expected = [1.1, 'is', 'not', 1]
|
|
got = evaluate(parse(source))
|
|
assert got == expected
|
|
|
|
|
|
def test_evaluate_if_true() -> None:
|
|
source = '(if 1 10 no-such-thing)'
|
|
expected = 10
|
|
got = evaluate(parse(source))
|
|
assert got == expected
|
|
|
|
|
|
def test_evaluate_if_false() -> None:
|
|
source = '(if 0 no-such-thing 20)'
|
|
expected = 20
|
|
got = evaluate(parse(source))
|
|
assert got == expected
|
|
|
|
|
|
def test_define(std_env: Environment) -> None:
|
|
source = '(define answer (* 6 7))'
|
|
got = evaluate(parse(source), std_env)
|
|
assert got is None
|
|
assert std_env['answer'] == 42
|
|
|
|
|
|
def test_lambda(std_env: Environment) -> None:
|
|
source = '(lambda (a b) (if (>= a b) a b))'
|
|
func = evaluate(parse(source), std_env)
|
|
assert func.parms == ['a', 'b']
|
|
assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b']
|
|
assert func.env is std_env
|
|
assert func(1, 2) == 2
|
|
assert func(3, 2) == 3
|
|
|
|
|
|
def test_begin(std_env: Environment) -> None:
|
|
source = """
|
|
(begin
|
|
(define x (* 2 3))
|
|
(* x 7)
|
|
)
|
|
"""
|
|
got = evaluate(parse(source), std_env)
|
|
assert got == 42
|
|
|
|
|
|
def test_invocation_builtin_car(std_env: Environment) -> None:
|
|
source = '(car (quote (11 22 33)))'
|
|
got = evaluate(parse(source), std_env)
|
|
assert got == 11
|
|
|
|
|
|
def test_invocation_builtin_append(std_env: Environment) -> None:
|
|
source = '(append (quote (a b)) (quote (c d)))'
|
|
got = evaluate(parse(source), std_env)
|
|
assert got == ['a', 'b', 'c', 'd']
|
|
|
|
|
|
def test_invocation_builtin_map(std_env: Environment) -> None:
|
|
source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))'
|
|
got = evaluate(parse(source), std_env)
|
|
assert got == [2, 4, 6]
|
|
|
|
|
|
def test_invocation_user_procedure(std_env: Environment) -> None:
|
|
source = """
|
|
(begin
|
|
(define max (lambda (a b) (if (>= a b) a b)))
|
|
(max 22 11)
|
|
)
|
|
"""
|
|
got = evaluate(parse(source), std_env)
|
|
assert got == 22
|