@@ -13,9 +13,9 @@ internal struct Parser {
1313 // MARK: Types
1414
1515 /// An equation component.
16- private struct EquationComponent {
17- let regex : String
18- let terminatingRegex : String
16+ private struct EquationComponent < T , U > {
17+ let regex : Regex < T >
18+ let terminatingRegex : Regex < U >
1919 let equation : Component . ComponentType
2020 let supportsRecursion : Bool
2121 }
@@ -24,38 +24,46 @@ internal struct Parser {
2424
2525 /// An inline equation component.
2626 private static let inline = EquationComponent (
27- regex: #" \$(.|\s)*?\$" # ,
28- terminatingRegex: #"\$" # ,
27+ regex: #/ \$(.|\s)*?\$/ # ,
28+ terminatingRegex: #/\$/ # ,
2929 equation: . inlineEquation,
3030 supportsRecursion: false )
3131
3232 /// An TeX-style block equation component.
3333 private static let tex = EquationComponent (
34- regex: #" \$\$(.|\s)*?\$\$" # ,
35- terminatingRegex: #" \$\$" # ,
34+ regex: #/ \$\$(.|\s)*?\$\$/ # ,
35+ terminatingRegex: #/ \$\$/ # ,
3636 equation: . texEquation,
3737 supportsRecursion: false )
3838
3939 /// A block equation.
4040 private static let block = EquationComponent (
41- regex: #" \\\[(.|\s)*\\\]" # ,
42- terminatingRegex: #" \\\]" # ,
41+ regex: #/ \\\[(.|\s)*? \\\]/ # ,
42+ terminatingRegex: #/ \\\]/ # ,
4343 equation: . blockEquation,
4444 supportsRecursion: false )
4545
4646 /// A named equation component.
4747 private static let named = EquationComponent (
48- regex: #" \\begin{equation}(.|\s)*\\end{equation}" # ,
49- terminatingRegex: #" \\end{equation}" # ,
48+ regex: #/ \\begin{equation}(.|\s)*\\end{equation}/ # ,
49+ terminatingRegex: #/ \\end{equation}/ # ,
5050 equation: . namedEquation,
5151 supportsRecursion: true )
5252
53+ /// A named no number equation component.
54+ private static let namedNoNumber = EquationComponent (
55+ regex: #/\\begin{equation\*}(.|\s)*\\end{equation\*}/# ,
56+ terminatingRegex: #/\\end{equation\*}/# ,
57+ equation: . namedNoNumberEquation,
58+ supportsRecursion: true )
59+
5360 // Order matters
5461 private static let allEquations : [ EquationComponent ] = [
5562 inline,
5663 tex,
5764 block,
58- named
65+ named,
66+ namedNoNumber
5967 ]
6068
6169}
@@ -95,72 +103,90 @@ extension Parser {
95103 /// - Parameter input: The input string.
96104 /// - Returns: An array of LaTeX components.
97105 static func parse( _ input: String ) -> [ Component ] {
98- let matches = allEquations. map ( { ( $0, input. range ( of: $0. regex, options: . regularExpression) ) } ) . filter { match in
99- guard let range = match. 1 else { return false }
100- let firstIndex = range. lowerBound
101- let lastIndex = range. upperBound
102- let componentIsEmpty = Component ( text: String ( input [ range] ) , type: match. 0 . equation) . text. isEmpty
103- let previousIndexLast = input. index ( lastIndex, offsetBy: - 1 - match. 0 . equation. rightTerminator. count)
106+ // Get the first match of each each equation type
107+ let matches = allEquations. map ( { ( $0, input. firstMatch ( of: $0. regex) ) } )
108+
109+ // Filter the matches
110+ let filteredMatches = matches. filter { match in
111+ // We only want matches with ranges
112+ guard let range = match. 1 ? . range else {
113+ return false
114+ }
115+
116+ print ( " found match \( match. 0 . equation) " )
104117
105- if firstIndex == input. startIndex {
106- return input [ previousIndexLast] != " \\ " && !componentIsEmpty
118+ // Make sure the inner component is non-empty
119+ let text = Component ( text: String ( input [ range] ) , type: match. 0 . equation) . text
120+ print ( text)
121+ guard !text. isEmpty else {
122+ return false
107123 }
108124
109- let previousIndexFirst = input. index ( before: firstIndex)
110- return input [ previousIndexFirst] != " \\ " && input [ previousIndexLast] != " \\ " && !componentIsEmpty
111- }
112-
113- let allStart = matches. map ( { $0. 1 ? . lowerBound } )
114- var equationRange : Range < String . Index > ?
115- var equation : Component . ComponentType = . text
116-
117- for match in matches {
118- guard isSmallest ( match. 1 ? . lowerBound, outOf: allStart) else {
119- continue
125+ // Make sure the starting terminator isn't escaped
126+ guard range. lowerBound >= input. startIndex else {
127+ return false
120128 }
121- guard let matchRange = match . 1 else {
122- continue
129+ if range . lowerBound > input . startIndex , input [ input . index ( before : range . lowerBound ) ] == " \\ " {
130+ return false
123131 }
132+ print ( " leading not escaped " )
124133
125- if match. 0 . supportsRecursion {
126- let terminatingMatches = input. ranges ( of: match. 0 . terminatingRegex) . filter { range in
127- let index = range. lowerBound
128- if index == input. startIndex { return true }
129- let previousIndex = input. index ( before: index)
130- return input [ previousIndex] != " \\ "
131- }
132- if let lastMatch = terminatingMatches. last {
133- equationRange = matchRange. lowerBound ..< lastMatch. upperBound
134- }
134+ // Make sure the ending terminator isn't escaped
135+ let endingTerminatorStartIndex = input. index ( range. upperBound, offsetBy: - match. 0 . equation. rightTerminator. count)
136+ guard endingTerminatorStartIndex >= input. startIndex else {
137+ return false
135138 }
136- else {
137- equationRange = match . 1
139+ if endingTerminatorStartIndex > input . startIndex , input [ input . index ( before : endingTerminatorStartIndex ) ] == " \\ " {
140+ return false
138141 }
142+ print ( " trailing not escaped " )
139143
140- if equationRange != nil {
141- equation = match. 0 . equation
142- break
143- }
144+ // The component has content and isn't escaped
145+ return true
144146 }
145147
146- if let equationRange = equationRange {
147- let stringBeforeEquation = String ( input [ ..< equationRange. lowerBound] )
148- let equationString = String ( input [ equationRange] )
149- let remainingString = String ( input [ equationRange. upperBound... ] )
150- var components = [ Component] ( )
151- if !stringBeforeEquation. isEmpty {
152- components. append ( Component ( text: stringBeforeEquation, type: . text) )
148+ // Get the first matched equation
149+ guard let smallestMatch = filteredMatches. min ( by: { $0. 1 !. range. lowerBound < $1. 1 !. range. lowerBound } ) else {
150+ return input. isEmpty ? [ ] : [ Component ( text: input, type: . text) ]
151+ }
152+
153+ // If the equation supports recursion, then we'll need to find the last
154+ // match of its terminating regex component.
155+ let equationRange : Range < String . Index >
156+ if smallestMatch. 0 . supportsRecursion {
157+ var terminatingMatches = input. matches ( of: smallestMatch. 0 . terminatingRegex) . filter { match in
158+ // Make sure the terminator isn't escaped
159+ if match. range. lowerBound == input. startIndex { return true }
160+ let previousIndex = input. index ( before: match. range. lowerBound)
161+ return input [ previousIndex] != " \\ "
153162 }
154- components . append ( Component ( text : equationString , type : equation ) )
155- if remainingString . isEmpty {
156- return components
163+ terminatingMatches . sort ( by : { $0 . range . lowerBound < $1 . range . lowerBound } )
164+ if let lastMatch = terminatingMatches . last {
165+ equationRange = smallestMatch . 1 ! . range . lowerBound ..< lastMatch . range . upperBound
157166 }
158167 else {
159- return components + parse ( remainingString )
168+ equationRange = smallestMatch . 1 ! . range
160169 }
161170 }
162-
163- return input. isEmpty ? [ ] : [ Component ( text: input, type: . text) ]
171+ else {
172+ equationRange = smallestMatch. 1 !. range
173+ }
174+
175+ // We got our equation range, so lets return the components.
176+ let stringBeforeEquation = String ( input [ ..< equationRange. lowerBound] )
177+ let equationString = String ( input [ equationRange] )
178+ let remainingString = String ( input [ equationRange. upperBound... ] )
179+ var components = [ Component] ( )
180+ if !stringBeforeEquation. isEmpty {
181+ components. append ( Component ( text: stringBeforeEquation, type: . text) )
182+ }
183+ components. append ( Component ( text: equationString, type: smallestMatch. 0 . equation) )
184+ if remainingString. isEmpty {
185+ return components
186+ }
187+ else {
188+ return components + parse( remainingString)
189+ }
164190 }
165191
166192}
0 commit comments