Skip to content

Commit 43b8c22

Browse files
add CString based search matcher
1 parent e54503e commit 43b8c22

2 files changed

Lines changed: 58 additions & 11 deletions

File tree

SearchExpressionParser/Parsing/Expressions.swift

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,67 @@
11
// Copyright © 2018 Christian Tietze. All rights reserved. Distributed under the MIT License.
22

3-
public protocol ExpressionSatisfiable {
3+
/// Literal string matching.
4+
public protocol StringExpressionSatisfiable {
45
func contains(phrase: String) -> Bool
56
}
67

7-
extension String: ExpressionSatisfiable {
8+
/// The fastest search on earch is `strstr(haystack, needle)` with lowercased string.
9+
/// This provides the needle as UTF-8, lowercased, precomposed string with canonical mapping.
10+
public protocol CStringExpressionSatisfiable {
11+
func matches(needle: ContainsNode.CString) -> Bool
12+
}
13+
14+
extension String: StringExpressionSatisfiable {
815
public func contains(phrase: String) -> Bool {
916
return self.contains(phrase)
1017
}
1118
}
1219

1320
public protocol Expression {
14-
func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool
21+
func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool
22+
func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool
1523
}
1624

1725
/// Wildcard that is satisfied by any string.
1826
public struct AnythingNode: Expression {
19-
public func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
27+
public func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
28+
return true
29+
}
30+
31+
public func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
2032
return true
2133
}
2234
}
2335

2436
public struct ContainsNode: Expression {
37+
public typealias CString = [CChar]
2538
public let string: String
39+
public let cString: CString
40+
41+
public static var cStringFactory: (String) -> CString = ContainsNode.cString(string:)
42+
43+
public static func cString(string: String) -> CString {
44+
return string.precomposedStringWithCanonicalMapping
45+
.lowercased()
46+
.cString(using: .utf8) ?? []
47+
}
2648

2749
public init(_ string: String) {
2850
self.string = string
51+
self.cString = ContainsNode.cStringFactory(string)
2952
}
3053

3154
public init(token: Token) {
3255
self.init(token.string)
3356
}
3457

35-
public func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
58+
public func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
3659
return satisfiable.contains(phrase: string)
3760
}
61+
62+
public func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
63+
return satisfiable.matches(needle: cString)
64+
}
3865
}
3966

4067
public struct NotNode: Expression {
@@ -44,7 +71,11 @@ public struct NotNode: Expression {
4471
self.expression = expression
4572
}
4673

47-
public func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
74+
public func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
75+
return !expression.isSatisfied(by: satisfiable)
76+
}
77+
78+
public func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
4879
return !expression.isSatisfied(by: satisfiable)
4980
}
5081
}
@@ -58,7 +89,11 @@ public struct AndNode: Expression {
5889
self.rhs = rhs
5990
}
6091

61-
public func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
92+
public func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
93+
return lhs.isSatisfied(by: satisfiable) && rhs.isSatisfied(by: satisfiable)
94+
}
95+
96+
public func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
6297
return lhs.isSatisfied(by: satisfiable) && rhs.isSatisfied(by: satisfiable)
6398
}
6499
}
@@ -72,7 +107,11 @@ public struct OrNode: Expression {
72107
self.rhs = rhs
73108
}
74109

75-
public func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
110+
public func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
111+
return lhs.isSatisfied(by: satisfiable) || rhs.isSatisfied(by: satisfiable)
112+
}
113+
114+
public func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
76115
return lhs.isSatisfied(by: satisfiable) || rhs.isSatisfied(by: satisfiable)
77116
}
78117
}

SearchExpressionParserTests/ExpressionTests.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import XCTest
55

66
class ExpressionTests: XCTestCase {
77

8-
var irrelevant: ExpressionSatisfiable {
8+
var irrelevant: StringExpressionSatisfiable {
99
return "irrelevant"
1010
}
1111

@@ -52,13 +52,21 @@ class ExpressionTests: XCTestCase {
5252
}
5353

5454
struct FalsyNode: Expression {
55-
func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
55+
func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
56+
return false
57+
}
58+
59+
func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
5660
return false
5761
}
5862
}
5963

6064
struct TruthyNode: Expression {
61-
func isSatisfied(by satisfiable: ExpressionSatisfiable) -> Bool {
65+
func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool {
66+
return true
67+
}
68+
69+
func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool {
6270
return true
6371
}
6472
}

0 commit comments

Comments
 (0)