140 lines
5.8 KiB
Swift
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")
|
|
}
|
|
}
|