2using System.Collections.Generic;
7 internal class BlockParseState
9 private readonly List<Block> blocks;
10 private string[] rows;
11 private int[] positions;
12 private string currentRow;
14 private int blockIndex;
15 private readonly
int endBlock;
20 private bool lineBreakAfter;
21 private readonly
bool preserveCrLf;
22 private char lastChar = (char)0;
24 public BlockParseState(
string[] Rows,
int[] Positions,
int Start,
int End,
bool PreserveCrLf, List<Block> Blocks,
int BlockIndex,
28 this.blockIndex = BlockIndex;
29 this.endBlock = EndBlock;
31 this.positions = Positions;
32 this.current = this.start = Start;
34 this.currentRow = this.rows[this.current];
35 this.lineBreakAfter = this.currentRow.EndsWith(
" ");
37 this.len = this.currentRow.Length;
38 this.preserveCrLf = PreserveCrLf;
40 if (this.lineBreakAfter)
42 this.currentRow = this.currentRow.Substring(0, this.len - 2);
47 public List<Block> Blocks => this.blocks;
48 public string[] Rows => this.rows;
49 public int[] Positions => this.positions;
50 public int Start => this.start;
51 public int End => this.end;
52 public int Current => this.current;
53 public int BlockIndex => this.blockIndex;
54 public int EndBlock => this.endBlock;
55 public bool PreserveCrLf => this.preserveCrLf;
57 public char NextNonWhitespaceChar()
59 char ch = this.NextChar();
61 while (ch > (
char)0 && (ch <=
' ' || ch == 160))
67 public char NextNonWhitespaceCharSameRow()
69 char ch = this.NextCharSameRow();
71 while (ch > (
char)0 && (ch <=
' ' || ch == 160))
72 ch = this.NextCharSameRow();
77 public char NextCharSameRow()
79 if (this.pos >= this.len)
80 return this.lastChar = (char)0;
82 return this.lastChar = this.currentRow[this.pos++];
85 public char PeekNextNonWhitespaceCharSameRow(
bool SkipWhitespace)
87 char ch = this.PeekNextCharSameRow();
89 if (ch > 0 && (ch <=
' ' || ch == 160))
95 this.NextCharSameRow();
96 ch = this.PeekNextCharSameRow();
98 while (ch > 0 && (ch <=
' ' || ch == 160));
102 int PosBak = this.pos;
103 int LenBak = this.len;
104 int CurrentBak = this.current;
105 string CurrentRowBak = this.currentRow;
106 bool LineBreakAfterBak = this.lineBreakAfter;
110 this.NextCharSameRow();
111 ch = this.PeekNextCharSameRow();
113 while (ch > 0 && (ch <=
' ' || ch == 160));
117 this.current = CurrentBak;
118 this.currentRow = CurrentRowBak;
119 this.lineBreakAfter = LineBreakAfterBak;
126 public char PeekNextNonWhitespaceChar(
bool SkipWhitespace)
128 char ch = this.PeekNextChar();
130 if (ch > 0 && (ch <=
' ' || ch == 160))
137 ch = this.PeekNextChar();
139 while (ch > 0 && (ch <=
' ' || ch == 160));
143 int PosBak = this.pos;
144 int LenBak = this.len;
145 int CurrentBak = this.current;
146 string CurrentRowBak = this.currentRow;
147 bool LineBreakAfterBak = this.lineBreakAfter;
152 ch = this.PeekNextChar();
154 while (ch > 0 && (ch <=
' ' || ch == 160));
158 this.current = CurrentBak;
159 this.currentRow = CurrentRowBak;
160 this.lineBreakAfter = LineBreakAfterBak;
167 public char PeekNextCharSameRow()
169 if (this.pos >= this.len)
172 return this.currentRow[this.pos];
175 public char PeekNextChar()
177 int PosBak = this.pos;
178 int LenBak = this.len;
179 int CurrentBak = this.current;
180 string CurrentRowBak = this.currentRow;
181 bool LineBreakAfterBak = this.lineBreakAfter;
183 char ch = this.NextChar();
187 this.current = CurrentBak;
188 this.currentRow = CurrentRowBak;
189 this.lineBreakAfter = LineBreakAfterBak;
194 public char[] PeekNextChars(
int Len)
196 int PosBak = this.pos;
197 int LenBak = this.len;
198 int CurrentBak = this.current;
199 string CurrentRowBak = this.currentRow;
200 bool LineBreakAfterBak = this.lineBreakAfter;
201 char[] Result =
new char[Len];
204 for (i = 0; i < Len; i++)
205 Result[i] = this.NextChar();
209 this.current = CurrentBak;
210 this.currentRow = CurrentRowBak;
211 this.lineBreakAfter = LineBreakAfterBak;
216 private class StateBackup
221 public char LastChar;
222 public string CurrentRow;
223 public bool LineBreakAfter;
226 private LinkedList<StateBackup> backup =
null;
228 public void BackupState()
230 StateBackup Backup =
new StateBackup()
234 Current = this.current,
235 CurrentRow = this.currentRow,
236 LineBreakAfter = this.lineBreakAfter,
237 LastChar = this.lastChar
240 if (this.backup is
null)
241 this.backup =
new LinkedList<StateBackup>();
243 this.backup.AddFirst(Backup);
246 public void RestoreState()
248 if (this.backup is
null || this.backup.First is
null)
249 throw new Exception(
"No state backup to restore.");
251 StateBackup Backup = this.backup.First.Value;
252 this.backup.RemoveFirst();
254 this.pos = Backup.Pos;
255 this.len = Backup.Len;
256 this.current = Backup.Current;
257 this.currentRow = Backup.CurrentRow;
258 this.lineBreakAfter = Backup.LineBreakAfter;
259 this.lastChar = Backup.LastChar;
262 public void DiscardBackup()
264 if (this.backup is
null || this.backup.First is
null)
265 throw new Exception(
"No state backup to discard.");
267 this.backup.RemoveFirst();
270 public void SkipWhitespaceSameRow(
int MaxSpaces)
274 while ((((ch = this.PeekNextCharSameRow()) <=
' ' && ch > 0) || ch == 160) && MaxSpaces > 0)
276 this.NextCharSameRow();
278 if (ch ==
' ' || ch == 160)
285 public char NextChar()
289 if (this.pos >= this.len)
292 if (this.current > this.end)
301 if (this.lineBreakAfter)
303 else if (this.preserveCrLf)
308 this.currentRow = this.rows[this.current];
310 this.len = this.currentRow.Length;
311 this.lineBreakAfter = this.currentRow.EndsWith(
" ");
313 if (this.lineBreakAfter)
315 this.currentRow = this.currentRow.Substring(0, this.len - 2);
321 ch = this.currentRow[this.pos++];
323 return this.lastChar = ch;
326 public char NextCharRaw()
330 if (this.pos >= this.len)
333 if (this.current > this.end)
342 this.currentRow = this.rows[this.current];
344 this.len = this.currentRow.Length;
345 this.lineBreakAfter = this.currentRow.EndsWith(
" ");
347 if (this.lineBreakAfter)
349 this.currentRow = this.currentRow.Substring(0, this.len - 2);
358 ch = this.currentRow[this.pos++];
360 return this.lastChar = ch;
363 public bool CheckRestOfTermination(
char ch,
int Count)
365 if (this.pos + Count > this.len)
370 for (i = 0; i < Count; i++)
372 if (this.currentRow[this.pos + i] != ch)
381 public int CurrentPosition
385 if (this.current <= this.end)
386 return this.positions[this.current] + this.pos;
388 return this.positions[this.end] + this.rows[this.end].Length;
392 public string RestOfRow()
396 if (this.pos >= this.len)
397 Result =
string.Empty;
399 Result = this.currentRow.Substring(this.pos);
402 if (this.current > this.end)
409 this.currentRow = this.rows[this.current];
411 this.len = this.currentRow.Length;
412 this.lineBreakAfter = this.currentRow.EndsWith(
" ");
414 if (this.lineBreakAfter)
416 this.currentRow = this.currentRow.Substring(0, this.len - 2);
424 public bool IsFirstCharOnLine
428 int i = this.pos - 2;
434 if (i < 0 || i >= this.len)
437 while (i >= 0 && ((ch = this.currentRow[i]) <=
' ' || ch == 160))
448 return (this.pos >= this.len && this.current > this.end);
452 public string CurrentRow => this.currentRow;
454 public char LastCharacter => this.lastChar;
456 public string UntilToken(
string Token)
458 StringBuilder sb =
new StringBuilder();
460 int c = Token.Length;
465 ch = this.NextCharRaw();
469 if (!(this.blocks is
null) && this.blockIndex < this.endBlock)
471 Block Next = this.blocks[++this.blockIndex];
473 this.rows = Next.Rows;
474 this.positions = Next.Positions;
475 this.current = this.start = Next.Start;
477 this.currentRow = this.rows[this.current];
478 this.lineBreakAfter = this.currentRow.EndsWith(
" ");
480 this.len = this.currentRow.Length;
482 if (this.lineBreakAfter)
484 this.currentRow = this.currentRow.Substring(0, this.len - 2);
494 sb.Append(Token.Substring(0, i));
496 return sb.ToString();
503 sb.Append(Token.Substring(0, i));
513 sb.Append(Token.Substring(0, i));
519 else if (
char.ToUpper(ch) == Token[i])
523 return sb.ToString();
529 sb.Append(Token.Substring(0, i));