X-Git-Url: https://git.r.bdr.sh/rbdr/map/blobdiff_plain/fdb4633d3e9158e457d57e820df6e1efb4df39c2..f93215a7b37e065a0a52a852bb2c35d2a3d77b0b:/Map/Logic/MapParser/MapParser.swift?ds=inline diff --git a/Map/Logic/MapParser/MapParser.swift b/Map/Logic/MapParser/MapParser.swift index 5f78d5d..61f3e51 100644 --- a/Map/Logic/MapParser/MapParser.swift +++ b/Map/Logic/MapParser/MapParser.swift @@ -1,25 +1,76 @@ +/* + Copyright (C) 2024 Rubén Beltrán del Río + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see https://map.tranquil.systems. + */ import CoreGraphics import Foundation +struct MapParser { + static func parse(content: String) -> ParsedMap { + + let parsers = [ + AnyMapParserStrategy(NoteParserStrategy()), + AnyMapParserStrategy(VertexParserStrategy()), + AnyMapParserStrategy(EdgeParserStrategy()), + AnyMapParserStrategy(BlockerParserStrategy()), + AnyMapParserStrategy(OpportunityParserStrategy()), + AnyMapParserStrategy(StageParserStrategy()), + AnyMapParserStrategy(GroupParserStrategy()), + ] + let builder = MapBuilder() + + let lines = content.split(whereSeparator: \.isNewline) + + for (index, line) in lines.enumerated() { + for parser in parsers { + if parser.canHandle(line: String(line)) { + let (type, object) = parser.handle( + index: index, line: String(line), vertices: builder.vertices) + builder.addObjectToMap(type: type, object: object) + break + } + } + } + + return builder.build() + } +} + // MARK: - Types struct MapParsingPatterns { static let vertex = try! NSRegularExpression( pattern: - "([^\\(\\[\\]]*?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?", - options: .caseInsensitive) + "^([^\\(\\[\\]]*?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?[\\s]*$", + options: [.caseInsensitive, .anchorsMatchLines]) static let edge = try! NSRegularExpression( - pattern: "(.+?)[\\s]*-([->])[\\s]*(.+)", options: .caseInsensitive) + pattern: "^(.+?)[\\s]*-([->])[\\s]*(.+)", options: [.caseInsensitive, .anchorsMatchLines]) static let blocker = try! NSRegularExpression( - pattern: "\\[(Blocker)\\][\\s]*(.+)", options: .caseInsensitive) + pattern: "^\\[(Blocker)\\][\\s]*(.+)", options: [.caseInsensitive, .anchorsMatchLines]) static let opportunity = try! NSRegularExpression( - pattern: "\\[(Evolution)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)", - options: .caseInsensitive) + pattern: "^\\[(Evolution)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)", + options: [.caseInsensitive, .anchorsMatchLines]) static let note = try! NSRegularExpression( - pattern: "\\[(Note)\\][\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(.*)", - options: .caseInsensitive) + pattern: + "^\\[(Note)\\][\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(.*)", + options: [.caseInsensitive, .anchorsMatchLines]) static let stage = try! NSRegularExpression( - pattern: "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive) + pattern: "^\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", + options: [.caseInsensitive, .anchorsMatchLines]) + static let group = try! NSRegularExpression( + pattern: "^\\[(Group)\\][\\s]*(.+)", options: [.caseInsensitive, .anchorsMatchLines]) } struct ParsedMap { @@ -29,12 +80,14 @@ struct ParsedMap { let opportunities: [Opportunity] let notes: [Note] let stages: [CGFloat] + let groups: [[Vertex]] static let empty: ParsedMap = ParsedMap( - vertices: [], edges: [], blockers: [], opportunities: [], notes: [], stages: defaultDimensions) + vertices: [], edges: [], blockers: [], opportunities: [], notes: [], stages: defaultDimensions, + groups: []) } -struct Vertex { +struct Vertex: Identifiable, Hashable { let id: Int let label: String let position: CGPoint @@ -115,6 +168,7 @@ class MapBuilder { private var opportunities: [Opportunity] = [] private var notes: [Note] = [] private var stages: [CGFloat] = defaultDimensions + private var groups: [[Vertex]] = [] func addObjectToMap(type: Any.Type, object: Any) { if type == Vertex.self { @@ -136,7 +190,7 @@ class MapBuilder { let opportunity = object as! Opportunity opportunities.append(opportunity) } - + if type == Note.self { let note = object as! Note notes.append(note) @@ -146,12 +200,18 @@ class MapBuilder { let stageDimensions = object as! StageDimensions stages[stageDimensions.index] = stageDimensions.dimensions } + + if type == [Vertex].self { + let group = object as! [Vertex] + groups.append(group) + } } func build() -> ParsedMap { let mappedVertices = vertices.map { label, vertex in return vertex } return ParsedMap( - vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities, notes: notes, - stages: stages) + vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities, + notes: notes, + stages: stages, groups: groups) } }