├── codecov.yml
├── .swiftlint.yml
├── Sources
└── SwiftFSM
│ ├── Internal
│ ├── Syntax
│ │ ├── .Ulysses-Group.plist
│ │ ├── Override.swift
│ │ ├── ResultBuilder.swift
│ │ ├── SuperState.swift
│ │ ├── Actions.swift
│ │ ├── Then.swift
│ │ ├── When.swift
│ │ ├── CompoundSyntax.swift
│ │ ├── Define.swift
│ │ └── Matching.swift
│ ├── Matching
│ │ ├── Collection+Combinations.swift
│ │ └── AnyPredicate.swift
│ ├── Nodes
│ │ ├── AnyTraceable.swift
│ │ ├── SyntaxNode.swift
│ │ ├── Validation
│ │ │ ├── MatchResolvingNode
│ │ │ │ ├── MRNBase.swift
│ │ │ │ ├── LazyMRN.swift
│ │ │ │ └── EagerMRN.swift
│ │ │ └── SemanticValidationNode.swift
│ │ ├── GivenNode.swift
│ │ ├── ThenNode.swift
│ │ ├── WhenNode.swift
│ │ ├── ActionsNode.swift
│ │ ├── MatchingNode.swift
│ │ ├── ActionsResolvingNode.swift
│ │ ├── NodeConvenience.swift
│ │ └── DefineNode.swift
│ └── FSM
│ │ ├── AnyAction.swift
│ │ ├── EagerFSM.swift
│ │ ├── Logger.swift
│ │ ├── LazyFSM.swift
│ │ └── FSMBase.swift
│ └── Public
│ ├── FSMValue
│ ├── FSMValue+Bool.swift
│ ├── FSMValue+Nil.swift
│ ├── FSMValue+Interpolation.swift
│ ├── FSMValue+Equatable.swift
│ ├── FSMValue+String.swift
│ ├── FSMVaue+Dictionary.swift
│ ├── FSMValue+Array.swift
│ ├── FSMValue+Comparable.swift
│ ├── FSMValue.swift
│ └── FSMValue+Numbers.swift
│ ├── OperatorSyntax
│ ├── AnyActionOperators.swift
│ └── PipeOperators.swift
│ ├── FSM.swift
│ └── FunctionSyntax
│ └── ExpandedSyntaxBuilder.swift
├── .gitignore
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ └── xcschemes
│ └── swift-fsm.xcscheme
├── TestPlans
└── SwiftFSMTests.xctestplan
├── Package.resolved
├── Tests
└── SwiftFSMTests
│ ├── Syntax
│ ├── CompoundBlockSyntax
│ │ ├── ThenBlockTests.swift
│ │ ├── OverrideBlockTests.swift
│ │ ├── WhenBlockTests.swift
│ │ ├── SuperStateTests.swift
│ │ ├── DefineTests.swift
│ │ ├── BlockTestsBase.swift
│ │ ├── BuilderTests.swift
│ │ ├── ConditionBlockTests.swift
│ │ └── MatchingBlockTests.swift
│ ├── ResultBuilderTests.swift
│ ├── AnyActionSyntaxTests.swift
│ └── CompoundSyntaxTests.swift
│ ├── Matching
│ ├── CombinationsTests.swift
│ └── PredicateTests.swift
│ ├── Nodes
│ ├── AnyTraceableTests.swift
│ ├── AnyActionTests.swift
│ ├── ActionsNodeTests.swift
│ ├── MatchingNodeTests.swift
│ ├── ThenNodeTests.swift
│ ├── WhenNodeTests.swift
│ ├── NodeTests.swift
│ ├── GivenNodeTests.swift
│ ├── MatchResolvingNode
│ │ ├── MRNTestBase.swift
│ │ ├── LazyMRNTests.swift
│ │ └── EagerMRNTests.swift
│ ├── ActionsResolvingNodeTests.swift
│ └── DefineNodeTests.swift
│ ├── ManualTests.swift
│ ├── PublicAPITests.swift
│ └── FSM
│ └── LoggingTests.swift
├── LICENSE.txt
├── Package.swift
└── .github
└── workflows
└── swift.yml
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - Tests/
3 | coverage:
4 | status:
5 | project: off
6 | patch: off
7 |
8 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - type_name
3 | - force_cast
4 | - force_try
5 | - identifier_name
6 | - nesting
7 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Syntax/.Ulysses-Group.plist:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drseg/swift-fsm/HEAD/Sources/SwiftFSM/Internal/Syntax/.Ulysses-Group.plist
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Public/FSMValue/FSMValue+Bool.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension FSMValue: ExpressibleByBooleanLiteral where T == Bool {
4 | public init(booleanLiteral value: Bool) {
5 | self = .some(value)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Public/FSMValue/FSMValue+Nil.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension FSMValue: ExpressibleByNilLiteral where T: ExpressibleByNilLiteral {
4 | public init(nilLiteral: ()) {
5 | self = .some(nil)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Public/FSMValue/FSMValue+Interpolation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension FSMValue: CustomStringConvertible {
4 | public var description: String {
5 | switch self {
6 | case let .some(value): "\(value)"
7 | default: "\(Self.self).any"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Syntax/Override.swift:
--------------------------------------------------------------------------------
1 | public extension Syntax {
2 | struct Override {
3 | public func callAsFunction(
4 | @MWTABuilder _ group: () -> [MatchingWhenThenActions]
5 | ) -> [MatchingWhenThenActions] {
6 | return group().asOverrides()
7 | }
8 | }
9 | }
10 |
11 | extension [Syntax.MatchingWhenThenActions] {
12 | func asOverrides() -> Self {
13 | (map(\.node) as? [OverridableNode])?.forEach {
14 | $0.isOverride = true
15 | }
16 | return self
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TestPlans/SwiftFSMTests.xctestplan:
--------------------------------------------------------------------------------
1 | {
2 | "configurations" : [
3 | {
4 | "id" : "6E0881C6-B000-4A98-BB5A-1DD6663DE788",
5 | "name" : "Test Scheme Action",
6 | "options" : {
7 |
8 | }
9 | }
10 | ],
11 | "defaultOptions" : {
12 | "testExecutionOrdering" : "random"
13 | },
14 | "testTargets" : [
15 | {
16 | "parallelizable" : true,
17 | "target" : {
18 | "containerPath" : "container:",
19 | "identifier" : "SwiftFSMTests",
20 | "name" : "SwiftFSMTests"
21 | }
22 | }
23 | ],
24 | "version" : 1
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Syntax/ResultBuilder.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public protocol ResultBuilder {
4 | associatedtype T
5 | }
6 |
7 | public extension ResultBuilder {
8 | static func buildExpression( _ row: [T]) -> [T] {
9 | row
10 | }
11 |
12 | static func buildExpression( _ row: T) -> [T] {
13 | [row]
14 | }
15 |
16 | static func buildBlock(_ cs: [T]...) -> [T] {
17 | cs.flattened
18 | }
19 | }
20 |
21 | extension Collection where Element: Collection {
22 | var flattened: [Element.Element] {
23 | flatMap { $0 }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Matching/Collection+Combinations.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Collection where Element: Collection {
4 | typealias Output = [[Element.Element]]
5 |
6 | func combinations() -> Output {
7 | guard !isEmpty else { return [] }
8 |
9 | return reduce([[]], combinations)
10 | }
11 |
12 | private func combinations(_ c1: Output, _ c2: Element) -> Output {
13 | c1.reduce(into: []) { combinations, elem1 in
14 | c2.forEach { elem2 in
15 | combinations.append(elem1 + [elem2])
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Nodes/AnyTraceable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct AnyTraceable: @unchecked Sendable {
4 | let base: AnyHashable
5 | let file: String
6 | let line: Int
7 |
8 | init(_ base: H?, file: String, line: Int) {
9 | self.base = base!
10 | // this arcane syntax ensures 'base' is never optional
11 | self.file = file
12 | self.line = line
13 | }
14 | }
15 |
16 | extension AnyTraceable: Hashable {
17 | static func == (lhs: Self, rhs: Self) -> Bool {
18 | lhs.base == rhs.base
19 | }
20 |
21 | func hash(into hasher: inout Hasher) {
22 | hasher.combine(base)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Public/FSMValue/FSMValue+Equatable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension FSMValue {
4 | public static func == (lhs: Self, rhs: Self) -> Bool {
5 | guard lhs.isSome, rhs.isSome else { return true }
6 |
7 | return lhs.wrappedValue == rhs.wrappedValue
8 | }
9 |
10 | public static func == (lhs: Self, rhs: T) -> Bool {
11 | lhs.wrappedValue == rhs
12 | }
13 |
14 | public static func == (lhs: T, rhs: Self) -> Bool {
15 | lhs == rhs.wrappedValue
16 | }
17 |
18 | public static func != (lhs: Self, rhs: T) -> Bool {
19 | lhs.wrappedValue != rhs
20 | }
21 |
22 | public static func != (lhs: T, rhs: Self) -> Bool {
23 | lhs != rhs.wrappedValue
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "a217560c1de9268368c95c798a750c705f880ab338a334cc8a977b0f57487677",
3 | "pins" : [
4 | {
5 | "identity" : "swift-algorithms",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/apple/swift-algorithms",
8 | "state" : {
9 | "revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
10 | "version" : "1.2.0"
11 | }
12 | },
13 | {
14 | "identity" : "swift-numerics",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/apple/swift-numerics",
17 | "state" : {
18 | "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
19 | "version" : "1.0.2"
20 | }
21 | }
22 | ],
23 | "version" : 3
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/SwiftFSMTests/Syntax/CompoundBlockSyntax/ThenBlockTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import SwiftFSM
3 |
4 | class ThenBlockTests: BlockTestsBase {
5 | func testThenBlockWithMTA() async {
6 | let node = (then(1) { mwaBlock }).thenBlockNode; let line = #line
7 | assertThenNode(node, state: 1, sutFile: #file, sutLine: line)
8 | await assertMWAResult(node.rest, sutLine: mwaLine)
9 | }
10 |
11 | func testThenBlockWithMA() async {
12 | let node = (then(1) { maBlock }).thenBlockNode; let line = #line
13 | assertThenNode(node, state: 1, sutFile: #file, sutLine: line)
14 | await assertMAResult(node.rest, sutLine: maLine)
15 | }
16 | }
17 |
18 | private extension Syntax.CompoundSyntaxGroup {
19 | var thenBlockNode: ThenBlockNode {
20 | node as! ThenBlockNode
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/FSM/AnyAction.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public typealias FSMAction = @isolated(any) () async -> Void
4 | public typealias FSMActionWithEvent = @isolated(any) (Event) async -> Void
5 |
6 | public struct AnyAction: @unchecked Sendable {
7 | public enum NullEvent: FSMHashable { case null }
8 |
9 | private let base: Any
10 |
11 | init(_ action: @escaping FSMAction) {
12 | base = action
13 | }
14 |
15 | init(_ action: @escaping FSMActionWithEvent) {
16 | base = action
17 | }
18 |
19 | func callAsFunction(_ event: Event = NullEvent.null) async {
20 | if let base = self.base as? FSMAction {
21 | await base()
22 | } else if let base = self.base as? FSMActionWithEvent {
23 | await base(event)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/SwiftFSM/Internal/Nodes/SyntaxNode.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol SyntaxNode