├── .gitignore ├── Package.swift ├── README.md └── Sources ├── DFA ├── DFA.monopic ├── DFA.swift ├── DFADesign.swift ├── DFARuleBook.swift ├── FARule.swift └── main.swift ├── DPDA ├── DPDA.swift ├── DPDADesign.swift ├── DPDARuleBook.swift ├── PDAConfiguration.swift ├── PDARule.swift ├── Stack.swift └── main.swift ├── FARule.swift ├── NFA ├── DFA.swift ├── DFADesign.swift ├── DFARuleBook.swift ├── FARule.swift ├── NFA.monopic ├── NFA.swift ├── NFADesign.swift ├── NFARuleBook.swift ├── NFASimulation.monopic ├── NFASimulation.swift ├── freeMoveNFA.monopic └── main.swift ├── NPDA ├── NPDA.swift ├── NPDADesign.swift ├── NPDARuleBook.swift ├── PDAConfiguration.swift ├── PDARule.swift ├── Stack.swift └── main.swift ├── PDAConfiguration.swift ├── PDARule.swift ├── RE ├── FARule.swift ├── NFA.swift ├── NFADesign.swift ├── NFARuleBook.swift ├── Pattern.swift └── main.swift ├── Stack.swift └── TM ├── DTM.swift ├── DTMRuleBook.swift ├── TMConfiguration.swift ├── TMRule.swift ├── Tape.swift └── main.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .build/ 4 | .swiftpm/ 5 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "computation", 7 | products: [ 8 | .executable(name: "dfa", targets: ["DFA"]), 9 | .executable(name: "nfa", targets: ["NFA"]), 10 | .executable(name: "re", targets: ["RE"]), 11 | .executable(name: "dpda", targets: ["DPDA"]), 12 | .executable(name: "npda", targets: ["NPDA"]), 13 | .executable(name: "tm", targets: ["TM"]), 14 | ], 15 | targets: [ 16 | .target( 17 | name: "DFA", 18 | exclude: ["DFA.monopic"] 19 | ), 20 | .target( 21 | name: "NFA", 22 | exclude: [ 23 | "NFA.monopic", 24 | "NFASimulation.monopic", 25 | "freeMoveNFA.monopic", 26 | ] 27 | ), 28 | .target(name: "RE"), 29 | .target(name: "DPDA"), 30 | .target(name: "NPDA"), 31 | .target(name: "TM"), 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Understanding Computation: From Simple Machines to Impossible Programs 2 | 3 | Rewrite some Ruby code in this book (中译《计算的本质》) with Swift. 4 | 5 | # Build 6 | 7 | ``` bash 8 | swift build 9 | ``` 10 | 11 | # Run 12 | 13 | - DFA, Deterministic Finite Automation (确定性有限自动机), `swift run dfa` 14 | - NFA, Nondeterministic Finite Automaton (非确定性有限自动机), `swift run nfa` 15 | - RE, Regular Expression (正则表达式), `swift run re` 16 | - DPDA, Deterministic PushDown Automaton (确定性下推自动机), `swift run dpda` 17 | - NPDA, Nondeterministic PushDown Automaton (非确定性下推自动机), `swift run npda` 18 | - TM, Turing Machine (图灵机), `swift run tm` 19 | 20 | # Contact 21 | 22 | [@nixzhu](https://twitter.com/nixzhu) 23 | -------------------------------------------------------------------------------- /Sources/DFA/DFA.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixzhu/computation/7b77bed3bd63c43a12342e5a592f6d804686638f/Sources/DFA/DFA.monopic -------------------------------------------------------------------------------- /Sources/DFA/DFA.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DFA { 4 | var currentState: State 5 | let acceptStates: Set 6 | let ruleBook: DFARuleBook 7 | 8 | var accepting: Bool { 9 | acceptStates.contains(currentState) 10 | } 11 | 12 | mutating func readCharacter(_ character: Character) { 13 | if let state = ruleBook.nextState(from: currentState, for: character) { 14 | currentState = state 15 | } else { 16 | print("Invalid character: \(character)") 17 | } 18 | } 19 | 20 | mutating func readString(_ string: String) { 21 | string.forEach { readCharacter($0) } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/DFA/DFADesign.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DFADesign { 4 | let startState: State 5 | let acceptStates: Set 6 | let ruleBook: DFARuleBook 7 | 8 | func generateDFA() -> DFA { 9 | .init(currentState: startState, acceptStates: acceptStates, ruleBook: ruleBook) 10 | } 11 | 12 | func canAcceptsString(_ string: String) -> Bool { 13 | var dfa = generateDFA() 14 | dfa.readString(string) 15 | 16 | return dfa.accepting 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/DFA/DFARuleBook.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DFARuleBook { 4 | let rules: [FARule] 5 | 6 | func nextState(from state: State, for character: Character) -> State? { 7 | ruleFor(state: state, character: character)?.followState 8 | } 9 | 10 | private func ruleFor(state: State, character: Character) -> FARule? { 11 | rules.first(where: { $0.canAppliesTo(state: state, character: character) }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/DFA/FARule.swift: -------------------------------------------------------------------------------- 1 | ../FARule.swift -------------------------------------------------------------------------------- /Sources/DFA/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | // b a a,b 4 | // ┌───┐ ┌───┐ ┌───┐ 5 | // │ ▼ │ ▼ │ ▼ 6 | // ┌─────┐ a ┌─────┐ b ╔═════╗ 7 | // ─────▶│ 1 │─────▶│ 2 │─────▶║ 3 ║ 8 | // └─────┘ └─────┘ ╚═════╝ 9 | 10 | print("Accepts string which contains `ab`:") 11 | 12 | let rules = [ 13 | FARule(state: 1, character: "a", nextState: 2), 14 | FARule(state: 1, character: "b", nextState: 1), 15 | FARule(state: 2, character: "a", nextState: 2), 16 | FARule(state: 2, character: "b", nextState: 3), 17 | FARule(state: 3, character: "a", nextState: 3), 18 | FARule(state: 3, character: "b", nextState: 3), 19 | ] 20 | 21 | let ruleBook = DFARuleBook(rules: rules) 22 | 23 | ["a", "baa", "baba"].forEach { 24 | let dfaDesign = DFADesign(startState: 1, acceptStates: [3], ruleBook: ruleBook) 25 | let result = dfaDesign.canAcceptsString($0) 26 | print("\($0)\t\(result)") 27 | } 28 | -------------------------------------------------------------------------------- /Sources/DPDA/DPDA.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DPDA { 4 | var currentConfiguration: PDAConfiguration? 5 | let acceptStates: Set 6 | let ruleBook: DPDARuleBook 7 | 8 | var accepting: Bool { 9 | guard let configuration = currentConfiguration else { 10 | return false 11 | } 12 | return acceptStates.contains(configuration.state) 13 | } 14 | 15 | private var stuck: Bool { 16 | currentConfiguration == nil 17 | } 18 | 19 | init( 20 | currentConfiguration: PDAConfiguration, 21 | acceptStates: Set, 22 | ruleBook: DPDARuleBook 23 | ) { 24 | self.currentConfiguration = ruleBook.followFreeMoves(for: currentConfiguration) 25 | self.acceptStates = acceptStates 26 | self.ruleBook = ruleBook 27 | } 28 | 29 | mutating func readCharacter(_ character: Character) { 30 | guard let configuration = currentConfiguration else { 31 | return 32 | } 33 | if let nextConfiguration = ruleBook.nextConfiguration(from: configuration, for: character) { 34 | currentConfiguration = ruleBook.followFreeMoves(for: nextConfiguration) 35 | } else { 36 | currentConfiguration = nil 37 | } 38 | } 39 | 40 | mutating func readString(_ string: String) { 41 | for character in string { 42 | readCharacter(character) 43 | 44 | if stuck { 45 | break 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/DPDA/DPDADesign.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DPDADesign { 4 | let startState: State 5 | let bottomCharacter: Character 6 | let acceptStates: Set 7 | let ruleBook: DPDARuleBook 8 | 9 | func canAcceptsString(_ string: String) -> Bool { 10 | var dpda = generateDPDA() 11 | dpda.readString(string) 12 | 13 | return dpda.accepting 14 | } 15 | 16 | func generateDPDA() -> DPDA { 17 | let startStack = Stack([bottomCharacter]) 18 | let startConfiguration = PDAConfiguration(state: startState, stack: startStack) 19 | return .init( 20 | currentConfiguration: startConfiguration, 21 | acceptStates: acceptStates, 22 | ruleBook: ruleBook 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/DPDA/DPDARuleBook.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DPDARuleBook { 4 | let rules: [PDARule] 5 | 6 | func nextConfiguration( 7 | from configuration: PDAConfiguration, 8 | for character: Character? 9 | ) -> PDAConfiguration? { 10 | ruleFor(configuration: configuration, character: character)? 11 | .followConfiguration(from: configuration) 12 | } 13 | 14 | func canAppliesTo(configuration: PDAConfiguration, character: Character?) -> Bool { 15 | ruleFor(configuration: configuration, character: character) != nil 16 | } 17 | 18 | func followFreeMoves(for configuration: PDAConfiguration) -> PDAConfiguration { 19 | if canAppliesTo(configuration: configuration, character: nil) { 20 | return followFreeMoves(for: nextConfiguration(from: configuration, for: nil)!) 21 | } else { 22 | return configuration 23 | } 24 | } 25 | 26 | private func ruleFor( 27 | configuration: PDAConfiguration, 28 | character: Character? 29 | ) -> PDARule? { 30 | rules.first(where: { $0.canAppliesTo(configuration: configuration, character: character) }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/DPDA/PDAConfiguration.swift: -------------------------------------------------------------------------------- 1 | ../PDAConfiguration.swift -------------------------------------------------------------------------------- /Sources/DPDA/PDARule.swift: -------------------------------------------------------------------------------- 1 | ../PDARule.swift -------------------------------------------------------------------------------- /Sources/DPDA/Stack.swift: -------------------------------------------------------------------------------- 1 | ../Stack.swift -------------------------------------------------------------------------------- /Sources/DPDA/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | // 识别括号匹配 4 | 5 | let configuration = PDAConfiguration(state: 1, stack: Stack(["$"])) 6 | 7 | let rules = [ 8 | PDARule(state: 1, character: "(", nextState: 2, popCharacter: "$", pushCharacters: ["b", "$"]), 9 | PDARule(state: 2, character: "(", nextState: 2, popCharacter: "b", pushCharacters: ["b", "b"]), 10 | PDARule(state: 2, character: ")", nextState: 2, popCharacter: "b", pushCharacters: []), 11 | PDARule(state: 2, character: nil, nextState: 1, popCharacter: "$", pushCharacters: ["$"]), 12 | ] 13 | 14 | let ruleBook = DPDARuleBook(rules: rules) 15 | 16 | let dpdaDesign = DPDADesign( 17 | startState: 1, 18 | bottomCharacter: "$", 19 | acceptStates: [1], 20 | ruleBook: ruleBook 21 | ) 22 | 23 | print(dpdaDesign.canAcceptsString("(((((((((())))))))))")) 24 | print(dpdaDesign.canAcceptsString("()(())((()))(()(()))")) 25 | print(dpdaDesign.canAcceptsString("(()(()(()()(()()))()")) 26 | print(dpdaDesign.canAcceptsString("())")) 27 | -------------------------------------------------------------------------------- /Sources/FARule.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct FARule { 4 | let state: State 5 | let character: Character? 6 | let nextState: State 7 | 8 | var followState: State { 9 | nextState 10 | } 11 | 12 | func canAppliesTo(state: State, character: Character?) -> Bool { 13 | self.state == state && self.character == character 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/NFA/DFA.swift: -------------------------------------------------------------------------------- 1 | ../DFA/DFA.swift -------------------------------------------------------------------------------- /Sources/NFA/DFADesign.swift: -------------------------------------------------------------------------------- 1 | ../DFA/DFADesign.swift -------------------------------------------------------------------------------- /Sources/NFA/DFARuleBook.swift: -------------------------------------------------------------------------------- 1 | ../DFA/DFARuleBook.swift -------------------------------------------------------------------------------- /Sources/NFA/FARule.swift: -------------------------------------------------------------------------------- 1 | ../FARule.swift -------------------------------------------------------------------------------- /Sources/NFA/NFA.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixzhu/computation/7b77bed3bd63c43a12342e5a592f6d804686638f/Sources/NFA/NFA.monopic -------------------------------------------------------------------------------- /Sources/NFA/NFA.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NFA { 4 | var currentStates: Set 5 | let acceptStates: Set 6 | let ruleBook: NFARuleBook 7 | 8 | var accepting: Bool { 9 | !acceptStates.isDisjoint(with: currentStates) 10 | } 11 | 12 | init(currentStates: Set, acceptStates: Set, ruleBook: NFARuleBook) { 13 | self.currentStates = ruleBook.followFreeMoves(for: currentStates) 14 | self.acceptStates = acceptStates 15 | self.ruleBook = ruleBook 16 | } 17 | 18 | mutating func readCharacter(_ character: Character) { 19 | let _currentStates = ruleBook.nextStates(from: currentStates, for: character) 20 | currentStates = ruleBook.followFreeMoves(for: _currentStates) 21 | } 22 | 23 | mutating func readString(_ string: String) { 24 | string.forEach { readCharacter($0) } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/NFA/NFADesign.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NFADesign { 4 | let startState: State 5 | let acceptStates: Set 6 | let ruleBook: NFARuleBook 7 | 8 | func generateNFA(from currentStates: Set? = nil) -> NFA { 9 | .init( 10 | currentStates: currentStates ?? [startState], 11 | acceptStates: acceptStates, 12 | ruleBook: ruleBook 13 | ) 14 | } 15 | 16 | func canAcceptsString(_ string: String) -> Bool { 17 | var nfa = generateNFA() 18 | nfa.readString(string) 19 | 20 | return nfa.accepting 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/NFA/NFARuleBook.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NFARuleBook { 4 | let rules: [FARule] 5 | 6 | var alphabet: Set { 7 | Set(rules.compactMap { $0.character }) 8 | } 9 | 10 | func nextStates(from states: Set, for character: Character?) -> Set { 11 | Set(states.flatMap({ followStatesFor(state: $0, character: character) })) 12 | } 13 | 14 | private func followStatesFor(state: State, character: Character?) -> [State] { 15 | rulesFor(state: state, character: character).map { $0.followState } 16 | } 17 | 18 | private func rulesFor(state: State, character: Character?) -> [FARule] { 19 | rules.filter { $0.canAppliesTo(state: state, character: character) } 20 | } 21 | 22 | func followFreeMoves(for states: Set) -> Set { 23 | let moreStates = nextStates(from: states, for: nil) 24 | if moreStates.isSubset(of: states) { 25 | return states 26 | } else { 27 | return followFreeMoves(for: states.union(moreStates)) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/NFA/NFASimulation.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixzhu/computation/7b77bed3bd63c43a12342e5a592f6d804686638f/Sources/NFA/NFASimulation.monopic -------------------------------------------------------------------------------- /Sources/NFA/NFASimulation.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NFASimulation { 4 | let nfaDesign: NFADesign 5 | 6 | func generateDFADesign() -> DFADesign> { 7 | let startState = nfaDesign.generateNFA().currentStates 8 | let (states, rules) = discoverStatesAndRules(for: [startState]) 9 | let acceptStates = Set(states.filter({ nfaDesign.generateNFA(from: $0).accepting })) 10 | return .init(startState: startState, acceptStates: acceptStates, ruleBook: .init(rules: rules)) 11 | } 12 | 13 | private func nextState(from state: Set, for character: Character) -> Set { 14 | var nfa = nfaDesign.generateNFA(from: state) 15 | nfa.readCharacter(character) 16 | 17 | return nfa.currentStates 18 | } 19 | 20 | private func rulesFor(state: Set) -> [FARule>] { 21 | nfaDesign.ruleBook.alphabet.map { 22 | .init(state: state, character: $0, nextState: nextState(from: state, for: $0)) 23 | } 24 | } 25 | 26 | private func discoverStatesAndRules( 27 | for states: Set> 28 | ) -> (Set>, [FARule>]) { 29 | let rules = states.flatMap { rulesFor(state: $0) } 30 | let moreStates = Set(rules.map { $0.followState }) 31 | if moreStates.isSubset(of: states) { 32 | return (states, rules) 33 | } else { 34 | return discoverStatesAndRules(for: states.union(moreStates)) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/NFA/freeMoveNFA.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixzhu/computation/7b77bed3bd63c43a12342e5a592f6d804686638f/Sources/NFA/freeMoveNFA.monopic -------------------------------------------------------------------------------- /Sources/NFA/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | do { 4 | // a,b 5 | // ┌───┐ 6 | // │ ▼ 7 | // ┌─────┐ b ┌─────┐ a,b ┌─────┐ a,b ╔═════╗ 8 | // ─────▶│ 1 │─────▶│ 2 │─────▶│ 3 │─────▶║ 4 ║ 9 | // └─────┘ └─────┘ └─────┘ ╚═════╝ 10 | 11 | print("Accepts string which the third-last character is `b`:") 12 | 13 | let rules = [ 14 | FARule(state: 1, character: "a", nextState: 1), 15 | FARule(state: 1, character: "b", nextState: 1), 16 | FARule(state: 1, character: "b", nextState: 2), 17 | FARule(state: 2, character: "a", nextState: 3), 18 | FARule(state: 2, character: "b", nextState: 3), 19 | FARule(state: 3, character: "a", nextState: 4), 20 | FARule(state: 3, character: "b", nextState: 4), 21 | ] 22 | 23 | let ruleBook = NFARuleBook(rules: rules) 24 | 25 | ["bab", "bbbbb", "bbabb"].forEach { 26 | let nfaDesign = NFADesign(startState: 1, acceptStates: [4], ruleBook: ruleBook) 27 | let result = nfaDesign.canAcceptsString($0) 28 | print("\($0)\t\(result)") 29 | } 30 | } 31 | 32 | do { 33 | // a 34 | // ┌───────────┐ 35 | // │ ▼ 36 | // ┌─────┐ ┌─────┐ 37 | // ─ ▶│ 2 │ │ 3 │ 38 | // │ └─────┘ └─────┘ 39 | // ▲ a │ 40 | // │ └───────────┘ 41 | // 42 | // ┌─────┐ │ 43 | // ─────▶│ 1 │─ ─ ─ ┌─────┐ a ┌─────┐ 44 | // └─────┘ └ ─ ─ ▶│ 4 │────────▶│ 5 │ 45 | // └─────┘ └─────┘ 46 | // ▲ │ 47 | // │ a ┌─────┐ a │ 48 | // └────│ 6 │◀───┘ 49 | // └─────┘ 50 | 51 | print("Accepts string which length is a multiple of two or three:") 52 | 53 | let rules = [ 54 | FARule(state: 1, character: nil, nextState: 2), 55 | FARule(state: 1, character: nil, nextState: 4), 56 | FARule(state: 2, character: "a", nextState: 3), 57 | FARule(state: 3, character: "a", nextState: 2), 58 | FARule(state: 4, character: "a", nextState: 5), 59 | FARule(state: 5, character: "a", nextState: 6), 60 | FARule(state: 6, character: "a", nextState: 4), 61 | ] 62 | 63 | let ruleBook = NFARuleBook(rules: rules) 64 | 65 | ["a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa"].forEach { 66 | let nfaDesign = NFADesign(startState: 1, acceptStates: [2, 4], ruleBook: ruleBook) 67 | let result = nfaDesign.canAcceptsString($0) 68 | print("\($0)\t\(result)") 69 | } 70 | } 71 | 72 | do { 73 | // a 74 | // ┌───┐ 75 | // │ ▼ a b 76 | // ┌─────┐────────────▶┌─────┐────────────▶╔═════╗ 77 | // ───────▶│ 1 │ │ 2 │ ║ 3 ║ 78 | // └─────┘─ ─ ─ ─ ─ ─ ▶└─────┘◀ ─ ─ ─ ─ ─ ─╚═════╝ 79 | // ▲ │ 80 | // │ b │ 81 | // └───────────────────────────────────────┘ 82 | 83 | print("Convert NFA to DFA:") 84 | 85 | let rules = [ 86 | FARule(state: 1, character: "a", nextState: 1), 87 | FARule(state: 1, character: "a", nextState: 2), 88 | FARule(state: 1, character: nil, nextState: 2), 89 | FARule(state: 2, character: "b", nextState: 3), 90 | FARule(state: 3, character: "b", nextState: 1), 91 | FARule(state: 3, character: nil, nextState: 2), 92 | ] 93 | 94 | let ruleBook = NFARuleBook(rules: rules) 95 | 96 | let nfaDesign = NFADesign(startState: 1, acceptStates: [3], ruleBook: ruleBook) 97 | 98 | let simulation = NFASimulation(nfaDesign: nfaDesign) 99 | 100 | let dfaDesign = simulation.generateDFADesign() 101 | 102 | ["aaa", "aab", "bbbabb"].forEach { 103 | let result = dfaDesign.canAcceptsString($0) 104 | print("\($0)\t\(result)") 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Sources/NPDA/NPDA.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NPDA { 4 | var currentConfigurations: Set> 5 | let acceptStates: Set 6 | let ruleBook: NPDARuleBook 7 | 8 | var accepting: Bool { 9 | for configuration in currentConfigurations { 10 | if acceptStates.contains(configuration.state) { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | 17 | init( 18 | currentConfigurations: Set>, 19 | acceptStates: Set, 20 | ruleBook: NPDARuleBook 21 | ) { 22 | self.currentConfigurations = ruleBook.followFreeMoves(for: currentConfigurations) 23 | self.acceptStates = acceptStates 24 | self.ruleBook = ruleBook 25 | } 26 | 27 | mutating func readCharacter(_ character: Character) { 28 | let configurations = ruleBook.nextConfigurations(from: currentConfigurations, for: character) 29 | currentConfigurations = ruleBook.followFreeMoves(for: configurations) 30 | } 31 | 32 | mutating func readString(_ string: String) { 33 | string.forEach { readCharacter($0) } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/NPDA/NPDADesign.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NPDADesign { 4 | let startState: State 5 | let bottomCharacter: Character 6 | let acceptStates: Set 7 | let ruleBook: NPDARuleBook 8 | 9 | func canAcceptsString(_ string: String) -> Bool { 10 | var npda = generateNPDA() 11 | npda.readString(string) 12 | 13 | return npda.accepting 14 | } 15 | 16 | func generateNPDA() -> NPDA { 17 | let startStack = Stack([bottomCharacter]) 18 | let startConfiguration = PDAConfiguration(state: startState, stack: startStack) 19 | return .init( 20 | currentConfigurations: [startConfiguration], 21 | acceptStates: acceptStates, 22 | ruleBook: ruleBook 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/NPDA/NPDARuleBook.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct NPDARuleBook { 4 | let rules: [PDARule] 5 | 6 | func followFreeMoves(for configurations: Set>) -> Set> { 7 | let moreConfigurations = nextConfigurations(from: configurations, for: nil) 8 | if moreConfigurations.isSubset(of: configurations) { 9 | return configurations 10 | } else { 11 | return followFreeMoves(for: configurations.union(moreConfigurations)) 12 | } 13 | } 14 | 15 | func nextConfigurations( 16 | from configurations: Set>, 17 | for character: Character? 18 | ) -> Set> { 19 | let nextConfigurations = configurations 20 | .flatMap { followConfigurationsFor(configuration: $0, character: character) } 21 | return .init(nextConfigurations) 22 | } 23 | 24 | func followConfigurationsFor( 25 | configuration: PDAConfiguration, 26 | character: Character? 27 | ) -> [PDAConfiguration] { 28 | rulesFor(configuration: configuration, character: character) 29 | .map { $0.followConfiguration(from: configuration) } 30 | } 31 | 32 | private func rulesFor( 33 | configuration: PDAConfiguration, 34 | character: Character? 35 | ) -> [PDARule] { 36 | rules.filter { $0.canAppliesTo(configuration: configuration, character: character) } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/NPDA/PDAConfiguration.swift: -------------------------------------------------------------------------------- 1 | ../PDAConfiguration.swift -------------------------------------------------------------------------------- /Sources/NPDA/PDARule.swift: -------------------------------------------------------------------------------- 1 | ../PDARule.swift -------------------------------------------------------------------------------- /Sources/NPDA/Stack.swift: -------------------------------------------------------------------------------- 1 | ../Stack.swift -------------------------------------------------------------------------------- /Sources/NPDA/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | // 识别回文 4 | 5 | let rules = [ 6 | PDARule(state: 1, character: "a", nextState: 1, popCharacter: "$", pushCharacters: ["a", "$"]), 7 | PDARule(state: 1, character: "a", nextState: 1, popCharacter: "a", pushCharacters: ["a", "a"]), 8 | PDARule(state: 1, character: "a", nextState: 1, popCharacter: "b", pushCharacters: ["a", "b"]), 9 | PDARule(state: 1, character: "b", nextState: 1, popCharacter: "$", pushCharacters: ["b", "$"]), 10 | PDARule(state: 1, character: "b", nextState: 1, popCharacter: "a", pushCharacters: ["b", "a"]), 11 | PDARule(state: 1, character: "b", nextState: 1, popCharacter: "b", pushCharacters: ["b", "b"]), 12 | PDARule(state: 1, character: nil, nextState: 2, popCharacter: "$", pushCharacters: ["$"]), 13 | PDARule(state: 1, character: nil, nextState: 2, popCharacter: "a", pushCharacters: ["a"]), 14 | PDARule(state: 1, character: nil, nextState: 2, popCharacter: "b", pushCharacters: ["b"]), 15 | PDARule(state: 2, character: "a", nextState: 2, popCharacter: "a", pushCharacters: []), 16 | PDARule(state: 2, character: "b", nextState: 2, popCharacter: "b", pushCharacters: []), 17 | PDARule(state: 2, character: nil, nextState: 3, popCharacter: "$", pushCharacters: ["$"]), 18 | ] 19 | 20 | let ruleBook = NPDARuleBook(rules: rules) 21 | 22 | let npdaDesign = NPDADesign( 23 | startState: 1, 24 | bottomCharacter: "$", 25 | acceptStates: [3], 26 | ruleBook: ruleBook 27 | ) 28 | 29 | print(npdaDesign.canAcceptsString("abba")) 30 | print(npdaDesign.canAcceptsString("babbaabbab")) 31 | print(npdaDesign.canAcceptsString("abb")) 32 | print(npdaDesign.canAcceptsString("baabaa")) 33 | -------------------------------------------------------------------------------- /Sources/PDAConfiguration.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct PDAConfiguration: Hashable { 4 | let state: State 5 | let stack: Stack 6 | } 7 | -------------------------------------------------------------------------------- /Sources/PDARule.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct PDARule { 4 | let state: State 5 | let character: Character? 6 | let nextState: State 7 | let popCharacter: Character 8 | let pushCharacters: [Character] 9 | 10 | func canAppliesTo(configuration: PDAConfiguration, character: Character?) -> Bool { 11 | self.state == configuration.state 12 | && self.popCharacter == configuration.stack.top 13 | && self.character == character 14 | } 15 | 16 | func followConfiguration(from configuration: PDAConfiguration) -> PDAConfiguration { 17 | .init(state: nextState, stack: nextStack(for: configuration)) 18 | } 19 | 20 | private func nextStack(for configuration: PDAConfiguration) -> Stack { 21 | let poppedStack = configuration.stack.pop() 22 | return pushCharacters.reversed().reduce(poppedStack, { $0.push($1) }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/RE/FARule.swift: -------------------------------------------------------------------------------- 1 | ../FARule.swift -------------------------------------------------------------------------------- /Sources/RE/NFA.swift: -------------------------------------------------------------------------------- 1 | ../NFA/NFA.swift -------------------------------------------------------------------------------- /Sources/RE/NFADesign.swift: -------------------------------------------------------------------------------- 1 | ../NFA/NFADesign.swift -------------------------------------------------------------------------------- /Sources/RE/NFARuleBook.swift: -------------------------------------------------------------------------------- 1 | ../NFA/NFARuleBook.swift -------------------------------------------------------------------------------- /Sources/RE/Pattern.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | enum Pattern { 4 | case empty 5 | case literal(character: Character) 6 | indirect case concatenate(firstPart: Pattern, secondPart: Pattern) 7 | indirect case choose(firstPart: Pattern, secondPart: Pattern) 8 | indirect case `repeat`(pattern: Pattern) 9 | 10 | var `precedence`: Int { 11 | switch self { 12 | case .empty: return 3 13 | case .literal: return 3 14 | case .concatenate: return 1 15 | case .choose: return 0 16 | case .repeat: return 2 17 | } 18 | } 19 | 20 | var string: String { 21 | switch self { 22 | case .empty: 23 | return "" 24 | case .literal(let character): 25 | return String(character) 26 | case .concatenate(let firstPart, let secondPart): 27 | return [firstPart, secondPart].map({ $0.bracket(`precedence`) }).joined(separator: "") 28 | case .choose(let firstPart, let secondPart): 29 | return [firstPart, secondPart].map({ $0.bracket(`precedence`) }).joined(separator: "|") 30 | case .repeat(let pattern): 31 | return pattern.bracket(`precedence`) + "*" 32 | } 33 | } 34 | 35 | var nfaDesign: NFADesign { 36 | switch self { 37 | case .empty: 38 | let startState = UniqueState.nextState() 39 | let acceptStates: Set = [startState] 40 | let ruleBook = NFARuleBook(rules: []) 41 | return .init(startState: startState, acceptStates: acceptStates, ruleBook: ruleBook) 42 | case .literal(let character): 43 | let startState = UniqueState.nextState() 44 | let acceptState = UniqueState.nextState() 45 | let rule = FARule(state: startState, character: character, nextState: acceptState) 46 | let ruleBook = NFARuleBook(rules: [rule]) 47 | return .init(startState: startState, acceptStates: [acceptState], ruleBook: ruleBook) 48 | case .concatenate(let firstPart, let secondPart): 49 | let firstPartNFADesign = firstPart.nfaDesign 50 | let secondPartNFADesign = secondPart.nfaDesign 51 | let startState = firstPartNFADesign.startState 52 | let acceptStates = secondPartNFADesign.acceptStates 53 | let rules = firstPartNFADesign.ruleBook.rules + secondPartNFADesign.ruleBook.rules 54 | let extraRules = firstPartNFADesign.acceptStates.map({ 55 | FARule(state: $0, character: nil, nextState: secondPartNFADesign.startState) 56 | }) 57 | let ruleBook = NFARuleBook(rules: rules + extraRules) 58 | return .init(startState: startState, acceptStates: acceptStates, ruleBook: ruleBook) 59 | case .choose(let firstPart, let secondPart): 60 | let firstPartNFADesign = firstPart.nfaDesign 61 | let secondPartNFADesign = secondPart.nfaDesign 62 | let startState = UniqueState.nextState() 63 | let acceptStates = firstPartNFADesign.acceptStates.union(secondPartNFADesign.acceptStates) 64 | let rules = firstPartNFADesign.ruleBook.rules + secondPartNFADesign.ruleBook.rules 65 | let extraRules = [firstPartNFADesign, secondPartNFADesign].map { 66 | FARule(state: startState, character: nil, nextState: $0.startState) 67 | } 68 | let ruleBook = NFARuleBook(rules: rules + extraRules) 69 | return .init(startState: startState, acceptStates: acceptStates, ruleBook: ruleBook) 70 | case .repeat(let pattern): 71 | let patternNFADesign = pattern.nfaDesign 72 | let startState = UniqueState.nextState() 73 | let acceptStates = patternNFADesign.acceptStates.union([startState]) 74 | let rules = patternNFADesign.ruleBook.rules 75 | let extraRules = patternNFADesign.acceptStates.map { 76 | FARule(state: $0, character: nil, nextState: patternNFADesign.startState) 77 | } + [FARule(state: startState, character: nil, nextState: patternNFADesign.startState)] 78 | let ruleBook = NFARuleBook(rules: rules + extraRules) 79 | return .init(startState: startState, acceptStates: acceptStates, ruleBook: ruleBook) 80 | } 81 | } 82 | 83 | private func bracket(_ outerPrecedence: Int) -> String { 84 | if `precedence` < outerPrecedence { 85 | return "(\(string))" 86 | } else { 87 | return string 88 | } 89 | } 90 | 91 | func canMatchesString(_ string: String) -> Bool { 92 | nfaDesign.canAcceptsString(string) 93 | } 94 | } 95 | 96 | private class UniqueState { 97 | static let shared = UniqueState() 98 | 99 | private var count = 0 100 | 101 | private init() {} 102 | 103 | static func nextState() -> Int { 104 | defer { 105 | shared.count += 1 106 | } 107 | return shared.count 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Sources/RE/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | let pattern = Pattern.repeat(pattern: 4 | Pattern.concatenate( 5 | firstPart: Pattern.literal(character: "a"), 6 | secondPart: Pattern.choose( 7 | firstPart: Pattern.empty, 8 | secondPart: Pattern.literal(character: "b") 9 | ) 10 | ) 11 | ) 12 | 13 | print(pattern.string) 14 | 15 | ["", "a", "ab", "aba", "abab", "abaab", "abba"].forEach { 16 | print("\($0)\t\(pattern.canMatchesString($0))") 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Stack.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct Stack: Hashable { 4 | private let items: [Item] 5 | 6 | var top: Item? { 7 | items.last 8 | } 9 | 10 | func push(_ item: Item) -> Stack { 11 | Stack(items + [item]) 12 | } 13 | 14 | func pop() -> Stack { 15 | var _items = items 16 | _items.removeLast() 17 | 18 | return Stack(_items) 19 | } 20 | 21 | init(_ items: [Item]) { 22 | self.items = items 23 | } 24 | } 25 | 26 | extension Stack: CustomStringConvertible { 27 | var description: String { 28 | "|" + items.map({ "\($0)" }).joined(separator: "|") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/TM/DTM.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DTM { 4 | var currentConfiguration: TMConfiguration? 5 | let acceptStates: Set 6 | let ruleBook: DTMRuleBook 7 | 8 | var accepting: Bool { 9 | guard let currentConfiguration = currentConfiguration else { 10 | return false 11 | } 12 | return acceptStates.contains(currentConfiguration.state) 13 | } 14 | 15 | private var stuck: Bool { 16 | guard let currentConfiguration = currentConfiguration else { 17 | return true 18 | } 19 | return !accepting && !ruleBook.canAppliesTo(configuration: currentConfiguration) 20 | } 21 | 22 | mutating func step() { 23 | guard let configuration = currentConfiguration else { 24 | return 25 | } 26 | currentConfiguration = ruleBook.nextConfiguration(from: configuration) 27 | } 28 | 29 | mutating func run() { 30 | while !accepting && !stuck { 31 | step() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/TM/DTMRuleBook.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct DTMRuleBook { 4 | let rules: [TMRule] 5 | 6 | func canAppliesTo(configuration: TMConfiguration) -> Bool { 7 | ruleFor(configuration: configuration) != nil 8 | } 9 | 10 | func nextConfiguration(from configuration: TMConfiguration) -> TMConfiguration? { 11 | ruleFor(configuration: configuration)?.followConfiguration(from: configuration) 12 | } 13 | 14 | private func ruleFor(configuration: TMConfiguration) -> TMRule? { 15 | rules.first(where: { $0.canAppliesTo(configuration: configuration) }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/TM/TMConfiguration.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct TMConfiguration { 4 | let state: State 5 | let tape: Tape 6 | } 7 | -------------------------------------------------------------------------------- /Sources/TM/TMRule.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | enum Direction { 4 | case left 5 | case right 6 | } 7 | 8 | struct TMRule { 9 | let state: State 10 | let character: Character 11 | let nextState: State 12 | let writeCharacter: Character 13 | let direction: Direction 14 | 15 | func canAppliesTo(configuration: TMConfiguration) -> Bool { 16 | (state == configuration.state) && (character == configuration.tape.middleCharacter) 17 | } 18 | 19 | func followConfiguration(from configuration: TMConfiguration) -> TMConfiguration { 20 | TMConfiguration(state: nextState, tape: nextTape(for: configuration)) 21 | } 22 | 23 | func nextTape(for configuration: TMConfiguration) -> Tape { 24 | let writtenTape = configuration.tape.writeCharacter(writeCharacter) 25 | switch direction { 26 | case .left: 27 | return writtenTape.moveHeadLeft() 28 | case .right: 29 | return writtenTape.moveHeadRight() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/TM/Tape.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | struct Tape { 4 | let leftCharacters: [Character] 5 | let middleCharacter: Character 6 | let rightCharacters: [Character] 7 | let blankCharacter: Character 8 | 9 | func writeCharacter(_ character: Character) -> Tape { 10 | .init( 11 | leftCharacters: leftCharacters, 12 | middleCharacter: character, 13 | rightCharacters: rightCharacters, 14 | blankCharacter: blankCharacter 15 | ) 16 | } 17 | 18 | func moveHeadLeft() -> Tape { 19 | var _leftCharacters = leftCharacters 20 | let leftLast: Character? = _leftCharacters.isEmpty ? nil : _leftCharacters.removeLast() 21 | return .init( 22 | leftCharacters: _leftCharacters, 23 | middleCharacter: leftLast ?? blankCharacter, 24 | rightCharacters: [middleCharacter] + rightCharacters, 25 | blankCharacter: blankCharacter 26 | ) 27 | } 28 | 29 | func moveHeadRight() -> Tape { 30 | var _rightCharacters = rightCharacters 31 | let rightFirst: Character? = _rightCharacters.isEmpty ? nil : _rightCharacters.removeFirst() 32 | return .init( 33 | leftCharacters: leftCharacters + [middleCharacter], 34 | middleCharacter: rightFirst ?? blankCharacter, 35 | rightCharacters: _rightCharacters, 36 | blankCharacter: blankCharacter 37 | ) 38 | } 39 | } 40 | 41 | extension Tape: CustomStringConvertible { 42 | var description: String { 43 | "Tape: " 44 | + leftCharacters.map({ "\($0)" }).joined(separator: "") 45 | + "(\(middleCharacter))" 46 | + rightCharacters.map({ "\($0)" }).joined(separator: "") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/TM/main.swift: -------------------------------------------------------------------------------- 1 | // @nixzhu (zhuhongxu@gmail.com) 2 | 3 | // Recognize string "aaabbbccc" 4 | 5 | let tape = Tape( 6 | leftCharacters: [], 7 | middleCharacter: "a", 8 | rightCharacters: ["a", "a", "b", "b", "b", "c", "c", "c"], 9 | blankCharacter: "_" 10 | ) 11 | 12 | let configuration = TMConfiguration(state: 1, tape: tape) 13 | 14 | let rules = [ 15 | // 状态 1: 向右扫描,查找 a 16 | TMRule(state: 1, character: "X", nextState: 1, writeCharacter: "X", direction: .right), 17 | TMRule(state: 1, character: "a", nextState: 2, writeCharacter: "X", direction: .right), 18 | TMRule(state: 1, character: "_", nextState: 6, writeCharacter: "_", direction: .left), 19 | 20 | // 状态 2: 向右扫描,查找 b 21 | TMRule(state: 2, character: "a", nextState: 2, writeCharacter: "a", direction: .right), 22 | TMRule(state: 2, character: "X", nextState: 2, writeCharacter: "X", direction: .right), 23 | TMRule(state: 2, character: "b", nextState: 3, writeCharacter: "X", direction: .right), 24 | 25 | // 状态 3: 向右扫描,查找 c 26 | TMRule(state: 3, character: "b", nextState: 3, writeCharacter: "b", direction: .right), 27 | TMRule(state: 3, character: "X", nextState: 3, writeCharacter: "X", direction: .right), 28 | TMRule(state: 3, character: "c", nextState: 4, writeCharacter: "X", direction: .right), 29 | 30 | // 状态 4: 向右扫描,查找字符串结束标记 31 | TMRule(state: 4, character: "c", nextState: 4, writeCharacter: "c", direction: .right), 32 | TMRule(state: 4, character: "_", nextState: 5, writeCharacter: "_", direction: .left), 33 | 34 | // 状态 5: 向左扫描,查找字符串开始标记 35 | TMRule(state: 5, character: "a", nextState: 5, writeCharacter: "a", direction: .left), 36 | TMRule(state: 5, character: "b", nextState: 5, writeCharacter: "b", direction: .left), 37 | TMRule(state: 5, character: "c", nextState: 5, writeCharacter: "c", direction: .left), 38 | TMRule(state: 5, character: "X", nextState: 5, writeCharacter: "X", direction: .left), 39 | TMRule(state: 5, character: "_", nextState: 1, writeCharacter: "_", direction: .right), 40 | ] 41 | 42 | let ruleBook = DTMRuleBook(rules: rules) 43 | 44 | var dtm = DTM(currentConfiguration: configuration, acceptStates: [6], ruleBook: ruleBook) 45 | 46 | (0..<10).forEach { _ in 47 | dtm.step() 48 | } 49 | print(dtm.currentConfiguration!) 50 | print(dtm.accepting) 51 | 52 | (0..<25).forEach { _ in 53 | dtm.step() 54 | } 55 | print(dtm.currentConfiguration!) 56 | print(dtm.accepting) 57 | 58 | dtm.run() 59 | print(dtm.currentConfiguration!) 60 | print(dtm.accepting) 61 | --------------------------------------------------------------------------------