Skip to content

Commit e676304

Browse files
committed
Initial commit
0 parents  commit e676304

File tree

6 files changed

+159
-0
lines changed

6 files changed

+159
-0
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

LICENSE.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 objc.io
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.swift

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "AttributeGraph",
8+
products: [
9+
// Products define the executables and libraries a package produces, making them visible to other packages.
10+
.library(
11+
name: "AttributeGraph",
12+
targets: ["AttributeGraph"]),
13+
],
14+
targets: [
15+
// Targets are the basic building blocks of a package, defining a module or a test suite.
16+
// Targets can depend on other targets in this package and products from dependencies.
17+
.target(
18+
name: "AttributeGraph"),
19+
.testTarget(
20+
name: "AttributeGraphTests",
21+
dependencies: ["AttributeGraph"]
22+
),
23+
]
24+
)

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Swift Talk
2+
## Attribute Graph (Part 1)
3+
4+
This is the code that accompanies Swift Talk Episode 429: [Attribute Graph (Part 1)](https://talk.objc.io/episodes/S01E429-attribute-graph-part-1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Based on https://www.semanticscholar.org/paper/A-System-for-Efficient-and-Flexible-One-Way-in-C%2B%2B-Hudson/9609985dbef43633f4deb88c949a9776e0cd766b
2+
// https://repository.gatech.edu/server/api/core/bitstreams/3117139f-5de2-4f1f-9662-8723bae97a6d/content
3+
4+
final class AttributeGraph {
5+
var nodes: [AnyNode] = []
6+
7+
func input<A>(name: String, _ value: A) -> Node<A> {
8+
let n = Node(name: name, wrappedValue: value)
9+
nodes.append(n)
10+
return n
11+
}
12+
13+
func rule<A>(name: String, _ rule: @escaping () -> A) -> Node<A> {
14+
let n = Node(name: name, rule: rule)
15+
nodes.append(n)
16+
return n
17+
}
18+
19+
func graphViz() -> String {
20+
let nodes = nodes.map(\.name).joined(separator: "\n")
21+
let edges = ""
22+
return """
23+
digraph {
24+
\(nodes)
25+
\(edges)
26+
}
27+
"""
28+
}
29+
}
30+
31+
protocol AnyNode: AnyObject {
32+
var name: String { get }
33+
}
34+
35+
final class Edge {
36+
unowned var from: AnyNode
37+
unowned var to: AnyNode
38+
39+
init(from: AnyNode, to: AnyNode) {
40+
self.from = from
41+
self.to = to
42+
}
43+
}
44+
45+
final class Node<A>: AnyNode {
46+
var name: String
47+
var rule: (() -> A)?
48+
var incomingEdges: [Edge] = []
49+
var outgoingEdges: [Edge] = []
50+
51+
private var _cachedValue: A?
52+
53+
var wrappedValue: A {
54+
get {
55+
if _cachedValue == nil, let rule {
56+
_cachedValue = rule()
57+
}
58+
return _cachedValue!
59+
}
60+
set {
61+
assert(rule == nil)
62+
_cachedValue = newValue
63+
}
64+
}
65+
66+
init(name: String, wrappedValue: A) {
67+
self.name = name
68+
self._cachedValue = wrappedValue
69+
}
70+
71+
init(name: String, rule: @escaping () -> A) {
72+
self.name = name
73+
self.rule = rule
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Testing
2+
@testable import AttributeGraph
3+
4+
@Test func example() async throws {
5+
let graph = AttributeGraph()
6+
let a = graph.input(name: "A", 10)
7+
let b = graph.input(name: "B", 20)
8+
let c = graph.rule(name: "C") { a.wrappedValue + b.wrappedValue }
9+
#expect(c.wrappedValue == 30)
10+
11+
let str = """
12+
digraph {
13+
A
14+
B
15+
C
16+
A -> C
17+
B -> C
18+
}
19+
"""
20+
#expect(str == graph.graphViz())
21+
22+
// a.wrappedValue = 40
23+
// c == 60
24+
//
25+
// a -> c
26+
// b -> c
27+
}

0 commit comments

Comments
 (0)