-
Notifications
You must be signed in to change notification settings - Fork 0
/
AnsiStringParser.cs
216 lines (192 loc) · 7.36 KB
/
AnsiStringParser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//================================================================================================
// AnsiStringParser
//
// Parses a string or sequence of strings into an ordered list of IAnsiStringParserElement
// instances representing printable text strings, control codes, and escape sequences in the
// input data.
//================================================================================================
using System.Text;
using Microlithix.Text.Ansi.Element;
namespace Microlithix.Text.Ansi;
///----------------------------------------------------------------------------
/// <summary>
/// Parses strings containing ANSI escape code sequences into a
/// structured representation suitable for higher-level processing.
/// </summary>
///
/// <remarks>
/// The UTF-16 character strings to be parsed may contain printable text,
/// control codes, and ANSI escape sequences. They will be parsed into
/// <see href="../docs/Elements.md">elements</see> implementing the
/// <see cref="IAnsiStringParserElement"/> interface.
///
/// Note that this module implements a parser only, and not an interpreter.
/// The interpretation of the elements is domain-dependent and left to the
/// consuming application.
/// </remarks>
///----------------------------------------------------------------------------
public class AnsiStringParser {
///........................................................................
/// <summary>
/// Creates a new <see cref="AnsiStringParser"/> instance with
/// default settings.
/// </summary>
///........................................................................
public AnsiStringParser() {
parser = new(GatherElements);
}
///........................................................................
/// <summary>
/// Creates a new <see cref="AnsiStringParser"/> instance with
/// specified settings.
/// </summary>
///
/// <param name="settings">
/// A <see cref="AnsiParserSettings"/> record for configuring the
/// behavior of the parser.
/// </param>
///........................................................................
public AnsiStringParser(AnsiParserSettings settings) {
parser = new(settings, GatherElements);
}
///........................................................................
/// <summary>
/// Resets the parser to its initial state.
/// </summary>
///
/// <remarks>
/// Normally the parser remembers its state so that repeated calls to
/// <see cref="Parse"/> will be interpreted as a single continuous
/// sequence of characters. However, it may sometimes be necessary or
/// useful to reset the state, such as when clearing the display.
/// </remarks>
///........................................................................
public void Reset() {
parser.Reset();
ClearState();
ClearOutput();
}
///........................................................................
/// <summary>
/// Parses a string into an ordered list of
/// <see href="../docs/Elements.md">elements</see>.
/// </summary>
///
/// <param name="text">
/// The UTF-16 string to be parsed.
/// </param>
///
/// <returns>
/// An ordered list of <see href="../docs/Elements.md">elements</see>
/// representing the printable text strings, control codes, and escape
/// sequences found in the string.
/// </returns>
///
/// <remarks>
/// The character will be parsed in the context of the character
/// stream already received by prior invocations of this method.
/// </remarks>
///........................................................................
public List<IAnsiStringParserElement> Parse(string text) {
ClearOutput();
foreach (char ch in text) parser.Parse(ch);
return Consolidate();
}
///........................................................................
/// <summary>
/// Converts a list of parsed <see href="../docs/Elements.md">elements</see>
/// into a string containing only the printable characters.
/// </summary>
///
/// <param name="elements">
/// A list holding the <see href="../docs/Elements.md">elements</see>
/// to convert.
/// </param>
///
/// <returns>
/// A string containing only the printable characters from the
/// input elements.
/// </returns>
///
/// <remarks>
/// All of the control codes and escape sequences in the list of elements
/// are ignored. Note that the DEL character (<c>0x7f</c>) is considered
/// to be printable, so it will be retained in the returned string.
/// </remarks>
/// ........................................................................
public static string PrintableString(List<IAnsiStringParserElement> elements) {
StringBuilder builder = new();
foreach (IAnsiStringParserElement element in elements) {
if (element is AnsiPrintableString printableString) {
builder.Append(printableString.Text);
}
}
return builder.ToString();
}
//-----------------------
// Non-public properties
//-----------------------
private AnsiStreamParser parser;
private List<IAnsiStreamParserElement> streamElements = new();
private string text = "";
private bool printMode = false;
//--------------------
// Non-public methods
//--------------------
private void GatherElements(IAnsiStreamParserElement element) {
// Gather the elements as they are produced by the
// AnsiStreamParser instance and assemble them into a list.
streamElements.Add(element);
}
private void ClearOutput() {
streamElements.Clear();
}
private void ClearState() {
text = "";
printMode = false;
}
private List<IAnsiStringParserElement> Consolidate() {
List<IAnsiStringParserElement> parsedElements = new();
foreach (IAnsiStreamParserElement element in streamElements) {
// If we have accumulated a print string and the next element
// is not an AnsiPrintElement, then add a print element.
if (printMode && element is not AnsiPrintableChar) {
printMode = false;
parsedElements.Add(new AnsiPrintableString(text));
}
// Then process the next element in the queue.
IAnsiStringParserElement? consolidatedElement = ConsolidateElement(element);
if (consolidatedElement is not null) parsedElements.Add(consolidatedElement);
}
// We have processed all of the elements.
if (printMode) {
// Terminate an accumulated print string and
// add it to the list of generated elements.
printMode = false;
parsedElements.Add(new AnsiPrintableString(text));
}
return parsedElements;
}
private IAnsiStringParserElement? ConsolidateElement(IAnsiStreamParserElement element) {
// Consolidates IAnsiStreamParserElement instances into IAnsiStringParserElement
// instances. If the element is already an IAnsiStringParserElement instance,
// then it is returned as-is.
if (element is IAnsiStringParserElement) return (IAnsiStringParserElement)element;
switch (element) {
case AnsiPrintableChar e:
if (!printMode) text = "";
printMode = true;
text += e.Character;
return null;
case AnsiControlStringInitiator _:
text = "";
return null;
case AnsiControlStringChar e:
text += e.Character;
return null;
case AnsiControlStringTerminator e:
return new AnsiControlString(e.Type, text);
}
throw new ArgumentOutOfRangeException(nameof(element), "Unknown type");
}
}