@@ -8,6 +8,12 @@
9import Foundation
9import Foundation
11+typealias Lines = [String]
12+typealias Element = [String]
13+let commentChars = CharacterSet.init(charactersIn: "#/")
14+let styleCharecters = CharacterSet.init(charactersIn: "_*")
11enum BracketStyle {
17enum BracketStyle {
12 case square
18 case square
13 case round
19 case round
@@ -61,11 +67,11 @@ default:
61 if let surl = args.popFirst() {
67 if let surl = args.popFirst() {
62 outputUrl = URL(fileURLWithPath: surl)
68 outputUrl = URL(fileURLWithPath: surl)
64− case "-s":
70+ case "-b:s":
65 style = .square
71 style = .square
66− case "-r":
72+ case "-b:r":
67 style = .round
73 style = .round
68− case "-l":
74+ case "-b:l":
69 style = .line
75 style = .line
@@ -81,7 +87,6 @@ enum ElementStyle: String {
81 case italic = "italic"
87 case italic = "italic"
82 case boldItalic = "bold-italic"
88 case boldItalic = "bold-italic"
84−let styleCharecters = CharacterSet.init(charactersIn: "_*")
85extension String.SubSequence {
90extension String.SubSequence {
86 var style: ElementStyle {
91 var style: ElementStyle {
87 let num = self.prefix(while: {styleCharecters.contains($0.unicodeScalars.first!)}).count
92 let num = self.prefix(while: {styleCharecters.contains($0.unicodeScalars.first!)}).count
@@ -100,79 +105,209 @@ extension String.SubSequence {
100 return self.trimmingCharacters(in: styleCharecters)
105 return self.trimmingCharacters(in: styleCharecters)
103−func process(mth: String) -> String {
109+func process(element: Element) -> String {
104− let whitespace = CharacterSet.whitespaces
110+ var out = ""
112+ var inMatrix = false
106 var seperated = false
114 var seperated = false
107− var afterSeparator: [String.SubSequence] = []
115+ var afterSeparator: [Substring] = []
109− var out = "<math>"
117+ var small = false
110− let lines = mth.split(separator: "\n", omittingEmptySubsequences: false)
118+
111− .map {$0.trimmingCharacters(in: .whitespaces)}
119+ let arrows = CharacterSet.init(charactersIn: "⟺⟷⟼⟻⟶⟹⟵⟸")
112− var insideMatrix = false
120+
121+ let nEl = element.map { (el) -> String in
123+ case let l where l.last == ">" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "="}).count > 0:
126+ case let l where l.last == ">" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
129+ case let l where l.last == ">" && l.first == "|" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
132+ case let l where l.last == "|" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
135+ case let l where l.last == ">" && l.dropLast().filter({$0 == "-"}).count > 0:
138+ case let l where l.last == ">" && l.dropLast().filter({$0 == "="}).count > 0:
141+ case let l where l.first == "<" && l.dropFirst().filter({$0 == "-"}).count > 0:
144+ case let l where l.first == "<" && l.dropFirst().filter({$0 == "="}).count > 0:
152+ func process(lineElement: Substring, withAttributes attributes: String = "", style: ElementStyle = .none) -> Substring {
153+ func extractNextElement(from line: Substring) -> (element: Substring, left: Substring) {
154+ if line.first != "(" {
155+ var i = line.startIndex
156+ var el: Substring = ""
157+ while i < line.endIndex && CharacterSet.alphanumerics.contains(line[i].unicodeScalars.first!) {
159+ i = line.index(after: i)
161+ return (el, line[i...])
164+ var i = line.startIndex
165+ var el: Substring = ""
166+ while i < line.endIndex {
175+ i = line.index(after: i)
182+ print("Mismatched brackets")
185+ return (el.dropFirst(), line[i...])
188+ var i = lineElement.startIndex
189+ while i < lineElement.endIndex {
190+ switch lineElement[i] {
192+ let parts = extractNextElement(from: lineElement[lineElement.index(after: i)...])
193+ return "<mo mathvariant=\"\(style.rawValue)\" \(attributes)>\(lineElement[..<i].filter({$0 != "\\"}))</mo>" + "<msup><mo></mo><mn>\(parts.element)</mn></msup>" + process(lineElement: parts.left, withAttributes: attributes, style: style)
195+ let parts = extractNextElement(from: lineElement[lineElement.index(after: i)...])
196+ return "<mo mathvariant=\"\(style.rawValue)\" \(attributes)>\(lineElement[..<i].filter({$0 != "\\"}))</mo>" + "<mover accent=\"true\"><mrow><mo>\(parts.element)</mo></mrow><mo>_</mo></mover>" + process(lineElement: parts.left, withAttributes: attributes, style: style)
198+ i = lineElement.index(after: i)
200+ let parts = extractNextElement(from: lineElement[lineElement.index(after: i)...])
201+ return "<mo mathvariant=\"\(style.rawValue)\" \(attributes)>\(lineElement[..<i].filter({$0 != "\\"}))</mo>" + "<msub><mo></mo><mn>\(parts.element)</mn></msub>" + process(lineElement: parts.left, withAttributes: attributes, style: style)
205+ i = lineElement.index(after: i)
207+ return Substring("<mo mathvariant=\"\(style.rawValue)\" \(attributes)>\(lineElement.filter({$0 != "\\"}))</mo>")
113− func pureProcess(line: Substring) {
209+ func process(line: Substring) {
114 out.append("<mtr>")
210 out.append("<mtr>")
115 for element in line.split(
211 for element in line.split(
116− whereSeparator: {c in whitespace.contains(c.unicodeScalars.first!)}) {
212+ whereSeparator: {c in CharacterSet.whitespaces.contains(c.unicodeScalars.first!)}) {
117 switch element {
213 switch element {
118 case "_":
214 case "_":
119 out.append("<mtd><mspace height=\"0.8em\"/></mtd>")
215 out.append("<mtd><mspace height=\"0.8em\"/></mtd>")
121 let eStyle = element.style
217 let eStyle = element.style
122− out.append("<mtd><mn mathvariant=\"\(eStyle.rawValue)\" height=\"0.8em\">\(element.trimmingStyleCharecters())</mn></mtd>")
218+ out.append("<mtd>\(process(lineElement: Substring(element.trimmingStyleCharecters()), withAttributes: "height=\"0.8em\"", style: eStyle))</mtd>")
125 out.append("</mtr>")
221 out.append("</mtr>")
127− func checkInsideMatrix() {
130− out.append("</mtable><mo>|</mo><mtable>")
131− for line in afterSeparator {
132− pureProcess(line: line)
135− out.append("</mtable><mo>\(style.closing)</mo></mrow>")
136− insideMatrix = false
141− func process(line: String.SubSequence) {
223+ func process(comment: String) {
142− switch line {
224+ if comment.hasPrefix("#") {
143− case "":
225+ out.append("<!-- \(comment.trimmingCharacters(in: commentChars).trimmingCharacters(in: .whitespaces)) -->")
147− out.append("<mrow><mo>\(style.opening)</mo><mtable>")
150− pureProcess(line: line)
154− for line in lines {
229+ for line in nEl {
155− if !insideMatrix {
230+ if inMatrix {
156− if lines.first?.contains("|") == true {
231+ if first && line.contains("|") {
157 seperated = true
232 seperated = true
160− if line == "" {
235+ case "]":
237+ out.append("</mtable><mo stretchy=\"true\">|</mo><mtable>")
238+ for line in afterSeparator {
161− process(line: Substring(line))
239+ process(line: line)
242+ out.append("</mtable><mo stretchy=\"true\">\(style.closing)</mo></mrow></mtd></mtr>")
162− } else if seperated {
247+ if seperated {
163− let split = line.split(separator: "|", maxSplits: 1, omittingEmptySubsequences: false)
248+ let split = line.split(separator: "|", maxSplits: 1, omittingEmptySubsequences: false)
164− if split.count != 2 {
249+ if split.count != 2 {
165− print("Inconsistent syntax")
250+ print("Inconsistent syntax. The `|` seperator isn't consistent within a matrix.")
253+ process(line: split[0])
254+ afterSeparator.append(split[1])
256+ process(line: Substring(line))
168− process(line: split[0])
169− afterSeparator.append(split[1])
171− process(line: Substring(line))
260+ switch line {
261+ case let l where l.hasPrefix("//"):
263+ case let l where l.hasPrefix("#"):
264+ out.append("</mtable></math>\n\n")
266+ out.append("\n\n<math><mtable>")
267+ break // TODO: Figure out what to do
269+ out.append("<mtr><mtd><mrow><mo stretchy=\"true\">\(style.opening)</mo><mtable>")
275+ out.append("</mtable><mtable>")
279+ case let l where arrows.contains(l.first!.unicodeScalars.first!):
280+ out.append("<mtr><mtd><mo>\(line)</mo></mtd></mtr>")
282+ out.append("<mtr><mtd>\(small ? "<mstyle scriptlevel=\"1\">" : "")\(process(lineElement: Substring(line)))\(small ? "</mstyle>" : "")</mtd></mtr>")
290+func process(mth: String) -> String {
292+ .split(separator: "\n", omittingEmptySubsequences: false)
293+ .map({$0.trimmingCharacters(in: .whitespaces)})
295+ var i = l.startIndex
296+ for u in 0..<max(0, l.count - 1) {
297+ if l[i] == "/" && l[l.index(after: i)] == "/" {
298+ return String(l.dropLast(l.count - u))
300+ i = l.index(after: i)
304+ .split(separator: "")
305+ var out = "<math><mtable>"
306+ for el in elements {
307+ out.append(process(element: Array(el)))
308+ out.append("</mtable><mtable>")
175− out.append("</math>")
310+ out.append("</mtable></math>")
176 return out
311 return out