// // Roman.swift // RegexInSwift : use regex to parse a string with a Roman number and returns its decimal value // // Created by Guy Lapalme on 2024-07-26. // let romanRE = #/ (M{1,3})? # thousands (C(?:M|D|C{,2})|DC{,3})? # hundreds (X(?:C|L|X{,2})|LX{,3})? # tens (I(?:X|V|I{,2})|VI{,3})? # units /# let romanVals = [ "C": 100, "CC": 200, "CCC": 300, "CD": 400, "CM": 900, "D": 500, "DC": 600, "DCC": 700, "DCCC": 800, "I": 1, "II": 2, "III": 3, "IV": 4, "IX": 9, "L": 50, "LX": 60, "LXX": 70, "LXXX": 80, "M": 1000, "MM": 2000, "MMM": 3000, "V": 5, "VI": 6, "VII": 7, "VIII": 8, "X": 10, "XC": 90, "XL": 40, "XX": 20, "XXX": 30 ] func parseRomanRE(_ s:String)->Int? { if let m = s.wholeMatch(of: romanRE){ var res = 0 let out = m.output if out.1 != nil {res += romanVals[String(out.1!)]!} if out.2 != nil {res += romanVals[String(out.2!)]!} if out.3 != nil {res += romanVals[String(out.3!)]!} if out.4 != nil {res += romanVals[String(out.4!)]!} return res } print("Invalid Roman number:\(s)") return nil } // //////////////////////////////////////////////// // using a RegexBuilder // import RegexBuilder func makeRB(_ i: String,_ v:String, _ x:String)->Capture<(Substring, Int)>{ return Capture { ChoiceOf { Regex {i ; ChoiceOf { x; v ; Repeat(...2) { i }}} Regex { v; Repeat(...3) { i }} } } transform:{str in romanVals[String(str)]!} } let romanRB = Regex { Optionally {Capture { Repeat(1...3){"M"} } transform: {str in 1000*str.count}} Optionally {makeRB("C","D","M")} Optionally {makeRB("X","L","C")} Optionally {makeRB("I","V","X")} } func parseRomanRB(_ s:String)->Int? { if let m = s.wholeMatch(of: romanRB){ let out = m.output return (out.1 ?? 0) + (out.2 ?? 0) + (out.3 ?? 0) + (out.4 ?? 0) } print("Invalid Roman number:\(s)") return nil } // a few tests, not at all exhaustive! func runRoman(){ print("** parseRomanRE tests") print(parseRomanRE("VII")!) print(parseRomanRE("XM") ?? 0) print(parseRomanRE("MMCDLXXVI")!) print("** parseRomanRB tests") print(parseRomanRB("VIII")!) print(parseRomanRB("DM") ?? 0) print(parseRomanRB("MMMDCCCLXVII")!) }