Files
kite/Tests/KiteCoreTests/EvalTests.swift
T

140 lines
5.8 KiB
Swift

import XCTest
@testable import KiteCore
final class EvalTests: XCTestCase {
private func eval(_ src: String) throws -> Value {
try Interpreter().evalString(src)
}
private func evalShow(_ src: String) throws -> String {
displayString(try eval(src))
}
func testArithmetic() throws {
XCTAssertEqual(try evalShow("(+ 1 2 3 4)"), "10")
XCTAssertEqual(try evalShow("(- 10 3 2)"), "5")
XCTAssertEqual(try evalShow("(* 2 3 4)"), "24")
XCTAssertEqual(try evalShow("(- 5)"), "-5")
XCTAssertEqual(try evalShow("(+ )"), "0")
}
func testRationalArithmetic() throws {
XCTAssertEqual(try evalShow("(/ 3 2)"), "3/2")
XCTAssertEqual(try evalShow("(/ 6 2)"), "3")
XCTAssertEqual(try evalShow("(+ 1/2 1/3)"), "5/6")
XCTAssertEqual(try evalShow("(* 2/3 3/4)"), "1/2")
}
func testRealArithmetic() throws {
XCTAssertEqual(try evalShow("(+ 1.5 2.5)"), "4.0")
XCTAssertEqual(try evalShow("(* 2 1.5)"), "3.0")
}
func testComparisons() throws {
XCTAssertEqual(try evalShow("(< 1 2 3)"), "#t")
XCTAssertEqual(try evalShow("(< 1 3 2)"), "#f")
XCTAssertEqual(try evalShow("(= 2 2 2)"), "#t")
XCTAssertEqual(try evalShow("(>= 3 3 2)"), "#t")
}
func testIntegerOps() throws {
XCTAssertEqual(try evalShow("(modulo 7 3)"), "1")
XCTAssertEqual(try evalShow("(modulo -7 3)"), "2")
XCTAssertEqual(try evalShow("(remainder -7 3)"), "-1")
XCTAssertEqual(try evalShow("(quotient 17 5)"), "3")
XCTAssertEqual(try evalShow("(expt 2 10)"), "1024")
XCTAssertEqual(try evalShow("(gcd 12 18)"), "6")
}
func testDefineAndLambda() throws {
XCTAssertEqual(try evalShow("(define (square x) (* x x)) (square 9)"), "81")
XCTAssertEqual(try evalShow("((lambda (a b) (+ a b)) 3 4)"), "7")
XCTAssertEqual(try evalShow("((lambda args (length args)) 1 2 3)"), "3")
XCTAssertEqual(try evalShow("((lambda (a . rest) rest) 1 2 3)"), "(2 3)")
}
func testClosuresAndSet() throws {
let counter = """
(define (make-counter)
(let ((n 0))
(lambda () (set! n (+ n 1)) n)))
(define c (make-counter))
(c) (c)
(c)
"""
XCTAssertEqual(try evalShow(counter), "3")
}
func testLetForms() throws {
XCTAssertEqual(try evalShow("(let ((a 1) (b 2)) (+ a b))"), "3")
XCTAssertEqual(try evalShow("(let* ((a 1) (b (+ a 1))) (* a b))"), "2")
XCTAssertEqual(try evalShow("(letrec ((e? (lambda (n) (if (= n 0) #t (o? (- n 1))))) (o? (lambda (n) (if (= n 0) #f (e? (- n 1)))))) (e? 10))"), "#t")
}
func testCondCaseAndOr() throws {
XCTAssertEqual(try evalShow("(cond ((= 1 2) 'a) ((= 1 1) 'b) (else 'c))"), "b")
XCTAssertEqual(try evalShow("(cond ((assv 2 '((1 . a) (2 . b))) => cdr) (else 'none))"), "b")
XCTAssertEqual(try evalShow("(case 3 ((1 2) 'low) ((3 4) 'mid) (else 'high))"), "mid")
XCTAssertEqual(try evalShow("(and 1 2 3)"), "3")
XCTAssertEqual(try evalShow("(and 1 #f 3)"), "#f")
XCTAssertEqual(try evalShow("(or #f #f 7)"), "7")
XCTAssertEqual(try evalShow("(or #f #f)"), "#f")
}
func testListOps() throws {
XCTAssertEqual(try evalShow("(car '(1 2 3))"), "1")
XCTAssertEqual(try evalShow("(cdr '(1 2 3))"), "(2 3)")
XCTAssertEqual(try evalShow("(cons 1 '(2 3))"), "(1 2 3)")
XCTAssertEqual(try evalShow("(append '(1 2) '(3 4) '(5))"), "(1 2 3 4 5)")
XCTAssertEqual(try evalShow("(reverse '(1 2 3))"), "(3 2 1)")
XCTAssertEqual(try evalShow("(length '(a b c d))"), "4")
XCTAssertEqual(try evalShow("(list-ref '(a b c) 1)"), "b")
XCTAssertEqual(try evalShow("(map (lambda (x) (* x x)) '(1 2 3))"), "(1 4 9)")
XCTAssertEqual(try evalShow("(filter even? '(1 2 3 4 5 6))"), "(2 4 6)")
XCTAssertEqual(try evalShow("(assq 'b '((a . 1) (b . 2)))"), "(b . 2)")
XCTAssertEqual(try evalShow("(member 3 '(1 2 3 4))"), "(3 4)")
}
func testPredicates() throws {
XCTAssertEqual(try evalShow("(null? '())"), "#t")
XCTAssertEqual(try evalShow("(pair? '(1))"), "#t")
XCTAssertEqual(try evalShow("(pair? '())"), "#f")
XCTAssertEqual(try evalShow("(eq? 'a 'a)"), "#t")
XCTAssertEqual(try evalShow("(equal? '(1 2 (3)) '(1 2 (3)))"), "#t")
XCTAssertEqual(try evalShow("(eqv? 2 2)"), "#t")
XCTAssertEqual(try evalShow("(number? 5)"), "#t")
XCTAssertEqual(try evalShow("(procedure? car)"), "#t")
}
func testQuasiquote() throws {
XCTAssertEqual(try evalShow("(let ((x 5)) `(a ,x c))"), "(a 5 c)")
XCTAssertEqual(try evalShow("(let ((xs '(2 3))) `(1 ,@xs 4))"), "(1 2 3 4)")
}
func testApply() throws {
XCTAssertEqual(try evalShow("(apply + '(1 2 3 4))"), "10")
XCTAssertEqual(try evalShow("(apply + 1 2 '(3 4))"), "10")
XCTAssertEqual(try evalShow("(apply max '(3 1 4 1 5 9 2 6))"), "9")
}
func testStrings() throws {
XCTAssertEqual(try evalShow("(string-append \"foo\" \"bar\")"), "foobar")
XCTAssertEqual(try evalShow("(string-length \"hello\")"), "5")
XCTAssertEqual(try evalShow("(substring \"hello\" 1 4)"), "ell")
XCTAssertEqual(try evalShow("(number->string 42)"), "42")
XCTAssertEqual(try evalShow("(string->number \"3/4\")"), "3/4")
}
func testUnboundVariableThrows() throws {
XCTAssertThrowsError(try eval("(+ 1 undefined-thing)"))
}
func testCapturedOutput() throws {
let interp = Interpreter()
var captured = ""
interp.output = { captured += $0 }
try interp.evalString("(display \"result: \") (display (+ 40 2)) (newline)")
XCTAssertEqual(captured, "result: 42\n")
}
}