Skip to content

Reorganize our modules #59

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 14 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
13 changes: 1 addition & 12 deletions Sources/Algorithms/Consumers/RegexConsumer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ public struct Regex {
public struct RegexConsumer: CollectionConsumer {
// NOTE: existential
let vm: Executor
let referenceVM: TortoiseVM

public init(regex: Regex) {
let ast = try! parse(regex.string, .traditional)
let program = Compiler(ast: ast).emit()
self.vm = Executor(program: program)
let legacyProgram = try! compile(ast, options: regex.options)
self.referenceVM = TortoiseVM(program: legacyProgram)
self.vm = _compileRegex(regex.string)
Copy link
Contributor

@rxwei rxwei Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can RegexConsume use the public API (e.g. firstMatch) instead? That way we wouldn't need to expose Compiler or Executor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it is RegexProtocol.match(in:). You can add a range parameter to it.

Copy link
Member Author

@milseman milseman Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would I use for types? It seems like DynamicCaptures will let me parse the string, but will trap at runtime:

  let regex: _StringProcessing.Regex<DynamicCaptures>
Could not cast value of type '()' (0x7fff815b12b8) to '_StringProcessing.DynamicCaptures' (0x10f67b618).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using MockRegexLiteral<()> instead lets me get past that, but then I hit test failures. If it's going to take too long, I'd rather get this more fundamental PR merged and tweak access control after

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this needs the following change:

--- a/Sources/_StringProcessing/RegexDSL/Core.swift
+++ b/Sources/_StringProcessing/RegexDSL/Core.swift
@@ -85,7 +85,13 @@ extension RegexProtocol {
     guard let result = executor.execute(input: input) else {
       return nil
     }
-    return RegexMatch(range: result.range, captures: () as! Capture)
+    let convertedCapture: Capture
+    if Capture.self == DynamicCaptures.self {
+      convertedCapture = DynamicCaptures.tuple([]) as! Capture
+    } else {
+      convertedCapture = () as! Capture
+    }
+    return RegexMatch(range: result.range, captures: convertedCapture)
   }
 }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address this later sounds good too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scratch that, I later also get a

Could not cast value of type 'Swift.Array<Swift.Substring>' (0x7fff815ac3d8) to '()' (0x7fff815b12b8).

}

public func consume(
Expand All @@ -32,12 +27,6 @@ public struct RegexConsumer: CollectionConsumer {
input: consumed.base,
in: index..<consumed.endIndex,
mode: .partialFromFront)
assert(
result?.range == referenceVM.execute(
input: consumed.base,
in: index..<consumed.endIndex,
mode: .partialFromFront
)?.range)
return result?.range.upperBound
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/_MatchingEngine/Regex/Parse/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ extension Source: _CollectionWrapper {
}

extension Source: _Peekable {
public typealias Output = Char
typealias Output = Char

public mutating func advance() {
mutating func advance() {
assert(!isEmpty)
_wrapped = _wrapped.dropFirst()
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/_MatchingEngine/Utility/Peek.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// MARK: - _Peekable

public protocol _Peekable {
protocol _Peekable {
associatedtype Output

var isEmpty: Bool { get }
Expand All @@ -9,7 +9,7 @@ public protocol _Peekable {
}

extension _Peekable where Self: Collection, Output == Element {
public func peek() -> Output? { self.first }
func peek() -> Output? { self.first }

@discardableResult
mutating func eat(upTo: Index) -> SubSequence {
Expand Down
9 changes: 5 additions & 4 deletions Sources/_StringProcessing/Capture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _MatchingEngine

// TODO: what here should be in the compile-time module?

public enum Capture {
enum Capture {
case atom(Any)
indirect case tuple([Capture])
indirect case optional(Capture?)
Expand All @@ -14,16 +14,17 @@ extension Capture {
elements.count == 1 ? elements[0] : .tuple(elements)
}

public static var void: Capture {
static var void: Capture {
.tuple([])
}

public var value: Any {
var value: Any {
switch self {
case .atom(let atom):
return atom
case .tuple(let elements):
return _tuple(of: elements.map(\.value))
return TypeConstruction.tuple(
of: elements.map(\.value))
case .array(let elements):
guard let first = elements.first else {
return [Any]()
Expand Down
21 changes: 14 additions & 7 deletions Sources/_StringProcessing/Compiler.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import _MatchingEngine

public struct RegexProgram {
struct RegexProgram {
typealias Program = _MatchingEngine.Program<String>
var program: Program
}

public class Compiler {
public let ast: AST
public let matchLevel: CharacterClass.MatchLevel
public let options: REOptions
class Compiler {
let ast: AST
let matchLevel: CharacterClass.MatchLevel
let options: REOptions
private var builder = RegexProgram.Program.Builder()

public init(
init(
ast: AST,
matchLevel: CharacterClass.MatchLevel = .graphemeCluster,
options: REOptions = []
Expand All @@ -21,7 +21,7 @@ public class Compiler {
self.options = options
}

public __consuming func emit() -> RegexProgram {
__consuming func emit() -> RegexProgram {
emit(ast)
builder.buildAccept()
return RegexProgram(program: builder.assemble())
Expand Down Expand Up @@ -202,3 +202,10 @@ public class Compiler {
}
}

public func _compileRegex(
_ regex: String, _ syntax: SyntaxOptions = .traditional
) -> Executor {
let ast = try! parse(regex, .traditional)
let program = Compiler(ast: ast).emit()
return Executor(program: program)
}
6 changes: 3 additions & 3 deletions Sources/_StringProcessing/Executor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import _MatchingEngine
public struct Executor {
let engine: Engine<String>

public init(program: RegexProgram, enablesTracing: Bool = false) {
init(program: RegexProgram, enablesTracing: Bool = false) {
self.engine = Engine(program.program, enableTracing: enablesTracing)
}

Expand All @@ -23,9 +23,9 @@ public struct Executor {
// Backward compatibility layer. To be removed when we deprecate legacy
// components.
extension Executor: VirtualMachine {
public static let motto = "Executor"
static let motto = "Executor"

public init(program: RegexProgram) {
init(program: RegexProgram) {
self.init(program: program, enablesTracing: false)
}
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/_StringProcessing/Legacy/VirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ public enum MatchMode {

public struct MatchResult {
public var range: Range<String.Index>
public var captures: Capture
var captures: Capture

public var destructure: (
var destructure: (
matched: Range<String.Index>, captures: Capture
) {
(range, captures)
}

public init(
init(
_ matched: Range<String.Index>, _ captures: Capture
) {
self.range = matched
self.captures = captures
}
}

public protocol VirtualMachine {
protocol VirtualMachine {
associatedtype Program

/// The backend's motto and general life philosophy.
Expand Down Expand Up @@ -128,7 +128,7 @@ extension RECode {
topLevelCaptures = [.array(topLevelCaptures)]
}

public func singleCapture() -> Capture {
func singleCapture() -> Capture {
.tupleOrAtom(topLevelCaptures)
}
}
Expand Down
118 changes: 58 additions & 60 deletions Sources/_StringProcessing/Utility/TypeConstruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,91 +48,89 @@ private func swift_getTupleTypeMetadata3(
proposedWitnesses: UnsafeRawPointer?
) -> (value: Any.Type, state: Int)

/// Returns a tuple metatype of the given element types.
public func tupleType<ElementTypes: BidirectionalCollection>(
of elementTypes: __owned ElementTypes
) -> Any.Type where ElementTypes.Element == Any.Type {
// From swift/ABI/Metadata.h:
// template <typename int_type>
// class TargetTupleTypeFlags {
// enum : int_type {
// NumElementsMask = 0x0000FFFFU,
// NonConstantLabelsMask = 0x00010000U,
// };
// int_type Data;
// ...
let elementCountFlag = 0x0000FFFF
assert(elementTypes.count != 1, "A one-element tuple is not a realistic Swift type")
assert(elementTypes.count <= elementCountFlag, "Tuple size exceeded \(elementCountFlag)")
switch elementTypes.count {
case 2:
return swift_getTupleTypeMetadata2(
enum TypeConstruction {

/// Returns a tuple metatype of the given element types.
public static func tupleType<
ElementTypes: BidirectionalCollection
>(
of elementTypes: __owned ElementTypes
) -> Any.Type where ElementTypes.Element == Any.Type {
// From swift/ABI/Metadata.h:
// template <typename int_type>
// class TargetTupleTypeFlags {
// enum : int_type {
// NumElementsMask = 0x0000FFFFU,
// NonConstantLabelsMask = 0x00010000U,
// };
// int_type Data;
// ...
let elementCountFlag = 0x0000FFFF
assert(elementTypes.count != 1, "A one-element tuple is not a realistic Swift type")
assert(elementTypes.count <= elementCountFlag, "Tuple size exceeded \(elementCountFlag)")
switch elementTypes.count {
case 2:
return swift_getTupleTypeMetadata2(
request: 0,
element1: elementTypes[elementTypes.startIndex],
element2: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 1)],
labels: nil,
proposedWitnesses: nil).value
case 3:
return swift_getTupleTypeMetadata3(
case 3:
return swift_getTupleTypeMetadata3(
request: 0,
element1: elementTypes[elementTypes.startIndex],
element2: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 1)],
element3: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 2)],
labels: nil,
proposedWitnesses: nil).value
default:
let result = elementTypes.withContiguousStorageIfAvailable { elementTypesBuffer in
swift_getTupleTypeMetadata(
default:
let result = elementTypes.withContiguousStorageIfAvailable { elementTypesBuffer in
swift_getTupleTypeMetadata(
request: 0,
flags: elementTypesBuffer.count,
elements: elementTypesBuffer.baseAddress,
labels: nil,
proposedWitnesses: nil).value
}
guard let result = result else {
fatalError("""
}
guard let result = result else {
fatalError("""
The collection of element types does not support an internal representation of
contiguous storage
""")
}
return result
}
return result
}
}

// FIXME: namespaces are hard
internal func _tuple<Elements: BidirectionalCollection>(
of elements: __owned Elements
) -> Any where Elements.Element == Any {
tuple(of: elements)
}

/// Creates a type-erased tuple with the given elements.
public func tuple<Elements: BidirectionalCollection>(
of elements: __owned Elements
) -> Any where Elements.Element == Any {
// Open existential on the overall tuple type.
func create<T>(_: T.Type) -> Any {
let baseAddress = UnsafeMutablePointer<T>.allocate(
capacity: MemoryLayout<T>.size)
defer { baseAddress.deallocate() }
// Initialize elements based on their concrete type.
var currentElementAddressUnaligned = UnsafeMutableRawPointer(baseAddress)
for element in elements {
// Open existential on each element type.
func initializeElement<T>(_ element: T) {
currentElementAddressUnaligned =
/// Creates a type-erased tuple with the given elements.
public static func tuple<Elements: BidirectionalCollection>(
of elements: __owned Elements
) -> Any where Elements.Element == Any {
// Open existential on the overall tuple type.
func create<T>(_: T.Type) -> Any {
let baseAddress = UnsafeMutablePointer<T>.allocate(
capacity: MemoryLayout<T>.size)
defer { baseAddress.deallocate() }
// Initialize elements based on their concrete type.
var currentElementAddressUnaligned = UnsafeMutableRawPointer(baseAddress)
for element in elements {
// Open existential on each element type.
func initializeElement<T>(_ element: T) {
currentElementAddressUnaligned =
currentElementAddressUnaligned.roundedUp(toAlignmentOf: T.self)
currentElementAddressUnaligned.bindMemory(
to: T.self, capacity: MemoryLayout<T>.size
).initialize(to: element)
// Advance to the next element (unaligned).
currentElementAddressUnaligned =
currentElementAddressUnaligned.bindMemory(
to: T.self, capacity: MemoryLayout<T>.size
).initialize(to: element)
// Advance to the next element (unaligned).
currentElementAddressUnaligned =
currentElementAddressUnaligned.advanced(by: MemoryLayout<T>.stride)
}
_openExistential(element, do: initializeElement)
}
_openExistential(element, do: initializeElement)
return baseAddress.move()
}
return baseAddress.move()
let elementTypes = elements.map { type(of: $0) }
return _openExistential(tupleType(of: elementTypes), do: create)
}
let elementTypes = elements.map { type(of: $0) }
return _openExistential(tupleType(of: elementTypes), do: create)
}
17 changes: 10 additions & 7 deletions Tests/UtilTests/UtilTests.swift
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import XCTest
@testable import _StringProcessing


class UtilTests: XCTestCase {
func testTupleTypeConstruction() {
XCTAssertTrue(tupleType(of: []) == Void.self)
XCTAssertTrue(tupleType(of: [Int.self, Any.self]) == (Int, Any).self)
XCTAssertTrue(TypeConstruction.tupleType(
of: []) == Void.self)
XCTAssertTrue(TypeConstruction.tupleType(
of: [Int.self, Any.self]) == (Int, Any).self)
XCTAssertTrue(
tupleType(of: [[Int].self, [Int: Int].self, Void.self, Any.self])
== ([Int], [Int: Int], Void, Any).self)
TypeConstruction.tupleType(
of: [[Int].self, [Int: Int].self, Void.self, Any.self])
== ([Int], [Int: Int], Void, Any).self)
}

func testTypeErasedTupleConstruction() throws {
let tuple0Erased = tuple(of: [1, 2, 3])
let tuple0Erased = TypeConstruction.tuple(of: [1, 2, 3])
let tuple0 = try XCTUnwrap(tuple0Erased as? (Int, Int, Int))
XCTAssertEqual(tuple0.0, 1)
XCTAssertEqual(tuple0.1, 2)
XCTAssertEqual(tuple0.2, 3)

let tuple1Erased = tuple(of: [[1, 2], [true, false], [3.0, 4.0]])
let tuple1Erased = TypeConstruction.tuple(
of: [[1, 2], [true, false], [3.0, 4.0]])
XCTAssertTrue(type(of: tuple1Erased) == ([Int], [Bool], [Double]).self)
let tuple1 = try XCTUnwrap(tuple1Erased as? ([Int], [Bool], [Double]))
XCTAssertEqual(tuple1.0, [1, 2])
Expand Down