// // HTMLParser.swift // // // Created by Isaac Paul on 7/31/24. // public class HTMLText : HTMLNode, IFlowContent { public var content: String public init(content: String) { self.content = content try! super.init( expectedAttributes: [:]) } override public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) { return (depth, content) } } public class XMLText : XMLNode { public var content: String public init(content: String) { self.content = content super.init() } } extension XMLParser { public func readObjects(endTag:String? = nil, validTags:[String]? = nil) throws -> [HTMLNode] { var allItems:[HTMLNode] = [] while let obj = try self.readObject(endTag: endTag, validTags: validTags, xmlToHtmlMapper) { allItems.append(obj) } return allItems } public func readObject(endTag:String? = nil, validTags:[String]? = nil, _ mapper:(_ name:String, _ attributes:[String:String], _ parser:XMLParser?) throws -> HTMLNode) throws -> HTMLNode? { let tag = try readToken() switch (tag) { case let .data(unicode): var result = "" result.unicodeScalars.append(contentsOf: unicode) return HTMLText(content: result) case .instruction: throw AppError("Unexpected Instruction") case let .tag_empty(name, attributes): if let validTags = validTags { if (validTags.firstIndex(of: name) == nil) { throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")") } } let mapped = try mapper(name, attributes, nil) return mapped case let .tag_end(name): if let tag = endTag, name != tag { throw AppError("Unexpected end tag: \(name) for element \(tag)") } return nil case let .tag_start(name, attributes): if let validTags = validTags { if (validTags.firstIndex(of: name) == nil) { throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")") } } return try mapper(name, attributes, self) case .none: if (endTag == nil) { return nil } else { throw AppError("Reached end of document before closing tag: \(endTag ?? "root")") } } } public func readObjectXml(endTag:String? = nil, validTags:[String]? = nil) throws -> XMLNode? { let tag = try readToken() switch (tag) { case let .data(unicode): var result = "" result.unicodeScalars.append(contentsOf: unicode) return XMLText(content: result) case .instruction: throw AppError("Unexpected Instruction") case let .tag_empty(name, attributes): if let validTags = validTags { if (validTags.firstIndex(of: name) == nil) { throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")") } } return try GenericXMLNode(name, attributes, nil) case let .tag_end(name): if let tag = endTag, name != tag { throw AppError("Unexpected end tag: \(name) for element \(tag)") } return nil case let .tag_start(name, attributes): if let validTags = validTags { if (validTags.firstIndex(of: name) == nil) { throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")") } } return try GenericXMLNode(name, attributes, self) case .none: if (endTag == nil) { return nil } else { throw AppError("Reached end of document before closing tag: \(endTag ?? "root")") } } } public func readDataAsString(endTag:String) throws -> String? { let tag = try readToken() switch (tag) { case let .data(unicode): var result = "" result.unicodeScalars.append(contentsOf: unicode) return result case .instruction: throw AppError("Unexpected Instruction") case let .tag_empty(name, attributes): throw AppError("Unexpected empty tag: \(name)") case let .tag_end(name): if (name != endTag) { throw AppError("Unexpected end tag: \(name)") } return nil case let .tag_start(name, attributes): throw AppError("Unexpected tag start: \(name)") case .none: throw AppError("Reached end of document before closing tag: \(endTag)") } } }