Skip to content

DSLTree #134

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 7 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ let package = Package(
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"_MatchingEngine",
"_StringProcessing"
]),

// MARK: Exercises
Expand Down
1 change: 1 addition & 0 deletions Sources/PatternConverter/PatternConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import ArgumentParser
import _MatchingEngine
import _StringProcessing

@main
struct PatternConverter: ParsableCommand {
Expand Down
6 changes: 3 additions & 3 deletions Sources/VariadicsGenerator/VariadicsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,13 @@ struct VariadicsGenerator: ParsableCommand {
output(" init(")
outputForEach(0..<arity, separator: ", ") { "_ x\($0): T\($0)" }
output(") {\n")
output(" \(patternProtocolRequirementName) = .init(ast: concat(\n ")
output(" \(patternProtocolRequirementName) = .init(node: .concatenation([\n ")
outputForEach(
0..<arity, separator: ", ", lineTerminator: ""
) { i in
"x\(i).\(patternProtocolRequirementName).ast.root"
"x\(i).\(patternProtocolRequirementName).root"
}
output("))\n")
output("]))\n")
output(" }\n}\n\n")

// Emit concatenation builders.
Expand Down
8 changes: 7 additions & 1 deletion Sources/_MatchingEngine/Engine/Processor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ extension Processor {
}

extension Processor {
var slice: Input.SubSequence {
// TODO: Should we whole-scale switch to slices, or
// does that depend on options for some anchors?
input[bounds]
}

// Advance in our input
//
// Returns whether the advance succeeded. On failure, our
Expand Down Expand Up @@ -125,7 +131,7 @@ extension Processor {
currentPosition < end ? input[currentPosition] : nil
}
func load(count: Int) -> Input.SubSequence? {
let slice = input[currentPosition...].prefix(count)
let slice = self.slice[currentPosition...].prefix(count)
guard slice.count == count else { return nil }
return slice
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/_MatchingEngine/Regex/AST/AST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension AST {
/// A node in the regex AST.
@frozen
public indirect enum Node:
Hashable/*, _ASTPrintable ASTValue, ASTAction*/
Hashable, _TreeNode //, _ASTPrintable ASTValue, ASTAction
{
/// ... | ... | ...
case alternation(Alternation)
Expand Down Expand Up @@ -94,7 +94,7 @@ extension AST.Node {
}
}

func `as`<T: _ASTNode>(_ t: T.Type = T.self) -> T? {
public func `as`<T: _ASTNode>(_ t: T.Type = T.self) -> T? {
_associatedValue as? T
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/_MatchingEngine/Regex/AST/ASTProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// MARK: - AST parent/child

protocol _ASTNode: _ASTPrintable {
public protocol _ASTNode: _ASTPrintable {
var location: SourceLocation { get }
}
extension _ASTNode {
Expand All @@ -41,7 +41,7 @@ extension AST.Quantification: _ASTParent {
var children: [AST.Node] { [child] }
}
extension AST.AbsentFunction: _ASTParent {
var children: [AST.Node] {
public var children: [AST.Node] {
switch kind {
case .repeater(let a), .stopper(let a): return [a]
case .expression(let a, _, let c): return [a, c]
Expand Down
4 changes: 2 additions & 2 deletions Sources/_MatchingEngine/Regex/AST/Group.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ extension AST.Group.Kind {
}
}

extension AST.Group {
extension AST.Group.Kind {
/// If this group is a lookaround assertion, return its direction
/// and whether it is positive or negative. Otherwise returns
/// `nil`.
public var lookaroundKind: (forwards: Bool, positive: Bool)? {
switch self.kind.value {
switch self {
case .lookahead: return (true, true)
case .negativeLookahead: return (true, false)
case .lookbehind: return (false, true)
Expand Down
185 changes: 126 additions & 59 deletions Sources/_MatchingEngine/Regex/Parse/CaptureStructure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,71 +25,138 @@ public enum CaptureStructure: Equatable {
}
}

extension CaptureStructure {
public init<C: Collection>(
alternating children: C
) where C.Element: _TreeNode {
assert(children.count > 1)
self = children
.map(\.captureStructure)
.reduce(.empty, +)
.map(CaptureStructure.optional)
}
public init<C: Collection>(
concatenating children: C
) where C.Element: _TreeNode {
self = children.map(\.captureStructure).reduce(.empty, +)
}

public init<T: _TreeNode>(
grouping child: T, as kind: AST.Group.Kind
) {
let innerCaptures = child.captureStructure
switch kind {
case .capture:
self = .atom() + innerCaptures
case .namedCapture(let name):
self = .atom(name: name.value) + innerCaptures
case .balancedCapture(let b):
self = .atom(name: b.name?.value) + innerCaptures
default:
precondition(!kind.isCapturing)
self = innerCaptures
}
}

public init<T: _TreeNode>(
grouping child: T,
as kind: AST.Group.Kind,
withTransform transform: CaptureTransform
) {
let innerCaptures = child.captureStructure
switch kind {
case .capture:
self = .atom(type: AnyType(transform.resultType)) + innerCaptures
case .namedCapture(let name):
self = .atom(name: name.value, type: AnyType(transform.resultType))
+ innerCaptures
default:
self = innerCaptures
}
}

// TODO: We'll likely want/need a generalization of
// conditional's condition kind.
public init<T: _TreeNode>(
condition: AST.Conditional.Condition.Kind,
trueBranch: T,
falseBranch: T
) {
// A conditional's capture structure is effectively that of an alternation
// between the true and false branches. However the condition may also
// have captures in the case of a group condition.
var captures = CaptureStructure.empty
switch condition {
case .group(let g):
captures = captures + AST.Node.group(g).captureStructure
default:
break
}
let branchCaptures = trueBranch.captureStructure +
falseBranch.captureStructure
self = captures + branchCaptures.map(
CaptureStructure.optional)
}

public init<T: _TreeNode>(
quantifying child: T, amount: AST.Quantification.Amount
) {
self = child.captureStructure.map(
amount == .zeroOrOne
? CaptureStructure.optional
: CaptureStructure.array)
}

// TODO: Will need to adjust for DSLTree support, and
// "absent" isn't the best name for these.
public init(
absent kind: AST.AbsentFunction.Kind
) {
// Only the child of an expression absent function is relevant, as the
// other expressions don't actually get matched against.
switch kind {
case .expression(_, _, let child):
self = child.captureStructure
case .clearer, .repeater, .stopper:
self = .empty
}
}

}

extension AST.Node {
public var captureStructure: CaptureStructure {
// Note: This implementation could be more optimized.
switch self {
case .alternation(let alternation):
assert(alternation.children.count > 1)
return alternation.children
.map(\.captureStructure)
.reduce(.empty, +)
.map(CaptureStructure.optional)
case .concatenation(let concatenation):
return concatenation.children.map(\.captureStructure).reduce(.empty, +)
case .group(let group):
let innerCaptures = group.child.captureStructure
switch group.kind.value {
case .capture:
return .atom() + innerCaptures
case .namedCapture(let name):
return .atom(name: name.value) + innerCaptures
case .balancedCapture(let b):
return .atom(name: b.name?.value) + innerCaptures
default:
precondition(!group.kind.value.isCapturing)
return innerCaptures
}
case .groupTransform(let group, let transform):
let innerCaptures = group.child.captureStructure
switch group.kind.value {
case .capture:
return .atom(type: AnyType(transform.resultType)) + innerCaptures
case .namedCapture(let name):
return .atom(name: name.value, type: AnyType(transform.resultType))
+ innerCaptures
default:
return innerCaptures
}
case let .alternation(a):
return CaptureStructure(alternating: a.children)

case let .concatenation(c):
return CaptureStructure(concatenating: c.children)

case let .group(g):
return CaptureStructure(
grouping: g.child, as: g.kind.value)

case .groupTransform(let g, let transform):
return CaptureStructure(
grouping: g.child,
as: g.kind.value,
withTransform: transform)

case .conditional(let c):
// A conditional's capture structure is effectively that of an alternation
// between the true and false branches. However the condition may also
// have captures in the case of a group condition.
var captures = CaptureStructure.empty
switch c.condition.kind {
case .group(let g):
captures = captures + AST.Node.group(g).captureStructure
default:
break
}
let branchCaptures = c.trueBranch.captureStructure +
c.falseBranch.captureStructure
return captures + branchCaptures.map(CaptureStructure.optional)

case .quantification(let quantification):
return quantification.child.captureStructure.map(
quantification.amount.value == .zeroOrOne
? CaptureStructure.optional
: CaptureStructure.array)
return CaptureStructure(
condition: c.condition.kind,
trueBranch: c.trueBranch,
falseBranch: c.falseBranch)

case .quantification(let q):
return CaptureStructure(
quantifying: q.child, amount: q.amount.value)

case .absentFunction(let abs):
// Only the child of an expression absent function is relevant, as the
// other expressions don't actually get matched against.
switch abs.kind {
case .expression(_, _, let child):
return child.captureStructure
case .clearer, .repeater, .stopper:
return .empty
}
return CaptureStructure(absent: abs.kind)

case .quote, .trivia, .atom, .customCharacterClass, .empty:
return .empty
}
Expand Down
Loading