From c925f014b46abc431d4930c794f642b8f1195498 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 26 Aug 2026 16:20:00 +0200 Subject: [PATCH] feat(builtins): add list, predicate, string, and i/o primitives --- Sources/KiteCore/Builtins.swift | 236 ++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/Sources/KiteCore/Builtins.swift b/Sources/KiteCore/Builtins.swift index 0e31083..866bed7 100644 --- a/Sources/KiteCore/Builtins.swift +++ b/Sources/KiteCore/Builtins.swift @@ -133,4 +133,240 @@ func installBuiltins(into env: Environment, interp: Interpreter) { def("odd?") { args in .bool(try intArg(args[0], "odd?") % 2 != 0) } def("1+") { args in numToValue(numAdd(try asNum(args[0], "1+"), .int(1))) } def("1-") { args in numToValue(numSub(try asNum(args[0], "1-"), .int(1))) } + + def("cons") { args in cons(args[0], args[1]) } + func pairArg(_ v: Value, _ who: String) throws -> Pair { + guard case .pair(let p) = v else { throw schemeError("\(who): expected pair", [v]) } + return p + } + def("car") { args in try pairArg(args[0], "car").car } + def("cdr") { args in try pairArg(args[0], "cdr").cdr } + def("caar") { args in try pairArg(try pairArg(args[0], "caar").car, "caar").car } + def("cadr") { args in try pairArg(try pairArg(args[0], "cadr").cdr, "cadr").car } + def("cdar") { args in try pairArg(try pairArg(args[0], "cdar").car, "cdar").cdr } + def("cddr") { args in try pairArg(try pairArg(args[0], "cddr").cdr, "cddr").cdr } + def("caddr") { args in + let d = try pairArg(args[0], "caddr").cdr + let dd = try pairArg(d, "caddr").cdr + return try pairArg(dd, "caddr").car + } + def("cadddr") { args in + let d = try pairArg(args[0], "cadddr").cdr + let dd = try pairArg(d, "cadddr").cdr + let ddd = try pairArg(dd, "cadddr").cdr + return try pairArg(ddd, "cadddr").car + } + def("set-car!") { args in try pairArg(args[0], "set-car!").car = args[1]; return .unspecified } + def("set-cdr!") { args in try pairArg(args[0], "set-cdr!").cdr = args[1]; return .unspecified } + def("list") { args in arrayToList(args) } + def("length") { args in .integer(try listToArray(args[0]).count) } + def("reverse") { args in arrayToList(try listToArray(args[0]).reversed()) } + def("append") { args in + if args.isEmpty { return .null } + var result = args[args.count - 1] + for list in args[0..<(args.count - 1)].reversed() { + let items = try listToArray(list) + result = arrayToList(items, tail: result) + } + return result + } + def("list-ref") { args in + let items = try listToArray(args[0]) + let i = try intArg(args[1], "list-ref") + guard i >= 0, i < items.count else { throw schemeError("list-ref: index out of range") } + return items[i] + } + def("list-tail") { args in + var cur = args[0] + let k = try intArg(args[1], "list-tail") + for _ in 0.. Bool) -> ([Value]) throws -> Value { + return { args in + var cur = args[1] + while case .pair(let p) = cur { + if pred(args[0], p.car) { return cur } + cur = p.cdr + } + return .bool(false) + } + } + def("member", .none, memberBy(equalValues)) + def("memq", .none, memberBy(eqv)) + def("memv", .none, memberBy(eqv)) + + func assocBy(_ pred: @escaping (Value, Value) -> Bool) -> ([Value]) throws -> Value { + return { args in + var cur = args[1] + while case .pair(let p) = cur { + if case .pair(let entry) = p.car, pred(args[0], entry.car) { return p.car } + cur = p.cdr + } + return .bool(false) + } + } + def("assoc", .none, assocBy(equalValues)) + def("assq", .none, assocBy(eqv)) + def("assv", .none, assocBy(eqv)) + + def("null?") { args in if case .null = args[0] { return .bool(true) }; return .bool(false) } + def("pair?") { args in if case .pair = args[0] { return .bool(true) }; return .bool(false) } + def("list?") { args in .bool(isProperList(args[0])) } + def("symbol?") { args in if case .symbol = args[0] { return .bool(true) }; return .bool(false) } + def("string?") { args in if case .string = args[0] { return .bool(true) }; return .bool(false) } + def("char?") { args in if case .char = args[0] { return .bool(true) }; return .bool(false) } + def("boolean?") { args in if case .bool = args[0] { return .bool(true) }; return .bool(false) } + def("number?") { args in + switch args[0] { + case .integer, .rational, .real: return .bool(true) + default: return .bool(false) + } + } + def("integer?") { args in if case .integer = args[0] { return .bool(true) }; return .bool(false) } + def("procedure?") { args in + switch args[0] { + case .builtin, .closure, .continuation: return .bool(true) + default: return .bool(false) + } + } + def("not") { args in .bool(!isTruthy(args[0])) } + def("eq?") { args in .bool(eqv(args[0], args[1])) } + def("eqv?") { args in .bool(eqv(args[0], args[1])) } + def("equal?") { args in .bool(equalValues(args[0], args[1])) } + + def("display") { [weak interp] args in interp?.output(displayString(args[0])); return .unspecified } + def("write") { [weak interp] args in interp?.output(writeString(args[0])); return .unspecified } + def("newline") { [weak interp] _ in interp?.output("\n"); return .unspecified } + def("error") { args in + let message: String + if let first = args.first, case .string(let s) = first { message = s } + else if let first = args.first { message = writeString(first) } + else { message = "error" } + throw schemeError(message, Array(args.dropFirst())) + } + + def("apply", .apply) { _ in .unspecified } + def("call-with-current-continuation", .callCC) { _ in .unspecified } + def("call/cc", .callCC) { _ in .unspecified } + + def("string-length") { args in + guard case .string(let s) = args[0] else { throw schemeError("string-length: expected string") } + return .integer(s.count) + } + def("string-append") { args in + var out = "" + for a in args { + guard case .string(let s) = a else { throw schemeError("string-append: expected string") } + out += s + } + return .string(out) + } + def("substring") { args in + guard case .string(let s) = args[0] else { throw schemeError("substring: expected string") } + let chars = Array(s) + let start = try intArg(args[1], "substring") + let end = args.count > 2 ? try intArg(args[2], "substring") : chars.count + guard start >= 0, end <= chars.count, start <= end else { throw schemeError("substring: range error") } + return .string(String(chars[start..symbol") { args in + guard case .string(let s) = args[0] else { throw schemeError("string->symbol: expected string") } + return .symbol(s) + } + def("symbol->string") { args in + guard case .symbol(let s) = args[0] else { throw schemeError("symbol->string: expected symbol") } + return .string(baseName(s)) + } + def("number->string") { args in .string(displayString(args[0])) } + def("string->number") { args in + guard case .string(let s) = args[0] else { throw schemeError("string->number: expected string") } + if let n = parseInteger(s) { return .integer(n) } + if let rat = parseRational(s) { return rat } + if let d = parseReal(s) { return .real(d) } + return .bool(false) + } + def("char->integer") { args in + guard case .char(let c) = args[0], let scalar = c.unicodeScalars.first else { + throw schemeError("char->integer: expected char") + } + return .integer(Int(scalar.value)) + } + def("integer->char") { args in + let n = try intArg(args[0], "integer->char") + guard let scalar = Unicode.Scalar(UInt32(n)) else { throw schemeError("integer->char: out of range") } + return .char(Character(scalar)) + } + def("char=?") { args in + guard case .char(let a) = args[0], case .char(let b) = args[1] else { + throw schemeError("char=?: expected chars") + } + return .bool(a == b) + } + + def("exact->inexact") { args in .real(numToDouble(try asNum(args[0], "exact->inexact"))) } + def("inexact->exact") { args in + switch args[0] { + case .integer, .rational: return args[0] + case .real(let d): + if d == d.rounded() { return .integer(Int(d)) } + return args[0] + default: throw schemeError("inexact->exact: expected number") + } + } }