day 19, part 1
This commit is contained in:
parent
4c80ebd83a
commit
8a975abe72
2 changed files with 235 additions and 0 deletions
142
day19/main.swift
Normal file
142
day19/main.swift
Normal file
|
@ -0,0 +1,142 @@
|
|||
import Foundation
|
||||
|
||||
let input = loadData(day: 19)
|
||||
let scanner = Scanner(string: input)
|
||||
scanner.charactersToBeSkipped = .whitespaces
|
||||
|
||||
enum Rule {
|
||||
case character(Character)
|
||||
indirect case sequence(Rule, Rule)
|
||||
indirect case alternative(Rule, Rule)
|
||||
case reference(Int)
|
||||
}
|
||||
|
||||
|
||||
typealias RuleSet = [Int: Rule]
|
||||
|
||||
extension Scanner {
|
||||
func parseRuleSet() -> RuleSet {
|
||||
var result: [Int: Rule] = [:]
|
||||
while !string("\n") {
|
||||
guard let (index, rule) = parseRuleDefinition() else { fatalError() }
|
||||
result[index] = rule
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func parseRuleDefinition() -> (Int, Rule)? {
|
||||
guard let label = parseLabel(),
|
||||
let rule = parseRule(),
|
||||
string("\n")
|
||||
else { return nil }
|
||||
return (label, rule)
|
||||
}
|
||||
|
||||
func parseLabel() -> Int? {
|
||||
guard let num = scanInt(),
|
||||
string(":")
|
||||
else { return nil }
|
||||
return num
|
||||
}
|
||||
|
||||
func parseRule() -> Rule? {
|
||||
if string("\""), let character = scanCharacter(), string("\"") {
|
||||
return .character(character)
|
||||
}
|
||||
|
||||
guard let sequence = parseSequence() else { return nil }
|
||||
|
||||
if string("|"), let rhs = parseSequence() {
|
||||
return .alternative(sequence, rhs)
|
||||
}
|
||||
|
||||
return sequence
|
||||
}
|
||||
|
||||
func parseReference() -> Rule? {
|
||||
guard let num = scanInt() else { return nil }
|
||||
return .reference(num)
|
||||
}
|
||||
|
||||
func parseSequence() -> Rule? {
|
||||
guard var sequence = parseReference() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
while let rhs = parseReference() {
|
||||
sequence = .sequence(sequence, rhs)
|
||||
}
|
||||
|
||||
return sequence
|
||||
}
|
||||
}
|
||||
|
||||
let rules = scanner.parseRuleSet()
|
||||
|
||||
extension Rule {
|
||||
func matches(rules: RuleSet, _ s: Substring) -> Substring? {
|
||||
switch self {
|
||||
case .character(let ch):
|
||||
if s.first == ch {
|
||||
return s.dropFirst()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case let .sequence(first, second):
|
||||
if let firstMatch = first.matches(rules: rules, s), let secondMatch = second.matches(rules: rules, firstMatch) {
|
||||
return secondMatch
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case let .alternative(first, second):
|
||||
if let firstMatch = first.matches(rules: rules, s) {
|
||||
return firstMatch
|
||||
}
|
||||
|
||||
if let secondMatch = second.matches(rules: rules, s) {
|
||||
return secondMatch
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case .reference(let index):
|
||||
return rules[index]!.matches(rules: rules, s)
|
||||
}
|
||||
}
|
||||
|
||||
func matches(rules: RuleSet, _ s: String) -> Bool {
|
||||
if let result = matches(rules: rules, s[...]), result.isEmpty {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension Scanner {
|
||||
func scanLine() -> String? {
|
||||
guard let result = scanUpToCharacters(from: .newlines),
|
||||
scanCharacters(from: .newlines) != nil else {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readLines() -> [String] {
|
||||
|
||||
var lines = [String]()
|
||||
while let line = scanner.scanLine() {
|
||||
lines.append(line)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let messages = scanner.readLines()
|
||||
|
||||
print("part 1", messages.lazy.filter { rules[0]!.matches(rules: rules, $0) }.count)
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue