on master help sign in
Mathdown/Mathdown/main.swift+210−27
since splitsnapshots
unifiedsplit
@@ -8,15 +8,24 @@
8
8
9import Foundation
9import Foundation
10
10
11+typealias Lines = [String]
12+typealias Element = [String]
13+let commentChars = CharacterSet.init(charactersIn: "#/")
14+let styleCharecters = CharacterSet.init(charactersIn: "_*")
15+
16+
11enum BracketStyle {
17enum BracketStyle {
12 case square
18 case square
13 case round
19 case round
20+ case line
14 var opening: Character {
21 var opening: Character {
15 switch self {
22 switch self {
16 case .round:
23 case .round:
17 return "("
24 return "("
18 case .square:
25 case .square:
19 return "["
26 return "["
27+ case .line:
28+ return "|"
20 }
29 }
21 }
30 }
22 var closing: Character {
31 var closing: Character {
@@ -25,6 +34,8 @@ enum BracketStyle {
25 return ")"
34 return ")"
26 case .square:
35 case .square:
27 return "]"
36 return "]"
37+ case .line:
38+ return "|"
28 }
39 }
29 }
40 }
30}
41}
@@ -56,10 +67,12 @@ default:
56 if let surl = args.popFirst() {
67 if let surl = args.popFirst() {
57 outputUrl = URL(fileURLWithPath: surl)
68 outputUrl = URL(fileURLWithPath: surl)
58 }
69 }
59− case "-s":
70+ case "-b:s":
60 style = .square
71 style = .square
61− case "-r":
72+ case "-b:r":
62 style = .round
73 style = .round
74+ case "-b:l":
75+ style = .line
63 default:
76 default:
64 return
77 return
65 }
78 }
@@ -74,7 +87,6 @@ enum ElementStyle: String {
74 case italic = "italic"
87 case italic = "italic"
75 case boldItalic = "bold-italic"
88 case boldItalic = "bold-italic"
76}
89}
77−let styleCharecters = CharacterSet.init(charactersIn: "_*")
78extension String.SubSequence {
90extension String.SubSequence {
79 var style: ElementStyle {
91 var style: ElementStyle {
80 let num = self.prefix(while: {styleCharecters.contains($0.unicodeScalars.first!)}).count
92 let num = self.prefix(while: {styleCharecters.contains($0.unicodeScalars.first!)}).count
@@ -93,38 +105,209 @@ extension String.SubSequence {
93 return self.trimmingCharacters(in: styleCharecters)
105 return self.trimmingCharacters(in: styleCharecters)
94 }
106 }
95}
107}
108+
96−func process(mth: String) -> String {
109+func process(element: Element) -> String {
97− let whitespace = CharacterSet.whitespaces
110+ var out = ""
98
111
99− var out = "<math>"
112+ var inMatrix = false
100− let lines = mth.split(separator: "\n", omittingEmptySubsequences: false)
113+ var first = false
101− .map {$0.trimmingCharacters(in: .whitespaces)}
114+ var seperated = false
102− var insideMatrix = false
115+ var afterSeparator: [Substring] = []
103− for line in lines {
116+
117+ var small = false
118+
119+ let arrows = CharacterSet.init(charactersIn: "⟺⟷⟼⟻⟶⟹⟵⟸")
120+
121+ let nEl = element.map { (el) -> String in
104− switch line {
122+ switch el {
105− case "":
123+ case let l where l.last == ">" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "="}).count > 0:
106− if insideMatrix {
124+ small = true
107− out.append("</mtable><mo>\(style.closing)</mo></mrow>")
125+ return "⟺"
108− insideMatrix = false
126+ case let l where l.last == ">" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
109− }
127+ small = true
128+ return "⟷"
129+ case let l where l.last == ">" && l.first == "|" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
130+ small = true
131+ return "⟼"
132+ case let l where l.last == "|" && l.first == "<" && l.dropLast().dropFirst().filter({$0 == "-"}).count > 0:
133+ small = true
134+ return "⟻"
135+ case let l where l.last == ">" && l.dropLast().filter({$0 == "-"}).count > 0:
136+ small = true
137+ return "⟶"
138+ case let l where l.last == ">" && l.dropLast().filter({$0 == "="}).count > 0:
139+ small = true
140+ return "⟹"
141+ case let l where l.first == "<" && l.dropFirst().filter({$0 == "-"}).count > 0:
142+ small = true
143+ return "⟵"
144+ case let l where l.first == "<" && l.dropFirst().filter({$0 == "="}).count > 0:
145+ small = true
146+ return "⟸"
110 default:
147 default:
111− if !insideMatrix {
148+ return el
112− out.append("<mrow><mo>\(style.opening)</mo><mtable>")
149+ }
113− insideMatrix = true
150+ }
151+
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!) {
158+ el.append(line[i])
159+ i = line.index(after: i)
160+ }
161+ return (el, line[i...])
162+ } else {
163+ var brackets = 0
164+ var i = line.startIndex
165+ var el: Substring = ""
166+ while i < line.endIndex {
167+ switch el[i] {
168+ case "(":
169+ brackets += 1
170+ case ")":
171+ brackets -= 1
172+ default:
173+ break
174+ }
175+ i = line.index(after: i)
176+ if brackets == 0 {
177+ break
178+ }
179+ el.append(el[i])
180+ }
181+ if brackets != 0 {
182+ print("Mismatched brackets")
183+ exit(0)
184+ }
185+ return (el.dropFirst(), line[i...])
114 }
186 }
187+ }
188+ var i = lineElement.startIndex
189+ while i < lineElement.endIndex {
190+ switch lineElement[i] {
191+ case "^":
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)
194+ case "!":
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)
197+ case "\\":
198+ i = lineElement.index(after: i)
199+ case "/":
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)
202+ default:
203+ break
204+ }
205+ i = lineElement.index(after: i)
206+ }
207+ return Substring("<mo mathvariant=\"\(style.rawValue)\" \(attributes)>\(lineElement.filter({$0 != "\\"}))</mo>")
208+ }
209+ func process(line: Substring) {
115− out.append("<mtr>")
210+ out.append("<mtr>")
116− for element in line.split(
211+ for element in line.split(
117− whereSeparator: {c in whitespace.contains(c.unicodeScalars.first!)}) {
212+ whereSeparator: {c in CharacterSet.whitespaces.contains(c.unicodeScalars.first!)}) {
213+ switch element {
214+ case "_":
215+ out.append("<mtd><mspace height=\"0.8em\"/></mtd>")
216+ default:
118 let eStyle = element.style
217 let eStyle = element.style
119− out.append("<mtd><mn mathvariant=\"\(eStyle.rawValue)\">\(element.trimmingStyleCharecters())</mn></mtd>")
218+ out.append("<mtd>\(process(lineElement: Substring(element.trimmingStyleCharecters()), withAttributes: "height=\"0.8em\"", style: eStyle))</mtd>")
219+ }
220+ }
221+ out.append("</mtr>")
222+ }
223+ func process(comment: String) {
224+ if comment.hasPrefix("#") {
225+ out.append("<!-- \(comment.trimmingCharacters(in: commentChars).trimmingCharacters(in: .whitespaces)) -->")
226+ }
227+ }
228+
229+ for line in nEl {
230+ if inMatrix {
231+ if first && line.contains("|") {
232+ seperated = true
233+ }
234+ switch line {
235+ case "]":
236+ if seperated {
237+ out.append("</mtable><mo stretchy=\"true\">|</mo><mtable>")
238+ for line in afterSeparator {
239+ process(line: line)
240+ }
241+ }
242+ out.append("</mtable><mo stretchy=\"true\">\(style.closing)</mo></mrow></mtd></mtr>")
243+ inMatrix = false
244+ seperated = false
245+ afterSeparator = []
246+ default:
247+ if seperated {
248+ let split = line.split(separator: "|", maxSplits: 1, omittingEmptySubsequences: false)
249+ if split.count != 2 {
250+ print("Inconsistent syntax. The `|` seperator isn't consistent within a matrix.")
251+ exit(0)
252+ }
253+ process(line: split[0])
254+ afterSeparator.append(split[1])
255+ } else {
256+ process(line: Substring(line))
257+ }
258+ }
259+ } else {
260+ switch line {
261+ case let l where l.hasPrefix("//"):
262+ break
263+ case let l where l.hasPrefix("#"):
264+ out.append("</mtable></math>\n\n")
265+ process(comment: l)
266+ out.append("\n\n<math><mtable>")
267+ break // TODO: Figure out what to do
268+ case "[":
269+ out.append("<mtr><mtd><mrow><mo stretchy=\"true\">\(style.opening)</mo><mtable>")
270+ inMatrix = true
271+ first = true
272+ continue
273+ case "":
274+ if !first {
275+ out.append("</mtable><mtable>")
276+ }
277+ first = true
278+ continue
279+ case let l where arrows.contains(l.first!.unicodeScalars.first!):
280+ out.append("<mtr><mtd><mo>\(line)</mo></mtd></mtr>")
281+ default:
282+ out.append("<mtr><mtd>\(small ? "<mstyle scriptlevel=\"1\">" : "")\(process(lineElement: Substring(line)))\(small ? "</mstyle>" : "")</mtd></mtr>")
120 }
283 }
121− out.append("</mtr>")
122 }
284 }
285+ first = false
123 }
286 }
124− if insideMatrix {
287+ return out
125− out.append("</mtable><mo>\(style.closing)</mo></mrow>")
288+}
289+
290+func process(mth: String) -> String {
291+ let elements = mth
292+ .split(separator: "\n", omittingEmptySubsequences: false)
293+ .map({$0.trimmingCharacters(in: .whitespaces)})
294+ .map({ l in
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))
299+ }
300+ i = l.index(after: i)
301+ }
302+ return l
303+ })
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>")
126 }
309 }
127− out.append("</math>")
310+ out.append("</mtable></math>")
128 return out
311 return out
129}
312}
130
313
README.md+45−7
@@ -3,10 +3,12 @@ A lightweight syntax for matrices.
3Outputs [MathML](https://en.wikipedia.org/wiki/MathML) format.
3Outputs [MathML](https://en.wikipedia.org/wiki/MathML) format.
4
4
5## Syntax
5## Syntax
6+```
7+[
6−```1 2 3
8+1 2 3
73 2 8
93 2 8
10+]
8```
11```
9−produces
10![](images/mat1.png)
12![](images/mat1.png)
11
13
12You can also use [Markdown](https://en.wikipedia.org/wiki/Markdown)-style text emphasis
14You can also use [Markdown](https://en.wikipedia.org/wiki/Markdown)-style text emphasis
@@ -15,17 +17,53 @@ You can also use [Markdown](https://en.wikipedia.org/wiki/Markdown)-style text e
15- `___<text>___` or `***<text>***` for ___italic and bold___
17- `___<text>___` or `***<text>***` for ___italic and bold___
16
18
17```
19```
20+[
18***1*** 2 __3__
21***1*** 2 __3__
19−3 _2_ 8
22+ 3 _2_ 8
23+]
20```
24```
21−produces
22![](images/mat2.png)
25![](images/mat2.png)
23
26
27+You can use `_` to leave an empty space in the matrix
28+
29+```
30+[
31+***1*** 2 __3__ | _
32+ _ _2_ 8 | 4
33+]
34+```
35+![](images/mat3.png)
36+
37+Newlines separate `elements` of your expressions.
38+`//` denotes the start of a normal comment. It is not present in the output.
39+
40+```
41+[
42+***1*** 2 __3__ | _ // This is a normal comment
43+_ _2_ 8 | 4
44+]
45+
46+1⊕=0
47+->
48+a=2b
49+
50+// This is also a normal comment
51+
52+[
53+1 2
54+5 3
55+]
56+```
57+![](images/mat4.png)
58+
59+`#` is used to make a `separator` comment. It separates your expressions into different `<math>` elements and is converted to an `xml` comment in the output document.
60+
24## Usage
61## Usage
25−`mathdown -r -i input.mth -o output.xml`
62+`mathdown -b:r -i input.mth -o output.xml`
26
63
27### Parameters
64### Parameters
28- `-i <file path>` Specifies the input path
65- `-i <file path>` Specifies the input path
29- `-o <file path>` Specifies the output pathfile
66- `-o <file path>` Specifies the output pathfile
30−- `-s` Uses square brackets
67+- `-b:s` Uses square brackets
31−- `-r` Uses round brackets
68+- `-b:r` Uses round brackets
69+- `-b:l` Uses line brackets
images/mat1.png

Binary file not shown.

images/mat2.png

Binary file not shown.

images/mat3.pngnew file

Binary file not shown.

images/mat4.pngnew file

Binary file not shown.