Skip to content

Test: add List.reverse and deinit testing to Swiftskell #73795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions test/Inputs/Swiftskell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,20 @@ public protocol Generator: ~Copyable {
func next() -> Maybe<Element>
}

/// Eager assertion function, to avoid autoclosures.
public func check(_ result: Bool, _ string: String? = nil,
_ file: String = #file, _ line: Int = #line) {
if result { return }
var msg = "assertion failure (\(file):\(line))"
if let extra = string {
msg += ":\t" + extra
}
fatalError(msg)
}

// MARK: Tuples
public enum Pair<L: ~Copyable, R: ~Copyable>: ~Copyable {
case elms(L, R)
case pair(L, R)
}
extension Pair: Copyable where L: Copyable, R: Copyable {}

Expand Down Expand Up @@ -153,6 +164,8 @@ public struct Box<Wrapped: ~Copyable>: ~Copyable {


/// MARK: Data.List
///
/// A singly-linked list
public enum List<Element: ~Copyable>: ~Copyable {
case cons(Element, Box<List<Element>>)
case empty
Expand All @@ -168,7 +181,7 @@ public enum List<Element: ~Copyable>: ~Copyable {
/// Pure Iteration
extension List where Element: ~Copyable {
/// Performs forward iteration through the list, accumulating a result value.
/// Returns f(xn,...,f(x2, f(x1, init))...), or init if the list is empty.
/// Returns f(xn,...,f(x2, f(x1, init))...), or `init` if the list is empty.
public borrowing func foldl<Out>(
init initial: consuming Out,
_ f: (borrowing Element, consuming Out) -> Out) -> Out
Expand All @@ -185,7 +198,7 @@ extension List where Element: ~Copyable {
}

/// Performs reverse iteration through the list, accumulating a result value.
/// Returns f(x1, f(x2,...,f(xn, init)...)) or init if the list is empty.
/// Returns f(x1, f(x2,...,f(xn, init)...)) or `init` if the list is empty.
public borrowing func foldr<Out>(
init initial: consuming Out,
_ f: (borrowing Element, consuming Out) -> Out) -> Out
Expand Down Expand Up @@ -228,30 +241,52 @@ extension List where Element: ~Copyable {
/// Basic utilities
extension List where Element: ~Copyable {
/// Is this list empty?
public borrowing func empty() -> Bool {
switch self {
case .empty: return true
case .cons(_, _): return false
///
/// Complexity: O(1)
public var isEmpty: Bool {
borrowing get {
switch self {
case .empty: true
case .cons(_, _): false
}
}
}

/// How many elements are in this list?
///
/// Complexity: O(n)
public borrowing func length() -> Int {
return foldl(init: 0) { $1 + 1 }
}

/// Pop the first element off the list, if present.
///
/// Complexity: O(1)
public consuming func pop() -> Optional<Pair<Element, List<Element>>> {
switch consume self {
case .empty: .none
case let .cons(elm, tail): .elms(elm, tail.take())
case let .cons(elm, tail): .pair(elm, tail.take())
}
}

/// Push an element onto the list.
/// Push an element onto the front of the list.
///
/// Complexity: O(1)
public consuming func push(_ newHead: consuming Element) -> List<Element> {
return List(newHead, self)
}

/// Produces a new list that is the reverse of this list.
///
/// Complexity: O(n)
public consuming func reverse() -> List<Element> {
var new = List<Element>()
while case let .pair(head, tail) = pop() {
new = new.push(head)
self = tail
}
return new
}
}

extension List: Show where Element: Show & ~Copyable {
Expand Down
38 changes: 24 additions & 14 deletions test/Interpreter/moveonly_swiftskell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,22 @@
// RUN: -module-name E -o %t/E %target-rpath(%t)
// RUN: %target-codesign %t/E
// RUN: %target-codesign %t/%target-library-name(Swiftskell)
// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) | %FileCheck %s
// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) \
// RUN: | %FileCheck %s --implicit-check-not destroy

// REQUIRES: executable_test

import Swiftskell

/// assertion function
func check(_ result: Bool, _ string: String? = nil, _ line: Int = #line) {
if result { return }
var msg = "assertion failure (line \(line))"
if let extra = string {
msg += ":\t" + extra
}
fatalError(msg)
}

/// Basic noncopyable type for testing.
struct File: ~Copyable, Show {
let id: Int
init(_ id: Int) {
self.id = id
}
func show() -> String { return id.show() }

deinit { print("destroying file \(id)") }
}


Expand All @@ -51,14 +44,31 @@ func testListBasic() {
var items = List<File>(length: 5) { .init($0) }
print(items.show()) // CHECK: [0, 1, 2, 3, 4, ]
check(items.length() == 5)
check(!items.empty())
check(!items.isEmpty)

items = List<File>(length: 5) { .init($0) }
// CHECK: destroying file 4
// CHECK: destroying file 3
// CHECK: destroying file 2
// CHECK: destroying file 1
// CHECK: destroying file 0

items = items.reverse()
check(items.length() == 5)
print(items.show()) // CHECK: [4, 3, 2, 1, 0, ]

items = .empty
// CHECK: destroying file 0
// CHECK: destroying file 1
// CHECK: destroying file 2
// CHECK: destroying file 3
// CHECK: destroying file 4

check(items.length() == 0)
check(items.empty())
check(items.isEmpty)

let nums = List<Int>().push(7).push(7).push(3)
print(nums.show()) // CHECK: [7, 7, 3, ]


}