]> git.r.bdr.sh - rbdr/map/blobdiff - Map/Logic/MapParser/MapParser.swift
Use comma separated strings
[rbdr/map] / Map / Logic / MapParser / MapParser.swift
index 5f78d5d46b43887a88d8abead935feb5c3ac98d8..61f3e5188c773241002178d6f5d501c5895d8158 100644 (file)
@@ -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)
   }
 }