@@ -44,6 +44,7 @@ public static Result<TextAtom> Build(string latex) {
44
44
StringBuilder mathLaTeX = null ;
45
45
bool backslashEscape = false ;
46
46
bool afterCommand = false ; //ignore spaces after command
47
+ bool afterNewline = false ;
47
48
int dollarCount = 0 ;
48
49
var globalAtoms = new TextAtomListBuilder ( ) ;
49
50
var breaker = new CustomBreaker { BreakNumberAfterText = true , ThrowIfCharOutOfRange = false } ;
@@ -93,27 +94,34 @@ Result CheckDollarCount(TextAtomListBuilder atoms) {
93
94
}
94
95
return Ok ( ) ;
95
96
}
96
- Result < int > BuildBreakList ( TextAtomListBuilder atoms , int i , bool oneCharOnly ) {
97
+ Result < int > BuildBreakList ( TextAtomListBuilder atoms , int i , bool oneCharOnly , char stopChar ) {
98
+ void ParagraphBreak ( ) {
99
+ atoms . Break ( 3 ) ;
100
+ #warning Should the newline and space occupy the same range?
101
+ atoms . TextLength -= 3 ;
102
+ atoms . Add ( Space . ParagraphIndent , 3 ) ;
103
+ }
97
104
for ( ; i < breakList . Count ; i ++ ) {
98
- ( int startAt , int endAt , char endingChar , WordKind wordKind ) ObtainRange ( int index ) =>
99
- ( index == 0 ? 0 : breakList [ index - 1 ] . breakAt , breakList [ index ] . breakAt , latex [ breakList [ index ] . breakAt - 1 ] , breakList [ index ] . wordKind ) ;
100
- var ( startAt , endAt , endingChar , wordKind ) = ObtainRange ( i ) ;
105
+ ( int startAt , int endAt , string textSection , WordKind wordKind ) ObtainRange ( int index ) {
106
+ var ( start , end ) = ( index == 0 ? 0 : breakList [ index - 1 ] . breakAt , breakList [ index ] . breakAt ) ;
107
+ return ( start , end , latex . Substring ( start , end - start ) , breakList [ index ] . wordKind ) ;
108
+ }
109
+ var ( startAt , endAt , textSection , wordKind ) = ObtainRange ( i ) ;
101
110
bool SetNextRange ( ) {
102
111
bool success = ++ i < breakList . Count ;
103
- if ( success ) ( startAt , endAt , endingChar , wordKind ) = ObtainRange ( i ) ;
112
+ if ( success ) ( startAt , endAt , textSection , wordKind ) = ObtainRange ( i ) ;
104
113
return success ;
105
114
}
106
115
Result < TextAtom > ReadArgumentAtom ( ) {
107
116
backslashEscape = false ;
108
117
var argAtoms = new TextAtomListBuilder ( ) ;
109
- i ++ ;
110
- BuildBreakList ( argAtoms , i , true ) . Bind ( index => i = index ) ;
118
+ if ( BuildBreakList ( argAtoms , ++ i , true , '\0 ' ) . Bind ( index => i = index ) . Error is string error ) return error ;
111
119
return argAtoms . Build ( ) ;
112
120
}
113
121
Result < string > ReadArgumentString ( ) {
114
122
afterCommand = false ;
115
123
if ( ! SetNextRange ( ) ) return Err ( "Missing argument" ) ;
116
- if ( endingChar != '{' ) return Err ( "Missing {" ) ;
124
+ if ( textSection != "{" ) return Err ( "Missing {" ) ;
117
125
int endingIndex = - 1 ;
118
126
//startAt + 1 to not start at the { we started at
119
127
bool isEscape = false ;
@@ -131,9 +139,10 @@ Result<string> ReadArgumentString() {
131
139
_ = SetNextRange ( ) ; //this never fails because the above check
132
140
return Ok ( resultText ) ;
133
141
}
134
- #warning
135
- //atoms.TextLength = startAt;
136
- if (endingChar == '$') {
142
+
143
+ atoms . TextLength = startAt ;
144
+ if ( stopChar > 0 && textSection [ 0 ] == stopChar ) return Ok ( i ) ;
145
+ if ( textSection == "$" ) {
137
146
if ( backslashEscape )
138
147
if ( displayMath != null ) mathLaTeX . Append ( @"\$" ) ;
139
148
else atoms . Add ( "$" ) ;
@@ -145,48 +154,71 @@ Result<string> ReadArgumentString() {
145
154
} else {
146
155
{ if ( CheckDollarCount ( atoms ) . Error is string error ) return error ; }
147
156
148
- //Normal unescaped text section, could be in display/inline math mode
149
157
if ( ! backslashEscape ) {
150
- var textSection = latex . Substring ( startAt , endAt - startAt ) ;
151
- switch ( endingChar ) {
152
- case '$' :
153
- throw new InvalidCodePathException ( "The $ case should have been accounted for." ) ;
154
- case '\\ ' :
155
- backslashEscape = true ;
156
- continue ;
157
- case var sp when wordKind == WordKind . Whitespace || wordKind == WordKind . NewLine :
158
- //Collpase spaces
159
- //Consume newlines after commands
160
- if ( displayMath == null )
158
+ //Unescaped text section, inside display/inline math mode
159
+ if ( displayMath != null )
160
+ switch ( textSection ) {
161
+ case "$" :
162
+ throw new InvalidCodePathException ( "The $ case should have been accounted for." ) ;
163
+ case "\\ " :
164
+ backslashEscape = true ;
165
+ continue ;
166
+ default :
167
+ mathLaTeX . Append ( textSection ) ;
168
+ break ;
169
+ }
170
+ //Unescaped text section, not inside display/inline math mode
171
+ else switch ( textSection ) {
172
+ case "$" :
173
+ throw new InvalidCodePathException ( "The $ case should have been accounted for." ) ;
174
+ case "\\ " :
175
+ backslashEscape = true ;
176
+ continue ;
177
+ case "{" :
178
+ if ( BuildBreakList ( atoms , ++ i , false , '}' ) . Bind ( index => i = index ) . Error is string error ) return error ;
179
+ break ;
180
+ case "}" :
181
+ return "Unexpected }, unbalanced braces" ;
182
+ case var _ when wordKind == WordKind . NewLine :
183
+ //Consume newlines after commands
184
+ //Double newline == paragraph break
185
+ if ( afterNewline ) {
186
+ ParagraphBreak ( ) ;
187
+ afterNewline = false ;
188
+ break ;
189
+ } else {
190
+ atoms . Add ( ) ;
191
+ afterNewline = true ;
192
+ continue ;
193
+ }
194
+ case var _ when wordKind == WordKind . Whitespace :
195
+ //Collpase spaces
161
196
if ( afterCommand ) continue ;
162
197
else atoms . Add ( ) ;
163
- else mathLaTeX . Append ( textSection ) ;
164
- break ;
165
- case var punc when displayMath == null && wordKind == WordKind . Punc && atoms . Last is TextAtom . Text t :
166
- //Append punctuation to text
167
- t . Append ( textSection ) ;
168
- break ;
169
- default : //Just ordinary text
170
- if ( displayMath == null )
198
+ break ;
199
+ case var punc when displayMath == null && wordKind == WordKind . Punc && atoms . Last is TextAtom . Text t :
200
+ //Append punctuation to text
201
+ t . Append ( textSection ) ;
202
+ break ;
203
+ default : //Just ordinary text
171
204
if ( oneCharOnly ) {
172
- if ( breakList [ i ] . breakAt < latex . Length ) { //don't re-read if we reached the end
173
- breakList [ i ] = new BreakAtInfo ( breakList [ i ] . breakAt + 1 , breakList [ i ] . wordKind ) ;
205
+ if ( startAt + 1 < endAt ) { //Only re-read if current break span is more than 1 long
174
206
i -- ;
207
+ breakList [ i ] = new BreakAtInfo ( breakList [ i ] . breakAt + 1 , breakList [ i ] . wordKind ) ;
175
208
}
176
209
atoms . Add ( textSection [ 0 ] . ToString ( ) ) ;
177
210
} else atoms . Add ( textSection ) ;
178
- else mathLaTeX . Append ( textSection ) ;
179
- break ;
180
- }
211
+ break ;
212
+ }
181
213
afterCommand = false ;
182
214
}
183
215
184
216
//Escaped text section but in inline/display math mode
185
217
else if ( displayMath != null ) {
186
- switch ( endingChar ) {
187
- case '$' :
218
+ switch ( textSection ) {
219
+ case "$" :
188
220
throw new InvalidCodePathException ( "The $ case should have been accounted for." ) ;
189
- case '(' :
221
+ case "(" :
190
222
switch ( displayMath ) {
191
223
case true :
192
224
return "Cannot open inline math mode in display math mode" ;
@@ -195,7 +227,7 @@ Result<string> ReadArgumentString() {
195
227
default :
196
228
throw new InvalidCodePathException ( "displayMath is null. This switch should not be hit." ) ;
197
229
}
198
- case ')' :
230
+ case ")" :
199
231
switch ( displayMath ) {
200
232
case true :
201
233
return "Cannot close inline math mode in display math mode" ;
@@ -209,7 +241,7 @@ Result<string> ReadArgumentString() {
209
241
throw new InvalidCodePathException ( "displayMath is null. This switch should not be hit." ) ;
210
242
}
211
243
break ;
212
- case '[' :
244
+ case "[" :
213
245
switch ( displayMath ) {
214
246
case true :
215
247
return "Cannot open display math mode in display math mode" ;
@@ -218,7 +250,7 @@ Result<string> ReadArgumentString() {
218
250
default :
219
251
throw new InvalidCodePathException ( "displayMath is null. This switch should not be hit." ) ;
220
252
}
221
- case ']' :
253
+ case "]" :
222
254
switch ( displayMath ) {
223
255
case true :
224
256
if ( atoms . Add ( mathLaTeX . ToString ( ) , true ) . Error is string mathError )
@@ -233,15 +265,14 @@ Result<string> ReadArgumentString() {
233
265
}
234
266
break ;
235
267
default :
236
- mathLaTeX . Append ( $@ "\{ latex . Substring ( startAt , endAt - startAt ) } ") ;
268
+ mathLaTeX . Append ( $@ "\{ textSection } ") ;
237
269
break ;
238
270
}
239
271
backslashEscape = false ;
240
272
} else {
241
-
242
273
//Escaped text section and not in inline/display math mode
243
274
afterCommand = true ;
244
- switch ( latex . Substring ( startAt , endAt - startAt ) ) {
275
+ switch ( textSection ) {
245
276
case "(" :
246
277
mathLaTeX = new StringBuilder ( ) ;
247
278
displayMath = false ;
@@ -264,10 +295,7 @@ Result<string> ReadArgumentString() {
264
295
atoms . Add ( ) ;
265
296
break ;
266
297
case "par" :
267
- atoms . Break ( 3 ) ;
268
- #warning Should the newline and space occupy the same range?
269
- atoms . TextLength -= 3 ;
270
- atoms . Add ( Space . ParagraphIndent , 3 ) ;
298
+ ParagraphBreak ( ) ;
271
299
break ;
272
300
case "fontsize" : {
273
301
if ( ReadArgumentString ( ) . Bind ( fontSize =>
@@ -340,12 +368,14 @@ Result<string> ReadArgumentString() {
340
368
backslashEscape = false ;
341
369
}
342
370
}
371
+ afterNewline = false ;
343
372
if ( oneCharOnly ) return Ok ( i ) ;
344
373
}
345
374
if ( backslashEscape ) return @"Unknown command \" ;
375
+ if ( stopChar > 0 ) return $@ "Expected { stopChar } ";
346
376
return Ok ( i ) ;
347
377
}
348
- { if ( BuildBreakList ( globalAtoms , 0 , false ) . Error is string error ) return error ; }
378
+ { if ( BuildBreakList ( globalAtoms , 0 , false , ' \0 ' ) . Error is string error ) return error ; }
349
379
{ if ( CheckDollarCount ( globalAtoms ) . Error is string error ) return error ; }
350
380
if ( displayMath != null ) return "Math mode was not terminated" ;
351
381
return globalAtoms . Build ( ) ;
0 commit comments