feat(core): add core value types and list operations
This commit is contained in:
1 parent
e673d09ac9
commit
48092fe48f
1 file changed
+274
@@ -0,0 +1,274 @@
|
||||
public final class Pair {
|
||||
public var car: Value
|
||||
public var cdr: Value
|
||||
public init(_ car: Value, _ cdr: Value) {
|
||||
self.car = car
|
||||
self.cdr = cdr
|
||||
}
|
||||
}
|
||||
|
||||
public final class Lambda {
|
||||
public let params: [String]
|
||||
public let rest: String?
|
||||
public let body: [Value]
|
||||
public let env: Environment
|
||||
public var name: String
|
||||
init(params: [String], rest: String?, body: [Value], env: Environment, name: String) {
|
||||
self.params = params
|
||||
self.rest = rest
|
||||
self.body = body
|
||||
self.env = env
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpecialKind {
|
||||
case none
|
||||
case apply
|
||||
case callCC
|
||||
case withExceptionHandler
|
||||
case dynamicWind
|
||||
case values
|
||||
case callWithValues
|
||||
}
|
||||
|
||||
public final class VectorRef {
|
||||
public var items: [Value]
|
||||
public init(_ items: [Value]) {
|
||||
self.items = items
|
||||
}
|
||||
}
|
||||
|
||||
public final class ErrorObject {
|
||||
public let message: String
|
||||
public let irritants: [Value]
|
||||
public let kind: String
|
||||
public init(message: String, irritants: [Value], kind: String = "error") {
|
||||
self.message = message
|
||||
self.irritants = irritants
|
||||
self.kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
public final class Promise {
|
||||
public var forced: Bool
|
||||
public var value: Value
|
||||
public var thunk: Value
|
||||
public init(thunk: Value) {
|
||||
self.forced = false
|
||||
self.value = .unspecified
|
||||
self.thunk = thunk
|
||||
}
|
||||
}
|
||||
|
||||
public final class Builtin {
|
||||
public let name: String
|
||||
public let special: SpecialKind
|
||||
public let fn: ([Value]) throws -> Value
|
||||
init(name: String, special: SpecialKind = .none, fn: @escaping ([Value]) throws -> Value) {
|
||||
self.name = name
|
||||
self.special = special
|
||||
self.fn = fn
|
||||
}
|
||||
}
|
||||
|
||||
public indirect enum Value {
|
||||
case null
|
||||
case bool(Bool)
|
||||
case integer(Int)
|
||||
case bigint(BigInt)
|
||||
case rational(BigInt, BigInt)
|
||||
case real(Double)
|
||||
case char(Character)
|
||||
case string(String)
|
||||
case symbol(String)
|
||||
case pair(Pair)
|
||||
case vector(VectorRef)
|
||||
case hashTable(HashTable)
|
||||
case port(Port)
|
||||
case errorObject(ErrorObject)
|
||||
case promise(Promise)
|
||||
case builtin(Builtin)
|
||||
case closure(Lambda)
|
||||
case continuation(Kont, [Winder], [Value])
|
||||
case values([Value])
|
||||
case syntax(SyntaxRules)
|
||||
case unspecified
|
||||
case eof
|
||||
}
|
||||
|
||||
let hygieneMark: Character = "\u{1}"
|
||||
|
||||
func baseName(_ name: String) -> String {
|
||||
if let idx = name.firstIndex(of: hygieneMark) {
|
||||
return String(name[..<idx])
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func isTruthy(_ value: Value) -> Bool {
|
||||
if case .bool(false) = value { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
func cons(_ a: Value, _ d: Value) -> Value {
|
||||
.pair(Pair(a, d))
|
||||
}
|
||||
|
||||
func arrayToList(_ items: [Value], tail: Value = .null) -> Value {
|
||||
var acc = tail
|
||||
for item in items.reversed() {
|
||||
acc = cons(item, acc)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func listToArray(_ value: Value) throws -> [Value] {
|
||||
var out: [Value] = []
|
||||
var cur = value
|
||||
while true {
|
||||
switch cur {
|
||||
case .null:
|
||||
return out
|
||||
case .pair(let p):
|
||||
out.append(p.car)
|
||||
cur = p.cdr
|
||||
default:
|
||||
throw schemeError("expected proper list", [value])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listToArrayWithTail(_ value: Value) -> ([Value], Value) {
|
||||
var out: [Value] = []
|
||||
var cur = value
|
||||
while case .pair(let p) = cur {
|
||||
out.append(p.car)
|
||||
cur = p.cdr
|
||||
}
|
||||
return (out, cur)
|
||||
}
|
||||
|
||||
func isProperList(_ value: Value) -> Bool {
|
||||
var slow = value
|
||||
var fast = value
|
||||
while true {
|
||||
if case .null = fast { return true }
|
||||
guard case .pair(let fp) = fast else { return false }
|
||||
fast = fp.cdr
|
||||
if case .null = fast { return true }
|
||||
guard case .pair(let fp2) = fast else { return false }
|
||||
fast = fp2.cdr
|
||||
guard case .pair(let sp) = slow else { return false }
|
||||
slow = sp.cdr
|
||||
if case .pair(let a) = fast, case .pair(let b) = slow, a === b { return false }
|
||||
}
|
||||
}
|
||||
|
||||
func gcdInt(_ a: Int, _ b: Int) -> Int {
|
||||
var x = a == Int.min ? a : abs(a)
|
||||
var y = b == Int.min ? b : abs(b)
|
||||
x = x < 0 ? -(x + 1) &+ 1 : x
|
||||
y = y < 0 ? -(y + 1) &+ 1 : y
|
||||
while y != 0 {
|
||||
(x, y) = (y, x % y)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func normalizeBig(_ value: BigInt) -> Value {
|
||||
value.fitsInt ? .integer(value.toInt()) : .bigint(value)
|
||||
}
|
||||
|
||||
func bigFrom(_ value: Value) -> BigInt? {
|
||||
switch value {
|
||||
case .integer(let n): return BigInt(n)
|
||||
case .bigint(let b): return b
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeRatio(_ numerator: BigInt, _ denominator: BigInt) -> Value {
|
||||
var num = numerator
|
||||
var den = denominator
|
||||
if den.isNegative {
|
||||
num = num.negated
|
||||
den = den.negated
|
||||
}
|
||||
let divisor = BigInt.gcd(num, den)
|
||||
if !divisor.isZero && !divisor.isOne {
|
||||
num = num.quotient(divisor)
|
||||
den = den.quotient(divisor)
|
||||
}
|
||||
if den.isOne { return normalizeBig(num) }
|
||||
return .rational(num, den)
|
||||
}
|
||||
|
||||
func makeRational(_ n: Int, _ d: Int) -> Value {
|
||||
makeRatio(BigInt(n), BigInt(d))
|
||||
}
|
||||
|
||||
func eqv(_ a: Value, _ b: Value) -> Bool {
|
||||
switch (a, b) {
|
||||
case (.null, .null): return true
|
||||
case (.unspecified, .unspecified): return true
|
||||
case (.eof, .eof): return true
|
||||
case (.bool(let x), .bool(let y)): return x == y
|
||||
case (.integer(let x), .integer(let y)): return x == y
|
||||
case (.bigint(let x), .bigint(let y)): return x == y
|
||||
case (.rational(let xn, let xd), .rational(let yn, let yd)): return xn == yn && xd == yd
|
||||
case (.real(let x), .real(let y)): return x == y
|
||||
case (.char(let x), .char(let y)): return x == y
|
||||
case (.symbol(let x), .symbol(let y)): return x == y
|
||||
case (.string(let x), .string(let y)): return x == y
|
||||
case (.pair(let x), .pair(let y)): return x === y
|
||||
case (.vector(let x), .vector(let y)): return x === y
|
||||
case (.hashTable(let x), .hashTable(let y)): return x === y
|
||||
case (.port(let x), .port(let y)): return x === y
|
||||
case (.errorObject(let x), .errorObject(let y)): return x === y
|
||||
case (.promise(let x), .promise(let y)): return x === y
|
||||
case (.closure(let x), .closure(let y)): return x === y
|
||||
case (.builtin(let x), .builtin(let y)): return x === y
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
func equalValues(_ a: Value, _ b: Value) -> Bool {
|
||||
switch (a, b) {
|
||||
case (.pair(let x), .pair(let y)):
|
||||
return equalValues(x.car, y.car) && equalValues(x.cdr, y.cdr)
|
||||
case (.string(let x), .string(let y)):
|
||||
return x == y
|
||||
case (.vector(let x), .vector(let y)):
|
||||
if x.items.count != y.items.count { return false }
|
||||
for i in 0..<x.items.count where !equalValues(x.items[i], y.items[i]) { return false }
|
||||
return true
|
||||
default:
|
||||
return eqv(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func schemeHash(_ value: Value, _ depth: Int = 0) -> Int {
|
||||
if depth > 12 { return 0 }
|
||||
switch value {
|
||||
case .null: return 1
|
||||
case .bool(let b): return b ? 2 : 3
|
||||
case .integer(let n): return n.hashValue
|
||||
case .bigint(let b): return b.hashValue
|
||||
case .rational(let n, let d): return n.hashValue ^ (d.hashValue &* 31)
|
||||
case .real(let d): return d.hashValue
|
||||
case .char(let c): return c.hashValue
|
||||
case .string(let s): return s.hashValue
|
||||
case .symbol(let s): return s.hashValue
|
||||
case .pair(let p):
|
||||
return (schemeHash(p.car, depth + 1) &* 31) &+ schemeHash(p.cdr, depth + 1) &+ 7
|
||||
case .vector(let v):
|
||||
var acc = 17
|
||||
for item in v.items.prefix(8) { acc = (acc &* 31) &+ schemeHash(item, depth + 1) }
|
||||
return acc
|
||||
case .eof: return 5
|
||||
case .unspecified: return 6
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user